はじめに .NET Compact Framework は、モバイル デバイス用の、最高ではないにしても、優れた API です。グラフィックス エンジンは、レンダリング速度を向上させ、メモリ消費量を削減するために大幅に調整されています。しかし、より良いグラフィックス体験を求めるユーザーの高まる要求を満たすには程遠いようです。 .NET Compact Framework で高度なベクター グラフィックス レンダリング機能の一部を取得しようとすると、面倒な作業になる場合があります。開発者には 2 つの選択肢があります。
1. ネイティブ コードに目を向けます。たとえば、Pocket PC Game API は良い選択かもしれません。そのパフォーマンスは印象的です。詳細については、 http://msdn.microsoft.com/mobile/samples/default.aspx?pull=/library/en-us/dnnetcomp/html/gmangame aspで非常に包括的な記事を参照してください。問題は、ネイティブ コードがベクター グラフィックス レンダリングをサポートしておらず、一部のデバイスと互換性がないことです。また、Pocket PC エミュレータでは動作しない場合があります。このようなプログラムをデバッグすることがどれほど難しいか想像できるでしょう。
2. 次世代モバイル グラフィック エンジンが登場するまでお待ちください。私の知る限り、Windows CE 5 には強力な Direct3D Mobile エンジンが搭載される予定です。これはモバイル ゲーム開発者にとっては朗報ですが、Direct3D は 2 次元グラフィックスには適していません。一般的なアプリケーションで使用するには複雑すぎます。
私たちに必要なのは、GDI+ のような強力で使いやすい 2D グラフィックス エンジンです。そこで、XrossOne GDI+ プロジェクトをゼロから開発しました。これは完全に C# マネージド コードで記述されており、ネイティブ コードや安全でないコードは含まれていません。数か月にわたる努力の結果、この記事の冒頭でダウンロード可能なオリジナル バージョンを提供できるようになりました。
はじめに このプロジェクトの開始時から、XrossOne GDI+ エンジンはさまざまなハンドヘルド デバイスやプラットフォーム間で中立であるべきだということが常に念頭にありました。その結果、Pocket PC、Windows CE、スマートフォン、Windows .NET、および Mono と互換性があります。同じランタイムを別のターゲットにコピーしても、引き続き正常に動作します。
次の表は、全体的なアーキテクチャをまとめたものです。
レイヤー名前空間XrosOne GDI+ API XrosOne.Drawing
固定小数点ベースの 2 次元グラフィックス エンジン XrossOne.DrawingFP
16.16 固定小数点計算エンジン XrossOne.FixedPoint
XrossOne GDI+ には 3 つのレイヤーがあります。最下位レベルは「16.16 固定小数点演算エンジン」です。主要クラスの 1 つである MathFP は、Beartronics J2ME ライブラリから採用されています。 sqrt、atan、PointFP.Distancecalculation など、いくつかの関数が最適化されました。名前空間 XrossOne.FixedPoint の下には、SingleFP、DoubleFP、MatrixFP という他の 3 つのクラスがあります。 SingleFP は、16.16 固定小数点数のヘルパー クラスです。これは、固定小数点型と標準型 (int、float、string) の間の変換に便利です。 MatrixFP は、固定小数点 2D 変換用に作成されています。固定小数点計算は精度が低いため、カスケード変換では一部の精度が失われる可能性があります。たとえば、ほとんどの場合、2 回の逆演算では元の行列を復元できません。 DoubleFP はライブラリを完成させるために存在しますが、まだ使用されていません。
XrossOne GDI+の中核となるのは「固定小数点ベースの2次元グラフィックスエンジン」です。アンチエイリアス描画、ライン キャップ/ジョイント装飾、2D 変換、グラデーション塗りつぶし、アルファ チャネル合成など、多くの複雑なベクター グラフィックス アルゴリズムを実装しています。ネイティブ GDI+ の高度な機能のほとんどはここにあります。ただし、固定小数点ベースのインターフェイスはプログラマにとって使いやすいものではないため、直接使用するのは少数の場合に限定してください。ただし、この状況についてはあまり心配する必要はありません。適切にカプセル化された API が利用可能です。これらは XrossOne.Drawing 名前空間で見つけることができます。 XrossOne.Drawing のクラスは、各クラスの末尾に「X」が付いている点を除いて、System.Drawing のクラスと非常によく似ています。たとえば、XrossOne.Drawing.PenX クラスは System.Drawing.Pen と同等です。 GDI+ プログラムを XrossOne GDI+ に変換するには、ちょっとしたコツがあります。 using セクションで、XrossOne GDI+ クラスの名前を同等のクラスに変更します。例:
Pen = XrossOne.Drawing.PenX を使用します。
LinearGradientBrush = XrossOne.Drawing.LinearGradientBrushX を使用します。
Matrix = XrossOne.Drawing.MatrixX を使用します。
主な機能 アンチエイリアス ベクター グラフィックス描画
XrossOne Mobile GDI+ では、線分、長方形、多角形、楕円、扇形、ベジェ スプライン、カーディナル スプラインなど、あらゆる種類の 2 次元幾何学的形状をレンダリングできます。ただし、セクター、ベジェ スプライン、基数スプラインは .NET Compact Framework では使用できません。さらに、すべてのグラフィックスはレンダリング時に自動的にアンチエイリアス処理されます。これにより、非常に滑らかな品質を実現できます。 .NET Compact Framework では、ブラシの幅は 1 ピクセルに固定されています。この制限は XrossOne GDI+ には存在しません。図 1 に示すように、さまざまなサイズのブラシをすべての形状のアウトラインに適用できます。
図 1. アンチエイリアス処理されたベクトル グラフィックス描画
コード例 1
//背景をクリアし、変換状態をリセットします
gx.Clear(Color.White);
gx.ResetTransform();
//斜めのグリッドを背景として描画します
PenX ペン = new PenX(Utils.FromArgb(0x40, Color.LightGray), 5);
for (int i = -高さ; i < 幅 + 高さ; i+=20)
{
gx.DrawLine(ペン, i, 0, i + 高さ, 高さ);
gx.DrawLine(ペン, i, 0, i - 高さ, 高さ);
}
//10.5 ピクセルのペンで DarkMagenta の長方形を描画します
カラー c = Utils.FromArgb(0x80, Color.DarkMagenta);
ペン = 新しい PenX(c, 10.5f);
gx.DrawRectangle(ペン, 50, 20, 150, 200);
//GreenYellowの長方形を塗りつぶします
c = Utils.FromArgb(0xA0, Color.GreenYellow);
BrushX ブラシ = 新しい SolidBrushX(c);
gx.FillRectangle(ブラシ, 120, 50, 90, 150);
//10.5 ピクセルのペンで BlueViolet の楕円を描画します
c = Utils.FromArgb(0x80, Color.BlueViolet);
ペン = 新しい PenX(c, 10.5f);
gx.DrawEllipse(ペン, 50, 20, 150, 80);
// 赤い楕円を塗りつぶす
c = Utils.FromArgb(0xA0, Color.Red);
ブラシ = 新しい SolidBrushX(c);
gx.FillEllipse(ブラシ, 20, 50, 80, 150);
//HotPink パイを 156.5 度から -280.9 度まで描画します
pen.Color = Utils.FromArgb(0xA0, Color.HotPink);
gx.DrawPie(ペン、3.6f、120.3f、200.8f、130.1f、156.5f、-280.9f);
//オレンジ色のベジェ曲線を描画します
c = Utils.FromArgb(0xA0, Color.Orange);
ペン = 新しい PenX(c, 16);
ポイント開始 = 新しいポイント(70, 100);
ポイント制御1 = 新しいポイント(100, 10);
ポイント制御2 = 新しいポイント(150, 50);
ポイント end1 = 新しいポイント(200, 200);
ポイント コントロール 3 = 新しいポイント(100, 150);
ポイント control4 = 新しいポイント(50, 200);
ポイント end2 = 新しいポイント(10, 150);
Point[] bezierPoints ={start, control1, control2, end1, control3, control4, end2};
ペン.EndCap = LineCapX.Round;
gx.DrawBeziers(ペン, bezierPoints);
//リフレッシュ
無効化();
XrossOne GDI+ とネイティブ GDI+ のベクトル グラフィックス出力は、基数スプラインを除いて同一です。私のアルゴリズムは、Jean-Yves Queinec の記事「ベジェ曲線を使用した平滑化アルゴリズム」から引用したものです。したがって、以下の図 2 に示すように、出力間にいくつかの違いが見つかる場合があります。
図 2. DrawCurve/DrawClosedCurve の出力
ベクター グラフィックス レンダリング機能のほとんどは実装されていますが、まだ行う必要のある作業がいくつかあります。一部の関数 (DrawString、DrawImage、DrawPath など) は、次のバージョンまで使用できなくなります。
グラデーション塗りつぶし
ネイティブ GDI+ には、SolidBrush、LinearGradientBrush、PathGradientBrush、TextureBrush、および HatchBrush の 5 つのブラシがあります。ただし、このバージョンでは、SolidBrush と LinearGradientBrush のみが使用可能です。 XrosOne GDI+ は RadialGradientBrush をサポートしますが、PathGradientBrush はサポートしません。以下の図 5 は、グラデーション塗りつぶしを示しています。
図 5. グラデーション塗りつぶし
コード例 4
//背景をクリアし、変換状態をリセットします
gx.Clear(Color.White);
gx.ResetTransform();
// 長方形を白黒の LinearGradientBrushX で塗りつぶします
長方形 r = 新しい長方形(20, 50, 300, 100);
カラー c1 = カラー.ブラック;
カラー c2 = カラー.ホワイト;
BrushX ブラシ 1 = 新しい LinearGradientBrushX(r, c1, c2, 30F);
gx.FillRectangle(brush1, r);
// 長方形を 7 色の LinearGradientBrushX で塗りつぶします
r = 新しい長方形(90, 100, 150, 100);
LinearGradientBrushX br = new LinearGradientBrushX(r,Color.Black,Color.Black, 60F);
ColorBlendX cb = 新しい ColorBlendX();
cb.Positions=新しい浮動小数点数[7];
int i=0;
for(float f=0;f<=1;f+=1.0f/6)
cb.Positions[i++]=f;
cb.Colors=新しい色[]
{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);
// 7 色の RadialGradientBrushX で長方形を塗りつぶします
rY += 50;
RadialGradientBrushX ブラシ 2 = 新しい RadialGradientBrushX(r, Color.Black,Color.Black, 220F);
Brush2.InterpolationColors = cb;
gx.RotateTransform(-45F);
gx.TranslateTransform(-200, -170);
gx.FillRectangle(brush2, r);
//リフレッシュ
無効化();
アルファチャンネル合成
System.Drawing 名前空間の Color 構造体は、.NET Framework と .NET Compact Framework の両方で使用できます。違いは、アルファ コンポーネントが無効になっていることと、.NET Compact Framework では色相-彩度-明るさ (HSB) 値が使用できないことです。幸いなことに、アルファ チャネル合成は XrossOne GDI+ で完全に機能します (前のグラフィックスの例から推測できるように)。
パフォーマンス 3.505 ms 1.602 ms 118.8 % DrawCurve 4.006 ms 1.402 185.7 % DrawPie 6.810 ms 2.003 ms 240.0% TranslateTransform 10.615 ms 3.405ミリ秒 211.7% スケールトランスフォーム 秒 0.801 ミリ秒 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 %
確かに、ハンドヘルド PC の CPU は、標準的な PC の CPU よりもはるかに性能が低いことがよくあります。大量の計算によりハンドヘルド デバイスの応答性が低下し、ユーザーがイライラする可能性があります。言い換えれば、ハンドヘルド デバイスのソフトウェアにとってパフォーマンスは非常に重要です。したがって、主要な状況で XrossOne Mobile GDI+ を使用する前に、その全体的なパフォーマンスを分析することをお勧めします。 .NET Compact Framework の GDI+ では同等の機能のほとんどが利用できないため、ベンチマークは XrossOne Mobile GDI+ と .NET Framework の GDI+ の間で実行されました。テストは、ベクトル グラフィックス レンダリング、2D 変換、およびグラデーション塗りつぶしのカテゴリで実行されました。テスト シナリオは同じ条件下で実行されました。ベンチマーク プログラムはダウンロード パッケージに含まれており、http://www.xrossone.com/projects.php?menu=4 でグラフィック出力をすぐに確認できます。
XrossOne Mobile GDI+ は完全に C# マネージド コードで記述されており、全体的なパフォーマンスは許容範囲内です (以下の表を参照)。ただし、2D 変換とグラデーション塗りつぶしは将来のバージョンでさらに最適化する必要があります。
ソリューション 66.7 % DrawBezier 4.106 ミリ