MainUnit.pas 27 KB

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