Утечка памяти при асинхронной загрузке изображений BitmapSource - PullRequest
4 голосов
/ 11 марта 2010

У меня есть несколько изображений, которые я загружаю в ListBox в своем приложении WPF. Первоначально я использовал GDI для изменения размера изображений (оригиналы занимают слишком много памяти). Это было хорошо, за исключением того, что они принимали около 400 мс на изображение. Не так хорошо Поэтому в поисках другого решения я нашел метод, который использует TransformedBitmap (который наследуется от BitmapSource). Это здорово, подумал я, я могу это использовать. За исключением того, что у меня сейчас где-то утечки памяти ...

Я загружаю изображения асинхронно, используя BackgroundWorker, например:

BitmapSource bs = ImageUtils.ResizeBitmapSource(ImageUtils.GetImageSource(photo.FullName));
                //BitmapSource bs = ImageUtils.GetImageSource(photo.FullName);
                bs.Freeze();

                this.dispatcher.Invoke(new Action(() => { photo.Source = bs; }));

GetImageSource просто получает растровое изображение из пути и затем преобразует его в BitmapSource.

Вот фрагмент кода для ResizeBitmapSource:

const int thumbnailSize = 200;
        int width;
        int height;

        if (bs.Width > bs.Height)
        {
            width = thumbnailSize;
            height = (int)(bs.Height * thumbnailSize / bs.Width);
        }
        else
        {
            height = thumbnailSize;
            width = (int)(bs.Width * thumbnailSize / bs.Height);
        }

        BitmapSource tbBitmap = new TransformedBitmap(bs,
                                                           new ScaleTransform(width / bs.Width,
                                                                              height / bs.Height, 0, 0));

        return tbBitmap;

Этот код по сути является кодом из: http://rongchaua.net/blog/c-wpf-fast-image-resize/

Есть идеи, что может быть причиной утечки?

редактировать: Вот код для GetImageSource, как было запрошено

using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
            {
                using (var bmp = Image.FromStream(stream, false, false))
                {
                    // Use WPF to resize
                    var bitmapSource = ConvertBitmapToBitmapSource(bmp);
                    bitmapSource = ResizeBitmapSource(bitmapSource);
                    return bitmapSource;
                }
            }

Ответы [ 2 ]

4 голосов
/ 12 марта 2010

Я думаю, вы неправильно поняли, как работает TransformedBitmap. Он содержит ссылку на исходное растровое изображение и преобразует его в память. Возможно, вы могли бы закодировать преобразованное растровое изображение в поток памяти и прочитать его обратно. Я не уверен, насколько быстро это будет, но тогда вы не будете держаться за полноразмерное растровое изображение.

Я нашел это сообщение в блоге , в котором возвращен WriteableBitmap с TransformedBitmap в качестве источника. WriteableBitmap скопирует данные пикселей в буфер памяти в инициализаторе, поэтому он фактически не удерживает ссылку на TransformedBitmap или полноразмерное изображение.

1 голос
/ 12 марта 2010

Скорее всего, при просмотре вашего кода вам может потребоваться избавиться от растрового изображения, возвращаемого при вызове ImageUtils.GetImageSource (photo.FullName).

Я также отметил в блоге, на который вы указали, что автор добавил обновление (11 марта) о вставке оператора using для предотвращения утечек памяти.

...