简介 对于移动设备而言,.NET Compact Framework 即使不是最佳的 API,也是极好的 API。它的图形引擎受到很大的限制,以便提高呈现速度和降低内存消耗。但是,它似乎远远无法满足用户日益增长的对更好的图形体验的要求。尝试去获得 .NET Compact Framework 中的一些高级矢量图形呈现功能可能是一项乏味的任务。开发人员具有两个选择:
1.求助于本机代码。例如,Pocket PC Game API 可能是一项不错的选择。它的性能令人印象深刻。有关详细信息,请参阅位于以下位置的一篇非常全面的文章:http://msdn.microsoft.com/mobility/samples/default.aspx?pull=/library/en-us/dnnetcomp/html/gmangame.asp。问题在于本机代码不支持矢量图形呈现,并且与某些设备不兼容。此外,它可能无法与 Pocket PC 仿真程序协同工作。您可以想象调试这样的程序有多么困难。
2.请等待下一代移动图形引擎问世。据我所知,Windows CE 5 中将包含一个强大的 Direct3D Mobile引擎。这对于移动游戏开发人员来说是一个好消息,但是 Direct3D 不适合于二维图形。它太复杂了,因而无法在一般应用程序中应用。
我们所需要的是像 GDI+ 这样强大而易于使用的二维图形引擎。因此,我从零开始开发 XrossOne GDI+ 项目。它完全是用 C# 托管代码编写的,不包含任何本机代码或不安全的代码。经过几个月的艰苦工作之后,我终于可以在本文开头提供可下载的原始版本。
开始工作 从该项目一开始,我就一直铭记 XrossOne GDI+ 引擎应当对不同的手持设备和平台保持中立。结果,它可以与 Pocket PC、Windows CE、Smartphones、Windows .NET 和 Mono 兼容。您可以将同一个运行库复制到不同的目标,而它仍然可以正常工作。
下表概括了总体体系结构。
层 命名空间 XrossOne GDI+ API XrossOne.Drawing 基于定点的二维图形引擎 XrossOne.DrawingFP 16.16 定点计算引擎 XrossOne.FixedPoint
XrossOne GDI+ 中有三个层。最低层为“16.16 定点计算引擎”。其中一个主类 — MathFP — 是从 Beartronics J2ME 库 改编而来的。一些函数已经进行了优化,其中包括 sqrt、atan 和 PointFP.Distancecalculation。在命名空间 XrossOne.FixedPoint 下面,有其他三个类:SingleFP、DoubleFP 和 MatrixFP。SingleFP 是一个用于 16.16 定点数的 Helper 类。它为在定点类型和标准类型(int、float、string)之间进行转换提供了方便。MatrixFP 是为定点二维变换编写的。因为定点计算的精度较低,所以级联变换可能会损失一些精确性。例如,在大多数情况下,两次求逆运算无法还原原始矩阵。DoubleFP 的存在是为了使该库完备,但尚未使用。
“基于定点的二维图形引擎”是 XrossOne GDI+ 的内核。它实现了很多复杂的矢量图形算法,例如,反锯齿绘图、线帽/联接装饰、二维变换、渐变填充、Alpha 通道合成等等。这里可以找到本机 GDI+ 中的大多数高级功能。但是,您只应在少数情况下直接使用它,因为它的基于定点的接口对于程序员而言不够友好,但是不必过分担心这种情况。有一个封装良好的 API 可供使用。您可以在 XrossOne.Drawing 命名空间中找到它们。XrossOne.Drawing 中的类非常类似于 System.Drawing 中的类,不同之处在于每个类的末尾有一个字母“X”。例如,XrossOne.Drawing.PenX 类等效于 System.Drawing.Pen。有一个用于将 GDI+ 程序转换到 XrossOne GDI+ 的小窍门。在 using 节中,将 XrossOne GDI+ 类重命名为它们的等效类。例如:
using Pen = XrossOne.Drawing.PenX; using LinearGradientBrush = XrossOne.Drawing. LinearGradientBrushX; using Matrix = XrossOne.Drawing.MatrixX;
主要功能 反锯齿矢量图形绘图
通过 XrossOne Mobile GDI+ 可以呈现所有种类的二维几何图形,例如,线段、矩形、多边形、椭圆、扇形、贝塞尔样条曲线、基数样条曲线等等。但是,扇形、贝塞尔样条曲线和基数样条曲线在 .NET Compact Framework 中不可用。此外,所有图形在呈现时都自动进行反锯齿处理。这有助于获得超级平滑质量。在 .NET Compact Framework 中,画笔的宽度被固定为 1 个像素。这一限制在 XrossOne GDI+ 中不存在。画笔的不同大小可以应用于所有图形的轮廓,如图 1 所示。
 图 1. 反锯齿矢量图形绘图
代码示例 1
//Clear the background and reset the transform state gx.Clear(Color.White); gx.ResetTransform(); //Draw skew grid as the background PenX pen = new PenX(Utils.FromArgb(0x40, Color.LightGray), 5); for (int i = -Height; i < Width + Height; i+=20) { gx.DrawLine(pen, i, 0, i + Height, Height); gx.DrawLine(pen, i, 0, i - Height, Height); }
//Draw a DarkMagenta rectangle with a 10.5-pixel pen Color c = Utils.FromArgb(0x80, Color.DarkMagenta); pen = new PenX(c, 10.5f); gx.DrawRectangle(pen, 50, 20, 150, 200);
//Fill a GreenYellow rectangle c = Utils.FromArgb(0xA0, Color.GreenYellow); BrushX brush = new SolidBrushX(c); gx.FillRectangle(brush, 120, 50, 90, 150);
//Draw a BlueViolet ellipse with a 10.5-pixel pen c = Utils.FromArgb(0x80, Color.BlueViolet); pen = new PenX(c, 10.5f); gx.DrawEllipse(pen, 50, 20, 150, 80);
//Fill a Red ellipse c = Utils.FromArgb(0xA0, Color.Red); brush = new SolidBrushX(c); gx.FillEllipse(brush, 20, 50, 80, 150);
//Draw a HotPink pie from 156.5 degree to -280.9 degree pen.Color = Utils.FromArgb(0xA0, Color.HotPink); gx.DrawPie(pen, 3.6f, 120.3f, 200.8f, 130.1f, 156.5f, -280.9f);
//Draw Orange Bezier curves c = Utils.FromArgb(0xA0, Color.Orange); pen = new PenX(c, 16); Point start = new Point(70, 100); Point control1 = new Point(100, 10); Point control2 = new Point(150, 50); Point end1 = new Point(200, 200); Point control3 = new Point(100, 150); Point control4 = new Point(50, 200); Point end2 = new Point(10, 150); Point[] bezierPoints ={start, control1, control2, end1, control3, control4, end2}; pen.EndCap = LineCapX.Round; gx.DrawBeziers(pen, bezierPoints);
//Refresh Invalidate();
XrossOne GDI+ 和本机 GDI+ 的矢量图形输出是相同的,但基数样条曲线除外。我的算法取自 Jean-Yves Queinec 撰写的文章 Smoothing Algorithm Using Bezier Curves。因此,您可能发现在它们的输出之间存在一些差异,如下面的图 2 所示。
 图 2. DrawCurve/DrawClosedCurve 的输出
尽管大多数矢量图形呈现函数都已经得到实现,但仍然有一些工作需要完成。某些函数(DrawString、DrawImage、DrawPath 等等)直到下一个版本才可用。
线帽/联接装饰
为了使 .NET Compact Framework 保持紧凑,GDI+ 桌面版本中的很多功能在该精简版本中不可用。其中一项功能是线帽/联接装饰。当然,当轮廓的宽度为一个像素时,任何装饰都似乎是不必要的,但是在 XrossOne GDI+ 中,线帽/联接装饰却是绘制不同大小的轮廓所必备的。XrossOne GDI+ 中支持四个线帽(Flat、Round、Triangle 和 Square)和三个线段联接(Bevel、Miter 和 Round)。上述装饰的示例如图 3 所示。
 图 3. 线帽/联接装饰
代码示例 2
//Clear the background and reset the transform state gx.Clear(Color.White); gx.ResetTransform();
//Draw a pentacle with Miter line join and Round line cap PenX pen = new PenX(Color.Orange, 15); Point p1 = new Point(150, 80); Point p2 = new Point(100, 230); Point p3 = new Point(240, 150); Point p4 = new Point(60, 150); Point p5 = new Point(200, 230); Point[] points ={p1,p2,p3,p4,p5}; MatrixX m = new MatrixX(); m.Translate(-26, -70); m.TransformPoints(points); pen.LineJoin = LineJoinX.Miter; pen.EndCap = LineCapX.Round; pen.StartCap = LineCapX.Round; gx.DrawLines(pen, points);
//Draw a pentacle with Bevel line join and Triangle line cap pen = new PenX(Utils.FromArgb(0x80, Color.Blue), 15); Point[] points2 ={p1,p2,p3,p4,p5}; m = new MatrixX(); m.Translate(-10, -30); m.TransformPoints(points2); pen.LineJoin = LineJoinX.Bevel; pen.StartCap = LineCapX.Triangle; gx.DrawLines(pen, points2);
//Draw a pentacle with Round line join and Round line cap pen = new PenX(Utils.FromArgb(0x80, Color.BlueViolet), 20); Point[] points3 ={p1,p2,p3,p4,p5}; m = new MatrixX(); m.Translate(-40, 20); m.TransformPoints(points3); pen.LineJoin = LineJoinX.Round; pen.EndCap = LineCapX.Round; gx.DrawLines(pen, points3);
//Refresh Invalidate();
二维变换
变换功能在 .NET Compact Framework 中不可用,这意味着,如果您要通过 30 度旋转来绘制椭圆,则您必须计算它的轮廓并且逐个像素地绘制它。在大多数情况下,这是乏味和低效的。幸运的是,XrossOne GDI+ 提供了功能完善的二维变换。您可以移动、缩放和旋转您喜欢的任何图形,如图 4 所示。
 图 4. 二维变换
代码示例 3
//Clear the background and reset the transform state gx.Clear(Color.White); gx.ResetTransform();
//Draw a rectangle and apply scale transform to it Color c = Utils.FromArgb(0x80, Color.Orange); PenX pen = new PenX(c, 7.5f); pen.LineJoin = LineJoinX.Miter; gx.DrawRectangle(pen, 10, 10, 50, 50); gx.ScaleTransform(3, 3); gx.DrawRectangle(pen, 10, 10, 50, 50);
//Draw a series of ellipses and rotate 10 degrees between the preceding and the successor c = Utils.FromArgb(0x80, Color.BlueViolet); pen = new PenX(c, 5.5f); gx.ScaleTransform(0.25F, 0.25F); gx.RotateTransform(-40); for (int j = 0; j < 10; j ++) { gx.RotateTransform(10); gx.DrawEllipse(pen, 100, 50, 50, 130); }
//Draw a series of triangles and apply translate transform to them pen = new PenX(Utils.FromArgb(0x80, Color.Blue), 10); Point p1 = new Point(120, 80); Point p2 = new Point(100, 200); Point p3 = new Point(140, 200); Point[] points ={p1,p2,p3}; gx.Transform = new MatrixX(); pen.LineJoin = LineJoinX.Round; for (int i = 0; i <= 40; i +=10) { gx.TranslateTransform(20, 10); gx.DrawPolygon(pen, points); }
//Refresh Invalidate();
渐变填充
在本机 GDI+ 中有五种画刷 — SolidBrush、LinearGradientBrush、PathGradientBrush、TextureBrush 和 HatchBrush。但是,在该版本中,只有 SolidBrush 和 LinearGradientBrush 可用。XrossOne GDI+ 支持 RadialGradientBrush 而不是 PathGradientBrush。下面的图 5 演示了渐变填充。
 图 5. 渐变填充
代码示例 4
//Clear the background and reset the transform state gx.Clear(Color.White); gx.ResetTransform();
//Fill a rectangle with a black-white LinearGradientBrushX Rectangle r = new Rectangle(20, 50, 300, 100); Color c1 = Color.Black; Color c2 = Color.White; BrushX brush1 = new LinearGradientBrushX(r, c1, c2, 30F); gx.FillRectangle(brush1, r);
//Fill a rectangle with a 7-color LinearGradientBrushX r = new Rectangle(90, 100, 150, 100); LinearGradientBrushX br = new LinearGradientBrushX(r,Color.Black,Color.Black, 60F); ColorBlendX cb = new ColorBlendX(); cb.Positions=new float[7]; int i=0; for(float f=0;f<=1;f+=1.0f/6) cb.Positions[i++]=f; cb.Colors=new Color[] {Color.Red,Color.Orange,Color.Yellow,Color.Green,Color.Blue,Color.Indigo,Color.Violet}; br.InterpolationColors=cb; gx.TranslateTransform(160, 10); gx.RotateTransform(60F); gx.FillRectangle(br, r);
//Fill a rectangle with a 7-color RadialGradientBrushX r.Y += 50; RadialGradientBrushX brush2 = new RadialGradientBrushX(r, Color.Black,Color.Black, 220F); brush2.InterpolationColors = cb; gx.RotateTransform(-45F); gx.TranslateTransform(-200, -170); gx.FillRectangle(brush2, r);
//Refresh Invalidate();
Alpha 通道合成
System.Drawing 命名空间中的 Color 结构在 .NET Framework 和 .NET Compact Framework 中都可用。区别在于 .NET Compact Framework 中禁用了 alpha 成分并且色调-饱和度-亮度 (HSB) 值不可用。幸运的是,alpha 通道合成可以完美地与 XrossOne GDI+ 协同工作(您可能已经从前面的图形示例中推断出这一点)。
性能 手持 PC 的 CPU 的功能确实通常要比标准 PC 的 CPU 差很多。繁重的计算可能使手持设备的响应速度降低,从而可能使用户变得不胜其烦。换句话说,性能对于手持设备软件而言至关重要。因此,在重大场合下使用 XrossOne Mobile GDI+ 之前,您可能希望分析它的总体性能。因为 GDI+ 中大多数对应于 .NET Compact Framework 的等效函数都不可用,所以基准测试是针对 .NET Framework 在 XrossOne Mobile GDI+ 和 GDI+ 之间进行的。测试是在下列类别中执行的:矢量图形呈现、二维变换和渐变填充。测试方案在相同的条件下执行。您可以在下载软件包中找到基准测试程序,并且可以在 http://www.xrossone.com/projects.php?menu=4 快速查看它们的图形输出。
XrossOne Mobile GDI+ 完全是用 C# 托管代码编写的,它的总体性能可以接受(参见下表),尽管二维变换和渐变填充需要在以后的版本中进一步优化。
DrawLine | 2.604 ms | 0.901 ms | 189.0% | DrawRect | 3.705 ms | 1.602 ms | 131.3% | DrawPolygon | 3.205 ms | 1.502 ms | 113.4% | DrawEllipse | 6.409 ms | 2.403 ms | 166.7% | DrawBezier | 3.505 ms | 1.602 ms | 118.8% | DrawCurve | 4.006 ms | 1.402 ms | 185.7% | DrawPie | 6.810 ms | 2.003 ms | 240.0% | TranslateTransform | 10.615 ms | 3.405 ms | 211.7% | ScaleTransform | 4.106 ms | 0.801 ms | 412.6% | RotateTransform | 7.811 ms | 1.803 ms | 333.2% | LinearGradient (1) | 9.013 ms | 2.103 ms | 328.6% | LinearGradient (2) | 8.012 ms | 1.803 ms | 344.4% |
缺俚墓δ?/STRONG> 在上述部分中,我们已经演示了 XrossOne Mobile GDI+ 的一些令人兴奋的功能。但是,该版本中仍然缺少一些功能:
• 文本输出
• 光栅输出
• 虚线样式支持
• HatchBrush、PathGradiantBrush、TextureBrush
• Pen.CustomStartCap、Pen.CustomEndCap
小结 我们相信 XrossOne Mobile GDI+ 可以帮助 .NET Compact Framework 开发人员创建更加引人注目的图形接口。考虑到它的小内存足迹 (72kb),它的功能已经相当丰富了。而且,它的性能对于一般应用程序而言已经足够了。此外,多亏它的纯粹托管代码设计,XrossOne GDI+ 是一个跨平台、跨设备的二维图形引擎。
|