Not long after V1 of the Compact Framework went out we started getting questions about support (or lack thereof) for advanced graphics and drawing functions available on the desktop framework but missing from the Compact Framework. For V1 we made a decision based on both size and performance to implement System.Windows.Forms and System.Drawing over the native functionality of WindowsCE/PocketPC/Smartphone. Since there is no version of GDI+ on these platforms, System.Drawing was limited to GDI (actually GWES) functionality and lacked a number of the more sophisticated features such as image rotation and coordinate scaling (among other things).
However, with the advent of Windows Mobile 5.0 we have reason for some rejoicing: alpha blending. This ability allows some interesting effects which can really spiff up an application.
Briefly, there are two ways to do alpha blending in WM5: the AlphaBlend() function and with the Image COM object in the Imaging API.
First, AlphaBlend().
The AlphaBlend function has the ability to do either constant alpha blending over the entire source image or pixel level blending for bitmaps with an alpha channel. Unfortunately, the NETCF bitmap loader creates a bitmap with the same pixel format as the display when loading a bitmap file or resource and loses the alpha channel information (if present). This means that the AlphaBlend function can only be used for constant value alpha blending of images.
Here is a set of declarations for using AlphaBlend through p/invoke:
public struct BlendFunction{ public byte BlendOp; public byte BlendFlags; public byte SourceConstantAlpha; public byte AlphaFormat;}
public enum BlendOperation : byte{ AC_SRC_OVER = 0x00}
public enum BlendFlags : byte{ Zero = 0x00}
public enum SourceConstantAlpha : byte{ Transparent = 0x00, Opaque = 0xFF}
public enum AlphaFormat : byte{ AC_SRC_ALPHA = 0x01}
public class PlatformAPIs{ [DllImport("coredll.dll")] extern public static Int32 AlphaBlend(IntPtr hdcDest, Int32 xDest, Int32 yDest, Int32 cxDest, Int32 cyDest, IntPtr hdcSrc, Int32 xSrc, Int32 ySrc, Int32 cxSrc, Int32 cySrc, BlendFunction blendFunction);}
Here's an example of using the function inside an override of OnPaint():
Next, the Image object (or IImage interface) from the Imaging API.
If we instantiate an IImagingFactory and use it to load our image from a file or resource, the alpha channel will be preserved (don't, however, confuse the IImage COM interface we get back with a System.Drawing.Image managed object). Then we can ask the Imaging object to draw itself and it will use the alpha channel information in the image during the render.
Here is the declaration of the enums and COM interfaces:
// Pulled from gdipluspixelformats.h in the Windows Mobile 5.0 Pocket PC SDKpublic enum PixelFormatID : int{ PixelFormatIndexed = 0x00010000, // Indexes into a palette PixelFormatGDI = 0x00020000, // Is a GDI-supported format PixelFormatAlpha = 0x00040000, // Has an alpha component PixelFormatPAlpha = 0x00080000, // Pre-multiplied alpha PixelFormatExtended = 0x00100000, // Extended color 16 bits/channel PixelFormatCanonical = 0x00200000, PixelFormatUndefined = 0, PixelFormatDontCare = 0, PixelFormat1bppIndexed = (1 | ( 1 << 8) | PixelFormatIndexed | PixelFormatGDI), PixelFormat4bppIndexed = (2 | ( 4 << 8) | PixelFormatIndexed | PixelFormatGDI), PixelFormat8bppIndexed = (3 | ( 8 << 8) | PixelFormatIndexed | PixelFormatGDI), PixelFormat16bppRGB555 = (5 | (16 << 8) | PixelFormatGDI), PixelFormat16bppRGB565 = (6 | (16 << 8) | PixelFormatGDI), PixelFormat16bppARGB1555 = (7 | (16 << 8) | PixelFormatAlpha | PixelFormatGDI), PixelFormat24bppRGB = (8 | (24 << 8) | PixelFormatGDI), PixelFormat32bppRGB = (9 | (32 << 8) | PixelFormatGDI), PixelFormat32bppARGB = (10 | (32 << 8) | PixelFormatAlpha | PixelFormatGDI | PixelFormatCanonical), PixelFormat32bppPARGB = (11 | (32 << 8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatGDI), PixelFormat48bppRGB = (12 | (48 << 8) | PixelFormatExtended), PixelFormat64bppARGB = (13 | (64 << 8) | PixelFormatAlpha | PixelFormatCanonical | PixelFormatExtended), PixelFormat64bppPARGB = (14 | (64 << 8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatExtended), PixelFormatMax = 15}
// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDKpublic enum BufferDisposalFlag : int{ BufferDisposalFlagNone, BufferDisposalFlagGlobalFree, BufferDisposalFlagCoTaskMemFree, BufferDisposalFlagUnmapView}
// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDKpublic enum InterpolationHint : int{ InterpolationHintDefault, InterpolationHintNearestNeighbor, InterpolationHintBilinear, InterpolationHintAveraging, InterpolationHintBicubic}
// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDKpublic struct ImageInfo{ // I am being lazy here, I don't care at this point about the RawDataFormat GUID public uint GuidPart1; public uint GuidPart2; public uint GuidPart3; public uint GuidPart4; public PixelFormatID pixelFormat; public uint Width; public uint Height; public uint TileWidth; public uint TileHeight; public double Xdpi; public double Ydpi; public uint Flags;}
// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK[ComImport, Guid("327ABDA7-072B-11D3-9D7B-0000F81EF32E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)][ComVisible(true)]public interface IImagingFactory{ uint CreateImageFromStream(); // This is a place holder uint CreateImageFromFile(string filename, out IImage image); // We need the MarshalAs attribute here to keep COM interop from sending the buffer down as a Safe Array. uint CreateImageFromBuffer([MarshalAs(UnmanagedType.LPArray)] byte[] buffer, uint size, BufferDisposalFlag disposalFlag, out IImage image); uint CreateNewBitmap(); // This is a place holder uint CreateBitmapFromImage(); // This is a place holder uint CreateBitmapFromBuffer(); // This is a place holder uint CreateImageDecoder(); // This is a place holder uint CreateImageEncoderToStream(); // This is a place holder uint CreateImageEncoderToFile(); // This is a place holder uint GetInstalledDecoders(); // This is a place holder uint GetInstalledEncoders(); // This is a place holder uint InstallImageCodec(); // This is a place holder uint UninstallImageCodec(); // This is a place holder}
// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK[ComImport, Guid("327ABDA9-072B-11D3-9D7B-0000F81EF32E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)][ComVisible(true)]public interface IImage{ uint GetPhysicalDimension(out Size size); uint GetImageInfo(out ImageInfo info); uint SetImageFlags(uint flags); // "Correct" declaration: uint Draw(IntPtr hdc, ref Rectangle dstRect, ref Rectangle srcRect); uint Draw(IntPtr hdc, ref Rectangle dstRect, IntPtr NULL); uint PushIntoSink(); // This is a place holder uint GetThumbnail(uint thumbWidth, uint thumbHeight, out IImage thumbImage);}
Here is how to load an image from a file:
Here is how to render the image during the OnPaint():
// The bitmap needs to be created with the 32bpp pixel format for the IImage to do the right thing.
I've attached a simple WM5 PocketPC project for VS 2005 which includes several images (two .PNGs with alpha data) and demonstrates the use of AlphaBlend() and the Imaging API. Please note that there is no hardware acceleration of alpha blending on devices so use this functionality prudently or expect to notice an effect on your rendering performance.
This posting is provided "AS IS" with no warranties, and confers no rights.