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