Merge pull request #20 from paulcbetts/winrt-bitmap-cleanup

WinRT bitmap cleanup
This commit is contained in:
Paul Betts 2013-09-27 11:45:44 -07:00
Родитель 35d8ede0e1 8722093c66
Коммит acc7f09fde
4 изменённых файлов: 532 добавлений и 2 удалений

Просмотреть файл

@ -106,7 +106,9 @@
<Compile Include="PlatformModeDetector.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WinRT\Bitmaps.cs" />
<Compile Include="WinRT\BitmapsFallback.cs" />
<Compile Include="WinRT\Color.cs" />
<Compile Include="WinRT\NativeMethods.cs" />
<Compile Include="WinRT\Point.cs" />
<Compile Include="WinRT\Rect.cs" />
</ItemGroup>

Просмотреть файл

@ -20,7 +20,23 @@ namespace Splat
using (var rwStream = new InMemoryRandomAccessStream()) {
await sourceStream.CopyToAsync(rwStream.AsStreamForWrite());
var decoder = await BitmapDecoder.CreateAsync(rwStream);
var decoder = default(BitmapDecoder);
bool tryFallback = false;
try {
decoder = await BitmapDecoder.CreateAsync(rwStream);
} catch (Exception ex) {
if (ex.Message.Contains("0x88982F50") || ex.Message.Contains("0x88982F60")) {
// NB: Can't await in a catch block, have to do some silliness
tryFallback = true;
} else {
throw;
}
}
if (tryFallback) {
return await new FallbackBitmapLoader().Load(sourceStream, desiredWidth, desiredHeight);
}
var transform = new BitmapTransform();
if (desiredWidth != null) {
@ -31,7 +47,7 @@ namespace Splat
var pixelData = await decoder.GetPixelDataAsync(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);
var pixels = pixelData.DetachPixelData();
WriteableBitmap bmp = new WriteableBitmap((int)decoder.OrientedPixelWidth, (int)decoder.OrientedPixelHeight);
var bmp = new WriteableBitmap((int)decoder.OrientedPixelWidth, (int)decoder.OrientedPixelHeight);
using (var bmpStream = bmp.PixelBuffer.AsStream()) {
bmpStream.Seek(0, SeekOrigin.Begin);
bmpStream.Write(pixels, 0, (int)bmpStream.Length);

Просмотреть файл

@ -0,0 +1,360 @@
using Splat;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.UI.Xaml.Media.Imaging;
namespace Splat
{
class FallbackBitmapLoader : IBitmapLoader
{
public async Task<IBitmap> Load(Stream sourceStream, float? desiredWidth, float? desiredHeight)
{
var factory = new ImagingFactory();
var decoder = factory.CreateDecoderFromStream(sourceStream, WICDecodeOptions.WICDecodeMetadataCacheOnLoad);
var frame = decoder.GetFrame(0);
var frameSize = frame.GetSize();
var convertedSource = default(BitmapSource);
if (!frame.GetPixelFormat().Equals(NativeMethods.WICPixelFormat32bppBGRA)) {
var converter = factory.CreateFormatConverter();
converter.Initialize(frame, NativeMethods.WICPixelFormat32bppBGRA);
convertedSource = converter;
} else {
convertedSource = frame;
}
var buffer = convertedSource.CopyPixels();
var bmp = new WriteableBitmap(frameSize.Width, frameSize.Height);
using (var stream = bmp.PixelBuffer.AsStream()) {
stream.Write(buffer, 0, buffer.Length);
}
bmp.Invalidate();
return new WriteableBitmapImageBitmap(bmp);
}
public async Task<IBitmap> LoadFromResource(string source, float? desiredWidth, float? desiredHeight)
{
throw new NotImplementedException();
}
public IBitmap Create(float width, float height)
{
throw new NotImplementedException();
}
class ImagingFactory : IDisposable
{
IDisposable inner;
IWICImagingFactory comObject;
IntPtr nativePointer;
IntPtr riidPointer;
public ImagingFactory()
{
var riid = typeof(IWICImagingFactory).GetTypeInfo().GUID;
byte[] riidBytes = riid.ToByteArray();
riidPointer = Marshal.AllocHGlobal(riidBytes.Length);
Marshal.Copy(riidBytes, 0, riidPointer, riidBytes.Length);
var localQuery = new MultiQueryInterface() {
InterfaceIID = riidPointer,
IUnknownPointer = IntPtr.Zero,
ResultCode = 0,
};
var result = NativeMethods.CoCreateInstanceFromApp(NativeMethods.CLSID_WICImagingFactory, IntPtr.Zero, CLSCTX.CLSCTX_INPROC_SERVER, IntPtr.Zero, 1, ref localQuery);
if (result != NativeMethods.S_OK || localQuery.ResultCode != NativeMethods.S_OK) {
throw new Exception("CoCreateInstanceFromApp failed");
}
nativePointer = localQuery.IUnknownPointer;
comObject = (IWICImagingFactory)Marshal.GetObjectForIUnknown(nativePointer);
inner = new DelegateDisposable(() => {
Marshal.FreeHGlobal(riidPointer);
Marshal.ReleaseComObject(comObject);
});
}
~ImagingFactory() { Dispose(); }
public void Dispose()
{
Interlocked.Exchange(ref inner, DelegateDisposable.Empty).Dispose();
}
public BitmapDecoder CreateDecoderFromStream(Stream stream, WICDecodeOptions wicDecodeOptions)
{
IntPtr nativePointer;
var nullGuid = Guid.Empty;
var result = comObject.CreateDecoderFromStream(new ManagedIStream(stream), ref nullGuid, wicDecodeOptions, out nativePointer);
if (result != NativeMethods.S_OK) throw new Exception("CreateDecoderFromStream failed");
return new BitmapDecoder(nativePointer);
}
public FormatConverter CreateFormatConverter()
{
IntPtr nativePointer;
var result = comObject.CreateFormatConverter(out nativePointer);
if (result != NativeMethods.S_OK)
throw new Exception("CreateFormatConverter failed");
return new FormatConverter(nativePointer);
}
}
class BitmapDecoder : IDisposable
{
IWICBitmapDecoder comObject;
IntPtr nativePointer;
IDisposable inner;
internal BitmapDecoder(IntPtr nativePointer)
{
this.nativePointer = nativePointer;
this.comObject = (IWICBitmapDecoder)Marshal.GetObjectForIUnknown(nativePointer);
inner = new DelegateDisposable(() => Marshal.ReleaseComObject(comObject));
}
public BitmapFrameDecode GetFrame(uint index)
{
IntPtr nativePointer;
var result = comObject.GetFrame(index, out nativePointer);
if (result != NativeMethods.S_OK) {
throw new Exception("GetFrame failed");
}
return new BitmapFrameDecode(nativePointer);
}
~BitmapDecoder() { Dispose(); }
public void Dispose()
{
Interlocked.Exchange(ref inner, DelegateDisposable.Empty).Dispose();
}
}
class BitmapSource : IDisposable
{
IWICBitmapSource comObject;
public IntPtr NativePointer { get; private set; }
IDisposable inner;
internal BitmapSource(IntPtr nativePointer)
{
NativePointer = nativePointer;
comObject = (IWICBitmapSource)Marshal.GetObjectForIUnknown(NativePointer);
inner = new DelegateDisposable(() => Marshal.ReleaseComObject(comObject));
}
public System.Drawing.Size GetSize()
{
uint width, height;
comObject.GetSize(out width, out height);
// this will throw an exception if dimensions are > Int32.MaxValue
// in fact all of this code assumes dimensions will not overflow under arithmetic
return new System.Drawing.Size(checked((int)width), checked((int)height));
}
public Guid GetPixelFormat()
{
Guid pixelFormat;
comObject.GetPixelFormat(out pixelFormat);
return pixelFormat;
}
public byte[] CopyPixels()
{
uint width, height;
GetSizeInternal(out width, out height);
byte[] pixels = new byte[width * height * 4];
uint bufferSize = width * height * 32;
IntPtr buffer = Marshal.AllocCoTaskMem(checked((int)bufferSize));
comObject.CopyPixels(
IntPtr.Zero,
width * 4,
bufferSize,
buffer);
Marshal.Copy(buffer, pixels, 0, pixels.Length);
Marshal.FreeCoTaskMem(buffer);
return pixels;
}
private void GetSizeInternal(out uint width, out uint height)
{
comObject.GetSize(out width, out height);
}
~BitmapSource() { Dispose(); }
public void Dispose()
{
Interlocked.Exchange(ref inner, DelegateDisposable.Empty).Dispose();
}
}
class BitmapFrameDecode : BitmapSource
{
internal BitmapFrameDecode(IntPtr nativePointer) : base(nativePointer) { }
}
class FormatConverter : BitmapSource
{
IWICFormatConverter comObject;
internal FormatConverter(IntPtr nativePointer)
: base(nativePointer)
{
this.comObject = (IWICFormatConverter)Marshal.GetObjectForIUnknown(NativePointer);
}
public void Initialize(BitmapSource bitmapSource, Guid pixelFormat)
{
comObject.Initialize(bitmapSource.NativePointer, pixelFormat, 0, IntPtr.Zero, 0.0f, 0);
}
}
class ManagedIStream : IStream
{
Stream _stream;
public ManagedIStream(Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream");
_stream = stream;
}
public void Read(byte[] pv, int cb, IntPtr pcbRead)
{
int val = _stream.Read(pv, 0, cb);
if (pcbRead != IntPtr.Zero) {
Marshal.WriteInt32(pcbRead, val);
}
}
public void Write(byte[] pv, int cb, IntPtr pcbWritten)
{
_stream.Write(pv, 0, cb);
if (pcbWritten != IntPtr.Zero) {
Marshal.WriteInt32(pcbWritten, cb);
}
}
public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
{
SeekOrigin origin;
switch (dwOrigin)
{
case 0: origin = SeekOrigin.Begin; break;
case 1: origin = SeekOrigin.Current; break;
case 2: origin = SeekOrigin.End; break;
default: throw new ArgumentOutOfRangeException("dwOrigin");
}
long val = _stream.Seek(dlibMove, origin);
if (plibNewPosition != IntPtr.Zero) {
Marshal.WriteInt64(plibNewPosition, val);
}
}
public void SetSize(long libNewSize)
{
throw new NotSupportedException();
}
public void Stat(out STATSTG pstatstg, int grfStatFlag)
{
pstatstg = new STATSTG {
type = 2,
cbSize = _stream.Length,
};
if (_stream.CanRead && _stream.CanWrite) {
pstatstg.grfMode = 0x00000002;
} else if (_stream.CanWrite) {
pstatstg.grfMode = 0x00000001;
} else if (_stream.CanRead) {
pstatstg.grfMode = 0x00000000;
} else {
throw new IOException();
}
}
public void Clone(out IStream ppstm)
{
throw new NotSupportedException();
}
public void Commit(int grfCommitFlags)
{
throw new NotImplementedException();
}
public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
{
throw new NotImplementedException();
}
public void LockRegion(long libOffset, long cb, int dwLockType)
{
throw new NotImplementedException();
}
public void Revert()
{
throw new NotImplementedException();
}
public void UnlockRegion(long libOffset, long cb, int dwLockType)
{
throw new NotImplementedException();
}
}
}
sealed class DelegateDisposable : IDisposable
{
Action block;
static IDisposable empty;
public static IDisposable Empty {
get { return empty ?? (empty = new DelegateDisposable(() => {})); }
}
public DelegateDisposable(Action block)
{
this.block = block;
}
public void Dispose()
{
block();
}
}
}

Просмотреть файл

@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;
namespace Splat
{
internal static class NativeMethods
{
public const int S_OK = 0;
public static readonly Guid CLSID_WICImagingFactory = new Guid("cacaf262-9370-4615-a13b-9f5539da4c0a");
public static readonly Guid WICPixelFormat1bppIndexed = new Guid("6fddc324-4e03-4bfe-b185-3d77768dc901");
public static readonly Guid WICPixelFormatBlackWhite = new Guid("6fddc324-4e03-4bfe-b185-3d77768dc905");
public static readonly Guid WICPixelFormat8bppIndexed = new Guid("6fddc324-4e03-4bfe-b185-3d77768dc904");
public static readonly Guid WICPixelFormatDontCare = new Guid("6fddc324-4e03-4bfe-b185-3d77768dc900");
public static readonly Guid WICPixelFormat24bppBGR = new Guid("6fddc324-4e03-4bfe-b185-3d77768dc90c");
public static readonly Guid WICPixelFormat32bppBGRA = new Guid("6fddc324-4e03-4bfe-b185-3d77768dc90e");
public static readonly Guid WICPixelFormat48bppRGB = new Guid("6fddc324-4e03-4bfe-b185-3d77768dc915");
[DllImport("ole32.dll", ExactSpelling = true, EntryPoint = "CoCreateInstanceFromApp", PreserveSig = true)]
public static extern int CoCreateInstanceFromApp(
[In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
IntPtr pUnkOuter,
CLSCTX dwClsContext,
IntPtr reserved,
int countMultiQuery,
ref MultiQueryInterface query);
}
[Flags]
internal enum CLSCTX : uint
{
CLSCTX_INPROC_SERVER = 0x1,
CLSCTX_INPROC_HANDLER = 0x2,
CLSCTX_LOCAL_SERVER = 0x4,
CLSCTX_INPROC_SERVER16 = 0x8,
CLSCTX_REMOTE_SERVER = 0x10,
CLSCTX_INPROC_HANDLER16 = 0x20,
CLSCTX_RESERVED1 = 0x40,
CLSCTX_RESERVED2 = 0x80,
CLSCTX_RESERVED3 = 0x100,
CLSCTX_RESERVED4 = 0x200,
CLSCTX_NO_CODE_DOWNLOAD = 0x400,
CLSCTX_RESERVED5 = 0x800,
CLSCTX_NO_CUSTOM_MARSHAL = 0x1000,
CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000,
CLSCTX_NO_FAILURE_LOG = 0x4000,
CLSCTX_DISABLE_AAA = 0x8000,
CLSCTX_ENABLE_AAA = 0x10000,
CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000,
CLSCTX_ACTIVATE_32_BIT_SERVER = 0x40000,
CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000,
CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
CLSCTX_ALL = CLSCTX_SERVER | CLSCTX_INPROC_HANDLER
}
internal enum WICDecodeOptions : uint
{
WICDecodeMetadataCacheOnDemand = 0,
WICDecodeMetadataCacheOnLoad = 1,
WICMETADATACACHEOPTION_FORCE_DWORD = 0x7fffffff
}
[Guid("ec5ec8a9-c395-4314-9c77-54d7a935ff70")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
internal interface IWICImagingFactory
{
void CreateDecoderFromFilenameDummy();
int CreateDecoderFromStream(
IStream pIStream,
ref Guid pguidVendor,
WICDecodeOptions metadataOptions,
out IntPtr ppIDecoder);
void CreateDecoderFromFileHandleDummy();
void CreateComponentInfoDummy();
void CreateDecoderDummy();
void CreateEncoderDummy();
void CreatePaletteDummy();
int CreateFormatConverter(out IntPtr ppIFormatConverter);
}
[Guid("9EDDE9E7-8DEE-47ea-99DF-E6FAF2ED44BF")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
internal interface IWICBitmapDecoder
{
void QueryCapabilityDummy();
void InitializeDummy();
void GetContainerFormatDummy();
void GetDecoderInfoDummy();
void CopyPaletteDummy();
void GetMetadataQueryReaderDummy();
void GetPreviewDummy();
void GetColorContextsDummy();
void GetThumbnailDummy();
void GetFrameCountDummy();
int GetFrame(uint index, out IntPtr ppIFrameDecode);
}
[Guid("00000120-a8f2-4877-ba0a-fd2b6645fb94")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
internal interface IWICBitmapSource
{
void GetSize(out uint puiWidth, out uint puiHeight);
void GetPixelFormat(out Guid pPixelFormat);
void GetResolutionDummy();
void CopyPaletteDummy();
void CopyPixels(
IntPtr prc, // WICRect
uint cbStride,
uint cbBufferSize,
IntPtr pbBuffer);
}
[Guid("00000301-a8f2-4877-ba0a-fd2b6645fb94")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
internal interface IWICFormatConverter
{
#region IWICBitmapSource
void GetSizeDummy();
void GetPixelFormatDummy();
void GetResolutionDummy();
void CopyPaletteDummy();
void CopyPixelsDummy();
#endregion
void Initialize(
IntPtr pISource,
[MarshalAs(UnmanagedType.LPStruct)]
Guid dstFormat,
uint dither,
IntPtr pIPalette,
double alphaThresholdPercent,
uint paletteTranslate);
}
[StructLayout(LayoutKind.Sequential)]
internal struct MultiQueryInterface
{
public IntPtr InterfaceIID;
public IntPtr IUnknownPointer;
public int ResultCode;
}
}