MainUnit.pas 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. unit MainUnit;
  2. interface
  3. {$I GR32.inc}
  4. uses
  5. {$IFDEF FPC} LCLIntf, {$ELSE} Windows, {$ENDIF} SysUtils, Types, Classes,
  6. Graphics, Controls, Forms, Dialogs, Math, ExtCtrls, StdCtrls, Menus, GR32,
  7. GR32_Polygons, GR32_Image, GR32_Layers, GR32_Transforms, GR32_ColorGradients;
  8. type
  9. TMainForm = class(TForm)
  10. BtnDefaults: TButton;
  11. CmbLUT: TComboBox;
  12. ImgView32: TImgView32;
  13. LblColorStopsTop: TLabel;
  14. LblLookupTableOrder: TLabel;
  15. MainMenu: TMainMenu;
  16. MemoColorStops: TMemo;
  17. MnuExit: TMenuItem;
  18. MnuFile: TMenuItem;
  19. MnuFileOpen: TMenuItem;
  20. MnuFileSaveAs: TMenuItem;
  21. MnuLookupTableOrder: TMenuItem;
  22. MnuOrder4: TMenuItem;
  23. MnuOrder5: TMenuItem;
  24. MnuOrder6: TMenuItem;
  25. MnuOrder7: TMenuItem;
  26. MnuOrder8: TMenuItem;
  27. MnuOrder9: TMenuItem;
  28. MnuOrder10: TMenuItem;
  29. MnuOrder11: TMenuItem;
  30. MnuOrder12: TMenuItem;
  31. MnuOrder13: TMenuItem;
  32. MnuPad: TMenuItem;
  33. MnuRadialFillStyle: TMenuItem;
  34. MnuReflect: TMenuItem;
  35. MnuRepeat: TMenuItem;
  36. MnuSimple: TMenuItem;
  37. MnuWrapMode: TMenuItem;
  38. MnuSVG: TMenuItem;
  39. N1: TMenuItem;
  40. OpenDialog: TOpenDialog;
  41. PnlControl: TPanel;
  42. RgpEllipseFillStyle: TRadioGroup;
  43. RgpWrapMode: TRadioGroup;
  44. SaveDialog: TSaveDialog;
  45. procedure FormCreate(Sender: TObject);
  46. procedure FormDestroy(Sender: TObject);
  47. procedure FormKeyPress(Sender: TObject; var Key: Char);
  48. procedure BtnDefaultsClick(Sender: TObject);
  49. procedure BtnExitClick(Sender: TObject);
  50. procedure CmbLUTChange(Sender: TObject);
  51. procedure ImgView32DblClick(Sender: TObject);
  52. procedure ImgView32MouseDown(Sender: TObject; Button: TMouseButton;
  53. Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
  54. procedure ImgView32MouseUp(Sender: TObject; Button: TMouseButton;
  55. Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
  56. procedure ImgView32MouseMove(Sender: TObject; Shift: TShiftState; X,
  57. Y: Integer; Layer: TCustomLayer);
  58. procedure MemoColorStopsChange(Sender: TObject);
  59. procedure MnuFileOpenClick(Sender: TObject);
  60. procedure MnuFileSaveAsClick(Sender: TObject);
  61. procedure MnuOrderClick(Sender: TObject);
  62. procedure MnuRadialFillStyleClick(Sender: TObject);
  63. procedure MnuSpreadClick(Sender: TObject);
  64. procedure RgpEllipseFillStyleClick(Sender: TObject);
  65. procedure RgpWrapModeClick(Sender: TObject);
  66. private
  67. FDpiScale: single;
  68. FKnobBitmap: TBitmap32;
  69. FKnobRadius: Integer;
  70. FControlKnob: PPoint;
  71. FLinearStart: TPoint;
  72. FLinearEnd: TPoint;
  73. FRadialOrigin: TPoint;
  74. FRadialX: TPoint;
  75. FRadialY: TPoint;
  76. FLinearBounds: TRect;
  77. FRadialBounds: TRect;
  78. FGradient: TColor32Gradient;
  79. FGradientLUT: TColor32LookupTable;
  80. FTextNotesPoly: TArrayOfArrayOfFloatPoint;
  81. FTextTopPoly: TArrayOfArrayOfFloatPoint;
  82. FTextBottomPoly: TArrayOfArrayOfFloatPoint;
  83. FTextGR32: TArrayOfArrayOfFloatPoint;
  84. procedure LUTOrderChangedHandler(Sender: TObject);
  85. public
  86. procedure DrawImage;
  87. end;
  88. var
  89. MainForm: TMainForm;
  90. implementation
  91. uses
  92. GR32_Geometry, GR32_VectorUtils, GR32_Gamma, GR32_Paths, {$IFDEF FPC}
  93. GR32_Text_LCL_Win {$ELSE} GR32_Text_VCL {$ENDIF};
  94. {$IFDEF FPC}
  95. {$R *.lfm}
  96. {$R data.res}
  97. {$ELSE}
  98. {$R *.dfm}
  99. {$R data.res}
  100. {$ENDIF}
  101. const
  102. Colors: array[0..147] of TIdentMapEntry = (
  103. (Value: Integer($FF000000); Name: 'clBlack32'),
  104. (Value: Integer($FF3F3F3F); Name: 'clDimGray32'),
  105. (Value: Integer($FF7F7F7F); Name: 'clGray32'),
  106. (Value: Integer($FFBFBFBF); Name: 'clLightGray32'),
  107. (Value: Integer($FFFFFFFF); Name: 'clWhite32'),
  108. (Value: Integer($FF7F0000); Name: 'clMaroon32'),
  109. (Value: Integer($FF007F00); Name: 'clGreen32'),
  110. (Value: Integer($FF7F7F00); Name: 'clOlive32'),
  111. (Value: Integer($FF00007F); Name: 'clNavy32'),
  112. (Value: Integer($FF7F007F); Name: 'clPurple32'),
  113. (Value: Integer($FF007F7F); Name: 'clTeal32'),
  114. (Value: Integer($FFFF0000); Name: 'clRed32'),
  115. (Value: Integer($FF00FF00); Name: 'clLime32'),
  116. (Value: Integer($FFFFFF00); Name: 'clYellow32'),
  117. (Value: Integer($FF0000FF); Name: 'clBlue32'),
  118. (Value: Integer($FFFF00FF); Name: 'clFuchsia32'),
  119. (Value: Integer($FF00FFFF); Name: 'clAqua32'),
  120. (Value: Integer($FFF0F8FF); Name: 'clAliceBlue32'),
  121. (Value: Integer($FFFAEBD7); Name: 'clAntiqueWhite32'),
  122. (Value: Integer($FF7FFFD4); Name: 'clAquamarine32'),
  123. (Value: Integer($FFF0FFFF); Name: 'clAzure32'),
  124. (Value: Integer($FFF5F5DC); Name: 'clBeige32'),
  125. (Value: Integer($FFFFE4C4); Name: 'clBisque32'),
  126. (Value: Integer($FFFFEBCD); Name: 'clBlancheDalmond32'),
  127. (Value: Integer($FF8A2BE2); Name: 'clBlueViolet32'),
  128. (Value: Integer($FFA52A2A); Name: 'clBrown32'),
  129. (Value: Integer($FFDEB887); Name: 'clBurlyWood32'),
  130. (Value: Integer($FF5F9EA0); Name: 'clCadetblue32'),
  131. (Value: Integer($FF7FFF00); Name: 'clChartReuse32'),
  132. (Value: Integer($FFD2691E); Name: 'clChocolate32'),
  133. (Value: Integer($FFFF7F50); Name: 'clCoral32'),
  134. (Value: Integer($FF6495ED); Name: 'clCornFlowerBlue32'),
  135. (Value: Integer($FFFFF8DC); Name: 'clCornSilk32'),
  136. (Value: Integer($FFDC143C); Name: 'clCrimson32'),
  137. (Value: Integer($FF00008B); Name: 'clDarkBlue32'),
  138. (Value: Integer($FF008B8B); Name: 'clDarkCyan32'),
  139. (Value: Integer($FFB8860B); Name: 'clDarkGoldenRod32'),
  140. (Value: Integer($FFA9A9A9); Name: 'clDarkGray32'),
  141. (Value: Integer($FF006400); Name: 'clDarkGreen32'),
  142. (Value: Integer($FFA9A9A9); Name: 'clDarkGrey32'),
  143. (Value: Integer($FFBDB76B); Name: 'clDarkKhaki32'),
  144. (Value: Integer($FF8B008B); Name: 'clDarkMagenta32'),
  145. (Value: Integer($FF556B2F); Name: 'clDarkOliveGreen32'),
  146. (Value: Integer($FFFF8C00); Name: 'clDarkOrange32'),
  147. (Value: Integer($FF9932CC); Name: 'clDarkOrchid32'),
  148. (Value: Integer($FF8B0000); Name: 'clDarkRed32'),
  149. (Value: Integer($FFE9967A); Name: 'clDarkSalmon32'),
  150. (Value: Integer($FF8FBC8F); Name: 'clDarkSeaGreen32'),
  151. (Value: Integer($FF483D8B); Name: 'clDarkSlateBlue32'),
  152. (Value: Integer($FF2F4F4F); Name: 'clDarkSlateGray32'),
  153. (Value: Integer($FF2F4F4F); Name: 'clDarkSlateGrey32'),
  154. (Value: Integer($FF00CED1); Name: 'clDarkTurquoise32'),
  155. (Value: Integer($FF9400D3); Name: 'clDarkViolet32'),
  156. (Value: Integer($FFFF1493); Name: 'clDeepPink32'),
  157. (Value: Integer($FF00BFFF); Name: 'clDeepSkyBlue32'),
  158. (Value: Integer($FF1E90FF); Name: 'clDodgerBlue32'),
  159. (Value: Integer($FFB22222); Name: 'clFireBrick32'),
  160. (Value: Integer($FFFFFAF0); Name: 'clFloralWhite32'),
  161. (Value: Integer($FFDCDCDC); Name: 'clGainsBoro32'),
  162. (Value: Integer($FFF8F8FF); Name: 'clGhostWhite32'),
  163. (Value: Integer($FFFFD700); Name: 'clGold32'),
  164. (Value: Integer($FFDAA520); Name: 'clGoldenRod32'),
  165. (Value: Integer($FFADFF2F); Name: 'clGreenYellow32'),
  166. (Value: Integer($FF808080); Name: 'clGrey32'),
  167. (Value: Integer($FFF0FFF0); Name: 'clHoneyDew32'),
  168. (Value: Integer($FFFF69B4); Name: 'clHotPink32'),
  169. (Value: Integer($FFCD5C5C); Name: 'clIndianRed32'),
  170. (Value: Integer($FF4B0082); Name: 'clIndigo32'),
  171. (Value: Integer($FFFFFFF0); Name: 'clIvory32'),
  172. (Value: Integer($FFF0E68C); Name: 'clKhaki32'),
  173. (Value: Integer($FFE6E6FA); Name: 'clLavender32'),
  174. (Value: Integer($FFFFF0F5); Name: 'clLavenderBlush32'),
  175. (Value: Integer($FF7CFC00); Name: 'clLawnGreen32'),
  176. (Value: Integer($FFFFFACD); Name: 'clLemonChiffon32'),
  177. (Value: Integer($FFADD8E6); Name: 'clLightBlue32'),
  178. (Value: Integer($FFF08080); Name: 'clLightCoral32'),
  179. (Value: Integer($FFE0FFFF); Name: 'clLightCyan32'),
  180. (Value: Integer($FFFAFAD2); Name: 'clLightGoldenRodYellow32'),
  181. (Value: Integer($FF90EE90); Name: 'clLightGreen32'),
  182. (Value: Integer($FFD3D3D3); Name: 'clLightGrey32'),
  183. (Value: Integer($FFFFB6C1); Name: 'clLightPink32'),
  184. (Value: Integer($FFFFA07A); Name: 'clLightSalmon32'),
  185. (Value: Integer($FF20B2AA); Name: 'clLightSeagreen32'),
  186. (Value: Integer($FF87CEFA); Name: 'clLightSkyblue32'),
  187. (Value: Integer($FF778899); Name: 'clLightSlategray32'),
  188. (Value: Integer($FF778899); Name: 'clLightSlategrey32'),
  189. (Value: Integer($FFB0C4DE); Name: 'clLightSteelblue32'),
  190. (Value: Integer($FFFFFFE0); Name: 'clLightYellow32'),
  191. (Value: Integer($FFC0C0C0); Name: 'clLtGray32'),
  192. (Value: Integer($FFA0A0A4); Name: 'clMedGray32'),
  193. (Value: Integer($FF808080); Name: 'clDkGray32'),
  194. (Value: Integer($FFC0DCC0); Name: 'clMoneyGreen32'),
  195. (Value: Integer($FFA6CAF0); Name: 'clLegacySkyBlue32'),
  196. (Value: Integer($FFFFFBF0); Name: 'clCream32'),
  197. (Value: Integer($FF32CD32); Name: 'clLimeGreen32'),
  198. (Value: Integer($FFFAF0E6); Name: 'clLinen32'),
  199. (Value: Integer($FF66CDAA); Name: 'clMediumAquamarine32'),
  200. (Value: Integer($FF0000CD); Name: 'clMediumBlue32'),
  201. (Value: Integer($FFBA55D3); Name: 'clMediumOrchid32'),
  202. (Value: Integer($FF9370DB); Name: 'clMediumPurple32'),
  203. (Value: Integer($FF3CB371); Name: 'clMediumSeaGreen32'),
  204. (Value: Integer($FF7B68EE); Name: 'clMediumSlateBlue32'),
  205. (Value: Integer($FF00FA9A); Name: 'clMediumSpringGreen32'),
  206. (Value: Integer($FF48D1CC); Name: 'clMediumTurquoise32'),
  207. (Value: Integer($FFC71585); Name: 'clMediumVioletRed32'),
  208. (Value: Integer($FF191970); Name: 'clMidnightBlue32'),
  209. (Value: Integer($FFF5FFFA); Name: 'clMintCream32'),
  210. (Value: Integer($FFFFE4E1); Name: 'clMistyRose32'),
  211. (Value: Integer($FFFFE4B5); Name: 'clMoccasin32'),
  212. (Value: Integer($FFFFDEAD); Name: 'clNavajoWhite32'),
  213. (Value: Integer($FFFDF5E6); Name: 'clOldLace32'),
  214. (Value: Integer($FF6B8E23); Name: 'clOliveDrab32'),
  215. (Value: Integer($FFFFA500); Name: 'clOrange32'),
  216. (Value: Integer($FFFF4500); Name: 'clOrangeRed32'),
  217. (Value: Integer($FFDA70D6); Name: 'clOrchid32'),
  218. (Value: Integer($FFEEE8AA); Name: 'clPaleGoldenRod32'),
  219. (Value: Integer($FF98FB98); Name: 'clPaleGreen32'),
  220. (Value: Integer($FFAFEEEE); Name: 'clPaleTurquoise32'),
  221. (Value: Integer($FFDB7093); Name: 'clPaleVioletred32'),
  222. (Value: Integer($FFFFEFD5); Name: 'clPapayaWhip32'),
  223. (Value: Integer($FFFFDAB9); Name: 'clPeachPuff32'),
  224. (Value: Integer($FFCD853F); Name: 'clPeru32'),
  225. (Value: Integer($FFDDA0DD); Name: 'clPlum32'),
  226. (Value: Integer($FFB0E0E6); Name: 'clPowderBlue32'),
  227. (Value: Integer($FFBC8F8F); Name: 'clRosyBrown32'),
  228. (Value: Integer($FF4169E1); Name: 'clRoyalBlue32'),
  229. (Value: Integer($FF8B4513); Name: 'clSaddleBrown32'),
  230. (Value: Integer($FFFA8072); Name: 'clSalmon32'),
  231. (Value: Integer($FFF4A460); Name: 'clSandyBrown32'),
  232. (Value: Integer($FF2E8B57); Name: 'clSeaGreen32'),
  233. (Value: Integer($FFFFF5EE); Name: 'clSeaShell32'),
  234. (Value: Integer($FFA0522D); Name: 'clSienna32'),
  235. (Value: Integer($FFC0C0C0); Name: 'clSilver32'),
  236. (Value: Integer($FF87CEEB); Name: 'clSkyBlue32'),
  237. (Value: Integer($FF6A5ACD); Name: 'clSlateBlue32'),
  238. (Value: Integer($FF708090); Name: 'clSlateGray32'),
  239. (Value: Integer($FF708090); Name: 'clSlateGrey32'),
  240. (Value: Integer($FFFFFAFA); Name: 'clSnow32'),
  241. (Value: Integer($FF00FF7F); Name: 'clSpringGreen32'),
  242. (Value: Integer($FF4682B4); Name: 'clSteelBlue32'),
  243. (Value: Integer($FFD2B48C); Name: 'clTan32'),
  244. (Value: Integer($FFD8BFD8); Name: 'clThistle32'),
  245. (Value: Integer($FFFF6347); Name: 'clTomato32'),
  246. (Value: Integer($FF40E0D0); Name: 'clTurquoise32'),
  247. (Value: Integer($FFEE82EE); Name: 'clViolet32'),
  248. (Value: Integer($FFF5DEB3); Name: 'clWheat32'),
  249. (Value: Integer($FFF5F5F5); Name: 'clWhiteSmoke32'),
  250. (Value: Integer($FF9ACD32); Name: 'clYellowGreen32'));
  251. { Miscellaneous functions }
  252. procedure StrToArrayColor32Gradient(s: TStrings; Gradient: TColor32Gradient);
  253. var
  254. I, J: Integer;
  255. Offset: TFloat;
  256. Color: TColor32;
  257. ColorStr: string;
  258. begin
  259. FormatSettings.DecimalSeparator := '.';
  260. Gradient.ClearColorStops;
  261. for i := 0 to s.Count - 1 do
  262. begin
  263. j := Pos(':', s[i]);
  264. if j < 2 then
  265. Continue;
  266. Offset := StrToFloatDef(Copy(s[i], 1, j - 1), -1);
  267. if (Offset < 0) then
  268. Continue;
  269. ColorStr := Trim(Copy(s[i], j + 1, 80));
  270. if not IdentToInt(ColorStr, Integer(Color), Colors) then
  271. Color := TColor32(StrToIntDef(ColorStr, $01010101));
  272. if Color <> $01010101 then
  273. Gradient.AddColorStop(Offset, Color);
  274. end;
  275. end;
  276. function LoadPolysFromResource(const ResName: string): TArrayOfArrayOfFloatPoint;
  277. var
  278. I,J, Count: Integer;
  279. ResStream: TResourceStream;
  280. function ReadInt: Integer;
  281. begin
  282. ResStream.Read(Result, SizeOf(Result));
  283. end;
  284. function ReadFloatPoint: TFloatPoint;
  285. begin
  286. ResStream.Read(Result.X, SizeOf(TFloat));
  287. ResStream.Read(Result.Y, SizeOf(TFloat));
  288. end;
  289. begin
  290. ResStream := TResourceStream.Create(hInstance, ResName, RT_RCDATA);
  291. try
  292. Count := ReadInt;
  293. SetLength(Result, Count);
  294. for I := 0 to Count - 1 do
  295. begin
  296. Count := ReadInt;
  297. SetLength(Result[I], Count);
  298. for J := 0 to Count - 1 do
  299. Result[I, J] := ReadFloatPoint;
  300. end;
  301. finally
  302. ResStream.Free;
  303. end;
  304. end;
  305. function DPIScale(value: integer): integer; overload;
  306. begin
  307. result := mulDiv(value, screen.PixelsPerInch, 96);
  308. end;
  309. function DPIScale(value: single): single; overload;
  310. begin
  311. result := value * screen.PixelsPerInch / 96;
  312. end;
  313. function DpiAwarePoint(const x, y: integer): TPoint;
  314. begin
  315. result := Gr32.Point(DPIScale(x), DPIScale(y));
  316. end;
  317. function DpiAwareRect(const l, t, r, b: integer): TRect;
  318. begin
  319. result := Rect(DPIScale(l), DPIScale(t), DPIScale(r), DPIScale(b));
  320. end;
  321. function DpiAwareFloatPoint(const x, y: integer): TFloatPoint;
  322. begin
  323. result := FloatPoint(DPIScale(x), DPIScale(y));
  324. end;
  325. function DpiAwareFloatRect(const l, t, r, b: single): TFloatRect;
  326. begin
  327. result := FloatRect(DPIScale(l), DPIScale(t), DPIScale(r), DPIScale(b));
  328. end;
  329. procedure OffsetPolygon(var polygon: TArrayOfFloatPoint; dx, dy: single);
  330. var
  331. i: integer;
  332. begin
  333. for i := 0 to high(polygon) do
  334. begin
  335. polygon[i].X := polygon[i].X + dx;
  336. polygon[i].Y := polygon[i].Y + dy;
  337. end;
  338. end;
  339. procedure OffsetPolyPolygon(var polygons: TArrayOfArrayOfFloatPoint; dx, dy: single);
  340. var
  341. i: integer;
  342. begin
  343. for i := 0 to high(polygons) do
  344. OffsetPolygon(polygons[i], dx, dy);
  345. end;
  346. { TMainForm }
  347. procedure TMainForm.FormCreate(Sender: TObject);
  348. var
  349. TextPath: TFlattenedPath;
  350. Outline: TArrayOfFloatPoint;
  351. Filler: TSamplerFiller;
  352. Sampler: TRadialGradientSampler;
  353. begin
  354. if Screen.PixelsPerInch > 96 then
  355. FDpiScale := Screen.PixelsPerInch/ 96 else
  356. FDpiScale := 1;
  357. ClientWidth := PnlControl.Width + DPIScale(400);
  358. ClientHeight := DPIScale(450);
  359. ImgView32.SetupBitmap(true, clCream32);
  360. FLinearBounds := DpiAwareRect(50, 50, 350, 200);
  361. FRadialBounds := DpiAwareRect(50, 250, 350, 400);
  362. FGradient := TColor32Gradient.Create;
  363. StrToArrayColor32Gradient(MemoColorStops.Lines, FGradient);
  364. FGradientLUT := TColor32LookupTable.Create;
  365. FGradientLUT.OnOrderChanged := LUTOrderChangedHandler;
  366. FGradient.FillColorLookUpTable(FGradientLUT);
  367. //These text paths only need to be gotten once ...
  368. TextPath := TFlattenedPath.Create;
  369. try
  370. TextToPath(Self.Font.Handle, TextPath, DpiAwareFloatRect(50, 10, 450, 30),
  371. 'Click & drag control buttons to adjust gradients', 0);
  372. FTextNotesPoly := TextPath.Path;
  373. with FLinearBounds do
  374. TextToPath(Self.Font.Handle, TextPath,
  375. FloatRect(Left, Bottom, Left + DPIScale(150),Bottom + DPIScale(20)),
  376. 'Linear gradients', 0);
  377. FTextTopPoly := TextPath.Path;
  378. with FRadialBounds do
  379. TextToPath(Self.Font.Handle, TextPath,
  380. FloatRect(Left, Bottom, Left + DPIScale(150), Bottom + DPIScale(20)),
  381. 'Radial gradients', 0);
  382. FTextBottomPoly := TextPath.Path;
  383. finally
  384. TextPath.Free;
  385. end;
  386. FTextGR32 := LoadPolysFromResource('Graphics32_Crv');
  387. OffsetPolyPolygon(FTextGR32, DPIScale(-42), 0);
  388. if FDpiScale > 1 then
  389. FTextGR32 := ScalePolyPolygon(FTextGR32, FDpiScale, FDpiScale);
  390. FKnobRadius := DPIScale(4);
  391. FKnobBitmap := TBitmap32.Create;
  392. FKnobBitmap.SetSize(2 * FKnobRadius + 2, 2 * FKnobRadius + 2);
  393. FKnobBitmap.DrawMode := dmBlend;
  394. FKnobBitmap.CombineMode := cmMerge;
  395. Sampler := TRadialGradientSampler.Create;
  396. try
  397. Sampler.Gradient.AddColorStop(0.0, $FFFFFFFF);
  398. Sampler.Gradient.AddColorStop(1.0, $FFA0A0A0);
  399. Sampler.Radius := FKnobRadius + FKnobRadius div 2;
  400. Sampler.Center := FloatPoint(FKnobRadius - 1.5, FKnobRadius - 1.5);
  401. Filler := TSamplerFiller.Create(Sampler);
  402. try
  403. Filler.Sampler := Sampler;
  404. Outline := Circle(FKnobRadius + 1, FKnobRadius + 1, FKnobRadius);
  405. PolygonFS(FKnobBitmap, Outline, Filler, pfWinding);
  406. PolylineFS(FKnobBitmap, Outline, clBlack32, True);
  407. finally
  408. Filler.Free;
  409. end;
  410. finally
  411. Sampler.Free;
  412. end;
  413. FLinearStart := DpiAwarePoint(100, 125);
  414. FLinearEnd := DpiAwarePoint(300, 125);
  415. FRadialOrigin := DpiAwarePoint(250, 350);
  416. with FRadialOrigin do
  417. begin
  418. FRadialX := GR32.Point(X - DPIScale(80), Y);
  419. FRadialY := GR32.Point(X, Y + DPIScale(40));
  420. end;
  421. DrawImage;
  422. end;
  423. procedure TMainForm.FormDestroy(Sender: TObject);
  424. begin
  425. FGradient.Free;
  426. FKnobBitmap.Free;
  427. end;
  428. procedure TMainForm.ImgView32DblClick(Sender: TObject);
  429. begin
  430. case 0 of
  431. 0:
  432. begin
  433. FLinearStart := DpiAwarePoint(200, 70);
  434. FLinearEnd := DpiAwarePoint(200, 170);
  435. end;
  436. 1:
  437. begin
  438. FLinearStart := DpiAwarePoint(200, 120);
  439. FLinearEnd := DpiAwarePoint(200, 120);
  440. end;
  441. 2:
  442. begin
  443. FLinearStart := DpiAwarePoint(200, 120);
  444. FLinearEnd := DpiAwarePoint(201, 120);
  445. end;
  446. 3:
  447. begin
  448. FLinearStart := DpiAwarePoint(200, 100);
  449. FLinearEnd := DpiAwarePoint(200, 140);
  450. end;
  451. end;
  452. FRadialOrigin := DpiAwarePoint(331, 325);
  453. DrawImage;
  454. end;
  455. function TestHitPoint(X, Y: Integer; Point: TPoint; Radius: TFloat): Boolean;
  456. begin
  457. Result := Sqr(X - Point.X) + Sqr(Y - Point.Y) < Sqr(Radius);
  458. end;
  459. procedure TMainForm.ImgView32MouseDown(Sender: TObject; Button: TMouseButton;
  460. Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
  461. begin
  462. if TestHitPoint(X, Y, FLinearStart, FKnobRadius) then
  463. FControlKnob := @FLinearStart;
  464. if TestHitPoint(X, Y, FLinearEnd, FKnobRadius) then
  465. FControlKnob := @FLinearEnd;
  466. if TestHitPoint(X, Y, FRadialX, FKnobRadius) then
  467. if ssCtrl in Shift then
  468. begin
  469. FRadialX.X := FRadialOrigin.X - Abs(FRadialOrigin.Y -
  470. FRadialY.Y);
  471. DrawImage;
  472. end
  473. else
  474. FControlKnob := @FRadialX;
  475. if TestHitPoint(X, Y, FRadialY, FKnobRadius) then
  476. if ssCtrl in Shift then
  477. begin
  478. FRadialY.Y := FRadialOrigin.Y + Abs(FRadialOrigin.X - FRadialX.X);
  479. DrawImage;
  480. end
  481. else
  482. FControlKnob := @FRadialY;
  483. if TestHitPoint(X, Y, FRadialOrigin, FKnobRadius) then
  484. FControlKnob := @FRadialOrigin;
  485. end;
  486. procedure TMainForm.ImgView32MouseMove(Sender: TObject; Shift: TShiftState;
  487. X, Y: Integer; Layer: TCustomLayer);
  488. var
  489. Delta: TPoint;
  490. begin
  491. if FControlKnob = @FLinearStart then
  492. begin
  493. X := EnsureRange(X, 10, ImgView32.ClientWidth - 10);
  494. Y := EnsureRange(Y, 10, ImgView32.ClientHeight - 10);
  495. if (Abs(FLinearEnd.X - X) < 1) and (Abs(FLinearEnd.Y - Y) < 1) then
  496. Exit;
  497. FLinearStart := GR32.Point(X, Y);
  498. DrawImage;
  499. Screen.Cursor := crHandPoint;
  500. end
  501. else if FControlKnob = @FLinearEnd then
  502. begin
  503. X := EnsureRange(X, 10, ImgView32.ClientWidth - 10);
  504. Y := EnsureRange(Y, 10, ImgView32.ClientHeight - 10);
  505. if (Abs(FLinearStart.X - X) < 1) and (Abs(FLinearStart.Y - Y) < 1) then
  506. Exit;
  507. FLinearEnd := GR32.Point(X, Y);
  508. DrawImage;
  509. Screen.Cursor := crHandPoint;
  510. end
  511. else if FControlKnob = @FRadialOrigin then
  512. begin
  513. X := EnsureRange(X, FRadialBounds.Left, FRadialBounds.Right);
  514. Y := EnsureRange(Y, FRadialBounds.Top, FRadialBounds.Bottom);
  515. Delta.X := X - FRadialOrigin.X;
  516. Delta.Y := Y - FRadialOrigin.Y;
  517. FRadialOrigin := GR32.Point(X, Y);
  518. FRadialX := OffsetPoint(FRadialX, Delta.X, Delta.Y);
  519. FRadialY := OffsetPoint(FRadialY, Delta.X, Delta.Y);
  520. DrawImage;
  521. Screen.Cursor := crHandPoint;
  522. end
  523. else if FControlKnob = @FRadialX then
  524. begin
  525. X := EnsureRange(X, 10, ImgView32.ClientWidth - 10);
  526. Delta.X := X - FRadialOrigin.X;
  527. if (Abs(Delta.X) < 3) then Exit;
  528. FRadialX := GR32.Point(FRadialOrigin.X + Delta.X, FRadialX.Y);
  529. DrawImage;
  530. Screen.Cursor := crHandPoint;
  531. end
  532. else if FControlKnob = @FRadialY then
  533. begin
  534. Y := EnsureRange(Y, 10, ImgView32.ClientHeight - 10);
  535. Delta.Y := Y - FRadialOrigin.Y;
  536. if (Abs(Delta.Y) < 3) then Exit;
  537. FRadialY := GR32.Point(FRadialY.X, FRadialOrigin.Y + Delta.Y);
  538. DrawImage;
  539. Screen.Cursor := crHandPoint;
  540. end else
  541. begin
  542. if TestHitPoint(X, Y, FLinearStart, FKnobRadius) or
  543. TestHitPoint(X, Y, FLinearEnd, FKnobRadius) or
  544. TestHitPoint(X, Y, FRadialOrigin, FKnobRadius) or
  545. TestHitPoint(X, Y, FRadialX, FKnobRadius) or
  546. TestHitPoint(X, Y, FRadialY, FKnobRadius) or
  547. Assigned(FControlKnob) then
  548. begin
  549. Screen.Cursor := crHandPoint;
  550. ImgView32.Cursor := crHandPoint;
  551. end
  552. else
  553. begin
  554. Screen.Cursor := crDefault;
  555. ImgView32.Cursor := crDefault;
  556. end;
  557. end;
  558. end;
  559. procedure TMainForm.ImgView32MouseUp(Sender: TObject; Button: TMouseButton;
  560. Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
  561. begin
  562. FControlKnob := nil;
  563. end;
  564. procedure TMainForm.DrawImage;
  565. var
  566. PolygonTop, PolygonBottom: TArrayOfFloatPoint;
  567. Delta: TPoint;
  568. LinearGradFiller: TCustomLinearGradientPolygonFiller;
  569. RadialGradFiller: TRadialGradientPolygonFiller;
  570. SVGStyleRadGradFiller: TSVGRadialGradientPolygonFiller;
  571. const
  572. SimpleStyle = 0;
  573. begin
  574. ImgView32.Bitmap.Clear(clCream32);
  575. ImgView32.Bitmap.FrameRectTS(FLinearBounds, clSilver32);
  576. ImgView32.Bitmap.FrameRectTS(FRadialBounds, clSilver32);
  577. //draw the top ellipse ...
  578. PolygonTop := Ellipse(200, 125, 100, 60);
  579. if FDpiScale > 1 then
  580. PolygonTop := ScalePolygon(PolygonTop, FDpiScale, FDpiScale);
  581. LinearGradFiller := TLinearGradientPolygonFiller.Create(FGradientLUT);
  582. try
  583. LinearGradFiller.StartPoint := FloatPoint(FLinearStart);
  584. LinearGradFiller.EndPoint := FloatPoint(FLinearEnd);
  585. LinearGradFiller.WrapMode := TWrapMode(RgpWrapMode.ItemIndex);
  586. PolygonFS(ImgView32.Bitmap, PolygonTop, LinearGradFiller);
  587. PolyLineFS(ImgView32.Bitmap, PolygonTop, clBlack32, True, 1);
  588. //use LinearGradFiller to fill 'Graphics32' text too ...
  589. LinearGradFiller.StartPoint := DpiAwareFloatPoint(230, 420);
  590. LinearGradFiller.EndPoint := DpiAwareFloatPoint(430, 420);
  591. PolyPolygonFS(ImgView32.Bitmap, FTextGR32, LinearGradFiller);
  592. PolyPolylineFS(ImgView32.Bitmap, FTextGR32, clBlack32, True, 1.2);
  593. finally
  594. LinearGradFiller.Free;
  595. end;
  596. //draw the bottom ellipse ...
  597. PolygonBottom := Ellipse(200, 325, 100, 60);
  598. if FDpiScale > 1 then
  599. PolygonBottom := ScalePolygon(PolygonBottom, FDpiScale, FDpiScale);
  600. if RgpEllipseFillStyle.ItemIndex = SimpleStyle then
  601. begin
  602. RadialGradFiller := TRadialGradientPolygonFiller.Create(FGradientLUT);
  603. try
  604. RadialGradFiller.WrapMode := TWrapMode(RgpWrapMode.ItemIndex);
  605. Delta.X := Abs(FRadialOrigin.X - FRadialX.X);
  606. Delta.Y := Abs(FRadialOrigin.Y - FRadialY.Y);
  607. with FRadialOrigin do
  608. RadialGradFiller.EllipseBounds := FloatRect(X - Delta.X, Y - Delta.Y,
  609. X + Delta.X, Y + Delta.Y);
  610. PolygonFS(ImgView32.Bitmap, PolygonBottom, RadialGradFiller);
  611. finally
  612. RadialGradFiller.Free;
  613. end;
  614. end else
  615. begin
  616. SVGStyleRadGradFiller := TSVGRadialGradientPolygonFiller.Create(FGradientLUT);
  617. try
  618. SVGStyleRadGradFiller.EllipseBounds := DpiAwareFloatRect(100, 265, 300, 385);
  619. SVGStyleRadGradFiller.FocalPoint := FloatPoint(FRadialOrigin);
  620. PolygonFS(ImgView32.Bitmap, PolygonBottom, SVGStyleRadGradFiller);
  621. finally
  622. SVGStyleRadGradFiller.Free;
  623. end;
  624. end;
  625. PolylineFS(ImgView32.Bitmap, PolygonBottom, ClBlack32, True, 1);
  626. //draw some text ...
  627. PolyPolygonFS(ImgView32.Bitmap, FTextNotesPoly, clBlack32);
  628. PolyPolygonFS(ImgView32.Bitmap, FTextTopPoly, clBlack32);
  629. PolyPolygonFS(ImgView32.Bitmap, FTextBottomPoly, clBlack32);
  630. with ImgView32.Bitmap do
  631. begin
  632. Draw(FLinearStart.X - FKnobRadius, FLinearStart.Y - FKnobRadius, FKnobBitmap);
  633. Draw(FLinearEnd.X - FKnobRadius, FLinearEnd.Y - FKnobRadius, FKnobBitmap);
  634. Draw(FRadialOrigin.X - FKnobRadius, FRadialOrigin.Y - FKnobRadius, FKnobBitmap);
  635. if RgpEllipseFillStyle.ItemIndex = SimpleStyle then
  636. begin
  637. Draw(FRadialX.X - FKnobRadius, FRadialX.Y - FKnobRadius, FKnobBitmap);
  638. Draw(FRadialY.X - FKnobRadius, FRadialY.Y - FKnobRadius, FKnobBitmap);
  639. end;
  640. end;
  641. end;
  642. procedure TMainForm.BtnDefaultsClick(Sender: TObject);
  643. begin
  644. with MemoColorStops do
  645. begin
  646. Clear;
  647. Lines.BeginUpdate;
  648. Lines.Add('0.0: clRed32');
  649. Lines.Add('0.1: clYellow32');
  650. Lines.Add('0.3: clLime32');
  651. Lines.Add('0.5: $AA00FFFF');
  652. Lines.Add('0.7: clBlue32');
  653. Lines.Add('0.9: clFuchsia32');
  654. Lines.Add('1.0: $80FF0000');
  655. Lines.EndUpdate;
  656. end;
  657. end;
  658. procedure TMainForm.BtnExitClick(Sender: TObject);
  659. begin
  660. Close;
  661. end;
  662. procedure TMainForm.CmbLUTChange(Sender: TObject);
  663. begin
  664. case CmbLUT.ItemIndex of
  665. 0: MnuOrder4.Checked := True;
  666. 1: MnuOrder5.Checked := True;
  667. 2: MnuOrder6.Checked := True;
  668. 3: MnuOrder7.Checked := True;
  669. 4: MnuOrder8.Checked := True;
  670. 5: MnuOrder9.Checked := True;
  671. 6: MnuOrder10.Checked := True;
  672. 7: MnuOrder11.Checked := True;
  673. 8: MnuOrder12.Checked := True;
  674. 9: MnuOrder13.Checked := True;
  675. end;
  676. FGradientLUT.Order := 4 + CmbLUT.ItemIndex;
  677. end;
  678. procedure TMainForm.MemoColorStopsChange(Sender: TObject);
  679. begin
  680. StrToArrayColor32Gradient(MemoColorStops.Lines, FGradient);
  681. FGradient.FillColorLookUpTable(FGradientLUT);
  682. DrawImage;
  683. end;
  684. procedure TMainForm.MnuFileOpenClick(Sender: TObject);
  685. begin
  686. if OpenDialog.Execute then
  687. MemoColorStops.Lines.LoadFromFile(OpenDialog.FileName);
  688. end;
  689. procedure TMainForm.MnuFileSaveAsClick(Sender: TObject);
  690. begin
  691. if SaveDialog.Execute then
  692. MemoColorStops.Lines.SaveToFile(SaveDialog.FileName);
  693. end;
  694. procedure TMainForm.MnuOrderClick(Sender: TObject);
  695. begin
  696. CmbLUT.ItemIndex := TMenuItem(Sender).Tag;
  697. TMenuItem(Sender).Checked := True;
  698. FGradientLUT.Order := 4 + CmbLUT.ItemIndex;
  699. end;
  700. procedure TMainForm.MnuRadialFillStyleClick(Sender: TObject);
  701. begin
  702. RgpEllipseFillStyle.ItemIndex := TMenuItem(Sender).Tag;
  703. TMenuItem(Sender).Checked := True;
  704. DrawImage;
  705. end;
  706. procedure TMainForm.MnuSpreadClick(Sender: TObject);
  707. begin
  708. RgpWrapMode.ItemIndex := TMenuItem(Sender).Tag;
  709. TMenuItem(Sender).Checked := True;
  710. DrawImage;
  711. end;
  712. procedure TMainForm.FormKeyPress(Sender: TObject; var Key: Char);
  713. begin
  714. if Key = #27 then
  715. Close;
  716. end;
  717. procedure TMainForm.RgpEllipseFillStyleClick(Sender: TObject);
  718. begin
  719. case RgpEllipseFillStyle.ItemIndex of
  720. 0: MnuSimple.Checked := True;
  721. 1: MnuSVG.Checked := True;
  722. end;
  723. DrawImage;
  724. end;
  725. procedure TMainForm.RgpWrapModeClick(Sender: TObject);
  726. begin
  727. case RgpWrapMode.ItemIndex of
  728. 0: MnuPad.Checked := True;
  729. 1: MnuReflect.Checked := True;
  730. 2: MnuRepeat.Checked := True;
  731. end;
  732. DrawImage;
  733. end;
  734. procedure TMainForm.LUTOrderChangedHandler(Sender: TObject);
  735. begin
  736. FGradient.FillColorLookUpTable(FGradientLUT);
  737. DrawImage;
  738. end;
  739. initialization
  740. SetGamma(1);
  741. end.