WPF Canvas中Image控件无法正确显示带透明区域的PNG图像
问题
我将一张带有透明区域的PNG图像加载到System.Drawing.Bitmap中,已确认其PixelFormat为Format32bppArgb,且通过image.Save("img.png")验证了图像Alpha值正确。
我通过以下代码将System.Drawing.Bitmap转换为BitmapSource:
BitmapData bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, image.PixelFormat); bitmapSource = BitmapSource.Create(image.Width, image.Height, image.HorizontalResolution, image.VerticalResolution, pf, null, bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride); image.UnlockBits(bitmapData);
顺带一提,为确保无误,我通过Marshal.Copy()将bitmapData.Scan0复制到byte[]中,验证了索引3及之后每4个字节的值为255或0,两者占比约50%,符合预期。
随后我将BitmapSource添加到Canvas中的Image控件:
Image im = new() { Source = bmpSource }; TheCanvas.Children.Add(im); Canvas.SetLeft(im, 0); Canvas.SetTop(im, 0);
尽管Canvas的Background为LightGray,但图像透明区域始终显示为白色。
我尝试使用以下辅助函数转换:
BitmapSource bs = Imaging.CreateBitmapSourceFromHBitmap(image.GetHbitmap(), IntPtr.Zero, new System.Windows.Int32Rect(0, 0, image.Width, image.Height), BitmapSizeOptions.FromEmptyOptions());
但结果并无变化。
Image控件没有Background属性,但似乎将其放入Canvas后,透明像素会被强制绘制成白色。
请问如何让图像正确显示,使Canvas的浅灰色背景在图像透明区域透出?
解决方案
1. 匹配WPF的预乘Alpha像素格式
WPF默认使用预乘Alpha的Pbgra32格式,而System.Drawing.Bitmap的Format32bppArgb是非预乘格式,格式不匹配会导致透明通道解析错误。修改转换代码中的像素格式参数:
// 明确指定WPF支持的预乘Alpha格式 var pf = PixelFormats.Pbgra32; BitmapData bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, image.PixelFormat); bitmapSource = BitmapSource.Create(image.Width, image.Height, image.HorizontalResolution, image.VerticalResolution, pf, null, bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride); image.UnlockBits(bitmapData);
2. 正确释放HBitmap资源(若使用CreateBitmapSourceFromHBitmap方法)
使用Imaging.CreateBitmapSourceFromHBitmap时,系统会创建GDI位图对象,未手动释放会引发资源泄漏和显示异常,修改后的代码如下:
// 引入GDI资源释放方法 [System.Runtime.InteropServices.DllImport("gdi32.dll")] private static extern bool DeleteObject(IntPtr hObject); // 转换并释放资源 IntPtr hBitmap = image.GetHbitmap(); try { BitmapSource bs = Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, new System.Windows.Int32Rect(0, 0, image.Width, image.Height), BitmapSizeOptions.FromEmptyOptions()); // 冻结BitmapSource提升性能并确保透明度正常 bs.Freeze(); bmpSource = bs; } finally { DeleteObject(hBitmap); }
3. 检查控件渲染设置
- 确保Image控件未设置
OpacityMask等干扰透明度的属性; - 可尝试设置
Image.Stretch="None",避免拉伸过程中透明通道被破坏; - 确认Canvas没有设置
ClipToBounds或其他遮挡背景的属性。
内容的提问来源于stack exchange,提问作者Betty Crokker
相关产品推荐
相关产品推荐

