Einführung Das .NET Compact Framework ist eine hervorragende, wenn nicht die beste API für mobile Geräte. Seine Grafik-Engine wird stark gedrosselt, um die Rendering-Geschwindigkeit zu erhöhen und den Speicherverbrauch zu reduzieren. Es scheint jedoch weit davon entfernt zu sein, den wachsenden Ansprüchen der Benutzer an ein besseres Grafikerlebnis gerecht zu werden. Der Versuch, einige der erweiterten Funktionen zum Rendern von Vektorgrafiken im .NET Compact Framework zu nutzen, kann eine mühsame Aufgabe sein. Entwickler haben zwei Möglichkeiten:
1. Wenden Sie sich dem nativen Code zu. Beispielsweise könnte die Pocket PC Game API eine gute Wahl sein. Seine Leistung ist beeindruckend. Weitere Informationen finden Sie in einem sehr umfassenden Artikel unter: http://msdn.microsoft.com/mobility/samples/default.aspx?pull=/library/en-us/dnnetcomp/html/gmangame asp . Das Problem besteht darin, dass der native Code das Rendern von Vektorgrafiken nicht unterstützt und mit einigen Geräten nicht kompatibel ist. Darüber hinaus funktioniert es möglicherweise nicht mit Pocket-PC-Emulatoren. Sie können sich vorstellen, wie schwierig es ist, ein solches Programm zu debuggen.
2. Bitte warten Sie, bis die mobile Grafik-Engine der nächsten Generation herauskommt. Soweit ich weiß, wird Windows CE 5 eine leistungsstarke Direct3D Mobile-Engine enthalten. Das sind gute Nachrichten für Entwickler mobiler Spiele, aber Direct3D ist nicht für zweidimensionale Grafiken geeignet. Es ist zu komplex, um in allgemeinen Anwendungen verwendet zu werden.
Was wir brauchen, ist eine leistungsstarke und benutzerfreundliche 2D-Grafik-Engine wie GDI+. Deshalb habe ich das XrossOne GDI+-Projekt von Grund auf entwickelt. Es ist vollständig in C#-verwaltetem Code geschrieben und enthält keinen nativen oder unsicheren Code. Nach Monaten harter Arbeit kann ich am Anfang dieses Artikels endlich die herunterladbare Originalversion bereitstellen.
Erste Schritte Von Beginn dieses Projekts an hatte ich immer im Kopf, dass die XrossOne GDI+-Engine auf verschiedenen Handheld-Geräten und Plattformen neutral sein sollte. Dadurch ist es kompatibel mit Pocket PC, Windows CE, Smartphones, Windows .NET und Mono. Sie können dieselbe Laufzeit auf ein anderes Ziel kopieren und es funktioniert weiterhin einwandfrei.
Die folgende Tabelle fasst die Gesamtarchitektur zusammen.
Layer-NamespaceXrossOne GDI+ API XrossOne.Drawing
Festkommabasierte zweidimensionale Grafik-Engine XrossOne.DrawingFP
16.16 Festkomma-Berechnungs-Engine XrossOne.FixedPoint
Es gibt drei Schichten in XrossOne GDI+. Die unterste Stufe ist die „16.16 Fixed-Point Computing Engine“. Eine der Hauptklassen – MathFP – ist aus der Beartronics J2ME-Bibliothek übernommen. Mehrere Funktionen wurden optimiert, darunter sqrt, atan und PointFP.Distancecalculation. Unter dem Namensraum XrossOne.FixedPoint gibt es drei weitere Klassen: SingleFP, DoubleFP und MatrixFP. SingleFP ist eine Hilfsklasse für 16.16 Festkommazahlen. Es bietet Komfort für die Konvertierung zwischen Festkommatypen und Standardtypen (int, float, string). MatrixFP ist für Festkomma-2D-Transformationen geschrieben. Da Festkommaberechnungen weniger präzise sind, kann es bei kaskadierten Transformationen zu einem gewissen Genauigkeitsverlust kommen. In den meisten Fällen können beispielsweise zwei Inversionsoperationen die ursprüngliche Matrix nicht wiederherstellen. DoubleFP ist vorhanden, um die Bibliothek zu vervollständigen, wird aber noch nicht verwendet.
„Festkommabasierte zweidimensionale Grafik-Engine“ ist der Kern von XrossOne GDI+. Es implementiert viele komplexe Vektorgrafikalgorithmen, wie z. B. Anti-Aliasing-Zeichnung, Linienabschluss-/Verbindungsdekoration, 2D-Transformationen, Farbverlaufsfüllungen, Alphakanal-Compositing und mehr. Die meisten erweiterten Funktionen in nativem GDI+ finden Sie hier. Allerdings sollten Sie es nur in wenigen Fällen direkt verwenden, da seine festkommabasierte Schnittstelle nicht programmiererfreundlich ist, aber machen Sie sich in dieser Situation keine allzu großen Sorgen. Es steht eine gut gekapselte API zur Verfügung. Sie finden sie im XrossOne.Drawing-Namespace. Die Klassen in XrossOne.Drawing sind den Klassen in System.Drawing sehr ähnlich, außer dass jede Klasse am Ende ein „X“ hat. Beispielsweise entspricht die Klasse XrossOne.Drawing.PenX der Klasse System.Drawing.Pen. Für die Konvertierung von GDI+-Programmen in XrossOne GDI+ gibt es einen kleinen Trick. Benennen Sie im Abschnitt „using“ die XrossOne GDI+-Klassen in die entsprechenden Klassen um. Beispiel:
using Pen = XrossOne.Drawing.PenX;
using LinearGradientBrush = XrossOne.Drawing.LinearGradientBrushX;
using Matrix = XrossOne.Drawing.MatrixX;
Hauptfunktionen Anti-Aliasing-Zeichnung von Vektorgrafiken
Mit XrossOne Mobile GDI+ können alle Arten von zweidimensionalen geometrischen Formen gerendert werden, z. B. Liniensegmente, Rechtecke, Polygone, Ellipsen, Sektoren, Bezier-Splines, Kardinal-Splines usw. Allerdings sind Sektoren, Bezier-Splines und Radix-Splines im .NET Compact Framework nicht verfügbar. Darüber hinaus werden alle Grafiken beim Rendern automatisch geglättet. Dadurch wird eine superglatte Qualität erreicht. Im .NET Compact Framework ist die Breite des Pinsels auf 1 Pixel festgelegt. Diese Einschränkung gibt es in XrossOne GDI+ nicht. Auf die Umrisse aller Formen können Pinsel unterschiedlicher Größe aufgetragen werden, wie in Abbildung 1 dargestellt.
Abbildung 1. Anti-Aliasing-Vektorgrafikzeichnung
Codebeispiel 1
//Löschen Sie den Hintergrund und setzen Sie den Transformationsstatus zurück
gx.Clear(Farbe.Weiß);
gx.ResetTransform();
//Zeichne ein Schräggitter als Hintergrund
PenX pen = new PenX(Utils.FromArgb(0x40, Color.LightGray), 5);
for (int i = -Height; i < Breite + Höhe; i+=20)
{
gx.DrawLine(pen, i, 0, i + Height, Height);
gx.DrawLine(pen, i, 0, i - Höhe, Höhe);
}
//Zeichnen Sie ein DarkMagenta-Rechteck mit einem 10,5-Pixel-Stift
Farbe c = Utils.FromArgb(0x80, Color.DarkMagenta);
pen = new PenX(c, 10.5f);
gx.DrawRectangle(pen, 50, 20, 150, 200);
//Füllen Sie ein grüngelbes Rechteck
c = Utils.FromArgb(0xA0, Color.GreenYellow);
BrushX-Pinsel = new SolidBrushX(c);
gx.FillRectangle(brush, 120, 50, 90, 150);
//Zeichne eine BlueViolet-Ellipse mit einem 10,5-Pixel-Stift
c = Utils.FromArgb(0x80, Color.BlueViolet);
pen = new PenX(c, 10.5f);
gx.DrawEllipse(pen, 50, 20, 150, 80);
//Eine rote Ellipse füllen
c = Utils.FromArgb(0xA0, Color.Red);
Pinsel = neues SolidBrushX(c);
gx.FillEllipse(brush, 20, 50, 80, 150);
//Zeichne einen HotPink-Kuchen von 156,5 Grad bis -280,9 Grad
pen.Color = Utils.FromArgb(0xA0, Color.HotPink);
gx.DrawPie(pen, 3.6f, 120.3f, 200.8f, 130.1f, 156.5f, -280.9f);
//Orange Bezier-Kurven zeichnen
c = Utils.FromArgb(0xA0, Color.Orange);
pen = new PenX(c, 16);
Punktstart = neuer Punkt (70, 100);
Punktkontrolle1 = neuer Punkt(100, 10);
Punktkontrolle2 = neuer Punkt(150, 50);
Punktende1 = neuer Punkt(200, 200);
Punktkontrolle3 = neuer Punkt(100, 150);
Punktkontrolle4 = neuer Punkt(50, 200);
Punkt end2 = neuer Punkt(10, 150);
Point[] bezierPoints ={start, control1, control2, end1, control3, control4, end2};
pen.EndCap = LineCapX.Round;
gx.DrawBeziers(pen, bezierPoints);
//Aktualisieren
Invalidate();
Die Vektorgrafikausgabe von XrossOne GDI+ und nativem GDI+ ist bis auf die Radix-Splines identisch. Mein Algorithmus stammt aus dem Artikel Smoothing Algorithm Using Bezier Curves von Jean-Yves Queinec. Daher kann es zu einigen Unterschieden zwischen den Ausgaben kommen, wie in Abbildung 2 unten dargestellt.
Abbildung 2. Ausgabe von DrawCurve/DrawClosedCurve
Obwohl die meisten Funktionen zum Rendern von Vektorgrafiken implementiert wurden, gibt es noch einiges zu tun. Einige Funktionen (DrawString, DrawImage, DrawPath usw.) werden erst in der nächsten Version verfügbar sein.
Farbverlaufsfüllung
In nativem GDI+ gibt es fünf Pinsel: SolidBrush, LinearGradientBrush, PathGradientBrush, TextureBrush und HatchBrush. Allerdings sind in dieser Version nur SolidBrush und LinearGradientBrush verfügbar. XrossOne GDI+ unterstützt RadialGradientBrush, aber nicht PathGradientBrush. Abbildung 5 unten zeigt eine Verlaufsfüllung.
Abbildung 5. Verlaufsfüllung
Codebeispiel 4
//Löschen Sie den Hintergrund und setzen Sie den Transformationsstatus zurück
gx.Clear(Farbe.Weiß);
gx.ResetTransform();
//Ein Rechteck mit einem schwarz-weißen LinearGradientBrushX füllen
Rechteck r = neues Rechteck (20, 50, 300, 100);
Farbe c1 = Farbe.Schwarz;
Farbe c2 = Farbe.Weiß;
BrushX brush1 = new LinearGradientBrushX(r, c1, c2, 30F);
gx.FillRectangle(brush1, r);
// Füllen Sie ein Rechteck mit einem 7-Farben-LinearGradientBrushX
r = neues Rechteck (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=neue Farbe[]
{Farbe.Rot, Farbe.Orange, Farbe.Gelb, Farbe.Grün, Farbe.Blau, Farbe.Indigo, Farbe.Violet};
br.InterpolationColors=cb;
gx.TranslateTransform(160, 10);
gx.RotateTransform(60F);
gx.FillRectangle(br, r);
// Füllen Sie ein Rechteck mit einem 7-Farben-RadialGradientBrushX
rY += 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);
//Aktualisieren
Invalidate();
Alpha-Kanal-Compositing
Die Color-Struktur im System.Drawing-Namespace ist sowohl im .NET Framework als auch im .NET Compact Framework verfügbar. Der Unterschied besteht darin, dass die Alpha-Komponente deaktiviert ist und der Hue-Saturation-Brightness (HSB)-Wert im .NET Compact Framework nicht verfügbar ist. Glücklicherweise funktioniert das Alphakanal-Compositing perfekt mit XrossOne GDI+ (wie Sie vielleicht aus dem vorherigen Grafikbeispiel abgeleitet haben).
Leistung 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 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 %
Es stimmt, dass Handheld-PC-CPUs oft deutlich leistungsschwächer sind als Standard-PC-CPUs. Umfangreiche Berechnungen können dazu führen, dass Handheld-Geräte weniger reagieren, was für Benutzer frustrierend sein kann. Mit anderen Worten: Die Leistung ist für die Software von Handheld-Geräten von entscheidender Bedeutung. Bevor Sie XrossOne Mobile GDI+ in einer größeren Situation verwenden, sollten Sie daher möglicherweise seine Gesamtleistung analysieren. Da die meisten entsprechenden Funktionen in GDI+ für das .NET Compact Framework nicht verfügbar sind, wurden die Benchmarks zwischen XrossOne Mobile GDI+ und GDI+ für das .NET Framework durchgeführt. Die Tests wurden in den folgenden Kategorien durchgeführt: Vektorgrafik-Rendering, 2D-Transformationen und Verlaufsfüllungen. Die Testszenarien wurden unter den gleichen Bedingungen durchgeführt. Sie finden die Benchmark-Programme im Download-Paket und können sich deren grafische Ausgabe schnell unter http://www.xrossone.com/projects.php?menu=4 ansehen.
XrossOne Mobile GDI+ ist vollständig in C#-verwaltetem Code geschrieben und seine Gesamtleistung ist akzeptabel (siehe Tabelle unten), obwohl 2D-Transformationen und Verlaufsfüllungen in zukünftigen Versionen einer weiteren Optimierung bedürfen.
Lösung 66,7 % DrawBezier ms. 21 1,7 %