174 lines
4.1 KiB
ObjectPascal

unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
StdCtrls, Spin, BGRAVirtualScreen, BGRABitmap, BGRABitmapTypes;
type
{ TForm1 }
TForm1 = class(TForm)
CheckBox1: TCheckBox;
Label1: TLabel;
Panel1: TPanel;
SpinEdit1: TSpinEdit;
VirtualScreen: TBGRAVirtualScreen;
Timer1: TTimer;
procedure FormDestroy(Sender: TObject);
procedure SpinEdit1Change(Sender: TObject);
procedure VirtualScreenRedraw(Sender: TObject; Bitmap: TBGRABitmap);
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
FInit: boolean;
procedure InitBalls(ACount: integer);
procedure Init;
function GetBallRect(AIndex: integer): TRect;
public
ballRadius: integer;
background: TBGRABitmap;
balls: array of record
ballPos: TPoint;
ballSpeed: TPoint;
ballColor: TBGRAPixel;
end;
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
uses BGRAGradients;
{ TForm1 }
procedure TForm1.VirtualScreenRedraw(Sender: TObject; Bitmap: TBGRABitmap);
var
i: Integer;
begin
Init;
Bitmap.FillRect(rect(0,0,Bitmap.Width,Bitmap.Height), background, dmSet);
for i := 0 to high(balls) do
with balls[i] do
Bitmap.EllipseAntialias(ballPos.x,ballPos.y, ballRadius,ballRadius, BGRABlack, 1, ballColor);
end;
procedure TForm1.SpinEdit1Change(Sender: TObject);
begin
initBalls(SpinEdit1.Value);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
background.Free;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
VirtualScreen.BitmapAutoScale:= false;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
i: Integer;
rects: array of TRect;
nbRects: integer;
begin
if not FInit then exit;
Timer1.Enabled:= false;
setlength(rects, length(balls)*2);
nbRects := 0;
for i := 0 to high(balls) do
with balls[i] do
begin
//rect to clear
rects[nbRects] := GetBallRect(i);
inc(nbRects);
inc(ballSpeed.Y);
inc(ballPos.X, ballSpeed.x);
inc(ballPos.Y, ballSpeed.y);
if BallPos.Y >= VirtualScreen.BitmapHeight-ballRadius then
begin
ballPos.Y := VirtualScreen.BitmapHeight-ballRadius;
ballSpeed.Y := -abs(ballSpeed.Y);
if (BallPos.X < -ballRadius) or (BallPos.X > VirtualScreen.BitmapWidth+ballRadius) then
begin
ballPos := Point(random(VirtualScreen.BitmapWidth), - ballRadius);
ballSpeed.Y := 0;
Continue;
end;
end else
begin
if BallPos.X+ballRadius >= VirtualScreen.BitmapWidth then
begin
BallPos.X := VirtualScreen.BitmapWidth-ballRadius;
ballSpeed.X := -abs(ballSpeed.x);
end else
if BallPos.X <= ballRadius then
begin
BallPos.X := ballRadius;
ballSpeed.X := abs(ballSpeed.x);
end;
end;
//rect to redraw
rects[nbRects] := GetBallRect(i);
inc(nbRects);
end;
if CheckBox1.Checked then
VirtualScreen.RedrawBitmap
else
VirtualScreen.RedrawBitmap(slice(rects,nbRects));
Timer1.Enabled:= true;
end;
procedure TForm1.InitBalls(ACount: integer);
var
i: Integer;
begin
randomize;
VirtualScreen.DiscardBitmap;
setlength(balls, ACount);
for i := 0 to high(balls) do
with balls[i] do
begin
ballPos := Point(random(VirtualScreen.BitmapWidth), (i*VirtualScreen.BitmapHeight div length(balls)) - ballRadius);
ballSpeed := Point(random(5)-2, 0);
ballColor := BGRA(random(256),random(256),random(256));
if ballColor.Lightness > 48000 then ballColor.Lightness:= 48000;
end;
end;
procedure TForm1.Init;
var
scale: Double;
begin
if FInit then exit;
FInit := true;
scale := VirtualScreen.BitmapScale*Screen.PixelsPerInch/96;
ballRadius := round(20*scale);
initBalls(SpinEdit1.Value);
background := CreateCyclicPerlinNoiseMap(round(256*scale),round(256*scale));
end;
function TForm1.GetBallRect(AIndex: integer): TRect;
begin
with balls[AIndex] do
result := Rect(ballPos.X-ballRadius, ballPos.Y-ballRadius,
ballPos.X+ballRadius+1, ballPos.Y+ballRadius+1);
end;
end.