WICBitmapSource.CopyPixels в GDI Bitmap Scan0 - PullRequest
0 голосов
/ 12 ноября 2011

Я изменил интерфейс 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?

1 Ответ

0 голосов
/ 12 ноября 2011

Похоже, что 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.

...