WICBitmapSource.CopyPixels 到 GDI 位图 Scan0

发布于 2024-12-15 02:41:11 字数 1466 浏览 1 评论 0原文

我修改了 C# IWICBitmapSource .CopyPixels 接口允许数组封送和传递指针:

    void CopyPixels(
        WICRect prc,
        uint cbStride,
        uint cbBufferSize,
        [Out]
        [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]
        byte[] pbBuffer
    );
    new void CopyPixels(
        WICRect prc,
        uint cbStride,
        uint cbBufferSize,
        IntPtr pbBuffer
        );

我这样调用

    public static Bitmap FromWic(IWICBitmapSource source) {
        Guid format;
        source.GetPixelFormat(out format);

        PixelFormat gdiFormat = ConversionUtils.GetPixelFormat(format);

        uint w, h;
        source.GetSize(out w, out h);

        Bitmap b = new Bitmap((int)w, (int)h, gdiFormat);


        BitmapData bd = b.LockBits(new Rectangle(0, 0, (int)w, (int)h),
                               ImageLockMode.ReadWrite, b.PixelFormat); 
        try {
            //Copy unmanaged-to-unmanaged
            source.CopyPixels(
                   new WICRect { X = 0, Y = 0, Width = (int)w, Height = (int)h },
                   (uint)bd.Stride, (uint)(bd.Stride * bd.Height), bd.Scan0);
        } finally {
            b.UnlockBits(bd);
        }
        return b;
    }

它代码执行没有错误,并且所有宽度,高度,步长和缓冲区大小值是正确的,但位图保持黑色,就好像 WIC 从未接触过它一样。

有什么想法为什么会发生这种情况吗? .NET 编组有问题吗?

I've modified the C# IWICBitmapSource.CopyPixels interface to allow both array marshaling and passing a pointer:

    void CopyPixels(
        WICRect prc,
        uint cbStride,
        uint cbBufferSize,
        [Out]
        [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]
        byte[] pbBuffer
    );
    new void CopyPixels(
        WICRect prc,
        uint cbStride,
        uint cbBufferSize,
        IntPtr pbBuffer
        );

I'm calling it like this

    public static Bitmap FromWic(IWICBitmapSource source) {
        Guid format;
        source.GetPixelFormat(out format);

        PixelFormat gdiFormat = ConversionUtils.GetPixelFormat(format);

        uint w, h;
        source.GetSize(out w, out h);

        Bitmap b = new Bitmap((int)w, (int)h, gdiFormat);


        BitmapData bd = b.LockBits(new Rectangle(0, 0, (int)w, (int)h),
                               ImageLockMode.ReadWrite, b.PixelFormat); 
        try {
            //Copy unmanaged-to-unmanaged
            source.CopyPixels(
                   new WICRect { X = 0, Y = 0, Width = (int)w, Height = (int)h },
                   (uint)bd.Stride, (uint)(bd.Stride * bd.Height), bd.Scan0);
        } finally {
            b.UnlockBits(bd);
        }
        return b;
    }

The code executes without errors, and all the width, height, stride, and buffer size values are correct, but the bitmap remains black, as if WIC never touched it.

Any ideas why this could be happening? Is there something wrong with the .NET marshalling?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(1

渔村楼浪 2024-12-22 02:41:11

看来WPF使用代理方法将数据复制到非托管内存。基于此,我找到了解决方案。下面的代码可以工作并且应该非常高效。

[DllImport("WindowsCodecs.dll", EntryPoint = "IWICBitmapSource_CopyPixels_Proxy")]
internal static extern int CopyPixels(IWICBitmapSource bitmap, IntPtr rect, uint cbStride, uint cbBufferSize, IntPtr pvPixels);

public static Bitmap FromWic(IWICBitmapSource source) {

    Guid format; //Get the WIC pixel format
    source.GetPixelFormat(out format);
    //Get the matching GDI format
    PixelFormat gdiFormat = ConversionUtils.GetPixelFormat(format);

    //If it's not GDI-supported format, convert it to one.
    IWICComponentFactory factory = null;
    IWICFormatConverter converter = null;
    try {
        if (gdiFormat == PixelFormat.Undefined) {
            factory = (IWICComponentFactory)new WICImagingFactory();
            converter = factory.CreateFormatConverter();
            converter.Initialize(source, Consts.GUID_WICPixelFormat32bppBGRA, WICBitmapDitherType.WICBitmapDitherTypeNone, null, 0.9f, WICBitmapPaletteType.WICBitmapPaletteTypeCustom);
            gdiFormat = PixelFormat.Format32bppArgb;
        }
        IWICBitmapSource data = converter != null ? converter : source;

        //Get the dimensions of the WIC bitmap
        uint w, h;
        data.GetSize(out w, out h);

        Bitmap b = new Bitmap((int)w, (int)h, gdiFormat);
        BitmapData bd = b.LockBits(new Rectangle(0, 0, (int)w, (int)h), ImageLockMode.WriteOnly, b.PixelFormat);
        try {
            long result = CopyPixels(data, IntPtr.Zero, (uint)bd.Stride, (uint)(bd.Stride * bd.Height), bd.Scan0);
            if (result == 0x80070057) throw new ArgumentException();
            if (result < 0) throw new Exception("HRESULT " + result);
            return b;
        } finally {
            b.UnlockBits(bd);
        }
    } finally {
        if (converter != null) Marshal.ReleaseComObject(converter);
        if (source != null) Marshal.ReleaseComObject(factory);
    }
}

请注意,我修改了 IWICBitmapSource_CopyPixels_Proxy dllimport 以使用 IWICBitmapSource 而不是 SafeMILHandle,并将 ref Int32Rect 修改为 IntPtr,以便我可以合法地传递 IntPtr.Zero (如果我传递一个要复制的矩形,我会收到 HRESULT 错误 - 而且我知道我的结构是值,我在其他 CopyPixels 调用中使用它。

It seems that WPF uses a proxy method to copy data to unmanaged memory. Based on that, I found a solution. The following code works and should be extremely efficient.

[DllImport("WindowsCodecs.dll", EntryPoint = "IWICBitmapSource_CopyPixels_Proxy")]
internal static extern int CopyPixels(IWICBitmapSource bitmap, IntPtr rect, uint cbStride, uint cbBufferSize, IntPtr pvPixels);

public static Bitmap FromWic(IWICBitmapSource source) {

    Guid format; //Get the WIC pixel format
    source.GetPixelFormat(out format);
    //Get the matching GDI format
    PixelFormat gdiFormat = ConversionUtils.GetPixelFormat(format);

    //If it's not GDI-supported format, convert it to one.
    IWICComponentFactory factory = null;
    IWICFormatConverter converter = null;
    try {
        if (gdiFormat == PixelFormat.Undefined) {
            factory = (IWICComponentFactory)new WICImagingFactory();
            converter = factory.CreateFormatConverter();
            converter.Initialize(source, Consts.GUID_WICPixelFormat32bppBGRA, WICBitmapDitherType.WICBitmapDitherTypeNone, null, 0.9f, WICBitmapPaletteType.WICBitmapPaletteTypeCustom);
            gdiFormat = PixelFormat.Format32bppArgb;
        }
        IWICBitmapSource data = converter != null ? converter : source;

        //Get the dimensions of the WIC bitmap
        uint w, h;
        data.GetSize(out w, out h);

        Bitmap b = new Bitmap((int)w, (int)h, gdiFormat);
        BitmapData bd = b.LockBits(new Rectangle(0, 0, (int)w, (int)h), ImageLockMode.WriteOnly, b.PixelFormat);
        try {
            long result = CopyPixels(data, IntPtr.Zero, (uint)bd.Stride, (uint)(bd.Stride * bd.Height), bd.Scan0);
            if (result == 0x80070057) throw new ArgumentException();
            if (result < 0) throw new Exception("HRESULT " + result);
            return b;
        } finally {
            b.UnlockBits(bd);
        }
    } finally {
        if (converter != null) Marshal.ReleaseComObject(converter);
        if (source != null) Marshal.ReleaseComObject(factory);
    }
}

Note that I modified the IWICBitmapSource_CopyPixels_Proxy dllimport to use IWICBitmapSource instead of SafeMILHandle, and I modified ref Int32Rect to IntPtr, so that I could legally pass IntPtr.Zero (I get an HRESULT error if I pass in a rectangle to copy - and I know my stucture is value, I use it in other CopyPixels calls.

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