UnitMain.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. unit UnitMain;
  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 Thick Line example for Graphics32
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Anders Melander
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2023
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * ***** END LICENSE BLOCK ***** *)
  31. interface
  32. uses
  33. Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  34. Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls,
  35. GR32_Image;
  36. type
  37. TFormThickLineTest = class(TForm)
  38. PaintBoxGDIThin: TPaintBox;
  39. PaintBox32_ThinAlpha: TPaintBox32;
  40. Label1: TLabel;
  41. Label2: TLabel;
  42. PaintBoxGDIThick: TPaintBox;
  43. PaintBox32_Thick: TPaintBox32;
  44. Label3: TLabel;
  45. Label4: TLabel;
  46. ButtonRedraw: TButton;
  47. Label5: TLabel;
  48. PaintBox32_ThickLine: TPaintBox32;
  49. Label6: TLabel;
  50. PaintBox32_Thin: TPaintBox32;
  51. procedure PaintBoxGDIThinPaint(Sender: TObject);
  52. procedure PaintBox32_ThinAlphaPaintBuffer(Sender: TObject);
  53. procedure PaintBoxGDIThickPaint(Sender: TObject);
  54. procedure PaintBox32_ThickPaintBuffer(Sender: TObject);
  55. procedure ButtonRedrawClick(Sender: TObject);
  56. procedure PaintBox32_ThickLinePaintBuffer(Sender: TObject);
  57. procedure PaintBoxClick(Sender: TObject);
  58. procedure PaintBox32Click(Sender: TObject);
  59. procedure PaintBox32_ThinPaintBuffer(Sender: TObject);
  60. private
  61. FDoPaint: boolean;
  62. procedure NotHung;
  63. public
  64. end;
  65. var
  66. FormThickLineTest: TFormThickLineTest;
  67. implementation
  68. {$R *.dfm}
  69. uses
  70. System.Types,
  71. System.Math,
  72. GR32_System,
  73. GR32.Lines.Thick,
  74. GR32,
  75. GR32_LowLevel,
  76. GR32_Paths,
  77. GR32_Brushes,
  78. GR32_Polygons;
  79. const
  80. MinLineCount = 200000;
  81. MinTestTime = 1000;
  82. MaxTestTime = 4500; // Windows will consider the application hung after 5 seconds
  83. ThickLineWidth = 10;
  84. //------------------------------------------------------------------------------
  85. procedure TFormThickLineTest.NotHung;
  86. var
  87. Msg: TMsg;
  88. begin
  89. // Pump WM_NULL so Windows doesn't consider application hung
  90. if PeekMessage(Msg, Handle, WM_NULL, WM_NULL, PM_NOREMOVE) and (Msg.message = WM_NULL) then
  91. PeekMessage(Msg, Handle, WM_NULL, WM_NULL, PM_REMOVE);
  92. end;
  93. //------------------------------------------------------------------------------
  94. procedure TFormThickLineTest.ButtonRedrawClick(Sender: TObject);
  95. begin
  96. ButtonRedraw.Enabled := False;
  97. ButtonRedraw.Update;
  98. FDoPaint := True;
  99. try
  100. Invalidate;
  101. PaintBox32_ThinAlpha.Invalidate;
  102. PaintBox32_Thin.Invalidate;
  103. PaintBox32_Thick.Invalidate;
  104. PaintBox32_ThickLine.Invalidate;
  105. Update;
  106. finally
  107. FDoPaint := False;
  108. ButtonRedraw.Enabled := True;
  109. end;
  110. end;
  111. procedure TFormThickLineTest.PaintBoxClick(Sender: TObject);
  112. begin
  113. FDoPaint := True;
  114. try
  115. TPaintBox(Sender).Invalidate;
  116. TPaintBox(Sender).Update;
  117. finally
  118. FDoPaint := False;
  119. end;
  120. end;
  121. procedure TFormThickLineTest.PaintBox32Click(Sender: TObject);
  122. begin
  123. FDoPaint := True;
  124. try
  125. TPaintBox32(Sender).Invalidate;
  126. TPaintBox32(Sender).Update;
  127. finally
  128. FDoPaint := False;
  129. end;
  130. end;
  131. //------------------------------------------------------------------------------
  132. procedure TFormThickLineTest.PaintBoxGDIThinPaint(Sender: TObject);
  133. var
  134. Stopwatch: TStopwatch;
  135. LineCount: integer;
  136. begin
  137. (*
  138. ** GDI, thin line. Aliased. No alpha blending.
  139. *)
  140. if (not FDoPaint) then
  141. exit;
  142. Screen.Cursor := crHourGlass;
  143. TPaintBox(Sender).Canvas.Brush.Color := clWhite;
  144. TPaintBox(Sender).Canvas.Brush.Style := bsSolid;
  145. TPaintBox(Sender).Canvas.FillRect(PaintBoxGDIThin.Canvas.ClipRect);
  146. TPaintBox(Sender).Canvas.Pen.Width := 1;
  147. TPaintBox(Sender).Canvas.MoveTo(0,0);
  148. RandSeed := 0;
  149. Stopwatch := TStopwatch.StartNew;
  150. LineCount := 0;
  151. while ((LineCount < MinLineCount) or (Stopwatch.ElapsedMilliseconds < MinTestTime)) and (Stopwatch.ElapsedMilliseconds < MaxTestTime) do
  152. begin
  153. Inc(LineCount);
  154. TPaintBox(Sender).Canvas.Pen.Color := Random($00FFFFFF);
  155. TPaintBox(Sender).Canvas.LineTo(Random(TPaintBox(Sender).Width), Random(TPaintBox(Sender).Height));
  156. end;
  157. Stopwatch.Stop;
  158. Label1.Caption := Format('TCanvas.LineTo, Width=1.'#13'Lines per second: %.0n', [LineCount / Stopwatch.ElapsedMilliseconds * 1000]);
  159. NotHung;
  160. Screen.Cursor := crDefault;
  161. end;
  162. //------------------------------------------------------------------------------
  163. procedure TFormThickLineTest.PaintBoxGDIThickPaint(Sender: TObject);
  164. var
  165. Stopwatch: TStopwatch;
  166. LineCount: integer;
  167. begin
  168. (*
  169. ** GDI, thick line. Aliased. No alpha blending.
  170. *)
  171. if (not FDoPaint) then
  172. exit;
  173. Screen.Cursor := crHourGlass;
  174. TPaintBox(Sender).Canvas.Pen.Width := ThickLineWidth;
  175. TPaintBox(Sender).Canvas.MoveTo(0,0);
  176. TPaintBox(Sender).Canvas.Brush.Color := clWhite;
  177. TPaintBox(Sender).Canvas.Brush.Style := bsSolid;
  178. TPaintBox(Sender).Canvas.FillRect(TPaintBox(Sender).Canvas.ClipRect);
  179. RandSeed := 0;
  180. Stopwatch := TStopwatch.StartNew;
  181. LineCount := 0;
  182. while ((LineCount < MinLineCount) or (Stopwatch.ElapsedMilliseconds < MinTestTime)) and (Stopwatch.ElapsedMilliseconds < MaxTestTime) do
  183. begin
  184. Inc(LineCount);
  185. TPaintBox(Sender).Canvas.Pen.Color := Random($00FFFFFF);
  186. TPaintBox(Sender).Canvas.LineTo(Random(TPaintBox(Sender).Width), Random(TPaintBox(Sender).Height));
  187. end;
  188. Stopwatch.Stop;
  189. Label3.Caption := Format('TCanvas.LineTo, Width=%d.'#13'Lines per second: %.0n', [ThickLineWidth, LineCount / Stopwatch.ElapsedMilliseconds * 1000]);
  190. NotHung;
  191. Screen.Cursor := crDefault;
  192. end;
  193. //------------------------------------------------------------------------------
  194. procedure TFormThickLineTest.PaintBox32_ThinAlphaPaintBuffer(Sender: TObject);
  195. var
  196. Stopwatch: TStopwatch;
  197. LineCount: integer;
  198. begin
  199. (*
  200. ** Graphics32, thin line. Anti-aliased & Alpha blended.
  201. *)
  202. if (not FDoPaint) then
  203. exit;
  204. Screen.Cursor := crHourGlass;
  205. TPaintBox32(Sender).Buffer.Clear(clWhite32);
  206. TPaintBox32(Sender).Buffer.DrawMode := dmOpaque;
  207. TPaintBox32(Sender).Buffer.CombineMode := cmBlend;
  208. TPaintBox32(Sender).Buffer.BeginLockUpdate; // No need for update handling, we will redraw everything
  209. TPaintBox32(Sender).Buffer.MoveTo(0, 0);
  210. RandSeed := 0;
  211. Stopwatch := TStopwatch.StartNew;
  212. LineCount := 0;
  213. while ((LineCount < MinLineCount) or (Stopwatch.ElapsedMilliseconds < MinTestTime)) and (Stopwatch.ElapsedMilliseconds < MaxTestTime) do
  214. begin
  215. Inc(LineCount);
  216. TPaintBox32(Sender).Buffer.PenColor := Color32(Random($00FFFFFF)); // Color32 to swap R and B
  217. TPaintBox32(Sender).Buffer.LineToAS(Random(TPaintBox32(Sender).Width), Random(TPaintBox32(Sender).Height));
  218. end;
  219. Stopwatch.Stop;
  220. TPaintBox32(Sender).Buffer.EndLockUpdate;
  221. Label2.Caption := Format('TBitmap32.LineToAS, Width=1.'#13'Lines per second: %.0n', [LineCount / Stopwatch.ElapsedMilliseconds * 1000]);
  222. NotHung;
  223. Screen.Cursor := crDefault;
  224. end;
  225. //------------------------------------------------------------------------------
  226. procedure TFormThickLineTest.PaintBox32_ThinPaintBuffer(Sender: TObject);
  227. var
  228. Stopwatch: TStopwatch;
  229. LineCount: integer;
  230. begin
  231. (*
  232. ** Graphics32, thin line. Aliased. No alpha blending.
  233. *)
  234. if (not FDoPaint) then
  235. exit;
  236. Screen.Cursor := crHourGlass;
  237. TPaintBox32(Sender).Buffer.Clear(clWhite32);
  238. TPaintBox32(Sender).Buffer.DrawMode := dmOpaque;
  239. TPaintBox32(Sender).Buffer.CombineMode := cmBlend;
  240. TPaintBox32(Sender).Buffer.BeginLockUpdate; // No need for update handling, we will redraw everything
  241. TPaintBox32(Sender).Buffer.MoveTo(0, 0);
  242. RandSeed := 0;
  243. Stopwatch := TStopwatch.StartNew;
  244. LineCount := 0;
  245. while ((LineCount < MinLineCount) or (Stopwatch.ElapsedMilliseconds < MinTestTime)) and (Stopwatch.ElapsedMilliseconds < MaxTestTime) do
  246. begin
  247. Inc(LineCount);
  248. TPaintBox32(Sender).Buffer.PenColor := Color32(Random($00FFFFFF)); // Color32 to swap R and B
  249. TPaintBox32(Sender).Buffer.LineToS(Random(TPaintBox32(Sender).Width), Random(TPaintBox32(Sender).Height));
  250. end;
  251. Stopwatch.Stop;
  252. TPaintBox32(Sender).Buffer.EndLockUpdate;
  253. Label6.Caption := Format('TBitmap32.LineToS, Width=1.'#13'Lines per second: %.0n', [LineCount / Stopwatch.ElapsedMilliseconds * 1000]);
  254. NotHung;
  255. Screen.Cursor := crDefault;
  256. end;
  257. //------------------------------------------------------------------------------
  258. procedure TFormThickLineTest.PaintBox32_ThickPaintBuffer(Sender: TObject);
  259. var
  260. Canvas: TCanvas32;
  261. Stroke: TStrokeBrush;
  262. LastPoint: TFloatPoint;
  263. Stopwatch: TStopwatch;
  264. LineCount: integer;
  265. begin
  266. (*
  267. ** Graphics32, thick line via TCanvas32. Anti-aliased & Alpha blended.
  268. *)
  269. if (not FDoPaint) then
  270. exit;
  271. Screen.Cursor := crHourGlass;
  272. TPaintBox32(Sender).Buffer.Clear(clWhite32);
  273. TPaintBox32(Sender).Buffer.DrawMode := dmOpaque;
  274. TPaintBox32(Sender).Buffer.CombineMode := cmBlend;
  275. TPaintBox32(Sender).Buffer.BeginLockUpdate; // No need for update handling, we will redraw everything
  276. Canvas := TCanvas32.Create(TPaintBox32(Sender).Buffer);
  277. try
  278. Stroke := TStrokeBrush(Canvas.Brushes.Add(TStrokeBrush));
  279. Stroke.StrokeWidth := ThickLineWidth;
  280. LastPoint := GR32.FloatPoint(0, 0);
  281. RandSeed := 0;
  282. Stopwatch := TStopwatch.StartNew;
  283. LineCount := 0;
  284. while ((LineCount < MinLineCount) or (Stopwatch.ElapsedMilliseconds < MinTestTime)) and (Stopwatch.ElapsedMilliseconds < MaxTestTime) do
  285. begin
  286. Inc(LineCount);
  287. Stroke.FillColor := Color32(Random($00FFFFFF)); // Color32 to swap R and B
  288. Canvas.MoveTo(LastPoint); // EndPath clears last point so we have to set it manually
  289. LastPoint := GR32.FloatPoint(Random(TPaintBox32(Sender).Width), Random(TPaintBox32(Sender).Height));
  290. Canvas.LineTo(LastPoint);
  291. Canvas.EndPath; // Each line must be its own path, with its own stroke color
  292. end;
  293. Stopwatch.Stop;
  294. TPaintBox32(Sender).Buffer.EndLockUpdate;
  295. finally
  296. Canvas.Free;
  297. end;
  298. Label4.Caption := Format('TCanvas32.LineTo, Width=%d.'#13'Lines per second: %.0n', [ThickLineWidth, LineCount / Stopwatch.ElapsedMilliseconds * 1000]);
  299. NotHung;
  300. Screen.Cursor := crDefault;
  301. end;
  302. //------------------------------------------------------------------------------
  303. procedure TFormThickLineTest.PaintBox32_ThickLinePaintBuffer(Sender: TObject);
  304. var
  305. LastPos, NewPos: TPoint;
  306. Stopwatch: TStopwatch;
  307. LineCount: integer;
  308. Color: TColor32;
  309. begin
  310. (*
  311. ** Graphics32, thick line via DrawThickLine. Aliased. No alpha blending.
  312. *)
  313. if (not FDoPaint) then
  314. exit;
  315. Screen.Cursor := crHourGlass;
  316. TPaintBox32(Sender).Buffer.Clear(clWhite32);
  317. TPaintBox32(Sender).Buffer.DrawMode := dmOpaque;
  318. TPaintBox32(Sender).Buffer.CombineMode := cmBlend;
  319. TPaintBox32(Sender).Buffer.BeginLockUpdate; // No need for update handling, we will redraw everything
  320. LastPos := GR32.Point(0, 0);
  321. RandSeed := 0;
  322. Stopwatch := TStopwatch.StartNew;
  323. LineCount := 0;
  324. while ((LineCount < MinLineCount) or (Stopwatch.ElapsedMilliseconds < MinTestTime)) and (Stopwatch.ElapsedMilliseconds < MaxTestTime) do
  325. begin
  326. Inc(LineCount);
  327. Color := Color32(Random($00FFFFFF)); // Color32 to swap R and B
  328. NewPos := GR32.Point(Random(TPaintBox32(Sender).Width), Random(TPaintBox32(Sender).Height));
  329. DrawThickLine(TPaintBox32(Sender).Buffer, LastPos.X, LastPos.Y, NewPos.X, NewPos.Y, ThickLineWidth, Color);
  330. LastPos := NewPos;
  331. end;
  332. Stopwatch.Stop;
  333. TPaintBox32(Sender).Buffer.EndLockUpdate;
  334. Label5.Caption := Format('Graphics32 DrawThickLine, Width=%d.'#13'Lines per second: %.0n', [ThickLineWidth, LineCount / Stopwatch.ElapsedMilliseconds * 1000]);
  335. NotHung;
  336. Screen.Cursor := crDefault;
  337. end;
  338. //------------------------------------------------------------------------------
  339. end.