MainUnit.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  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 Gradient Lines Example
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Alex A. Denisov
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2000-2005
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * Contributor(s):
  31. *
  32. * ***** END LICENSE BLOCK ***** *)
  33. interface
  34. {$include GR32.inc}
  35. {-$define FADE_BLEND}
  36. uses
  37. {$IFDEF FPC} LCLIntf, LResources, Buttons, {$ENDIF} SysUtils, Classes,
  38. Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Types,
  39. GR32,
  40. GR32_Blend,
  41. GR32_Image,
  42. GR32_System,
  43. GR32_LowLevel;
  44. type
  45. TVector2f = record
  46. X, Y: Single;
  47. end;
  48. TLine = class
  49. public
  50. Bitmap: TBitmap32;
  51. P1, P2: TVector2f; // positions
  52. V1, V2: TVector2f; // velocities
  53. C1, C2, C3: TColor32; // colors that define gradient pattern
  54. t1, t2, t3: Single;
  55. MaxVelocity: Single;
  56. constructor Create(ABitmap: TBitmap32);
  57. procedure Advance(DeltaT: Single);
  58. function GetLength: Single;
  59. procedure Paint;
  60. end;
  61. { TFormGradientLines }
  62. TFormGradientLines = class(TForm)
  63. BtnAddOne: TButton;
  64. BtnAddTen: TButton;
  65. BtnClear: TButton;
  66. LblTotal: TLabel;
  67. Memo: TMemo;
  68. PaintBox: TPaintBox32;
  69. PnlTotalLines: TPanel;
  70. RgpDraw: TRadioGroup;
  71. RgpFade: TRadioGroup;
  72. RepaintOpt: TCheckBox;
  73. TimerFrameRate: TTimer;
  74. procedure FormCreate(Sender: TObject);
  75. procedure RepaintOptClick(Sender: TObject);
  76. procedure BtnAddOneClick(Sender: TObject);
  77. procedure BtnAddTenClick(Sender: TObject);
  78. procedure BtnClearClick(Sender: TObject);
  79. procedure RgpFadeClick(Sender: TObject);
  80. procedure RgpDrawClick(Sender: TObject);
  81. procedure TimerFrameRateTimer(Sender: TObject);
  82. procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  83. protected
  84. FBenchMark: boolean;
  85. FBenchMarkCounter: integer;
  86. Lines: array of TLine;
  87. FadeCount: Integer;
  88. Pass: Integer;
  89. DrawPasses: Integer;
  90. FrameCount: integer;
  91. FStopwatch: TStopwatch;
  92. procedure AppEventsIdle(Sender: TObject; var Done: Boolean);
  93. procedure StartBenchmark;
  94. public
  95. procedure AddLine;
  96. procedure AddLines(N: Integer);
  97. end;
  98. var
  99. FormGradientLines: TFormGradientLines;
  100. implementation
  101. {$R *.dfm}
  102. uses
  103. Math,
  104. Windows;
  105. function VectorAdd(const A, B: TVector2f): TVector2f;
  106. begin
  107. Result.X := A.X + B.X;
  108. Result.Y := A.Y + B.Y;
  109. end;
  110. function VectorSub(const A, B: TVector2f): TVector2f;
  111. begin
  112. Result.X := A.X - B.X;
  113. Result.Y := A.Y - B.Y;
  114. end;
  115. function VectorLen(const A: TVector2f): Single;
  116. begin
  117. Result := SqRt(SqR(A.X) + SqR(A.Y));
  118. end;
  119. function VectorDot(const A, B: TVector2f): Single;
  120. begin
  121. Result := A.X * B.X + A.Y * B.Y;
  122. end;
  123. function VectorScale(const A: TVector2f; Factor: Single): TVector2f;
  124. begin
  125. Result.X := A.X * Factor;
  126. Result.Y := A.Y * Factor;
  127. end;
  128. { TLine }
  129. constructor TLine.Create(ABitmap: TBitmap32);
  130. begin
  131. Bitmap := ABitmap;
  132. MaxVelocity := 1;
  133. end;
  134. procedure TLine.Advance(DeltaT: Single);
  135. const
  136. COne400 : Single = 1 / 400;
  137. COne300 : Single = 1 / 300;
  138. procedure AdvancePoint(var P, V: TVector2f; t: Single);
  139. begin
  140. { apply velocities }
  141. P := VectorAdd(P, VectorScale(V, t));
  142. { reflect from walls }
  143. if P.X < 0 then
  144. begin
  145. P.X := 0;
  146. V.X := -V.X;
  147. end;
  148. if P.X >= FormGradientLines.PaintBox.Width then
  149. begin
  150. P.X := FormGradientLines.PaintBox.Width - 1;
  151. V.X := - V.X;
  152. end;
  153. if P.Y < 0 then
  154. begin
  155. P.Y := 0;
  156. V.Y := -V.Y;
  157. end;
  158. if P.Y >= FormGradientLines.PaintBox.Height then
  159. begin
  160. P.Y := FormGradientLines.PaintBox.Height - 1;
  161. V.Y := - V.Y
  162. end;
  163. { change velocity a little bit }
  164. V.X := V.X + t * (Random - 0.5) * 0.25;
  165. V.Y := V.Y + t * (Random - 0.5) * 0.25;
  166. { limit velocity }
  167. if VectorLen(V) > MaxVelocity then
  168. V := VectorScale(V, 1 / VectorLen(V));
  169. end;
  170. begin
  171. AdvancePoint(P1, V1, DeltaT);
  172. AdvancePoint(P2, V2, DeltaT);
  173. C1 := HSLtoRGB(t1, Sin(t1 * 0.55) * 0.4 + 0.6, 0.5);
  174. C1 := SetAlpha(C1, Round(Sin(t1) * 25 + 50));
  175. t1 := t1 + Random * COne300;
  176. C2 := HSLtoRGB(t2, Sin(t2 * 0.55) * 0.4 + 0.6, 0.5);
  177. C2 := SetAlpha(C2, Round(Sin(t2) * 25 + 50));
  178. t2 := t2 + Random * COne400;
  179. C3 := HSLtoRGB(t3, Sin(t3 * 0.55) * 0.4 + 0.6, 0.5);
  180. C3 := SetAlpha(C3, Round(Sin(t3) * 25 + 50));
  181. t3 := t3 + Random * COne400;
  182. end;
  183. function TLine.GetLength: Single;
  184. begin
  185. Result := VectorLen(VectorSub(P1, P2));
  186. end;
  187. procedure TLine.Paint;
  188. var
  189. L: Single;
  190. begin
  191. // this shows how to draw a gradient line
  192. L := GetLength;
  193. if L < 1 then Exit;
  194. Bitmap.SetStipple([C1, C2, C3]);
  195. Bitmap.StippleStep := 2 / L; {2 = 3 - 1 = Number of colors in a pattern - 1}
  196. Bitmap.StippleCounter := 0;
  197. Bitmap.LineFSP(P1.X, P1.Y, P2.X, P2.Y);
  198. end;
  199. { TFormGradientLines }
  200. procedure TFormGradientLines.FormCreate(Sender: TObject);
  201. begin
  202. FadeCount := 0;
  203. DrawPasses := 2;
  204. Application.OnIdle := AppEventsIdle;
  205. if (FindCmdLineSwitch('benchmark')) then
  206. StartBenchmark;
  207. end;
  208. procedure TFormGradientLines.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  209. begin
  210. if (Key <> VK_F1) then
  211. exit;
  212. Key := 0;
  213. StartBenchmark;
  214. end;
  215. procedure TFormGradientLines.AddLine;
  216. var
  217. L: TLine;
  218. begin
  219. SetLength(Lines, Length(Lines) + 1);
  220. L := TLine.Create(PaintBox.Buffer);
  221. Lines[High(Lines)] := L;
  222. L.t1 := Random * 3;
  223. L.t2 := Random * 3;
  224. L.t3 := Random * 3;
  225. L.P1.X := Random(PaintBox.Buffer.Width div 2 - 1);
  226. L.P2.X := Random(PaintBox.Buffer.Width div 2 - 1);
  227. L.P1.Y := Random(PaintBox.Buffer.Height div 2 - 1);
  228. L.P2.Y := Random(PaintBox.Buffer.Height div 2 - 1);
  229. PnlTotalLines.Caption := IntToStr(Length(Lines));
  230. end;
  231. procedure TFormGradientLines.AddLines(N: Integer);
  232. var
  233. Index: Integer;
  234. begin
  235. for Index := 0 to N - 1 do
  236. AddLine;
  237. end;
  238. procedure TFormGradientLines.AppEventsIdle(Sender: TObject; var Done: Boolean);
  239. var
  240. I, J: Integer;
  241. begin
  242. // We need to be continously called. Even when there are no
  243. // messages in the message queue. Otherwise the framerate calculation will
  244. // not work.
  245. Done := False;
  246. if (Length(Lines) = 0) then
  247. exit;
  248. PaintBox.BeginUpdate;
  249. try
  250. for J := 0 to DrawPasses - 1 do
  251. for I := 0 to High(Lines) do
  252. begin
  253. Lines[I].Advance(1);
  254. Lines[I].Paint;
  255. end;
  256. if FadeCount > 0 then
  257. begin
  258. if Pass = 0 then
  259. begin
  260. {$ifdef FADE_BLEND}
  261. // We fade out the existing image by blending black onto it. The alpha controls how fast we fade.
  262. // One problem with this method is that we can't ever fade to complete black due to rounding
  263. // errors when working with 8 bit color values.
  264. BlendMems($10000000, @PaintBox.Buffer.Bits[0], PaintBox.Buffer.Width * PaintBox.Buffer.Height);
  265. {$else}
  266. // Fade out by scaling the RGB: Faded = Colors * Weight / 255
  267. ScaleMems(@PaintBox.Buffer.Bits[0], PaintBox.Buffer.Width * PaintBox.Buffer.Height, $f0);
  268. {$endif}
  269. // We're modifying the buffer directly above, so force a complete invalidation.
  270. PaintBox.ForceFullInvalidate;
  271. end;
  272. Dec(Pass);
  273. if (Pass < 0) or (Pass > FadeCount) then
  274. Pass := FadeCount;
  275. end;
  276. finally
  277. PaintBox.EndUpdate;
  278. end;
  279. Inc(FrameCount);
  280. if (FBenchMark) then
  281. begin
  282. Dec(FBenchMarkCounter);
  283. if (FBenchMarkCounter <= 0) then
  284. Application.Terminate;
  285. end;
  286. end;
  287. procedure TFormGradientLines.BtnAddOneClick(Sender: TObject);
  288. begin
  289. TimerFrameRate.Enabled := False;
  290. RandSeed := 0;
  291. AddLine;
  292. FStopwatch := TStopwatch.StartNew;
  293. TimerFrameRate.Enabled := True;
  294. end;
  295. procedure TFormGradientLines.BtnAddTenClick(Sender: TObject);
  296. begin
  297. TimerFrameRate.Enabled := False;
  298. RandSeed := 0;
  299. AddLines(10);
  300. FStopwatch := TStopwatch.StartNew;
  301. TimerFrameRate.Enabled := True;
  302. end;
  303. procedure TFormGradientLines.BtnClearClick(Sender: TObject);
  304. var
  305. Index: Integer;
  306. begin
  307. for Index := High(Lines) downto 0 do
  308. Lines[Index].Free;
  309. Lines := nil;
  310. PaintBox.Buffer.Clear;
  311. PnlTotalLines.Caption := '0';
  312. Caption := '';
  313. TimerFrameRate.Enabled := False;
  314. end;
  315. procedure TFormGradientLines.RgpFadeClick(Sender: TObject);
  316. const
  317. FC: array [0..2] of Integer = (0, 20, 1);
  318. begin
  319. FadeCount := FC[RgpFade.ItemIndex];
  320. RepaintOpt.Enabled := (FadeCount <> 1);
  321. end;
  322. procedure TFormGradientLines.StartBenchmark;
  323. begin
  324. FBenchMark := True;
  325. FBenchMarkCounter := 100*1000;
  326. WindowState := wsMaximized;
  327. RgpDraw.ItemIndex := 2; // Fast draw
  328. RgpFade.ItemIndex := 0; // No fade
  329. RepaintOpt.Checked := True; // Repaint optimizer
  330. BtnAddTen.Click;
  331. end;
  332. procedure TFormGradientLines.TimerFrameRateTimer(Sender: TObject);
  333. var
  334. FPS: Single;
  335. begin
  336. FStopwatch.Stop;
  337. TTimer(Sender).Enabled := False;
  338. if (FStopwatch.ElapsedMilliseconds <> 0) then
  339. FPS := 1000 * FrameCount / FStopwatch.ElapsedMilliseconds
  340. else
  341. FPS := 0;
  342. if (FBenchMark) then
  343. Caption := Format('%.0n fps (%.0n)', [FPS, 1.0 * FBenchMarkCounter])
  344. else
  345. Caption := Format('%.0n fps', [FPS]);
  346. FrameCount := 0;
  347. TTimer(Sender).Enabled := True;
  348. FStopwatch := TStopwatch.StartNew;
  349. end;
  350. procedure TFormGradientLines.RgpDrawClick(Sender: TObject);
  351. begin
  352. DrawPasses := (RgpDraw.ItemIndex + 1) * 3 - 2;
  353. end;
  354. procedure TFormGradientLines.RepaintOptClick(Sender: TObject);
  355. begin
  356. if RepaintOpt.Checked then
  357. PaintBox.RepaintMode := rmOptimizer
  358. else
  359. PaintBox.RepaintMode := rmFull;
  360. end;
  361. end.