Почему я получаю ошибку нехватки памяти при использовании?

У меня есть следующий метод преобразования BitmapImage в System.Drawing.Bitmap:

public static Bitmap BitmapImageToBitmap(BitmapImage bitmapImage)
{
    Bitmap bitmap;

    using (var ms = new MemoryStream())
    {
        var encoder = new JpegBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(bitmapImage));
        encoder.Save(ms);

        bitmap = new Bitmap(ms);
    }

    return bitmap;
}

Всякий раз, когда я пытаюсь использовать возвращенный объект Bitmap, я получаю следующую ошибку:

Произошло OutOfMemoryException - Недостаточно памяти.

Однако всякий раз, когда я заменяю код следующим:

public static Bitmap BitmapImageToBitmap(BitmapImage bitmapImage)
{
    var ms = new MemoryStream();

    var encoder = new JpegBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(bitmapImage));

    encoder.Save(ms);

    return new Bitmap(ms);
}

Это прекрасно работает. Однако я почти уверен, что должен использовать using, поскольку объект MemoryStream реализует IDisposable. Что тут происходит?


person JMK    schedule 21.01.2013    source источник


Ответы (2)


Конструктор Bitmap Bitmap Constructor (Stream) утверждает, что

Вы должны держать поток открытым в течение всего времени существования Bitmap.

В вашем случае, когда вы используете оператор using, поток (будучи Disposable) автоматически удаляется, поэтому ваш объект Bitmap становится недействительным. Дело не в том, что вы выделяете слишком много памяти, а в том, что битовая карта указывает на то, чего больше не существует.

person Tigran    schedule 21.01.2013
comment
Какой рекомендуемый обходной путь? Создание временного растрового изображения, его копирование (с помощью new Bitmap(temp) или temp.Clone()), а затем удаление временного изображения? - person CodesInChaos; 21.01.2013
comment
@CodesInChaos: должно быть возможно (не совсем уверен в этом) избежать поддержки потока живым, если вы клонируете растровое изображение в другой экземпляр и только после уничтожения потока. - person Tigran; 21.01.2013

То, что @Tigran сказал, было абсолютно правильным, и я реализовал обходной путь @CodesInChaos следующим образом:

public static Bitmap BitmapImageToBitmap(BitmapImage bitmapImage)
{
    Bitmap bitmap;

    using (var ms = new MemoryStream())
    {
        var encoder = new JpegBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(bitmapImage));
        encoder.Save(ms);

        using (var localBitmap = new Bitmap(ms))
        {
            bitmap = localBitmap.Clone(new Rectangle(0, 0, localBitmap.Width, localBitmap.Height),
                   PixelFormat.Format32bppArgb);  
        }
    }

    return bitmap;
}
person JMK    schedule 21.01.2013
comment
не забудьте утилизировать localBitmap - person CodesInChaos; 21.01.2013
comment
Часть jpeg странная. Зачем сохранять в формате jpeg, а затем загружать снова? Единственный эффект от этого - снижение качества. Используйте формат без потерь, например .bmp в качестве формата сериализации. - person CodesInChaos; 21.01.2013
comment
Я работаю в WPF, и мне нужно использовать Aforge, поэтому я думаю, что мне нужно преобразовать в объект Bitmap, сделать то, что мне нужно, с Aforge, а затем преобразовать обратно, не следует ли мне этого делать? Также я на самом деле работаю с файлом BMP, но я обнаружил, что JpegBitmapEncoder тоже работает, как и некоторые другие кодировщики, и некоторые из них быстрее других, поэтому я пробую каждый из них, чтобы увидеть, что дает мне лучшую производительность - person JMK; 21.01.2013
comment
Моя проблема не в этом преобразовании (хотя есть способы получше), а в основном в том, что вы выбрали JPEG вместо формата без потерь (.bmp). - person CodesInChaos; 21.01.2013
comment
Когда я тестировал, я обнаружил, что JpegBitmapEncoder является самым быстрым кодировщиком, который работал, но я не понимал, что это приведет к потере качества по сравнению с более медленным BitmapEncoder, это то, к чему вы клоните? - person JMK; 21.01.2013
comment
Удивительно, но кодирование растровых изображений оказалось бы медленнее. Кодировка растрового изображения не должна быть чем-то большим, чем копия памяти. И да, я бы побеспокоился о потере качества из-за кодировки jpeg. - person CodesInChaos; 21.01.2013