Сброс BitmapEncoder выдает исключение аргумента

Я пытаюсь изменить размер изображений в своем приложении UWP. В большинстве случаев добавленный код работает, но иногда await encoder.FlushAsync(); выдает ошибку ArgumentException.

Я зашел в MSDN (https://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.imaging.bitmapencoder.bitmaptransform.aspx), и они говорят мне (в «Примечаниях»):

Если вы попытаетесь масштабировать изображение, хранящееся в формате индексированных пикселей, с помощью члена BitmapTransform, FlushAsync завершится ошибкой с HRESULT WINCODEC_ERR_INVALIDPARAMETER . Вместо этого вы должны использовать GetPixelDataAsync для получения масштабированных данных пикселей, а затем использовать SetPixelData, чтобы установить их в кодировщике.

Я пытался это сделать, посмотрите две закомментированные строки (которые выглядят как-то неправильно из-за повторения). Во второй строке (где я пытаюсь SetPixelData) кодировщик вознаграждает меня buffer allocated not sufficient Exception.

var decoder = await BitmapDecoder.CreateAsync(streamToReadFrom.AsStream().AsRandomAccessStream());
if (decoder.OrientedPixelHeight > height ||
    decoder.OrientedPixelWidth > width)
{
    var resizedStream = new InMemoryRandomAccessStream();
    BitmapEncoder encoder = await BitmapEncoder.CreateForTranscodingAsync(resizedStream, decoder);

    encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
    encoder.BitmapTransform.ScaledHeight = newHeight;
    encoder.BitmapTransform.ScaledWidth = newWidth;

    //"buffer allocated not sufficient"
    // var pd = await decoder.GetPixelDataAsync(BitmapPixelFormat.Rgba16, BitmapAlphaMode.Ignore,
    //             encoder.BitmapTransform, ExifOrientationMode.IgnoreExifOrientation, ColorManagementMode.DoNotColorManage);
    // encoder.SetPixelData(BitmapPixelFormat.Rgba16, BitmapAlphaMode.Ignore,
    //             decoder.OrientedPixelWidth, decoder.OrientedPixelHeight, decoder.DpiX, decoder.DpiY, pd.DetachPixelData());

    // write out to the stream
    // might fail cause https://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.imaging.bitmapencoder.bitmaptransform.aspx
    await encoder.FlushAsync();

    // Read out resizedStream and return
}

Пример изображения, вызывающего эту проблему: http://www.spiegel.de/images/image-1028227-hppano-lqbn.jpg. Модульный тест здесь: https://github.com/famoser/OfflineMedia/blob/master/Famoser.OfflineMedia.UnitTests/Presentation/ImageResizeTest.cs

Как я могу избежать ArgumentException? Как узнать, что изображение находится в «формате индексированных пикселей», и как я могу изменить размер этого формата?


person Florian Moser    schedule 27.07.2016    source источник


Ответы (1)


Во второй строке (где я пытаюсь установить SetPixelData) кодировщик вознаграждает меня буфером, выделенным недостаточно Exception.

Это связано с тем, что когда вы SetPixelData, данные в пикселях не совпадают с GetPixelDataAsync. Вы можете, например, написать такой код:

if (file != null)
{
    BitmapImage bmp = new BitmapImage();
    using(var imageStream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
    {
        BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream);
        InMemoryRandomAccessStream pixelras = new InMemoryRandomAccessStream();
        BitmapEncoder pixelencoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, pixelras);
        BitmapTransform transform = new BitmapTransform();
        transform.InterpolationMode = BitmapInterpolationMode.Fant;
        transform.ScaledHeight = 400;
        transform.ScaledWidth = 400;
        var provider = await decoder.GetPixelDataAsync(BitmapPixelFormat.Bgra8, 
            BitmapAlphaMode.Ignore, 
            transform, 
            ExifOrientationMode.RespectExifOrientation, 
            ColorManagementMode.DoNotColorManage);
        var pixels = provider.DetachPixelData();
        pixelencoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, 400, 
            400, decoder.DpiX, decoder.DpiY, pixels);

        try
        {
            await pixelencoder.FlushAsync();
        }
        catch(Exception ex)
        {

        }
        bmp.SetSource(pixelras);
        img.Source = bmp;                  
    }
}

Как узнать, что изображение находится в «формате индексированных пикселей», и как я могу изменить размер этого формата?

Я не смог найти никакого эффективного способа обнаружить изображение в индексированном формате пикселей, но, поскольку сказано

Если вы попытаетесь масштабировать изображение, хранящееся в формате индексированных пикселей, с помощью члена BitmapTransform, FlushAsync завершится ошибкой с HRESULT WINCODEC_ERR_INVALIDPARAMETER . Вместо этого вы должны использовать GetPixelDataAsync для получения масштабированных данных пикселей, а затем использовать SetPixelData, чтобы установить их в кодировщике.

Это метод использования перехвата исключения и повторного использования SetPixelData, например:

if (file != null)
{
    BitmapImage bmp = new BitmapImage();
    using(var imageStream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
    {
        BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream);
        InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream();
        BitmapEncoder encoder = await BitmapEncoder.CreateForTranscodingAsync(ras, decoder);

        encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
        encoder.BitmapTransform.ScaledHeight = 400;
        encoder.BitmapTransform.ScaledWidth = 400;

        try
        {
            await encoder.FlushAsync();
            bmp.SetSource(ras);
        }
        catch (Exception ex)
        {
            if (ex.HResult.ToString() == "WINCODEC_ERR_INVALIDPARAMETER")
            {
                InMemoryRandomAccessStream pixelras = new InMemoryRandomAccessStream();
                BitmapEncoder pixelencoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, pixelras)
                BitmapTransform transform = new BitmapTransform();
                transform.InterpolationMode = BitmapInterpolationMode.Fant;
                transform.ScaledHeight = 400;
                transform.ScaledWidth = 400;
                var provider = await decoder.GetPixelDataAsync(BitmapPixelFormat.Bgra8,
                    BitmapAlphaMode.Ignore,
                    transform,
                    ExifOrientationMode.RespectExifOrientation,
                    ColorManagementMode.DoNotColorManage);
                var pixels = provider.DetachPixelData();
                pixelencoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, 400,
                    400, decoder.DpiX, decoder.DpiY, pixels);
                try
                {
                    await pixelencoder.FlushAsync();
                    bmp.SetSource(pixelras);
                }
                catch
                {

                }
            }
        }                    
        img.Source = bmp;                  
    }
}
person Grace Feng    schedule 28.07.2016
comment
Большое спасибо! Как только я смогу это проверить, я попробую и приму ваш ответ. - person Florian Moser; 28.07.2016
comment
К сожалению, это не решило проблему. GetPixelDataAsync и SetPixelData работают нормально, но pixelencoder.FlushAsync(); по-прежнему выдает ArgumentException. Я добавил ссылку на изображение, которое вызывает эту проблему, на мой вопрос. - person Florian Moser; 31.07.2016
comment
@FlorianMoser, извините за поздний ответ, я просто тестирую свой первый блок кода с предоставленным вами изображением, я не могу воспроизвести проблему? По вашей второй ссылке я обнаружил, что вы скачали это изображение, возможно ли это при загрузке? Я не проверял загрузку изображения, а затем преобразовывал его, я просто поместил ваше изображение в библиотеку изображений и использовал FileOpenPicker для выбора этого изображения, все работает нормально. Моя версия ОС 10586. - person Grace Feng; 02.08.2016
comment
Я разрабатываю сборку Technical Preview (14393.5) и сборку для минимальной и максимальной версии 10586. Проблема все еще возникает на моей машине, я добавил больше модульных тестов, чтобы подтвердить это. Я думаю, что единственная разница между вашим примером и моим кодом - это используемые потоки, возможно, в этом проблема, я попробую еще, как только у меня будет время. - person Florian Moser; 02.08.2016
comment
больше не могу воспроизвести этот баг - person Florian Moser; 29.03.2017