C# 使用RenderTargetBitmap+drawingVisual生成png长图存在内存泄漏问题

原先使用了使用RenderTargetBitmap+drawingVisual生成png长图,后来发现存在内存泄漏问题,通过各种方式都没办法释放内存,方式如下:
/// <summary>
        /// 生成长图
        /// </summary>
        /// <param name="imageList"></param>
        /// <param name="toPath"></param>
        /// <returns></returns>
        public async Task<bool> CreateLongGraphAsync(List<SlideImageData> imageList, string toPath)
        {
            var bitmapSource = await CreateLongBitmapAsync(imageList);
            if (bitmapSource != null)
                SaveBitmap(toPath, bitmapSource);
            return true;
        }

        private async Task<RenderTargetBitmap> CreateLongBitmapAsync(List<SlideImageData> imageList)
        {
            if (imageList.Count <= 0) return null;
            double sumHeight = 0;
            int lineHeight = 5;
            if (imageList.Count == 1)
            {
                sumHeight = imageList.First().Height;
            }
            else
            {
                sumHeight = imageList.Sum(img => img.Height) + lineHeight * (imageList.Count - 1);
            }

            double maxWidth = imageList.Max(img => img.Width);
            var combineImg = new RenderTargetBitmap((int)maxWidth,
                (int)sumHeight,
                96d,
                96d,
                PixelFormats.Pbgra32);

            var drawingVisual = new DrawingVisual();
            using (var drawingContext = drawingVisual.RenderOpen())
            {
                int positionY = 0;
                foreach (var image in imageList)
                {
                    drawingContext.DrawImage(image.ImageSource, new Rect(0, positionY, image.Width, image.Height));
                    positionY += image.Height + lineHeight;

                }
            }
            combineImg.Render(drawingVisual);
            if (combineImg.CanFreeze)
                combineImg.Freeze();
            return combineImg;
        }
/// <summary>
/// 保存位图
/// </summary>
/// <param name="savePath"></param>
/// <param name="getSource"></param>
private void SaveBitmap(string savePath, RenderTargetBitmap getSource)
{
    PngBitmapEncoder encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(getSource));
    using FileStream fs = new FileStream(savePath, FileMode.Create, FileAccess.Read);
    encoder.Save(fs);
    getSource.Clear();
    getSource = null;
    GC.Collect();
}

跟踪主要是RenderTargetBitmap的内存没办法释放。所以替换了另一种方式Bitmap+Graphics生成png长图

/// <summary>
        /// 生成长图
        /// </summary>
        /// <param name="imageList"></param>
        /// <param name="toPath"></param>
        /// <returns></returns>
        public async Task<bool> CreateLongGraphAsync(List<SlideImageData> imageList, string toPath)
        {
            Bitmap mergedBitmap = MergeImages(imageList.Select(t => t.ImageSource).ToList());
            mergedBitmap.Save(toPath, ImageFormat.Png);

            //var bitmapSource = await CreateLongBitmapAsync(imageList);
            //if (bitmapSource != null)
            //    SaveBitmap(toPath, bitmapSource);
            return true;
        }

        // 定义合并图片的方法
        private Bitmap MergeImages(List<ImageSource> images)
        {
            if (images.Count <= 0) return null;
            double sumHeight = 0;
            int lineHeight = 5;
            if (images.Count == 1)
            {
                sumHeight = images.First().Height;
            }
            else
            {
                sumHeight = images.Sum(img => img.Height) + lineHeight * (images.Count - 1);
            }

            double maxWidth = images.Max(img => img.Width);

            // 创建一个位图对象
            Bitmap bitmap = new Bitmap((int)maxWidth, (int)sumHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            // 使用Graphics类绘制图片
            using (Graphics g = Graphics.FromImage(bitmap))
            {
                int y = 0;

                foreach (var image in images)
                {
                    g.DrawImage(ImageSourceToBitmap(image), new System.Drawing.Rectangle(0, y, (int)image.Width, (int)image.Height));
                    y += (int)image.Height;
                }
            }

            return bitmap;
        }

        // 定义将ImageSource转换为Bitmap的方法
        private Bitmap ImageSourceToBitmap(ImageSource imageSource)
        {
            BitmapImage bitmapImage = (BitmapImage)imageSource;
            Bitmap bitmap = new Bitmap(bitmapImage.PixelWidth, bitmapImage.PixelHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            bitmapImage.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride);

            bitmap.UnlockBits(data);

            return bitmap;
        }

        private BitmapImage ConvertRenderTargetBitmapToBitmapImage(RenderTargetBitmap wbm)
        {
            BitmapImage bmp = new BitmapImage();
            using (MemoryStream stream = new MemoryStream())
            {
                BmpBitmapEncoder encoder = new BmpBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(wbm));
                encoder.Save(stream);
                bmp.BeginInit();
                bmp.CacheOption = BitmapCacheOption.OnLoad;
                bmp.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
                bmp.StreamSource = new MemoryStream(stream.ToArray()); //stream;
                bmp.EndInit();
                bmp.Freeze();
            }
            return bmp;
        }

        /// <summary>
        /// 保存位图
        /// </summary>
        /// <param name="savePath"></param>
        /// <param name="getSource"></param>
        private void SaveBitmap(string savePath, RenderTargetBitmap getSource)
        {
            PngBitmapEncoder encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(getSource));
            using FileStream fs = new FileStream(savePath, FileMode.Create, FileAccess.Read);
            encoder.Save(fs);
            getSource.Clear();
            getSource = null;
            GC.Collect();
        }

 

posted @ 2023-05-29 10:39  log9527  阅读(207)  评论(1编辑  收藏  举报