123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- unit MainUnit;
- (* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1 or LGPL 2.1 with linking exception
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * Alternatively, the contents of this file may be used under the terms of the
- * Free Pascal modified version of the GNU Lesser General Public License
- * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
- * of this license are applicable instead of those above.
- * Please see the file LICENSE.txt for additional information concerning this
- * license.
- *
- * The Original Code is Gradient Lines Example
- *
- * The Initial Developer of the Original Code is
- * Alex A. Denisov
- *
- * Portions created by the Initial Developer are Copyright (C) 2000-2005
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * ***** END LICENSE BLOCK ***** *)
- interface
- {$include GR32.inc}
- {-$define FADE_BLEND}
- uses
- {$IFDEF FPC} LCLIntf, LResources, Buttons, {$ENDIF} SysUtils, Classes,
- Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Types,
- GR32,
- GR32_Blend,
- GR32_Image,
- GR32_System,
- GR32_LowLevel;
- type
- TVector2f = record
- X, Y: Single;
- end;
- TLine = class
- public
- Bitmap: TBitmap32;
- P1, P2: TVector2f; // positions
- V1, V2: TVector2f; // velocities
- C1, C2, C3: TColor32; // colors that define gradient pattern
- t1, t2, t3: Single;
- MaxVelocity: Single;
- constructor Create(ABitmap: TBitmap32);
- procedure Advance(DeltaT: Single);
- function GetLength: Single;
- procedure Paint;
- end;
- { TFormGradientLines }
- TFormGradientLines = class(TForm)
- BtnAddOne: TButton;
- BtnAddTen: TButton;
- BtnClear: TButton;
- LblTotal: TLabel;
- Memo: TMemo;
- PaintBox: TPaintBox32;
- PnlTotalLines: TPanel;
- RgpDraw: TRadioGroup;
- RgpFade: TRadioGroup;
- RepaintOpt: TCheckBox;
- TimerFrameRate: TTimer;
- procedure FormCreate(Sender: TObject);
- procedure RepaintOptClick(Sender: TObject);
- procedure BtnAddOneClick(Sender: TObject);
- procedure BtnAddTenClick(Sender: TObject);
- procedure BtnClearClick(Sender: TObject);
- procedure RgpFadeClick(Sender: TObject);
- procedure RgpDrawClick(Sender: TObject);
- procedure TimerFrameRateTimer(Sender: TObject);
- procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
- protected
- FBenchMark: boolean;
- FBenchMarkCounter: integer;
- Lines: array of TLine;
- FadeCount: Integer;
- Pass: Integer;
- DrawPasses: Integer;
- FrameCount: integer;
- FStopwatch: TStopwatch;
- procedure AppEventsIdle(Sender: TObject; var Done: Boolean);
- procedure StartBenchmark;
- public
- procedure AddLine;
- procedure AddLines(N: Integer);
- end;
- var
- FormGradientLines: TFormGradientLines;
- implementation
- {$R *.dfm}
- uses
- Math,
- Windows;
- function VectorAdd(const A, B: TVector2f): TVector2f;
- begin
- Result.X := A.X + B.X;
- Result.Y := A.Y + B.Y;
- end;
- function VectorSub(const A, B: TVector2f): TVector2f;
- begin
- Result.X := A.X - B.X;
- Result.Y := A.Y - B.Y;
- end;
- function VectorLen(const A: TVector2f): Single;
- begin
- Result := SqRt(SqR(A.X) + SqR(A.Y));
- end;
- function VectorDot(const A, B: TVector2f): Single;
- begin
- Result := A.X * B.X + A.Y * B.Y;
- end;
- function VectorScale(const A: TVector2f; Factor: Single): TVector2f;
- begin
- Result.X := A.X * Factor;
- Result.Y := A.Y * Factor;
- end;
- { TLine }
- constructor TLine.Create(ABitmap: TBitmap32);
- begin
- Bitmap := ABitmap;
- MaxVelocity := 1;
- end;
- procedure TLine.Advance(DeltaT: Single);
- const
- COne400 : Single = 1 / 400;
- COne300 : Single = 1 / 300;
- procedure AdvancePoint(var P, V: TVector2f; t: Single);
- begin
- { apply velocities }
- P := VectorAdd(P, VectorScale(V, t));
- { reflect from walls }
- if P.X < 0 then
- begin
- P.X := 0;
- V.X := -V.X;
- end;
- if P.X >= FormGradientLines.PaintBox.Width then
- begin
- P.X := FormGradientLines.PaintBox.Width - 1;
- V.X := - V.X;
- end;
- if P.Y < 0 then
- begin
- P.Y := 0;
- V.Y := -V.Y;
- end;
- if P.Y >= FormGradientLines.PaintBox.Height then
- begin
- P.Y := FormGradientLines.PaintBox.Height - 1;
- V.Y := - V.Y
- end;
- { change velocity a little bit }
- V.X := V.X + t * (Random - 0.5) * 0.25;
- V.Y := V.Y + t * (Random - 0.5) * 0.25;
- { limit velocity }
- if VectorLen(V) > MaxVelocity then
- V := VectorScale(V, 1 / VectorLen(V));
- end;
- begin
- AdvancePoint(P1, V1, DeltaT);
- AdvancePoint(P2, V2, DeltaT);
- C1 := HSLtoRGB(t1, Sin(t1 * 0.55) * 0.4 + 0.6, 0.5);
- C1 := SetAlpha(C1, Round(Sin(t1) * 25 + 50));
- t1 := t1 + Random * COne300;
- C2 := HSLtoRGB(t2, Sin(t2 * 0.55) * 0.4 + 0.6, 0.5);
- C2 := SetAlpha(C2, Round(Sin(t2) * 25 + 50));
- t2 := t2 + Random * COne400;
- C3 := HSLtoRGB(t3, Sin(t3 * 0.55) * 0.4 + 0.6, 0.5);
- C3 := SetAlpha(C3, Round(Sin(t3) * 25 + 50));
- t3 := t3 + Random * COne400;
- end;
- function TLine.GetLength: Single;
- begin
- Result := VectorLen(VectorSub(P1, P2));
- end;
- procedure TLine.Paint;
- var
- L: Single;
- begin
- // this shows how to draw a gradient line
- L := GetLength;
- if L < 1 then Exit;
- Bitmap.SetStipple([C1, C2, C3]);
- Bitmap.StippleStep := 2 / L; {2 = 3 - 1 = Number of colors in a pattern - 1}
- Bitmap.StippleCounter := 0;
- Bitmap.LineFSP(P1.X, P1.Y, P2.X, P2.Y);
- end;
- { TFormGradientLines }
- procedure TFormGradientLines.FormCreate(Sender: TObject);
- begin
- FadeCount := 0;
- DrawPasses := 2;
- Application.OnIdle := AppEventsIdle;
- if (FindCmdLineSwitch('benchmark')) then
- StartBenchmark;
- end;
- procedure TFormGradientLines.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
- begin
- if (Key <> VK_F1) then
- exit;
- Key := 0;
- StartBenchmark;
- end;
- procedure TFormGradientLines.AddLine;
- var
- L: TLine;
- begin
- SetLength(Lines, Length(Lines) + 1);
- L := TLine.Create(PaintBox.Buffer);
- Lines[High(Lines)] := L;
- L.t1 := Random * 3;
- L.t2 := Random * 3;
- L.t3 := Random * 3;
- L.P1.X := Random(PaintBox.Buffer.Width div 2 - 1);
- L.P2.X := Random(PaintBox.Buffer.Width div 2 - 1);
- L.P1.Y := Random(PaintBox.Buffer.Height div 2 - 1);
- L.P2.Y := Random(PaintBox.Buffer.Height div 2 - 1);
- PnlTotalLines.Caption := IntToStr(Length(Lines));
- end;
- procedure TFormGradientLines.AddLines(N: Integer);
- var
- Index: Integer;
- begin
- for Index := 0 to N - 1 do
- AddLine;
- end;
- procedure TFormGradientLines.AppEventsIdle(Sender: TObject; var Done: Boolean);
- var
- I, J: Integer;
- begin
- // We need to be continously called. Even when there are no
- // messages in the message queue. Otherwise the framerate calculation will
- // not work.
- Done := False;
- if (Length(Lines) = 0) then
- exit;
- PaintBox.BeginUpdate;
- try
- for J := 0 to DrawPasses - 1 do
- for I := 0 to High(Lines) do
- begin
- Lines[I].Advance(1);
- Lines[I].Paint;
- end;
- if FadeCount > 0 then
- begin
- if Pass = 0 then
- begin
- {$ifdef FADE_BLEND}
- // We fade out the existing image by blending black onto it. The alpha controls how fast we fade.
- // One problem with this method is that we can't ever fade to complete black due to rounding
- // errors when working with 8 bit color values.
- BlendMems($10000000, @PaintBox.Buffer.Bits[0], PaintBox.Buffer.Width * PaintBox.Buffer.Height);
- {$else}
- // Fade out by scaling the RGB: Faded = Colors * Weight / 255
- ScaleMems(@PaintBox.Buffer.Bits[0], PaintBox.Buffer.Width * PaintBox.Buffer.Height, $f0);
- {$endif}
- // We're modifying the buffer directly above, so force a complete invalidation.
- PaintBox.ForceFullInvalidate;
- end;
- Dec(Pass);
- if (Pass < 0) or (Pass > FadeCount) then
- Pass := FadeCount;
- end;
- finally
- PaintBox.EndUpdate;
- end;
- Inc(FrameCount);
- if (FBenchMark) then
- begin
- Dec(FBenchMarkCounter);
- if (FBenchMarkCounter <= 0) then
- Application.Terminate;
- end;
- end;
- procedure TFormGradientLines.BtnAddOneClick(Sender: TObject);
- begin
- TimerFrameRate.Enabled := False;
- RandSeed := 0;
- AddLine;
- FStopwatch := TStopwatch.StartNew;
- TimerFrameRate.Enabled := True;
- end;
- procedure TFormGradientLines.BtnAddTenClick(Sender: TObject);
- begin
- TimerFrameRate.Enabled := False;
- RandSeed := 0;
- AddLines(10);
- FStopwatch := TStopwatch.StartNew;
- TimerFrameRate.Enabled := True;
- end;
- procedure TFormGradientLines.BtnClearClick(Sender: TObject);
- var
- Index: Integer;
- begin
- for Index := High(Lines) downto 0 do
- Lines[Index].Free;
- Lines := nil;
- PaintBox.Buffer.Clear;
- PnlTotalLines.Caption := '0';
- Caption := '';
- TimerFrameRate.Enabled := False;
- end;
-
- procedure TFormGradientLines.RgpFadeClick(Sender: TObject);
- const
- FC: array [0..2] of Integer = (0, 20, 1);
- begin
- FadeCount := FC[RgpFade.ItemIndex];
- RepaintOpt.Enabled := (FadeCount <> 1);
- end;
- procedure TFormGradientLines.StartBenchmark;
- begin
- FBenchMark := True;
- FBenchMarkCounter := 100*1000;
- WindowState := wsMaximized;
- RgpDraw.ItemIndex := 2; // Fast draw
- RgpFade.ItemIndex := 0; // No fade
- RepaintOpt.Checked := True; // Repaint optimizer
- BtnAddTen.Click;
- end;
- procedure TFormGradientLines.TimerFrameRateTimer(Sender: TObject);
- var
- FPS: Single;
- begin
- FStopwatch.Stop;
- TTimer(Sender).Enabled := False;
- if (FStopwatch.ElapsedMilliseconds <> 0) then
- FPS := 1000 * FrameCount / FStopwatch.ElapsedMilliseconds
- else
- FPS := 0;
- if (FBenchMark) then
- Caption := Format('%.0n fps (%.0n)', [FPS, 1.0 * FBenchMarkCounter])
- else
- Caption := Format('%.0n fps', [FPS]);
- FrameCount := 0;
- TTimer(Sender).Enabled := True;
- FStopwatch := TStopwatch.StartNew;
- end;
- procedure TFormGradientLines.RgpDrawClick(Sender: TObject);
- begin
- DrawPasses := (RgpDraw.ItemIndex + 1) * 3 - 2;
- end;
- procedure TFormGradientLines.RepaintOptClick(Sender: TObject);
- begin
- if RepaintOpt.Checked then
- PaintBox.RepaintMode := rmOptimizer
- else
- PaintBox.RepaintMode := rmFull;
- end;
- end.
|