Replace GDI + DrawImage with PInvoked GDI and transparent PNG

I created an image service in C # that takes an image at a basic level (JPG), overlays another more transparent PNG (32 bit), and then displays the final image in JPG format. I am trying to squeeze every last millisecond from this function, and my code is the bottleneck when calling DrawImage 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 gained by calling GDI directly to P / Invoke, and tried to replace the DrawImage call. I created a unit test to try to duplicate the same JPG loading functions, and then overlay one transparent PNG on top of it.

Link: 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 does not create a layered image. Just PNG without base jpg. What should I do differently? I'm a bit out of my league when it comes straight to GDI.

+6
source share
2 answers

I ended up using SharpDX to access the WIC and Direct2d APIs. The results are impressive, to say the least. When compiling with Direct2d, I see increased performance by 400-500% compared to GDI +.

I also tried GDI + and a parallel task library to split the images into four quandrants and do compositional work in each core. The results were not as significant as using SharpDX.

Here is the code I used. The reference to "renderSettings" is just a configuration object. If necessary, replace renderLayer with the list of images.

 /* SharpDX */ using SharpDX; using SharpDX.Direct2D1; using SharpDX.DirectWrite; using SharpDX.DXGI; using SharpDX.IO; using SharpDX.WIC; using AlphaMode = SharpDX.Direct2D1.AlphaMode; using WicBitmap = SharpDX.WIC.Bitmap; using D2DPixelFormat = SharpDX.Direct2D1.PixelFormat; using WicPixelFormat = SharpDX.WIC.PixelFormat; using Rectangle = System.Drawing.Rectangle; using Bitmap = System.Drawing.Bitmap; public Image FlattenImageDirect2d() { List<string> renderLayers = new List<string>() { "image1.jpg", "image1.png", "image2.png", "image3.png", "image4.png", "image5.png", "image6.png", "image7.png" }; // Base image string baseLayer = renderLayers[0]; // Create WIC and D2D factories var wicFactory = new ImagingFactory(); var ddFactory = new SharpDX.Direct2D1.Factory(); // Get image size using WIC int baseWidth, baseHeight; using (var wicStream = new WICStream(wicFactory, renderDirectory + baseLayer, NativeFileAccess.Read)) { var jpegDecoder = new JpegBitmapDecoder(wicFactory); jpegDecoder.Initialize(wicStream, DecodeOptions.CacheOnDemand); var frame = jpegDecoder.GetFrame(0); baseWidth = frame.Size.Width; baseHeight = frame.Size.Height; frame.Dispose(); jpegDecoder.Dispose(); } // Resize image? bool resizeImage = (baseWidth != renderSettings.RenderWidth) || (baseHeight != renderSettings.RenderHeight); // Bitmaps and render target settings var wicBitmap = new WicBitmap(wicFactory, renderSettings.RenderWidth, renderSettings.RenderHeight, SharpDX.WIC.PixelFormat.Format32bppBGR, BitmapCreateCacheOption.CacheOnLoad); var renderTargetProperties = new RenderTargetProperties(RenderTargetType.Default, new D2DPixelFormat(Format.Unknown, AlphaMode.Unknown), 0, 0, RenderTargetUsage.None, FeatureLevel.Level_DEFAULT); var wicRenderTarget = new WicRenderTarget(ddFactory, wicBitmap, renderTargetProperties); // Create bitmap render target used to draw all images to SharpDX.Direct2D1.BitmapRenderTarget bitmapRenderTarget = new SharpDX.Direct2D1.BitmapRenderTarget(wicRenderTarget, CompatibleRenderTargetOptions.None, new D2DPixelFormat(Format.Unknown, AlphaMode.Premultiplied)); // Draw render layers for (int i = 0; i < renderLayers.Count; i++) { // First layer is always a jpeg, all other subsequent layers are png's ImageFormat imageFormat = (i == 0) ? ImageFormat.Jpeg : ImageFormat.Png; using (SharpDX.WIC.BitmapSource bitmapSource = LoadWicBitmap(wicFactory, renderDirectory + renderLayers[i], imageFormat, resizeImage, renderSettings.RenderWidth, renderSettings.RenderHeight)) { // Convert WIC pixel format to D2D1 format var formatConverter = new FormatConverter(wicFactory); formatConverter.Initialize(bitmapSource, SharpDX.WIC.PixelFormat.Format32bppPBGRA, BitmapDitherType.None, null, 0f, BitmapPaletteType.MedianCut); // Create direct 2d bitmap from wic bitmap SharpDX.Direct2D1.Bitmap direct2DBitmap = SharpDX.Direct2D1.Bitmap.FromWicBitmap(bitmapRenderTarget, formatConverter); // Draw direct2d image to bitmap render target wicRenderTarget.BeginDraw(); wicRenderTarget.DrawBitmap(direct2DBitmap, 1.0f, SharpDX.Direct2D1.BitmapInterpolationMode.Linear); wicRenderTarget.EndDraw(); // Clean up formatConverter.Dispose(); direct2DBitmap.Dispose(); } } // Final image data byte[] imageData; // Create streams to write output to. using (var memoryStream = new MemoryStream()) { using (var wicStream = new WICStream(wicFactory, memoryStream)) { // Encode wic bitmap var encoder = new JpegBitmapEncoder(wicFactory); encoder.Initialize(wicStream); var frameEncoder = new BitmapFrameEncode(encoder); frameEncoder.Initialize(); frameEncoder.SetSize(renderSettings.RenderWidth, renderSettings.RenderHeight); frameEncoder.PixelFormat = WicPixelFormat.FormatDontCare; frameEncoder.WriteSource(wicBitmap); frameEncoder.Commit(); encoder.Commit(); // Set image data memoryStream.Position = 0; imageData = memoryStream.ToArray(); // Clean up frameEncoder.Dispose(); encoder.Dispose(); wicBitmap.Dispose(); wicRenderTarget.Dispose(); bitmapRenderTarget.Dispose(); ddFactory.Dispose(); wicFactory.Dispose(); frameEncoder = null; encoder = null; wicBitmap = null; wicRenderTarget = null; bitmapRenderTarget = null; ddFactory = null; wicFactory = null; } } return Image.FromStream(new MemoryStream(imageData)); } private BitmapSource LoadWicBitmap(ImagingFactory wicFactory, string path, ImageFormat imageFormat, bool resize, int resizeWidth = 0, int resizeHeight = 0) { PngBitmapDecoder pngDecoder; JpegBitmapDecoder jpegDecoder; BitmapFrameDecode bitmapFrameDecode; var stream = new WICStream(wicFactory, path, NativeFileAccess.Read); // Load the appropriate decoder if (imageFormat == ImageFormat.Jpeg) { jpegDecoder = new JpegBitmapDecoder(wicFactory); jpegDecoder.Initialize(stream, DecodeOptions.CacheOnLoad); bitmapFrameDecode = jpegDecoder.GetFrame(0); jpegDecoder.Dispose(); } else { pngDecoder = new PngBitmapDecoder(wicFactory); pngDecoder.Initialize(stream, DecodeOptions.CacheOnDemand); bitmapFrameDecode = pngDecoder.GetFrame(0); pngDecoder.Dispose(); } // Clean up stream.Dispose(); // Resize if necessary if (resize) { // Prepare scaler var scaler = new BitmapScaler(wicFactory); scaler.Initialize(bitmapFrameDecode, resizeWidth, resizeHeight, SharpDX.WIC.BitmapInterpolationMode.Fant); return (BitmapSource)scaler; } return (BitmapSource)bitmapFrameDecode; } 
+7
source

This should work:

  private Bitmap GetImage() { //##################### Get the Bitmaps ############################ Bitmap sourceImage = new Bitmap("c:\\1.png"); Bitmap overlayImage = new Bitmap("c:\\2.png"); //##################### Get Hdc from baselayer ############################ Graphics sourceImageGraphics = Graphics.FromImage(sourceImage); IntPtr sourceImageHDC = sourceImageGraphics.GetHdc(); //##################### Get Cdc from second layer ############################ IntPtr overlayImageCDC = CreateCompatibleDC(sourceImageHDC); IntPtr overlayImageHandle = overlayImage.GetHbitmap(); SelectObject(overlayImageCDC, overlayImageHandle); /* * BitBlt from sourceImage is not neccessary, * because Graphics.FromImage(sourceImage) already did it for you */ //##################### Draw the second layer ############################ AlphaBlend(sourceImageHDC, 0, 0, overlayImage.Width, overlayImage.Height, overlayImageCDC, 0, 0, overlayImage.Width, overlayImage.Height, new BLENDFUNCTION(AC_SRC_OVER, 0, 0xff, AC_SRC_ALPHA)); //##################### Release everthing ############################ sourceImageGraphics.ReleaseHdc(sourceImageHDC); sourceImageGraphics.Dispose(); DeleteDC(overlayImageCDC); DeleteObject(overlayImageHandle); //##################### Return Image ############################ return sourceImage; } 
0
source

Source: https://habr.com/ru/post/898851/


All Articles