当Compact Framework 1.0推出不久,我们开始质疑为何在Compact Framework上不支持桌面Framework版本上拥有(或缺少的)的高级图形和绘图功能。在1.0版本,考虑到大小和性能,我们实现了WindowsCE/PocketPC/Smartphone上的System.Windows.Forms和System.Drawing的本地功能。既然在这些平台上没有GDI+,System.Drawing局限于GDI(实际上是GWES)功能,并且缺少了很多已经相当成熟的特性,比如图像旋转和坐标缩放(以及其它)。
然而,Windows Mobile 5.0的推出让我们有理由感到欣喜:alpha blending。这个功能可以用一些有趣的效果来装饰一个应用程序。
简而言之,在WM5里,有两种方法应用alpha混合:AlphaBlend()功能和Imaging API的Image COM对象。
首先介绍的是AlphaBlend()。
AlphaBlend功能可以在整个源图上进行常数alpha混合,或者在位图上通过alpha通道进行像素级别的混合。不幸的是,NETCF上的位图装载器在从像素格式的位图文件或者资源中创建一个位图对象进行显示时,丢失了alpha通道的信息(如果原图存在alpha通道),这就意味着Alpha混合功能只能对图象使用常数alpha混合。
以下是基于p/invoke调用的alpha混合的一组声明:
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);
}
以下是一个例子,代码构建于覆写的OnPaint()里:
// Load the image to use with the AlphaBlend API.
string path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
constantAlphaImage = new Bitmap(path + @"\blendme.bmp");
// AlphaBlend takes two HDC's - one source and one destination. Here's the source.
using (Graphics gxSrc = Graphics.FromImage(constantAlphaImage))
{
IntPtr hdcDst = e.Graphics.GetHdc();
IntPtr hdcSrc = gxSrc.GetHdc();
BlendFunction blendFunction = new BlendFunction();
blendFunction.BlendOp = (byte)BlendOperation.AC_SRC_OVER; // Only supported blend operation
blendFunction.BlendFlags = (byte)BlendFlags.Zero; // Documentation says put 0 here
blendFunction.SourceConstantAlpha = (byte)128; // Constant alpha factor
blendFunction.AlphaFormat = (byte)0; // Don't look for per pixel alpha
PlatformAPIs.AlphaBlend(hdcDst, left, top, width, height, hdcSrc, 0, 0, width, height, blendFunction);
gxSrc.ReleaseHdc(hdcSrc); // Required cleanup to GetHdc()
e.Graphics.ReleaseHdc(hdcDst); // Required cleanup to GetHdc()
}
下面介绍Imaging API的Image对象(或者IImage接口)。
如果我们实例化一个IImagingFactory并用它从文件或资源装载一个图像,alpha通道将被同时装载。然后我们能够调用Imaging对象来绘制它自身,它将在图像呈现的时候使用alpha通道信息。
以下是COM接口和枚举的声明:
// Pulled from gdipluspixelformats.h in the Windows Mobile 5.0 Pocket PC SDK
public 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 SDK
public enum BufferDisposalFlag : int
{
BufferDisposalFlagNone,
BufferDisposalFlagGlobalFree,
BufferDisposalFlagCoTaskMemFree,
BufferDisposalFlagUnmapView
}
// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
public enum InterpolationHint : int
{
InterpolationHintDefault,
InterpolationHintNearestNeighbor,
InterpolationHintBilinear,
InterpolationHintAveraging,
InterpolationHintBicubic
}
// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
public 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);
}
以下是怎样从文件装载一个图像:
string path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
// Load the image with alpha data through Imaging.
IImagingFactory factory = (IImagingFactory)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("327ABDA8-072B-11D3-9D7B-0000F81EF32E")));
IImage imagingImage;
factory.CreateImageFromFile(path + @"\ihavealpha.png", out imagingImage);
以下是怎样从内嵌资源装载一个图像:
string path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
IImagingFactory factory = (IImagingFactory)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("327ABDA8-072B-11D3-9D7B-0000F81EF32E")));
// Load the image with alpha data from an embedded resource through Imaging
// !! If your data source is not a MemoryStream, you will need to get your image data into a byte array and use it below. !!
MemoryStream strm = (MemoryStream)Assembly.GetExecutingAssembly().GetManifestResourceStream("AlphaExample.embedded.png");
byte[] pbBuf = strm.GetBuffer();
uint cbBuf = (uint)strm.Length;
IImage imagingResource;
factory.CreateImageFromBuffer(pbBuf, cbBuf, BufferDisposalFlag.BufferDisposalFlagNone, out imagingResource);
以下是怎样在OnPaint()中呈现图像:
// The bitmap needs to be created with the 32bpp pixel format for the IImage to do the right thing.
using (Bitmap backBuffer = new Bitmap(this.ClientSize.Width, this.ClientSize.Height, System.Drawing.Imaging.PixelFormat.Format32bppRgb))
{
using (Graphics gxBuffer = Graphics.FromImage(backBuffer))
{
gxBuffer.Clear(this.BackColor);
IntPtr hdcDest = gxBuffer.GetHdc();
Rectangle dstRect = new Rectangle(left, top, right, bottom);
// Ask the Image object from Imaging to draw itself.
imagingImage.Draw(hdcDest, ref dstRect, IntPtr.Zero);
gxBuffer.ReleaseHdc(hdcDest);
// Put the final composed image on screen.
e.Graphics.DrawImage(backBuffer, 0, 0);
}
}
我已经附加了一个简单的基于VS 2005的WM5 PocketPC项目,其中包括几个图像(两个带alpha数据的.PNGs),用于验证使用AlphaBlend()和Imaging API功能。请注意,设备上的alpha混合没有使用硬件加速,所以请谨慎使用此功能,并留意图像呈现时性能方面的影响。
附件:AlphaExample.zip
Published Friday, April 07, 2006 7:40 AM by clort