WICBitmapSource.Скопированные файлы в GDI Bitmap Scan0

#interop #marshalling #gdi #wic

#взаимодействие #сортировка #gdi #вик #wic

Вопрос:

Я модифицировал 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 никогда к нему не прикасался.

Есть идеи, почему это могло происходить? Что-то не так с .Сетевая сортировка?

Комментарии:

1. Вы не можете изменить фактическую реализацию этого интерфейса, написанного в машинном коде, нарисовав на нем счастливое лицо. Ключевое слово new просто не позволяет компилятору сообщить вам, что вы делаете это неправильно. Довольно примечательно, что вы не получаете исключение AccessViolation, но это, безусловно, возможно. Однако это не приведет к тому, на что вы надеетесь, это вызовет совершенно другой метод. Ты не можешь заставить это сработать.

2. Фактической реализацией является указатель (см. гиперссылку). Я просто меняю поведение маршалинга.

Ответ №1:

Похоже, что 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, чтобы использовать IWICBitmapSource вместо SafeMILHandle, и я изменил ref Int32Rect на IntPtr, чтобы я мог легально передавать IntPtr.Zero (я получаю ошибку HRESULT, если я передаю прямоугольник для копирования — и я знаю, что моя структура имеет значение, я использую ее в других Звонки с копипикселей.