บทนำ .NET Compact Framework เป็น API ที่ยอดเยี่ยมสำหรับอุปกรณ์มือถือ เอ็นจิ้นกราฟิกของมันถูกควบคุมปริมาณอย่างมากเพื่อเพิ่มความเร็วในการเรนเดอร์และลดการใช้หน่วยความจำ อย่างไรก็ตาม ดูเหมือนว่าจะยังห่างไกลจากการตอบสนองความต้องการที่เพิ่มขึ้นของผู้ใช้สำหรับประสบการณ์กราฟิกที่ดีขึ้น การพยายามรับความสามารถในการเรนเดอร์กราฟิกแบบเวกเตอร์ขั้นสูงใน .NET Compact Framework อาจเป็นงานที่น่าเบื่อ นักพัฒนามีสองทางเลือก:
1. เปลี่ยนเป็นโค้ดเนทีฟ ตัวอย่างเช่น Pocket PC Game API อาจเป็นตัวเลือกที่ดี ประสิทธิภาพมันน่าประทับใจ สำหรับข้อมูลเพิ่มเติม โปรดดูบทความที่ครอบคลุมได้ที่: http://msdn.microsoft.com/mobility/samples/default.aspx?pull=/library/en-us/dnnetcomp/html/gmangame ปัญหาคือโค้ดเนทิฟไม่รองรับการเรนเดอร์กราฟิกแบบเวกเตอร์ และเข้ากันไม่ได้กับอุปกรณ์บางชนิด นอกจากนี้ มันอาจไม่ทำงานกับโปรแกรมจำลอง Pocket PC คุณสามารถจินตนาการได้ว่าการดีบักโปรแกรมดังกล่าวนั้นยากเพียงใด
2. โปรดรอจนกว่าเอ็นจิ้นกราฟิกมือถือรุ่นต่อไปจะออกมา เท่าที่ฉันรู้ Windows CE 5 จะมีกลไก Direct3D Mobile อันทรงพลัง นี่เป็นข่าวดีสำหรับนักพัฒนาเกมมือถือ แต่ Direct3D ไม่เหมาะสำหรับกราฟิกสองมิติ มีความซับซ้อนเกินกว่าจะใช้ในการใช้งานทั่วไปได้
สิ่งที่เราต้องการคือเอ็นจิ้นกราฟิก 2D ที่ทรงพลังและใช้งานง่ายเช่น GDI+ ดังนั้นฉันจึงพัฒนาโครงการ XrossOne GDI+ ตั้งแต่เริ่มต้น เขียนด้วยโค้ดที่ได้รับการจัดการด้วย C# ทั้งหมด และไม่มีโค้ดเนทีฟหรือโค้ดที่ไม่ปลอดภัย หลังจากทำงานหนักมาหลายเดือน ในที่สุดฉันก็สามารถดาวน์โหลดเวอร์ชันต้นฉบับได้ในตอนต้นของบทความนี้
การเริ่มต้นใช้งาน ตั้งแต่เริ่มต้นโปรเจ็กต์นี้ ฉันคิดเสมอว่าเอ็นจิ้น XrossOne GDI+ ควรเป็นกลางในอุปกรณ์พกพาและแพลตฟอร์มต่างๆ ส่งผลให้สามารถใช้งานร่วมกับ Pocket PC, Windows CE, สมาร์ทโฟน, 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 เป็นคลาส Helper สำหรับตัวเลขจุดคงที่ 16.16 ให้ความสะดวกสบายในการแปลงระหว่างประเภทจุดคงที่และประเภทมาตรฐาน (int, float, string) MatrixFP ถูกเขียนขึ้นสำหรับการแปลง 2D จุดคงที่ เนื่องจากการคำนวณจุดคงที่มีความแม่นยำน้อยกว่า ความแม่นยำบางอย่างอาจสูญหายไปพร้อมกับการแปลงแบบเรียงซ้อน ตัวอย่างเช่น ในกรณีส่วนใหญ่ การดำเนินการผกผันสองครั้งไม่สามารถกู้คืนเมทริกซ์ดั้งเดิมได้ DoubleFP มีไว้เพื่อทำให้ไลบรารีเสร็จสมบูรณ์ แต่ยังไม่ได้ใช้
"เอ็นจิ้นกราฟิกสองมิติแบบจุดคงที่" คือแกนหลักของ XrossOne GDI+ ใช้อัลกอริธึมกราฟิกเวกเตอร์ที่ซับซ้อนมากมาย เช่น การวาดแบบ anti-aliased, การใช้เส้นหมวก/การตกแต่งข้อต่อ, การแปลง 2D, การเติมไล่ระดับสี, การผสมช่องอัลฟา และอื่นๆ คุณสมบัติขั้นสูงส่วนใหญ่ใน Native GDI+ สามารถพบได้ที่นี่ อย่างไรก็ตาม คุณควรใช้โดยตรงในบางกรณีเท่านั้น เนื่องจากอินเทอร์เฟซแบบจุดคงที่ไม่เป็นมิตรกับโปรแกรมเมอร์ แต่อย่ากังวลมากเกินไปเกี่ยวกับสถานการณ์นี้ มี API ที่ห่อหุ้มอย่างดี คุณสามารถค้นหาได้ในเนมสเปซ XrossOne. Drawing คลาสใน XrossOne. Drawing คล้ายกับคลาสใน System. Drawing มาก ยกเว้นว่าแต่ละคลาสจะมี "X" ต่อท้าย ตัวอย่างเช่น คลาส XrossOne. Drawing.PenX เทียบเท่ากับ System. Drawing.Pen มีเคล็ดลับเล็กน้อยในการแปลงโปรแกรม GDI+ เป็น XrossOne GDI+ ในส่วนการใช้งาน ให้เปลี่ยนชื่อคลาส XrossOne GDI+ เป็นคลาสที่เทียบเท่ากัน ตัวอย่างเช่น:
การใช้ Pen = XrossOne. Drawing.PenX;
ใช้ LinearGradientBrush = XrossOne. Drawing.LinearGradientBrushX;
โดยใช้ Matrix = XrossOne. Drawing.MatrixX;
คุณสมบัติหลัก การวาดภาพกราฟิกแบบเวกเตอร์แบบ Anti-Aliased
รูปทรงเรขาคณิตสองมิติทุกชนิดสามารถเรนเดอร์ผ่าน XrossOne Mobile GDI+ ได้ เช่น ส่วนของเส้นตรง สี่เหลี่ยม รูปหลายเหลี่ยม วงรี เซกเตอร์ เส้นโค้งเบซิเยร์ เส้นโค้งเชิงคาร์ดินัล เป็นต้น อย่างไรก็ตาม เซกเตอร์ Bezier splines และ Radix splines จะไม่พร้อมใช้งานใน .NET Compact Framework นอกจากนี้ กราฟิกทั้งหมดจะถูกป้องกันนามแฝงโดยอัตโนมัติเมื่อทำการเรนเดอร์ ช่วยให้ได้คุณภาพที่ราบรื่นเป็นพิเศษ ใน .NET Compact Framework ความกว้างของแปรงได้รับการแก้ไขเป็น 1 พิกเซล ข้อจำกัดนี้ไม่มีอยู่ใน XrossOne GDI+ แปรงขนาดต่างๆ สามารถใช้กับโครงร่างของรูปร่างทั้งหมดได้ ดังแสดงในรูปที่ 1
รูปที่ 1 การวาดภาพกราฟิกแบบเวกเตอร์แบบป้องกันนามแฝง
ตัวอย่างโค้ดที่ 1
//ล้างพื้นหลังและรีเซ็ตสถานะการแปลง
gx.Clear(สีขาว);
gx.ResetTransform();
//วาดตารางเอียงเป็นพื้นหลัง
ปากกา PenX = PenX ใหม่ (Utils.FromArgb (0x40, Color.LightGray), 5);
สำหรับ (int i = -Height; i < ความกว้าง + ความสูง; i+=20)
-
gx.DrawLine(ปากกา, i, 0, i + ส่วนสูง, ส่วนสูง);
gx.DrawLine(ปากกา, i, 0, i - ส่วนสูง, ส่วนสูง);
-
//วาดรูปสี่เหลี่ยมผืนผ้า DarkMagenta ด้วยปากกา 10.5 พิกเซล
สี c = Utils.FromArgb(0x80, Color.DarkMagenta);
ปากกา = PenX ใหม่ (c, 10.5f);
gx.DrawRectangle(ปากกา, 50, 20, 150, 200);
//เติมสี่เหลี่ยมสีเขียวเหลือง
c = Utils.FromArgb (0xA0, Color.GreenYellow);
แปรง BrushX = SolidBrushX ใหม่ (c);
gx.FillRectangle(แปรง, 120, 50, 90, 150);
//วาดรูปวงรี BlueViolet ด้วยปากกา 10.5 พิกเซล
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);
//วาดเส้นโค้งสีส้ม Bezier
c = Utils.FromArgb(0xA0, Color.Orange);
ปากกา = PenX ใหม่ (c, 16);
จุดเริ่มต้น = จุดใหม่ (70, 100);
จุดควบคุม 1 = จุดใหม่ (100, 10);
จุดควบคุม2 = จุดใหม่ (150, 50);
จุด end1 = จุดใหม่ (200, 200);
จุดควบคุม3 = จุดใหม่ (100, 150);
จุดควบคุม 4 = จุดใหม่ (50, 200);
จุด end2 = จุดใหม่ (10, 150);
จุด [] bezierPoints = {เริ่มต้น, control1, control2, end1, control3, control4, end2};
pen.EndCap = LineCapX.รอบ;
gx.DrawBeziers (ปากกา, bezierPoints);
//รีเฟรช
ทำให้ไม่ถูกต้อง();
เอาต์พุตกราฟิกแบบเวกเตอร์ของ XrossOne GDI+ และ GDI+ แบบเนทีฟจะเหมือนกัน ยกเว้นสไปน์ของ Radix อัลกอริทึมของฉันนำมาจากบทความ Smoothing Algorithm โดยใช้ Bezier Curves โดย Jean-Yves Queinec ดังนั้น คุณอาจพบความแตกต่างระหว่างผลลัพธ์ ดังแสดงในรูปที่ 2 ด้านล่าง
รูปที่ 2 ผลลัพธ์ของ DrawCurve/DrawClosedCurve
แม้ว่าฟังก์ชันการเรนเดอร์กราฟิกแบบเวกเตอร์ส่วนใหญ่จะถูกนำมาใช้แล้ว แต่ก็ยังมีงานบางอย่างที่ต้องทำ ฟังก์ชั่นบางอย่าง (DrawString, DrawImage, DrawPath ฯลฯ) จะไม่สามารถใช้งานได้จนกว่าจะถึงเวอร์ชันถัดไป
เติมไล่ระดับ
GDI+ แบบเนทีฟมีแปรงอยู่ 5 แบบ ได้แก่ SolidBrush, LinearGradientBrush, PathGradientBrush, TextureBrush และ HatchBrush อย่างไรก็ตาม ในเวอร์ชันนี้ มีเพียง SolidBrush และ LinearGradientBrush เท่านั้นที่พร้อมใช้งาน XrossOne GDI+ รองรับ RadialGradientBrush แต่ไม่รองรับ PathGradientBrush รูปที่ 5 ด้านล่างสาธิตการเติมแบบไล่ระดับ
รูปที่ 5 การเติมแบบไล่ระดับ
ตัวอย่างรหัส 4
//ล้างพื้นหลังและรีเซ็ตสถานะการแปลง
gx.Clear(สีขาว);
gx.ResetTransform();
//เติมสี่เหลี่ยมด้วย LinearGradientBrushX สีขาว-ดำ
สี่เหลี่ยมผืนผ้า r = สี่เหลี่ยมผืนผ้าใหม่ (20, 50, 300, 100);
สี c1 = สีดำ;
สี c2 = สีขาว;
BrushX brush1 = LinearGradientBrushX ใหม่ (r, c1, c2, 30F);
gx.FillRectangle(brush1, r);
//เติมสี่เหลี่ยมด้วย LinearGradientBrushX 7 สี
r = สี่เหลี่ยมผืนผ้าใหม่ (90, 100, 150, 100);
LinearGradientBrushX br = ใหม่ LinearGradientBrushX(r,Color.Black,Color.Black, 60F);
ColorBlendX cb = ColorBlendX(); ใหม่
cb.Positions=ลอยใหม่[7];
อินท์ i=0;
สำหรับ(ลอย f=0;f<=1;f+=1.0f/6)
cb.ตำแหน่ง[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);
//เติมสี่เหลี่ยมด้วย RadialGradientBrushX 7 สี
รี่ += 50;
RadialGradientBrushX brush2 = RadialGradientBrushX ใหม่ (r, Color.Black, Color.Black, 220F);
Brush2.InterpolationColors = CB;
gx.RotateTransform(-45F);
gx.TranslateTransform(-200, -170);
gx.FillRectangle(brush2, r);
//รีเฟรช
ทำให้ไม่ถูกต้อง();
การประกอบช่องอัลฟ่า
โครงสร้างสีในเนมสเปซ System. Drawing พร้อมใช้งานทั้งใน .NET Framework และ .NET Compact Framework ข้อแตกต่างคือคอมโพเนนต์อัลฟาถูกปิดใช้งาน และไม่มีค่า Hue-Saturation-Brightness (HSB) ใน .NET Compact Framework โชคดีที่การรวมช่องอัลฟาทำงานได้อย่างสมบูรณ์แบบกับ XrossOne GDI+ (ดังที่คุณอาจอนุมานได้จากตัวอย่างกราฟิกก่อนหน้านี้)
ผลงาน 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 วินาที 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 %
เป็นเรื่องจริงที่ CPU ของพีซีแบบพกพามักจะมีประสิทธิภาพน้อยกว่า CPU ของพีซีมาตรฐานมาก การคำนวณจำนวนมากอาจทำให้อุปกรณ์มือถือตอบสนองน้อยลง ซึ่งอาจทำให้ผู้ใช้หงุดหงิดได้ กล่าวอีกนัยหนึ่ง ประสิทธิภาพเป็นสิ่งสำคัญสำหรับซอฟต์แวร์อุปกรณ์มือถือ ดังนั้น ก่อนที่จะใช้ XrossOne Mobile GDI+ ในสถานการณ์สำคัญ คุณอาจต้องการวิเคราะห์ประสิทธิภาพโดยรวมของมัน เนื่องจากฟังก์ชันที่เทียบเท่ากันส่วนใหญ่ใน GDI+ สำหรับ .NET Compact Framework ไม่พร้อมใช้งาน การวัดประสิทธิภาพจึงดำเนินการระหว่าง XrossOne Mobile GDI+ และ GDI+ สำหรับ .NET Framework การทดสอบดำเนินการในหมวดหมู่ต่อไปนี้: การเรนเดอร์กราฟิกแบบเวกเตอร์ การแปลง 2D และการเติมไล่ระดับสี สถานการณ์การทดสอบถูกดำเนินการภายใต้เงื่อนไขเดียวกัน คุณสามารถค้นหาโปรแกรมวัดประสิทธิภาพได้ในแพ็คเกจดาวน์โหลด และดูเอาต์พุตกราฟิกของโปรแกรมเหล่านั้นได้อย่างรวดเร็วที่ http://www.xrossone.com/projects.php?menu=4
XrossOne Mobile GDI+ เขียนขึ้นทั้งหมดด้วยโค้ดที่ได้รับการจัดการด้วย C# และประสิทธิภาพโดยรวมก็ยอมรับได้ (ดูตารางด้านล่าง) แม้ว่าการแปลง 2D และการเติมไล่ระดับสีจะต้องได้รับการปรับให้เหมาะสมเพิ่มเติมในเวอร์ชันต่อๆ ไป
โซลูชัน 66.7 % DrawBezier มิลลิ