2
0

MandelUnit.pas 12 KB

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