取代GDI+使用 PInvoked GDI 和透明 PNG 绘制图像

发布于 2024-12-08 17:34:53 字数 3189 浏览 1 评论 0原文

我用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

木槿暧夏七纪年 2024-12-15 17:34:53

我最终使用 SharpDX 来访问 WIC 和 Direct2d API。至少可以说,结果令人印象深刻。当使用 Direct2d 进行合成时,我发现性能比 GDI+ 提高了 400-500%。

我还尝试了 GDI+ 和任务并行库,将图像分成四个象限,并在每个核心中进行合成工作。结果并不像使用 SharpDX 那样显着。

这是我最终使用的代码。对“renderSettings”的引用只是一个配置对象。根据需要与 renderLayer 图像列表一起替换。

/* 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;
}

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.

/* 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;
}
⒈起吃苦の倖褔 2024-12-15 17:34:53

这个应该可以工作:

    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;
    }

This one 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;
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文