246 lines
5.9 KiB
ObjectPascal

unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
ComCtrls, BGRAVirtualScreen, BGRABitmap, BGRABitmapTypes;
const
wind = -0.5; //1 means 45 degrees rain
rainDensity = 2; //strictly positive
type
{ TForm1 }
TForm1 = class(TForm)
vsRain: TBGRAVirtualScreen;
Timer1: TTimer;
TrackBar1: TTrackBar;
procedure RainRedraw(Sender: TObject; Bitmap: TBGRABitmap);
procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ private declarations }
bkg,stretchedBkg: TBGRABitmap;
prevTime: TDateTime;
prevTimeDefined: boolean;
rainData: array of record
x,ystart,yend: single;
rainWidth, rainSpeed: single;
grad: TBGRACustomGradient;
active: boolean;
inactiveTime: double;
end;
procedure ClearRainData;
procedure PrepareRainArray(nbRain: integer; ScaleX: single);
function PrepareRainDrop(i: integer; rainSizeX, rainSizeY: single): single;
procedure NeedRainArray(w, h, rainProba: integer; rainSizeX, rainSizeY: single);
procedure RainElapse(elapsed: double; rainProba, w, h: integer);
procedure RenderRain(Bitmap: TBGRABitmap);
end;
var
Form1: TForm1;
implementation
uses BGRAGradientScanner, Math;
{$R *.lfm}
{ TForm1 }
procedure TForm1.RainRedraw(Sender: TObject; Bitmap: TBGRABitmap);
var
elapsed: double;
ratio: single;
x,y,w,h: integer;
begin
if not prevTimeDefined then
begin
elapsed := 0;
end else
begin
elapsed := (Now-prevTime)*86400*10;
if elapsed < 0 then elapsed := 0;
end;
prevTime := now;
prevTimeDefined := true;
if Assigned(stretchedBkg) and
((stretchedBkg.Width <> Bitmap.Width) or (stretchedBkg.Height <> Bitmap.Height)) then
FreeAndNil(stretchedBkg);
if not Assigned(stretchedBkg) then
begin
ratio := max(Bitmap.Width/bkg.Width,Bitmap.Height/bkg.Height);
stretchedBkg := TBGRABitmap.Create(Bitmap.Width,Bitmap.Height,BGRABlack);
w := round(bkg.Width*ratio);
h := round(bkg.Height*ratio);
x := (Bitmap.Width-w) div 2;
y := (Bitmap.Height-h) div 2;
stretchedBkg.StretchPutImage(rect(x,y,x+w,y+h),bkg,dmDrawWithTransparency);
end;
RainElapse(elapsed,TrackBar1.Position,Bitmap.Width,Bitmap.Height);
Bitmap.PutImage(0,0,stretchedBkg,dmSet);
RenderRain(Bitmap);
end;
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
Timer1.Enabled := False;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
bkg := TBGRABitmap.Create('Lighthouse.jpg');
randomize;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
bkg.Free;
FreeAndNil(stretchedBkg);
ClearRainData;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled:= false;
vsRain.RedrawBitmap;
Timer1.Enabled:= true;
end;
procedure TForm1.ClearRainData;
var i: integer;
begin
for i := 0 to high(rainData) do
rainData[i].grad.Free;
rainData := nil;
end;
procedure TForm1.RenderRain(Bitmap: TBGRABitmap);
var
i,h2: Integer;
scan: TBGRAGradientScanner;
begin
h2 := Bitmap.Height div 2;
for i:= 0 to high(rainData) do
with rainData[i] do
if active then
begin
scan := TBGRAGradientScanner.Create(grad, gtLinear, PointF(0,ystart),PointF(0,yend));
Bitmap.DrawLineAntialias(x+(ystart-h2)*wind,ystart,x+(yend-h2)*wind,yend,scan,rainWidth,true);
scan.Free;
end;
end;
//returns raindrop height
function TForm1.PrepareRainDrop(i: integer; rainSizeX,rainSizeY: single): single;
var dist: single;
begin
with rainData[i] do
begin
dist := (random(100)+10)/10;
rainSpeed := 1/dist;
rainWidth := rainSizeX/dist;
if rainWidth < 1 then rainWidth := 1;
result := rainSizeY/dist*(random(50)+75)/100;
end;
end;
procedure TForm1.NeedRainArray(w, h, rainProba: integer; rainSizeX,rainSizeY: single);
var
nbRain: Integer;
i: Integer;
begin
nbRain := (w+round(abs(wind)*h)) *rainDensity;
if length(rainData)<> nbRain then
begin
PrepareRainArray(nbRain,1/rainDensity);
for i := 0 to high(rainData) do
with rainData[i] do
begin
x -= abs(wind)*h/2;
if random(1000) < rainProba then
begin
active := true;
ystart := Random(h*2)-h/2;
yend := ystart + PrepareRainDrop(i, rainSizeX,rainSizeY);
end;
end;
end;
end;
procedure TForm1.RainElapse(elapsed: double; rainProba,w,h: integer);
var
i: integer;
rainSizeY,rainSizeX: single;
delta: single;
begin
rainSizeY := 2+h*TrackBar1.Position/1000;
rainSizeX := 7*TrackBar1.Position/1000;
if rainSizeX < 4 then rainSizeX := 4;
NeedRainArray(w,h, rainProba, rainSizeX,rainSizeY);
for i := 0 to high(rainData) do
with rainData[i] do
if active then
begin
delta := h*rainSpeed*elapsed;
ystart += delta;
yend += delta;
if ystart >= h then
begin
if random(1000) < rainProba then
begin
yend := -(ystart-h);
ystart := yend - PrepareRainDrop(i, rainSizeX,rainSizeY);
end else
begin
active := false;
inactiveTime:= 0;
end;
end;
end else
begin
inactiveTime+= elapsed;
if inactiveTime > 0.5 then
begin
inactiveTime -= 0.5;
if random(1000) < rainProba then
begin
active := true;
ystart := -random(h)/2;
yend := ystart + PrepareRainDrop(i, rainSizeX,rainSizeY);
end;
end;
end;
end;
procedure TForm1.PrepareRainArray(nbRain: integer; ScaleX: single);
var
i: Integer;
begin
ClearRainData;
setlength(rainData, nbRain);
for i := 0 to high(rainData) do
with rainData[i] do
begin
x := i*scaleX;
grad := TBGRAMultiGradient.Create([BGRAPixelTransparent, BGRA(255,255,255,random(20)+50), BGRAPixelTransparent],[0,0.9,1],True);
active:= false;
inactiveTime := 0;
end;
end;
end.