MainUnit.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. unit MainUnit;
  2. (* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1 or LGPL 2.1 with linking exception
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * Alternatively, the contents of this file may be used under the terms of the
  16. * Free Pascal modified version of the GNU Lesser General Public License
  17. * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
  18. * of this license are applicable instead of those above.
  19. * Please see the file LICENSE.txt for additional information concerning this
  20. * license.
  21. *
  22. * The Original Code is GR32 Polygon Renderer Benchmark
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Mattias Andersson <[email protected]>
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2000-2012
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * ***** END LICENSE BLOCK ***** *)
  31. interface
  32. {$I GR32.inc}
  33. uses
  34. {$IFDEF Windows}Windows,{$ENDIF}
  35. SysUtils, Classes, Graphics, StdCtrls, Controls, Forms, Dialogs, ExtCtrls,
  36. GR32_Image, GR32_Paths, GR32, GR32_Polygons;
  37. const
  38. TEST_DURATION = 4000; // test for 4 seconds
  39. type
  40. TTestProc = procedure(Canvas: TCanvas32);
  41. { TMainForm }
  42. TMainForm = class(TForm)
  43. BtnBenchmark: TButton;
  44. BtnExit: TButton;
  45. CbxAllRenderers: TCheckBox;
  46. CbxAllTests: TCheckBox;
  47. CmbRenderer: TComboBox;
  48. CmbTest: TComboBox;
  49. GbxResults: TGroupBox;
  50. GbxSettings: TGroupBox;
  51. Img: TImage32;
  52. LblRenderer: TLabel;
  53. LblTest: TLabel;
  54. MemoLog: TMemo;
  55. PnlBenchmark: TPanel;
  56. PnlBottom: TPanel;
  57. PnlSpacer: TPanel;
  58. PnlTop: TPanel;
  59. procedure FormCreate(Sender: TObject);
  60. procedure BtnBenchmarkClick(Sender: TObject);
  61. procedure ImgResize(Sender: TObject);
  62. procedure BtnExitClick(Sender: TObject);
  63. private
  64. procedure RunTest(TestProc: TTestProc; TestTime: Int64 = TEST_DURATION);
  65. procedure WriteTestResult(OperationsPerSecond: Integer);
  66. end;
  67. var
  68. MainForm: TMainForm;
  69. implementation
  70. {$IFDEF FPC}
  71. {$R *.lfm}
  72. {$ELSE}
  73. {$R *.dfm}
  74. {$ENDIF}
  75. uses
  76. Math, GR32_System, GR32_LowLevel, GR32_Resamplers, GR32_Brushes,
  77. GR32_Backends, GR32_VPR2, GR32_PolygonsAggLite;
  78. const
  79. GridScale: Integer = 40;
  80. var
  81. TestRegistry: TStringList;
  82. procedure RegisterTest(const TestName: string; Test: TTestProc);
  83. begin
  84. if not Assigned(TestRegistry) then
  85. TestRegistry := TStringList.Create;
  86. TestRegistry.AddObject(TestName, TObject(@Test));
  87. end;
  88. procedure TMainForm.WriteTestResult(OperationsPerSecond: Integer);
  89. begin
  90. MemoLog.Lines.Add(Format('%s: %d op/s', [cmbRenderer.Text,
  91. OperationsPerSecond]));
  92. end;
  93. procedure TMainForm.RunTest(TestProc: TTestProc; TestTime: Int64);
  94. var
  95. Canvas: TCanvas32;
  96. i, t: Int64;
  97. begin
  98. TestTime := TestTime * 1000;
  99. RandSeed := 0;
  100. Img.Bitmap.Clear(clWhite32);
  101. Update;
  102. Canvas := TCanvas32.Create(Img.Bitmap);
  103. try try
  104. Canvas.Brushes.Add(TSolidBrush);
  105. Canvas.Brushes.Add(TStrokeBrush);
  106. Canvas.Brushes[0].Visible := True;
  107. Canvas.Brushes[1].Visible := False;
  108. i := 0;
  109. GlobalPerfTimer.Start;
  110. repeat
  111. TestProc(Canvas);
  112. TestProc(Canvas);
  113. TestProc(Canvas);
  114. TestProc(Canvas);
  115. TestProc(Canvas);
  116. TestProc(Canvas);
  117. TestProc(Canvas);
  118. TestProc(Canvas);
  119. TestProc(Canvas);
  120. TestProc(Canvas);
  121. t := GlobalPerfTimer.ReadValue;
  122. Inc(i, 10);
  123. until t > TestTime;
  124. WriteTestResult((i*1000000) div t);
  125. except
  126. MemoLog.Lines.Add(Format('%s: Failed', [cmbRenderer.Text]));
  127. end;
  128. finally
  129. Canvas.Free;
  130. end;
  131. Img.Invalidate; // VPR2 and VPR2X doesn't call TBitmap32.Changed when they draw
  132. end;
  133. function RandColor: TColor32; {$IFDEF USEINLINING} inline; {$ENDIF}
  134. begin
  135. Result := Random($FFFFFF) or Random($ff) shl 24;
  136. end;
  137. //----------------------------------------------------------------------------//
  138. // ellipses
  139. //----------------------------------------------------------------------------//
  140. procedure EllipseTest(Canvas: TCanvas32);
  141. var
  142. W, H: Integer;
  143. begin
  144. W := Canvas.Bitmap.Width;
  145. H := Canvas.Bitmap.Height;
  146. (Canvas.Brushes[0] as TSolidBrush).FillColor := RandColor;
  147. Canvas.Path.Ellipse(Random(W), Random(H), Random(W shr 1), Random(H shr 1));
  148. end;
  149. //----------------------------------------------------------------------------//
  150. // thin lines
  151. //----------------------------------------------------------------------------//
  152. procedure ThinLineTest(Canvas: TCanvas32);
  153. var
  154. W, H: Integer;
  155. begin
  156. W := Canvas.Bitmap.Width;
  157. H := Canvas.Bitmap.Height;
  158. Canvas.Brushes[0].Visible := False;
  159. Canvas.Brushes[1].Visible := True;
  160. with Canvas.Brushes[1] as TStrokeBrush do
  161. begin
  162. StrokeWidth := 1;
  163. FillColor := RandColor;
  164. end;
  165. Canvas.Path.BeginPath;
  166. Canvas.Path.MoveTo(Random(W), Random(H));
  167. Canvas.Path.LineTo(Random(W), Random(H));
  168. Canvas.Path.EndPath;
  169. end;
  170. //----------------------------------------------------------------------------//
  171. // thick lines
  172. //----------------------------------------------------------------------------//
  173. procedure ThickLineTest(Canvas: TCanvas32);
  174. var
  175. W, H: Integer;
  176. begin
  177. W := Canvas.Bitmap.Width;
  178. H := Canvas.Bitmap.Height;
  179. Canvas.Brushes[0].Visible := False;
  180. Canvas.Brushes[1].Visible := True;
  181. with Canvas.Brushes[1] as TStrokeBrush do
  182. begin
  183. StrokeWidth := 10;
  184. FillColor := RandColor;
  185. end;
  186. Canvas.Path.BeginPath;
  187. Canvas.Path.MoveTo(Random(W), Random(H));
  188. Canvas.Path.LineTo(Random(W), Random(H));
  189. Canvas.Path.EndPath;
  190. end;
  191. //----------------------------------------------------------------------------//
  192. // text
  193. //----------------------------------------------------------------------------//
  194. const
  195. STRINGS: array [0..5] of string = (
  196. 'Graphics32',
  197. 'Excellence endures!',
  198. 'Hello World!',
  199. 'Lorem ipsum dolor sit amet, consectetur adipisicing elit,' + #13#10 +
  200. 'sed do eiusmod tempor incididunt ut labore et dolore magna' + #13#10 +
  201. 'aliqua. Ut enim ad minim veniam, quis nostrud exercitation' + #13#10 +
  202. 'ullamco laboris nisi ut aliquip ex ea commodo consequat.',
  203. 'The quick brown fox jumps over the lazy dog.',
  204. 'Jackdaws love my big sphinx of quartz.');
  205. type
  206. TFontEntry = record
  207. Name: string;
  208. Size: Integer;
  209. Style: TFontStyles;
  210. end;
  211. const
  212. FACES: array [0..5] of TFontEntry = (
  213. (Name: 'Trebuchet MS'; Size: 24; Style: [fsBold]),
  214. (Name: 'Tahoma'; Size: 20; Style: [fsItalic]),
  215. (Name: 'Courier New'; Size: 14; Style: []),
  216. (Name: 'Georgia'; Size: 8; Style: [fsItalic]),
  217. (Name: 'Times New Roman'; Size: 12; Style: []),
  218. (Name: 'Garamond'; Size: 12; Style: [])
  219. );
  220. procedure TextTest(Canvas: TCanvas32);
  221. var
  222. W, H, I: Integer;
  223. Font: TFont;
  224. begin
  225. W := Canvas.Bitmap.Width;
  226. H := Canvas.Bitmap.Height;
  227. (Canvas.Brushes[0] as TSolidBrush).FillColor := RandColor;
  228. I := Random(5);
  229. Font := Canvas.Bitmap.Font;
  230. Font.Name := FACES[I].Name;
  231. Font.Size := FACES[I].Size;
  232. Font.Style := FACES[I].Style;
  233. Canvas.RenderText(Random(W), Random(H), STRINGS[I]);
  234. end;
  235. //----------------------------------------------------------------------------//
  236. // splines
  237. //----------------------------------------------------------------------------//
  238. function MakeCurve(const Points: TArrayOfFloatPoint; Kernel: TCustomKernel;
  239. Closed: Boolean; StepSize: Integer): TArrayOfFloatPoint;
  240. var
  241. I, J, F, H, Index, LastIndex, Steps, R: Integer;
  242. K, V, W, X, Y: TFloat;
  243. Delta: TFloatPoint;
  244. Filter: TFilterMethod;
  245. WrapProc: TWrapProc;
  246. PPoint: PFloatPoint;
  247. const
  248. WRAP_PROC: array[Boolean] of TWrapProc = (Clamp, Wrap);
  249. begin
  250. WrapProc := Wrap_PROC[Closed];
  251. Filter := Kernel.Filter;
  252. R := Ceil(Kernel.GetWidth);
  253. H := High(Points);
  254. LastIndex := H - Ord(not Closed);
  255. Steps := 0;
  256. for I := 0 to LastIndex do
  257. begin
  258. Index := WrapProc(I + 1, H);
  259. Delta.X := Points[Index].X - Points[I].X;
  260. Delta.Y := Points[Index].Y - Points[I].Y;
  261. Inc(Steps, Floor(Hypot(Delta.X, Delta.Y) / StepSize) + 1);
  262. end;
  263. SetLength(Result, Steps);
  264. PPoint := @Result[0];
  265. for I := 0 to LastIndex do
  266. begin
  267. Index := WrapProc(I + 1, H);
  268. Delta.X := Points[Index].X - Points[I].X;
  269. Delta.Y := Points[Index].Y - Points[I].Y;
  270. Steps := Floor(Hypot(Delta.X, Delta.Y) / StepSize);
  271. if Steps > 0 then
  272. begin
  273. K := 1 / Steps;
  274. V := 0;
  275. for J := 0 to Steps do
  276. begin
  277. X := 0; Y := 0;
  278. for F := -R to R do
  279. begin
  280. Index := WrapProc(I - F, H);
  281. W := Filter(F + V);
  282. X := X + W * Points[Index].X;
  283. Y := Y + W * Points[Index].Y;
  284. end;
  285. PPoint^ := FloatPoint(X, Y);
  286. Inc(PPoint);
  287. V := V + K;
  288. end;
  289. end;
  290. end;
  291. end;
  292. procedure SplinesTest(Canvas: TCanvas32);
  293. var
  294. Input, Points: TArrayOfFloatPoint;
  295. K: TSplineKernel;
  296. W, H, I: Integer;
  297. begin
  298. W := Canvas.Bitmap.Width;
  299. H := Canvas.Bitmap.Height;
  300. SetLength(Input, 10);
  301. for I := 0 to High(Input) do
  302. begin
  303. Input[I].X := Random(W);
  304. Input[I].Y := Random(H);
  305. end;
  306. K := TSplineKernel.Create;
  307. try
  308. Points := MakeCurve(Input, K, True, 3);
  309. finally
  310. K.Free;
  311. end;
  312. (Canvas.Brushes[0] as TSolidBrush).FillColor := RandColor;
  313. Canvas.Path.Polygon(Points);
  314. end;
  315. //----------------------------------------------------------------------------//
  316. procedure TMainForm.FormCreate(Sender: TObject);
  317. begin
  318. // set priority class and thread priority for better accuracy
  319. {$IFDEF MSWindows}
  320. SetPriorityClass(GetCurrentProcess, HIGH_PRIORITY_CLASS);
  321. SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_HIGHEST);
  322. {$ENDIF}
  323. CmbTest.Items := TestRegistry;
  324. CmbTest.ItemIndex := 0;
  325. PolygonRendererList.GetClassNames(CmbRenderer.Items);
  326. CmbRenderer.ItemIndex := 0;
  327. Img.SetupBitmap(True, clWhite32);
  328. end;
  329. procedure TMainForm.BtnBenchmarkClick(Sender: TObject);
  330. procedure TestRenderer;
  331. begin
  332. DefaultPolygonRendererClass := TPolygonRenderer32Class(
  333. PolygonRendererList[CmbRenderer.ItemIndex]);
  334. RunTest(TTestProc(cmbTest.Items.Objects[cmbTest.ItemIndex]));
  335. end;
  336. procedure TestAllRenderers;
  337. var
  338. I: Integer;
  339. begin
  340. for I := 0 to CmbRenderer.Items.Count - 1 do
  341. begin
  342. CmbRenderer.ItemIndex := I;
  343. TestRenderer;
  344. end;
  345. MemoLog.Lines.Add('');
  346. end;
  347. procedure PerformTest;
  348. begin
  349. MemoLog.Lines.Add(Format('=== Test: %s ===', [cmbTest.Text]));
  350. if CbxAllRenderers.Checked then
  351. TestAllRenderers
  352. else
  353. TestRenderer;
  354. end;
  355. procedure PerformAllTests;
  356. var
  357. I: Integer;
  358. begin
  359. for I := 0 to CmbTest.Items.Count - 1 do
  360. begin
  361. CmbTest.ItemIndex := I;
  362. Repaint;
  363. PerformTest;
  364. end;
  365. MemoLog.Lines.Add('');
  366. end;
  367. begin
  368. Screen.Cursor := crHourGlass;
  369. try
  370. if CbxAllTests.Checked then
  371. PerformAllTests
  372. else
  373. PerformTest;
  374. finally
  375. Screen.Cursor := crDefault;
  376. end;
  377. end;
  378. procedure TMainForm.ImgResize(Sender: TObject);
  379. begin
  380. Img.SetupBitmap(True, clWhite32);
  381. end;
  382. procedure TMainForm.BtnExitClick(Sender: TObject);
  383. begin
  384. Close;
  385. end;
  386. initialization
  387. RegisterTest('Ellipses', EllipseTest);
  388. RegisterTest('Thin Lines', ThinLineTest);
  389. RegisterTest('Thick Lines', ThickLineTest);
  390. RegisterTest('Splines', SplinesTest);
  391. if Assigned(TBitmap32.GetPlatformBackendClass.GetInterfaceEntry(ITextToPathSupport)) then
  392. RegisterTest('Text', TextTest);
  393. end.