fMandelbrot.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. unit fMandelbrot;
  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 MandelBrot Example
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Mattias Andersson <[email protected]>
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2000-2010
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * Contributor(s):
  31. * Michael Hansen <[email protected]>
  32. * Andre Beckedorf <[email protected]>
  33. *
  34. * ***** END LICENSE BLOCK ***** *)
  35. interface
  36. {$I GR32.inc}
  37. uses
  38. {$IFDEF FPC}LCLIntf, {$ENDIF}
  39. SysUtils,
  40. Classes,
  41. Graphics,
  42. Controls,
  43. Forms,
  44. ExtCtrls,
  45. Menus,
  46. ExtDlgs,
  47. Dialogs,
  48. {$IFDEF COMPILERXE2_UP} Types, {$ENDIF}
  49. GR32,
  50. GR32_Image,
  51. GR32_ExtImage,
  52. GR32_Resamplers,
  53. GR32_Rasterizers;
  54. type
  55. TRasterizerKind = (rkRegular, rkProgressive, rkSwizzling, rkTesseral,
  56. rkContour, rkMultithreadedRegularRasterizer);
  57. TSamplerKind = (skDefault, skSS2X, skSS3X, skSS4X, skPattern2, skPattern3,
  58. skPattern4);
  59. TMandelbrotPalette = (mpGR32, mpRainbow, mpMonochrome, mpSimple);
  60. TMandelbrotSampler = class(TCustomSampler)
  61. private
  62. FPalette: array of TColor32;
  63. FWidthInv, FHeightInv: Single;
  64. protected
  65. procedure CalculatePalette;
  66. public
  67. MaxIterations: Integer;
  68. Bounds: TFloatRect;
  69. Image: TCustomPaintBox32;
  70. Palette: TMandelbrotPalette;
  71. constructor Create(AImage: TCustomPaintBox32);
  72. function GetSampleFloat(X, Y: TFloat): TColor32; override;
  73. procedure PrepareSampling; override;
  74. end;
  75. TMainForm = class(TForm)
  76. Img: TSyntheticImage32;
  77. MainMenu: TMainMenu;
  78. miAdaptive: TMenuItem;
  79. miContour: TMenuItem;
  80. miDefault: TMenuItem;
  81. miExit: TMenuItem;
  82. miFile: TMenuItem;
  83. miMaxIterations: TMenuItem;
  84. miMaxIterations160: TMenuItem;
  85. miMaxIterations256: TMenuItem;
  86. miMaxIterations320: TMenuItem;
  87. miMaxIterations50: TMenuItem;
  88. miMaxIterations512: TMenuItem;
  89. miMultithreadedRegularRasterizer: TMenuItem;
  90. miPalette: TMenuItem;
  91. miPaletteDefault: TMenuItem;
  92. miPaletteMonochrome: TMenuItem;
  93. miPaletteRainbow: TMenuItem;
  94. miPaletteSimple: TMenuItem;
  95. miPatternSampler2x: TMenuItem;
  96. miPatternSampler3x: TMenuItem;
  97. miPatternSampler4x: TMenuItem;
  98. miProgressive: TMenuItem;
  99. miRasterizer: TMenuItem;
  100. miRegularSampling: TMenuItem;
  101. miSave: TMenuItem;
  102. miSuperSampler: TMenuItem;
  103. miSuperSampler2x: TMenuItem;
  104. miSuperSampler3x: TMenuItem;
  105. miSuperSampler4x: TMenuItem;
  106. miSwizzling: TMenuItem;
  107. miTesseral: TMenuItem;
  108. N2: TMenuItem;
  109. N3: TMenuItem;
  110. N5: TMenuItem;
  111. SavePictureDialog: TSavePictureDialog;
  112. procedure FormCreate(Sender: TObject);
  113. procedure FormDestroy(Sender: TObject);
  114. procedure FormShow(Sender: TObject);
  115. procedure miAdaptiveClick(Sender: TObject);
  116. procedure miDefaultClick(Sender: TObject);
  117. procedure miExitClick(Sender: TObject);
  118. procedure miMaxIterationsClick(Sender: TObject);
  119. procedure miPaletteClick(Sender: TObject);
  120. procedure miRasterizerClick(Sender: TObject);
  121. procedure miSaveClick(Sender: TObject);
  122. procedure ImgMouseDown(Sender: TObject; Button: TMouseButton;
  123. Shift: TShiftState; X, Y: Integer);
  124. procedure ImgMouseWheel(Sender: TObject; Shift: TShiftState;
  125. WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
  126. private
  127. procedure TranslateX(Amount: TFloat);
  128. procedure TranslateY(Amount: TFloat);
  129. procedure Zoom(Center: TPoint; Factor: TFloat);
  130. public
  131. Rasterizer: TRasterizer;
  132. Sampler: TCustomSampler;
  133. MandelSampler: TMandelbrotSampler;
  134. SuperSampler: TSuperSampler;
  135. AdaptiveSampler: TAdaptiveSuperSampler;
  136. JitteredSampler: TPatternSampler;
  137. SamplerKind: TSamplerKind;
  138. procedure SelectRasterizer(RasterizerKind: TRasterizerKind);
  139. procedure SelectSampler(ASamplerKind: TSamplerKind);
  140. end;
  141. var
  142. MainForm: TMainForm;
  143. implementation
  144. {$IFDEF FPC}
  145. {$R *.lfm}
  146. {$ELSE}
  147. {$R *.dfm}
  148. {$ENDIF}
  149. uses
  150. {$IFDEF Darwin}
  151. MacOSAll,
  152. {$ENDIF}
  153. GR32_Blend,
  154. GR32_LowLevel;
  155. // TMandelbrotSampler
  156. constructor TMandelbrotSampler.Create(AImage: TCustomPaintBox32);
  157. begin
  158. MaxIterations := 320;
  159. Palette := mpGR32;
  160. Bounds := FloatRect(-2, -2, 2, 2);
  161. Image := AImage;
  162. end;
  163. function TMandelbrotSampler.GetSampleFloat(X, Y: TFloat): TColor32;
  164. var
  165. CX, CY, ZX, ZY, ZXSqr, ZYSqr: Extended;
  166. I, W, M: Integer;
  167. C1, C2: TColor32;
  168. const
  169. CBailoutValue = 4;
  170. CQuarter = 0.25;
  171. begin
  172. CX := Bounds.Left + X * (Bounds.Right - Bounds.Left) * FWidthInv;
  173. CY := Bounds.Top + Y * (Bounds.Bottom - Bounds.Top) * FHeightInv;
  174. M := Length(FPalette) - 1;
  175. // Check whether point lies in the period-2 bulb
  176. ZY := Sqr(CY);
  177. if Sqr(CX - 1) + ZY < 0.0625 then
  178. begin
  179. Result := FPalette[M + 1];
  180. Exit;
  181. end;
  182. // Check whether point lies in the cardioid
  183. ZX := Sqr(CX + CQuarter) + ZY;
  184. if ZX * (ZX - Cx - CQuarter) < CQuarter * ZY then
  185. begin
  186. Result := FPalette[M + 1];
  187. Exit;
  188. end;
  189. // Mandelbrot iteration: Z(n+1) = Z(n+1)^2 + C
  190. ZX := 0;
  191. ZY := 0;
  192. ZXSqr := 0;
  193. ZYSqr := 0;
  194. I := 0;
  195. repeat
  196. ZY := 2 * ZY * ZX + CY;
  197. ZX := ZXSqr - ZYSqr - CX;
  198. ZXSqr := Sqr(ZX);
  199. ZYSqr := Sqr(ZY);
  200. if ZXSqr + ZYSqr > CBailoutValue then Break;
  201. Inc(I);
  202. until I = M;
  203. W := Round(16 * (ZX * ZX + ZY * ZY - 4));
  204. W := Clamp(W);
  205. C1 := FPalette[I];
  206. C2 := FPalette[I + 1];
  207. Result := CombineReg(C1, C2, W);
  208. EMMS;
  209. end;
  210. procedure TMandelbrotSampler.CalculatePalette;
  211. var
  212. I: Integer;
  213. S, T: TFloat;
  214. begin
  215. S := (321 / (MaxIterations + 1)) / 16;
  216. case Palette of
  217. mpGR32:
  218. for I := 0 to MaxIterations + 1 do
  219. FPalette[I] := HSLtoRGB(I * S + 0.5, 1 - I * S,
  220. 0.5 * (1 + Sin(3 + 14 * I * S)));
  221. mpRainbow:
  222. begin
  223. T := 1 / (MaxIterations + 1);
  224. for I := 0 to MaxIterations + 1 do
  225. FPalette[I] := HSLtoRGB(0.5 * (1 - Sqr(1 - I * T)), 1 - I * S,
  226. 0.1 + 0.5 * I * S);
  227. end;
  228. mpMonochrome:
  229. begin
  230. T := 1 / (MaxIterations + 1);
  231. for I := 0 to MaxIterations + 1 do
  232. FPalette[I] := Gray32(Round(255 * (1 - Sqr(Sqr(Sqr(1 - I * T))))));
  233. end;
  234. mpSimple:
  235. begin
  236. T := (1 shl 24) / (MaxIterations + 1);
  237. for I := 0 to MaxIterations + 1 do
  238. FPalette[I] := Round(I * T);
  239. end;
  240. end;
  241. end;
  242. procedure TMandelbrotSampler.PrepareSampling;
  243. begin
  244. FWidthInv := 1 / Image.Width;
  245. FHeightInv := 1 / Image.Height;
  246. SetLength(FPalette, MaxIterations + 1);
  247. CalculatePalette;
  248. end;
  249. // TMainForm
  250. procedure TMainForm.FormCreate(Sender: TObject);
  251. begin
  252. MandelSampler := TMandelbrotSampler.Create(Img);
  253. AdaptiveSampler := TAdaptiveSuperSampler.Create(MandelSampler);
  254. SuperSampler := TSuperSampler.Create(MandelSampler);
  255. JitteredSampler := TPatternSampler.Create(MandelSampler);
  256. Sampler := MandelSampler;
  257. end;
  258. procedure TMainForm.FormDestroy(Sender: TObject);
  259. begin
  260. (*
  261. Note: The synthetic image control must be freed before the samplers,
  262. since they are potentially used by the rendering thread.
  263. *)
  264. Img.Free;
  265. MandelSampler.Free;
  266. SuperSampler.Free;
  267. AdaptiveSampler.Free;
  268. JitteredSampler.Free;
  269. end;
  270. procedure TMainForm.FormShow(Sender: TObject);
  271. begin
  272. SelectRasterizer(rkProgressive);
  273. end;
  274. procedure TMainForm.SelectRasterizer(RasterizerKind: TRasterizerKind);
  275. const
  276. RASTERIZERCLASS: array[TRasterizerKind] of TRasterizerClass =
  277. (TRegularRasterizer, TProgressiveRasterizer, TSwizzlingRasterizer,
  278. TTesseralRasterizer, TContourRasterizer, TMultithreadedRegularRasterizer);
  279. begin
  280. Rasterizer := RASTERIZERCLASS[RasterizerKind].Create;
  281. if Rasterizer is TRegularRasterizer then
  282. TRegularRasterizer(Rasterizer).UpdateRowCount := 1;
  283. Rasterizer.Sampler := Sampler;
  284. Img.Rasterizer := Rasterizer;
  285. end;
  286. procedure TMainForm.SelectSampler(ASamplerKind: TSamplerKind);
  287. const
  288. SLEVEL: array [skSS2X..skSS4X] of Integer = (2, 3, 4);
  289. PSAMPLES: array [skPattern2..skPattern4] of Integer = (2, 3, 4);
  290. begin
  291. SamplerKind := ASamplerKind;
  292. miAdaptive.Enabled := False;
  293. case SamplerKind of
  294. skDefault: Sampler := MandelSampler;
  295. skSS2X..skSS4X:
  296. begin
  297. miAdaptive.Enabled := True;
  298. if miAdaptive.Checked then
  299. begin
  300. Sampler := AdaptiveSampler;
  301. AdaptiveSampler.Level := SLEVEL[SamplerKind];
  302. end
  303. else
  304. begin
  305. Sampler := SuperSampler;
  306. SuperSampler.SamplingX := SLEVEL[SamplerKind];
  307. SuperSampler.SamplingY := SLEVEL[SamplerKind];
  308. end;
  309. end;
  310. skPattern2..skPattern4:
  311. begin
  312. JitteredSampler.Pattern := CreateJitteredPattern(8, 8, PSAMPLES[SamplerKind], PSAMPLES[SamplerKind]);
  313. Sampler := JitteredSampler;
  314. end;
  315. end;
  316. Rasterizer.Sampler := Sampler;
  317. Img.Rasterize;
  318. end;
  319. procedure TMainForm.TranslateX(Amount: TFloat);
  320. begin
  321. MandelSampler.Bounds.Left := MandelSampler.Bounds.Left + Amount;
  322. MandelSampler.Bounds.Right := MandelSampler.Bounds.Right + Amount;
  323. Img.Rasterize;
  324. end;
  325. procedure TMainForm.TranslateY(Amount: TFloat);
  326. begin
  327. MandelSampler.Bounds.Top := MandelSampler.Bounds.Top + Amount;
  328. MandelSampler.Bounds.Bottom := MandelSampler.Bounds.Bottom + Amount;
  329. Img.Rasterize;
  330. end;
  331. procedure TMainForm.Zoom(Center: TPoint; Factor: TFloat);
  332. var
  333. cX, cY, L, T, W, H: Extended;
  334. begin
  335. cX := Center.X / Img.Width;
  336. cY := Center.Y / Img.Height;
  337. L := MandelSampler.Bounds.Left;
  338. T := MandelSampler.Bounds.Top;
  339. W := MandelSampler.Bounds.Right - MandelSampler.Bounds.Left;
  340. H := MandelSampler.Bounds.Bottom - MandelSampler.Bounds.Top;
  341. if W = 0 then W := H;
  342. if H = 0 then H := W;
  343. MandelSampler.Bounds.Left := cX * W - W * Factor * 0.5 + L;
  344. MandelSampler.Bounds.Top := cY * H - H * Factor * 0.5 + T;
  345. MandelSampler.Bounds.Right := W * Factor + MandelSampler.Bounds.Left;
  346. MandelSampler.Bounds.Bottom := H * Factor + MandelSampler.Bounds.Top;
  347. Img.Rasterize;
  348. end;
  349. procedure TMainForm.miRasterizerClick(Sender: TObject);
  350. var
  351. mi: TMenuItem;
  352. begin
  353. if not (Sender is TMenuItem) then Exit;
  354. mi := TMenuItem(Sender);
  355. mi.Checked := True;
  356. SelectRasterizer(TRasterizerKind(mi.Tag));
  357. end;
  358. procedure TMainForm.miDefaultClick(Sender: TObject);
  359. begin
  360. if (Sender is TMenuItem) then
  361. SelectSampler(TSamplerKind(TMenuItem(Sender).Tag));
  362. end;
  363. procedure TMainForm.miAdaptiveClick(Sender: TObject);
  364. begin
  365. SelectSampler(SamplerKind);
  366. end;
  367. procedure TMainForm.miSaveClick(Sender: TObject);
  368. begin
  369. if SavePictureDialog.Execute then
  370. Img.Buffer.SaveToFile(SavePictureDialog.FileName);
  371. end;
  372. procedure TMainForm.miExitClick(Sender: TObject);
  373. begin
  374. Close;
  375. end;
  376. procedure TMainForm.miMaxIterationsClick(Sender: TObject);
  377. begin
  378. TMenuItem(Sender).Checked := True;
  379. MandelSampler.MaxIterations := TMenuItem(Sender).Tag;
  380. Img.Rasterize;
  381. end;
  382. procedure TMainForm.miPaletteClick(Sender: TObject);
  383. begin
  384. TMenuItem(Sender).Checked := True;
  385. MandelSampler.Palette := TMandelbrotPalette(TMenuItem(Sender).Tag);
  386. Img.Rasterize;
  387. end;
  388. procedure TMainForm.ImgMouseDown(Sender: TObject; Button: TMouseButton;
  389. Shift: TShiftState; X, Y: Integer);
  390. begin
  391. case Button of
  392. mbLeft:
  393. Zoom(GR32.Point(X, Y), 0.5);
  394. mbRight:
  395. Zoom(GR32.Point(X, Y), 2);
  396. else
  397. // reset
  398. MandelSampler.Bounds := FloatRect(-2, -2, 2, 2);
  399. Img.Rasterize;
  400. end;
  401. end;
  402. procedure TMainForm.ImgMouseWheel(Sender: TObject; Shift: TShiftState;
  403. WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
  404. begin
  405. if ssShift in Shift then
  406. TranslateX(-0.001 * WheelDelta)
  407. else
  408. TranslateY(-0.001 * WheelDelta);
  409. end;
  410. end.