unit1.pas 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. unit Unit1;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
  6. ComCtrls, BGRAVirtualScreen, BGRABitmap, BGRABitmapTypes;
  7. const
  8. wind = -0.5; //1 means 45 degrees rain
  9. rainDensity = 2; //strictly positive
  10. type
  11. { TForm1 }
  12. TForm1 = class(TForm)
  13. vsRain: TBGRAVirtualScreen;
  14. Timer1: TTimer;
  15. TrackBar1: TTrackBar;
  16. procedure RainRedraw(Sender: TObject; Bitmap: TBGRABitmap);
  17. procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
  18. procedure FormCreate(Sender: TObject);
  19. procedure FormDestroy(Sender: TObject);
  20. procedure Timer1Timer(Sender: TObject);
  21. private
  22. { private declarations }
  23. bkg,stretchedBkg: TBGRABitmap;
  24. prevTime: TDateTime;
  25. prevTimeDefined: boolean;
  26. rainData: array of record
  27. x,ystart,yend: single;
  28. rainWidth, rainSpeed: single;
  29. grad: TBGRACustomGradient;
  30. active: boolean;
  31. inactiveTime: double;
  32. end;
  33. procedure ClearRainData;
  34. procedure PrepareRainArray(nbRain: integer; ScaleX: single);
  35. function PrepareRainDrop(i: integer; rainSizeX, rainSizeY: single): single;
  36. procedure NeedRainArray(w, h, rainProba: integer; rainSizeX, rainSizeY: single);
  37. procedure RainElapse(elapsed: double; rainProba, w, h: integer);
  38. procedure RenderRain(Bitmap: TBGRABitmap);
  39. end;
  40. var
  41. Form1: TForm1;
  42. implementation
  43. uses BGRAGradientScanner, Math;
  44. {$R *.lfm}
  45. { TForm1 }
  46. procedure TForm1.RainRedraw(Sender: TObject; Bitmap: TBGRABitmap);
  47. var
  48. elapsed: double;
  49. ratio: single;
  50. x,y,w,h: integer;
  51. begin
  52. if not prevTimeDefined then
  53. begin
  54. elapsed := 0;
  55. end else
  56. begin
  57. elapsed := (Now-prevTime)*86400*10;
  58. if elapsed < 0 then elapsed := 0;
  59. end;
  60. prevTime := now;
  61. prevTimeDefined := true;
  62. if Assigned(stretchedBkg) and
  63. ((stretchedBkg.Width <> Bitmap.Width) or (stretchedBkg.Height <> Bitmap.Height)) then
  64. FreeAndNil(stretchedBkg);
  65. if not Assigned(stretchedBkg) then
  66. begin
  67. ratio := max(Bitmap.Width/bkg.Width,Bitmap.Height/bkg.Height);
  68. stretchedBkg := TBGRABitmap.Create(Bitmap.Width,Bitmap.Height,BGRABlack);
  69. w := round(bkg.Width*ratio);
  70. h := round(bkg.Height*ratio);
  71. x := (Bitmap.Width-w) div 2;
  72. y := (Bitmap.Height-h) div 2;
  73. stretchedBkg.StretchPutImage(rect(x,y,x+w,y+h),bkg,dmDrawWithTransparency);
  74. end;
  75. RainElapse(elapsed,TrackBar1.Position,Bitmap.Width,Bitmap.Height);
  76. Bitmap.PutImage(0,0,stretchedBkg,dmSet);
  77. RenderRain(Bitmap);
  78. end;
  79. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  80. begin
  81. Timer1.Enabled := False;
  82. end;
  83. procedure TForm1.FormCreate(Sender: TObject);
  84. begin
  85. bkg := TBGRABitmap.Create('Lighthouse.jpg');
  86. randomize;
  87. end;
  88. procedure TForm1.FormDestroy(Sender: TObject);
  89. begin
  90. bkg.Free;
  91. FreeAndNil(stretchedBkg);
  92. ClearRainData;
  93. end;
  94. procedure TForm1.Timer1Timer(Sender: TObject);
  95. begin
  96. Timer1.Enabled:= false;
  97. vsRain.RedrawBitmap;
  98. Timer1.Enabled:= true;
  99. end;
  100. procedure TForm1.ClearRainData;
  101. var i: integer;
  102. begin
  103. for i := 0 to high(rainData) do
  104. rainData[i].grad.Free;
  105. rainData := nil;
  106. end;
  107. procedure TForm1.RenderRain(Bitmap: TBGRABitmap);
  108. var
  109. i,h2: Integer;
  110. scan: TBGRAGradientScanner;
  111. begin
  112. h2 := Bitmap.Height div 2;
  113. for i:= 0 to high(rainData) do
  114. with rainData[i] do
  115. if active then
  116. begin
  117. scan := TBGRAGradientScanner.Create(grad, gtLinear, PointF(0,ystart),PointF(0,yend));
  118. Bitmap.DrawLineAntialias(x+(ystart-h2)*wind,ystart,x+(yend-h2)*wind,yend,scan,rainWidth,true);
  119. scan.Free;
  120. end;
  121. end;
  122. //returns raindrop height
  123. function TForm1.PrepareRainDrop(i: integer; rainSizeX,rainSizeY: single): single;
  124. var dist: single;
  125. begin
  126. with rainData[i] do
  127. begin
  128. dist := (random(100)+10)/10;
  129. rainSpeed := 1/dist;
  130. rainWidth := rainSizeX/dist;
  131. if rainWidth < 1 then rainWidth := 1;
  132. result := rainSizeY/dist*(random(50)+75)/100;
  133. end;
  134. end;
  135. procedure TForm1.NeedRainArray(w, h, rainProba: integer; rainSizeX,rainSizeY: single);
  136. var
  137. nbRain: Integer;
  138. i: Integer;
  139. begin
  140. nbRain := (w+round(abs(wind)*h)) *rainDensity;
  141. if length(rainData)<> nbRain then
  142. begin
  143. PrepareRainArray(nbRain,1/rainDensity);
  144. for i := 0 to high(rainData) do
  145. with rainData[i] do
  146. begin
  147. x -= abs(wind)*h/2;
  148. if random(1000) < rainProba then
  149. begin
  150. active := true;
  151. ystart := Random(h*2)-h/2;
  152. yend := ystart + PrepareRainDrop(i, rainSizeX,rainSizeY);
  153. end;
  154. end;
  155. end;
  156. end;
  157. procedure TForm1.RainElapse(elapsed: double; rainProba,w,h: integer);
  158. var
  159. i: integer;
  160. rainSizeY,rainSizeX: single;
  161. delta: single;
  162. begin
  163. rainSizeY := 2+h*TrackBar1.Position/1000;
  164. rainSizeX := 7*TrackBar1.Position/1000;
  165. if rainSizeX < 4 then rainSizeX := 4;
  166. NeedRainArray(w,h, rainProba, rainSizeX,rainSizeY);
  167. for i := 0 to high(rainData) do
  168. with rainData[i] do
  169. if active then
  170. begin
  171. delta := h*rainSpeed*elapsed;
  172. ystart += delta;
  173. yend += delta;
  174. if ystart >= h then
  175. begin
  176. if random(1000) < rainProba then
  177. begin
  178. yend := -(ystart-h);
  179. ystart := yend - PrepareRainDrop(i, rainSizeX,rainSizeY);
  180. end else
  181. begin
  182. active := false;
  183. inactiveTime:= 0;
  184. end;
  185. end;
  186. end else
  187. begin
  188. inactiveTime+= elapsed;
  189. if inactiveTime > 0.5 then
  190. begin
  191. inactiveTime -= 0.5;
  192. if random(1000) < rainProba then
  193. begin
  194. active := true;
  195. ystart := -random(h)/2;
  196. yend := ystart + PrepareRainDrop(i, rainSizeX,rainSizeY);
  197. end;
  198. end;
  199. end;
  200. end;
  201. procedure TForm1.PrepareRainArray(nbRain: integer; ScaleX: single);
  202. var
  203. i: Integer;
  204. begin
  205. ClearRainData;
  206. setlength(rainData, nbRain);
  207. for i := 0 to high(rainData) do
  208. with rainData[i] do
  209. begin
  210. x := i*scaleX;
  211. grad := TBGRAMultiGradient.Create([BGRAPixelTransparent, BGRA(255,255,255,random(20)+50), BGRAPixelTransparent],[0,0.9,1],True);
  212. active:= false;
  213. inactiveTime := 0;
  214. end;
  215. end;
  216. end.