WICBitmapSource.CopyPixels到GDI位图Scan0

时间:2011-11-11 23:31:22

标签: interop marshalling gdi wic

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

    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编组是否有问题?

1 个答案:

答案 0 :(得分:0)

似乎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调用中使用它。