Introduction Le .NET Compact Framework est une excellente API, sinon la meilleure, pour les appareils mobiles. Son moteur graphique est fortement bridé afin d'augmenter la vitesse de rendu et de réduire la consommation de mémoire. Cependant, il semble loin de répondre aux demandes croissantes des utilisateurs pour une meilleure expérience graphique. Essayer d'obtenir certaines des fonctionnalités avancées de rendu de graphiques vectoriels dans le .NET Compact Framework peut être une tâche fastidieuse. Les développeurs ont deux options :
1. Se tourner vers le code natif. Par exemple, l'API de jeu Pocket PC pourrait être un bon choix. Ses performances sont impressionnantes. Pour plus d'informations, consultez un article très complet sur : http://msdn.microsoft.com/mobility/samples/default.aspx?pull=/library/en-us/dnnetcomp/html/gmangame asp . Le problème est que le code natif ne prend pas en charge le rendu des graphiques vectoriels et n'est pas compatible avec certains appareils. De plus, il se peut qu'il ne fonctionne pas avec les émulateurs Pocket PC. Vous pouvez imaginer à quel point il est difficile de déboguer un tel programme.
2. Veuillez attendre la sortie du moteur graphique mobile de nouvelle génération. Autant que je sache, Windows CE 5 contiendra un puissant moteur Direct3D Mobile. C'est une bonne nouvelle pour les développeurs de jeux mobiles, mais Direct3D n'est pas adapté aux graphiques bidimensionnels. Il est trop complexe pour être utilisé dans des applications générales.
Ce dont nous avons besoin, c'est d'un moteur graphique 2D puissant et facile à utiliser comme GDI+. Par conséquent, j’ai développé le projet XrossOne GDI+ à partir de zéro. Il est entièrement écrit en code managé C# et ne contient aucun code natif ou dangereux. Après des mois de travail acharné, je peux enfin proposer la version originale téléchargeable au début de cet article.
Mise en route Depuis le début de ce projet, j'ai toujours pensé que le moteur XrossOne GDI+ devait être neutre sur les différents appareils et plates-formes portables. De ce fait, il est compatible Pocket PC, Windows CE, Smartphones, Windows .NET et Mono. Vous pouvez copier le même runtime sur une cible différente et cela fonctionnera toujours correctement.
Le tableau suivant résume l’architecture globale.
Espace de noms de coucheXrossOne GDI+ API XrossOne.Drawing
Moteur graphique bidimensionnel basé sur le point fixe XrossOne.DrawingFP
16.16 Moteur de calcul de virgule fixe XrossOne.FixedPoint
Il existe trois couches dans XrossOne GDI+. Le niveau le plus bas est le « moteur de calcul à virgule fixe 16.16 ». L'une des classes principales, MathFP, est adaptée de la bibliothèque Beartronics J2ME. Plusieurs fonctions ont été optimisées, notamment sqrt, atan et PointFP.Distancecalculation. Sous l'espace de noms XrossOne.FixedPoint, il existe trois autres classes : SingleFP, DoubleFP et MatrixFP. SingleFP est une classe Helper pour les nombres à virgule fixe 16,16. Il offre une commodité pour la conversion entre les types à virgule fixe et les types standard (int, float, string). MatrixFP est écrit pour les transformations 2D à virgule fixe. Les calculs en virgule fixe étant moins précis, une certaine précision peut être perdue avec les transformations en cascade. Par exemple, dans la plupart des cas, deux opérations d'inversion ne permettent pas de restaurer la matrice d'origine. DoubleFP existe pour compléter la bibliothèque, mais n'est pas encore utilisé.
Le « moteur graphique bidimensionnel basé sur la virgule fixe » est le cœur de XrossOne GDI+. Il implémente de nombreux algorithmes de graphiques vectoriels complexes, tels que le dessin avec anticrénelage, la décoration de fin de ligne/joint, les transformations 2D, les remplissages dégradés, la composition de canal alpha, etc. La plupart des fonctionnalités avancées du GDI+ natif peuvent être trouvées ici. Cependant, vous ne devez l'utiliser directement que dans quelques cas car son interface basée sur des virgules fixes n'est pas conviviale pour les programmeurs, mais ne vous inquiétez pas trop de cette situation. Une API bien encapsulée est disponible. Vous pouvez les trouver dans l'espace de noms XrossOne.Drawing. Les classes de XrossOne.Drawing sont très similaires aux classes de System.Drawing, sauf que chaque classe a un « X » à la fin. Par exemple, la classe XrossOne.Drawing.PenX est équivalente à System.Drawing.Pen. Il existe une petite astuce pour convertir les programmes GDI+ en XrossOne GDI+. Dans la section using, renommez les classes XrossOne GDI+ en leurs classes équivalentes. Par exemple :
using Pen = XrossOne.Drawing.PenX ;
en utilisant LinearGradientBrush = XrossOne.Drawing.LinearGradientBrushX ;
en utilisant Matrix = XrossOne.Drawing.MatrixX ;
Principales caractéristiques Dessin de graphiques vectoriels anti-aliasés
Toutes sortes de formes géométriques bidimensionnelles peuvent être rendues via XrossOne Mobile GDI+, telles que des segments de ligne, des rectangles, des polygones, des ellipses, des secteurs, des splines de Bézier, des splines cardinales, etc. Toutefois, les secteurs, les splines de Bézier et les splines de base ne sont pas disponibles dans le .NET Compact Framework. De plus, tous les graphiques sont automatiquement anticrénelés lors du rendu. Cela permet d’obtenir une qualité ultra lisse. Dans le .NET Compact Framework, la largeur du pinceau est fixée à 1 pixel. Cette limitation n'existe pas dans XrossOne GDI+. Différentes tailles de pinceaux peuvent être appliquées aux contours de toutes les formes, comme le montre la figure 1.
Figure 1. Dessin graphique vectoriel anticrénelé
Exemple de code 1
//Efface l'arrière-plan et réinitialise l'état de transformation
gx.Clear(Couleur.Blanc);
gx.ResetTransform();
//Dessine une grille inclinée comme arrière-plan
Stylo PenX = new PenX(Utils.FromArgb(0x40, Color.LightGray), 5);
pour (int i = -Hauteur ; i < Largeur + Hauteur ; i+=20)
{
gx.DrawLine(stylo, i, 0, i + Hauteur, Hauteur);
gx.DrawLine(stylo, i, 0, i - Hauteur, Hauteur);
}
//Dessinez un rectangle DarkMagenta avec un stylo de 10,5 pixels
Couleur c = Utils.FromArgb(0x80, Color.DarkMagenta);
stylo = nouveau PenX(c, 10.5f);
gx.DrawRectangle(stylo, 50, 20, 150, 200);
//Remplir un rectangle VertJaune
c = Utils.FromArgb(0xA0, Color.GreenYellow);
Pinceau BrushX = nouveau SolidBrushX(c);
gx.FillRectangle(pinceau, 120, 50, 90, 150);
//Dessinez une ellipse BlueViolet avec un stylo de 10,5 pixels
c = Utils.FromArgb(0x80, Color.BlueViolet);
stylo = nouveau PenX(c, 10.5f);
gx.DrawEllipse(stylo, 50, 20, 150, 80);
//Remplir une ellipse rouge
c = Utils.FromArgb(0xA0, Color.Red);
pinceau = nouveau SolidBrushX(c);
gx.FillEllipse (pinceau, 20, 50, 80, 150);
//Dessinez une tarte HotPink de 156,5 degrés à -280,9 degrés
stylo.Color = Utils.FromArgb(0xA0, Color.HotPink);
gx.DrawPie (stylo, 3,6f, 120,3f, 200,8f, 130,1f, 156,5f, -280,9f);
//Dessiner des courbes de Bézier Orange
c = Utils.FromArgb(0xA0, Color.Orange);
stylo = nouveau PenX(c, 16);
Point de départ = nouveau point (70, 100) ;
Contrôle de point1 = nouveau point (100, 10) ;
Contrôle de point2 = nouveau point (150, 50) ;
Fin du point1 = nouveau point (200, 200) ;
Contrôle de point3 = nouveau point (100, 150) ;
Contrôle de point4 = nouveau point (50, 200) ;
Point end2 = nouveau point (10, 150);
Point[] bezierPoints ={début, contrôle1, contrôle2, fin1, contrôle3, contrôle4, fin2};
stylo.EndCap = LineCapX.Round;
gx.DrawBeziers(stylo, bezierPoints);
//Rafraîchir
Invalider();
La sortie graphique vectorielle de XrossOne GDI+ et du GDI+ natif est identique, à l'exception des splines de base. Mon algorithme est tiré de l'article Smoothing Algorithm Using Bezier Curves de Jean-Yves Queinec. Par conséquent, vous pouvez constater certaines différences entre leurs résultats, comme le montre la figure 2 ci-dessous.
Figure 2. Sortie de DrawCurve/DrawClosedCurve
Bien que la plupart des fonctions de rendu de graphiques vectoriels aient été implémentées, il reste encore du travail à faire. Certaines fonctions (DrawString, DrawImage, DrawPath, etc.) ne seront disponibles qu'à partir de la prochaine version.
remplissage dégradé
Il existe cinq pinceaux dans GDI+ natif : SolidBrush, LinearGradientBrush, PathGradientBrush, TextureBrush et HatchBrush. Cependant, dans cette version, seuls SolidBrush et LinearGradientBrush sont disponibles. XrossOne GDI+ prend en charge RadialGradientBrush mais pas PathGradientBrush. La figure 5 ci-dessous montre un remplissage dégradé.
Figure 5. Remplissage dégradé
Exemple de code 4
//Efface l'arrière-plan et réinitialise l'état de transformation
gx.Clear(Couleur.Blanc);
gx.ResetTransform();
//Remplir un rectangle avec un LinearGradientBrushX noir et blanc
Rectangle r = nouveau Rectangle (20, 50, 300, 100) ;
Couleur c1 = Couleur.Noir ;
Couleur c2 = Couleur.Blanc ;
BrushX brush1 = nouveau LinearGradientBrushX(r, c1, c2, 30F);
gx.FillRectangle(brush1, r);
//Remplir un rectangle avec un LinearGradientBrushX à 7 couleurs
r = nouveau Rectangle (90, 100, 150, 100) ;
LinearGradientBrushX br = nouveau LinearGradientBrushX(r,Color.Black,Color.Black, 60F);
ColorBlendX cb = new ColorBlendX();
cb.Positions=nouveau float[7];
entier je = 0 ;
pour(flottant f=0;f<=1;f+=1.0f/6)
cb.Positions[i++]=f;
cb.Colors=nouvelle couleur[]
{Couleur.Rouge, Couleur.Orange, Couleur.Jaune, Couleur.Vert, Couleur.Bleu, Couleur.Indigo, Couleur.Violet} ;
br.InterpolationColors=cb;
gx.TranslateTransform(160, 10);
gx.RotateTransform(60F);
gx.FillRectangle(br, r);
//Remplir un rectangle avec un RadialGradientBrushX à 7 couleurs
rY += 50 ;
RadialGradientBrushX brush2 = nouveau RadialGradientBrushX(r, Color.Black,Color.Black, 220F);
brush2.InterpolationColors = cb;
gx.RotateTransform(-45F);
gx.TranslateTransform(-200, -170);
gx.FillRectangle(brush2, r);
//Rafraîchir
Invalider();
Composition de canal alpha
La structure Color dans l’espace de noms System.Drawing est disponible dans le .NET Framework et le .NET Compact Framework. La différence est que le composant alpha est désactivé et que la valeur Hue-Saturation-Brightness (HSB) n'est pas disponible dans le .NET Compact Framework. Heureusement, la composition du canal alpha fonctionne parfaitement avec XrossOne GDI+ (comme vous l'avez peut-être déduit de l'exemple graphique précédent).
performance 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 21 1,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 %
Il est vrai que les processeurs des PC portables sont souvent beaucoup moins puissants que les processeurs des PC standards. Des calculs lourds peuvent rendre les appareils portables moins réactifs, ce qui peut devenir frustrant pour les utilisateurs. En d’autres termes, les performances sont essentielles pour les logiciels des appareils portables. Par conséquent, avant d'utiliser XrossOne Mobile GDI+ dans une situation majeure, vous souhaiterez peut-être analyser ses performances globales. Étant donné que la plupart des fonctions équivalentes dans GDI+ pour le .NET Compact Framework ne sont pas disponibles, les tests ont été effectués entre XrossOne Mobile GDI+ et GDI+ pour le .NET Framework. Les tests ont été effectués dans les catégories suivantes : rendu de graphiques vectoriels, transformations 2D et remplissages dégradés. Les scénarios de tests ont été exécutés dans les mêmes conditions. Vous pouvez trouver les programmes de référence dans le package de téléchargement et visualiser rapidement leur sortie graphique sur http://www.xrossone.com/projects.php?menu=4.
XrossOne Mobile GDI+ est entièrement écrit en code géré C# et ses performances globales sont acceptables (voir le tableau ci-dessous), bien que les transformations 2D et les remplissages dégradés nécessiteront une optimisation supplémentaire dans les versions futures.
Solution 66,7 % DrawBezier