取代GDI+使用 PInvoked GDI 和透明 PNG 绘制图像
我用 C# 创建了一个图像服务,它采用基础层图像 (JPG),再分层一层更透明的 PNG(32 位),然后输出最终的 JPG 图像。我试图从这个函数中挤出最后一毫秒,但我的代码在 GDI+ 中的 DrawImage 调用处遇到瓶颈。此处的托管代码:
// Load base image and create graphics
Image image = LoadImage(renderSettings.RenderedImageDirectory + baseLayer);
Graphics graphics = Graphics.FromImage(image);
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
// Draw additional layers to final image
for (int i = 1; i < renderLayers.Count; i++) {
// SLOW -- LoadImage just a utility method that returns an Image from disk or cache
graphics.DrawImage(LoadImage(renderSettings.RenderedImageDirectory + renderLayers[i]), 0, 0, image.Width, image.Height);
}
if (graphics != null) graphics.Dispose();
现在,我了解了通过 P/Invoke 直接调用 GDI 所获得的性能增益,并尝试替换 DrawImage 调用。我创建了一个单元测试来尝试复制加载 JPG 的相同功能,然后在其上分层放置一个透明 PNG。
参考:http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/29582142-0068-40dd-bd99-4b3883a76350
Bitmap sourceImage = new Bitmap("c:\\base.jpg");
Bitmap overlayImage = new Bitmap("c:\\layer1.png");
// NOTE: ImageHelper is a utility class containing all the P/Invoke stuff
// Get source image in memory
Graphics sourceImageGraphics = Graphics.FromImage(sourceImage);
IntPtr sourceImageHDC = sourceImageGraphics.GetHdc();
IntPtr sourceImageCDC = ImageHelper.CreateCompatibleDC(sourceImageHDC);
IntPtr sourceImageHandle = sourceImage.GetHbitmap();
ImageHelper.SelectObject(sourceImageCDC, sourceImageHandle);
// Get overlay image in memory
Graphics overlayImageGraphics = Graphics.FromImage(overlayImage);
IntPtr overlayImageHDC = overlayImageGraphics.GetHdc();
IntPtr overlayImageCDC = ImageHelper.CreateCompatibleDC(overlayImageHDC);
IntPtr overlayImageHandle = overlayImage.GetHbitmap();
ImageHelper.SelectObject(overlayImageCDC, overlayImageHandle);
ImageHelper.BitBlt(sourceImageHDC, 0, 0, sourceImage.Width, sourceImage.Height, overlayImageCDC, 0, 0, ImageHelper.TernaryRasterOperations.SRCAND);
ImageHelper.AlphaBlend(sourceImageHDC, 0, 0, sourceImage.Width, sourceImage.Height, overlayImageCDC, 0, 0, sourceImage.Width, sourceImage.Height, new ImageHelper.BLENDFUNCTION(ImageHelper.AC_SRC_OVER, 0, 0xff, ImageHelper.AC_SRC_ALPHA));
// Release source Image memory.
ImageHelper.DeleteDC(sourceImageCDC);
ImageHelper.DeleteObject(sourceImageHandle);
sourceImageGraphics.ReleaseHdc(sourceImageHDC);
sourceImageGraphics.Dispose();
// Release overlay Image memory.
ImageHelper.DeleteDC(overlayImageCDC);
ImageHelper.DeleteObject(overlayImageHandle);
overlayImageGraphics.ReleaseHdc(overlayImageHDC);
overlayImageGraphics.Dispose();
// Save to jpg
sourceImage.Save("c:\\output.jpg", ImageFormat.Jpeg);
但这无法生成分层图像。只是没有基本 JPG 的 PNG。我应该采取什么不同的做法?对于直接 GDI 来说,我有点格格不入。
I've created an image service in C# which takes a base layer image (JPG), layers one more more transparent PNG's (32 bit), and then outputs a final JPG image. I'm trying to squeeze every last millisecond out of this function and my code is bottlenecking at the DrawImage call in GDI+. Managed code here:
// Load base image and create graphics
Image image = LoadImage(renderSettings.RenderedImageDirectory + baseLayer);
Graphics graphics = Graphics.FromImage(image);
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
// Draw additional layers to final image
for (int i = 1; i < renderLayers.Count; i++) {
// SLOW -- LoadImage just a utility method that returns an Image from disk or cache
graphics.DrawImage(LoadImage(renderSettings.RenderedImageDirectory + renderLayers[i]), 0, 0, image.Width, image.Height);
}
if (graphics != null) graphics.Dispose();
Now, I read about the performance gains obtained by calling GDI directly by P/Invoke and made an attempt at replacing the DrawImage call. I created a unit test to try to duplicate the same functionality of loading a JPG and then layering one transparent PNG on top of it.
Ref: http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/29582142-0068-40dd-bd99-4b3883a76350
Bitmap sourceImage = new Bitmap("c:\\base.jpg");
Bitmap overlayImage = new Bitmap("c:\\layer1.png");
// NOTE: ImageHelper is a utility class containing all the P/Invoke stuff
// Get source image in memory
Graphics sourceImageGraphics = Graphics.FromImage(sourceImage);
IntPtr sourceImageHDC = sourceImageGraphics.GetHdc();
IntPtr sourceImageCDC = ImageHelper.CreateCompatibleDC(sourceImageHDC);
IntPtr sourceImageHandle = sourceImage.GetHbitmap();
ImageHelper.SelectObject(sourceImageCDC, sourceImageHandle);
// Get overlay image in memory
Graphics overlayImageGraphics = Graphics.FromImage(overlayImage);
IntPtr overlayImageHDC = overlayImageGraphics.GetHdc();
IntPtr overlayImageCDC = ImageHelper.CreateCompatibleDC(overlayImageHDC);
IntPtr overlayImageHandle = overlayImage.GetHbitmap();
ImageHelper.SelectObject(overlayImageCDC, overlayImageHandle);
ImageHelper.BitBlt(sourceImageHDC, 0, 0, sourceImage.Width, sourceImage.Height, overlayImageCDC, 0, 0, ImageHelper.TernaryRasterOperations.SRCAND);
ImageHelper.AlphaBlend(sourceImageHDC, 0, 0, sourceImage.Width, sourceImage.Height, overlayImageCDC, 0, 0, sourceImage.Width, sourceImage.Height, new ImageHelper.BLENDFUNCTION(ImageHelper.AC_SRC_OVER, 0, 0xff, ImageHelper.AC_SRC_ALPHA));
// Release source Image memory.
ImageHelper.DeleteDC(sourceImageCDC);
ImageHelper.DeleteObject(sourceImageHandle);
sourceImageGraphics.ReleaseHdc(sourceImageHDC);
sourceImageGraphics.Dispose();
// Release overlay Image memory.
ImageHelper.DeleteDC(overlayImageCDC);
ImageHelper.DeleteObject(overlayImageHandle);
overlayImageGraphics.ReleaseHdc(overlayImageHDC);
overlayImageGraphics.Dispose();
// Save to jpg
sourceImage.Save("c:\\output.jpg", ImageFormat.Jpeg);
But this fails to produce a layered image. Just the PNG without the base JPG. What should I be doing differently? I'm a little out of my league when in comes to straight GDI.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我最终使用 SharpDX 来访问 WIC 和 Direct2d API。至少可以说,结果令人印象深刻。当使用 Direct2d 进行合成时,我发现性能比 GDI+ 提高了 400-500%。
我还尝试了 GDI+ 和任务并行库,将图像分成四个象限,并在每个核心中进行合成工作。结果并不像使用 SharpDX 那样显着。
这是我最终使用的代码。对“renderSettings”的引用只是一个配置对象。根据需要与 renderLayer 图像列表一起替换。
I ended up using SharpDX to access both the WIC and Direct2d API's. The results are impressive to say the least. When compositing with Direct2d I'm seeing increased performance as much as 400-500% over GDI+.
I also tried GDI+ and the Task Parallel Library to break up images into four quandrants and do compositing work in each core. The results weren't nearly as signficant as using SharpDX.
Here's the code I ended up using. The reference to "renderSettings" is just a configuration object. Substitute as needed along with the renderLayer image list.
这个应该可以工作:
This one should work: