如何将Drawing.Image转换为Media.ImageSource以实现裁剪图像渲染?
嘿,我之前也遇到过把GDI+的System.Drawing.Image转成WPF的Media.ImageSource的需求,刚好能给你几个靠谱的解决方案:
方案1:通过内存流转换(通用兼容)
这是最通用的方法,不管你的Drawing.Image是Bitmap、Metafile还是其他子类都能适用,原理是把GDI+图像序列化到内存流,再用WPF的BitmapImage加载:
using System.Drawing; using System.Windows.Media; using System.Windows.Media.Imaging; using System.IO; // 转换方法 private ImageSource ConvertGdiImageToWpfImageSource(System.Drawing.Image gdiImage) { using (var memoryStream = new MemoryStream()) { // 把GDI+图像保存为PNG格式到内存流(PNG支持透明,也可以选JPEG等) gdiImage.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png); memoryStream.Position = 0; // 重置流指针到开头 var bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.CacheOption = BitmapCacheOption.OnLoad; // 确保流关闭后图像依然可用 bitmapImage.StreamSource = memoryStream; bitmapImage.EndInit(); // 冻结图像提升性能,并且允许跨线程访问(如果需要在非UI线程处理) bitmapImage.Freeze(); return bitmapImage; } }
方案2:直接从像素数据创建(高性能)
如果你的Drawing.Image是System.Drawing.Bitmap类型,这个方法会更高效——直接读取Bitmap的像素缓冲区创建WPF的BitmapSource,跳过序列化/反序列化的开销,适合处理大图像:
private ImageSource ConvertGdiBitmapToWpfImageSource(System.Drawing.Bitmap gdiBitmap) { // 锁定Bitmap的像素数据 var bitmapData = gdiBitmap.LockBits( new System.Drawing.Rectangle(0, 0, gdiBitmap.Width, gdiBitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, gdiBitmap.PixelFormat); // 根据GDI+像素格式匹配对应的WPF像素格式,这里以常见的32位RGB为例 var wpfPixelFormat = PixelFormats.Bgr32; if (gdiBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) { wpfPixelFormat = PixelFormats.Pbgra32; } // 创建WPF BitmapSource var bitmapSource = BitmapSource.Create( bitmapData.Width, bitmapData.Height, gdiBitmap.HorizontalResolution, gdiBitmap.VerticalResolution, wpfPixelFormat, null, bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride); // 解锁Bitmap像素数据 gdiBitmap.UnlockBits(bitmapData); bitmapSource.Freeze(); return bitmapSource; }
转换后使用ImageDrawing裁剪渲染
拿到ImageSource之后,就可以按你的计划创建ImageDrawing,指定裁剪区域后渲染到DrawingContext了:
// 假设你的原始GDI+图像是gdiImage ImageSource wpfImage = ConvertGdiImageToWpfImageSource(gdiImage); // 定义裁剪区域:左上角(10,10),宽150,高150 Rect cropArea = new Rect(10, 10, 150, 150); // 创建ImageDrawing var imageDrawing = new ImageDrawing(wpfImage, cropArea); // 渲染到DrawingContext drawingContext.DrawDrawing(imageDrawing);
注意事项
- 像素格式匹配:如果你的图像带透明通道,一定要对应正确的WPF像素格式(比如
Pbgra32对应GDI+的Format32bppArgb),否则透明效果会失效。 - 冻结ImageSource:如果你的转换操作在非UI线程执行,冻结
BitmapSource是必须的——WPF的UI元素默认不允许跨线程访问,冻结后的对象是只读且线程安全的。 - 资源释放:如果你的GDI+图像是从文件或其他资源加载的,记得在使用完后调用
Dispose()释放资源,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Julien Vernay
相关产品推荐
相关产品推荐

