bgrashape.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. // SPDX-License-Identifier: LGPL-3.0-linking-exception
  2. {
  3. Created by BGRA Controls Team
  4. Dibo, Circular, lainz (007) and contributors.
  5. For detailed information see readme.txt
  6. Site: https://sourceforge.net/p/bgra-controls/
  7. Wiki: http://wiki.lazarus.freepascal.org/BGRAControls
  8. Forum: http://forum.lazarus.freepascal.org/index.php/board,46.0.html
  9. }
  10. {******************************* CONTRIBUTOR(S) ******************************
  11. - Edivando S. Santos Brasil | [email protected]
  12. (Compatibility with delphi VCL 11/2018)
  13. ***************************** END CONTRIBUTOR(S) *****************************}
  14. unit BGRAShape;
  15. {$I bgracontrols.inc}
  16. interface
  17. uses
  18. Classes, SysUtils, {$IFDEF FPC}LResources,{$ENDIF} Forms, Controls, Graphics, Dialogs,
  19. {$IFNDEF FPC}Types, BGRAGraphics, GraphType, FPImage, {$ENDIF}
  20. BCBaseCtrls, BGRABitmap, BGRABitmapTypes, BCTypes;
  21. type
  22. TBGRAShapeType = (stRegularPolygon, stEllipse);
  23. { TBGRAShape }
  24. TBGRAShape = class(TBGRAGraphicCtrl)
  25. private
  26. { Private declarations }
  27. FBorderColor: TColor;
  28. FBorderOpacity: byte;
  29. FBorderStyle: TPenStyle;
  30. FBorderWidth: integer;
  31. FBorderGradient: TBCGradient;
  32. FUseBorderGradient: boolean;
  33. FFillColor: TColor;
  34. FFillOpacity: byte;
  35. FFillGradient: TBCGradient;
  36. FUseFillGradient: boolean;
  37. FRoundRadius: integer;
  38. FBGRA: TBGRABitmap;
  39. FSideCount: integer;
  40. FRatioXY: single;
  41. FUseRatioXY: boolean;
  42. FAngle: single;
  43. FShapeType: TBGRAShapeType;
  44. procedure SetAngle(const AValue: single);
  45. procedure SetBorderColor(const AValue: TColor);
  46. procedure SetBorderGradient(const AValue: TBCGradient);
  47. procedure SetBorderOpacity(const AValue: byte);
  48. procedure SetBorderStyle(const AValue: TPenStyle);
  49. procedure SetBorderWidth(AValue: integer);
  50. procedure SetFillColor(const AValue: TColor);
  51. procedure SetFillGradient(const AValue: TBCGradient);
  52. procedure SetFillOpacity(const AValue: byte);
  53. procedure SetRatioXY(const AValue: single);
  54. procedure SetRoundRadius(AValue: integer);
  55. procedure SetShapeType(const AValue: TBGRAShapeType);
  56. procedure SetSideCount(AValue: integer);
  57. procedure SetUseBorderGradient(const AValue: boolean);
  58. procedure SetUseFillGradient(const AValue: boolean);
  59. procedure SetUseRatioXY(const AValue: boolean);
  60. protected
  61. { Protected declarations }
  62. procedure Paint; override;
  63. public
  64. { Public declarations }
  65. constructor Create(AOwner: TComponent); override;
  66. destructor Destroy; override;
  67. public
  68. { Streaming }
  69. {$IFDEF FPC}
  70. procedure SaveToFile(AFileName: string);
  71. procedure LoadFromFile(AFileName: string);
  72. {$ENDIF}
  73. procedure OnFindClass({%H-}Reader: TReader; const AClassName: string;
  74. var ComponentClass: TComponentClass);
  75. published
  76. { Published declarations }
  77. property AutoSize;
  78. property Align;
  79. property Anchors;
  80. property Angle: single Read FAngle Write SetAngle {$IFDEF FPC}default 0{$ENDIF};
  81. property BorderWidth: integer Read FBorderWidth Write SetBorderWidth default 1;
  82. property BorderOpacity: byte Read FBorderOpacity Write SetBorderOpacity default 255;
  83. property BorderColor: TColor Read FBorderColor Write SetBorderColor;
  84. property BorderGradient: TBCGradient Read FBorderGradient Write SetBorderGradient;
  85. property BorderStyle: TPenStyle
  86. Read FBorderStyle Write SetBorderStyle default psSolid;
  87. property FillColor: TColor Read FFillColor Write SetFillColor;
  88. property FillOpacity: byte Read FFillOpacity Write SetFillOpacity;
  89. property FillGradient: TBCGradient Read FFillGradient Write SetFillGradient;
  90. property SideCount: integer Read FSideCount Write SetSideCount default 4;
  91. property RatioXY: single Read FRatioXY Write SetRatioXY {$IFDEF FPC}default 1{$ENDIF};
  92. property UseRatioXY: boolean Read FUseRatioXY Write SetUseRatioXY default False;
  93. property UseFillGradient: boolean Read FUseFillGradient
  94. Write SetUseFillGradient default False;
  95. property UseBorderGradient: boolean Read FUseBorderGradient
  96. Write SetUseBorderGradient default False;
  97. property ShapeType: TBGRAShapeType
  98. Read FShapeType Write SetShapeType default stRegularPolygon;
  99. property BorderSpacing;
  100. property Caption;
  101. property PopupMenu;
  102. property RoundRadius: integer Read FRoundRadius Write SetRoundRadius default 0;
  103. property Visible;
  104. property OnClick;
  105. property OnDblClick;
  106. property OnMouseDown;
  107. property OnMouseEnter;
  108. property OnMouseLeave;
  109. property OnMouseMove;
  110. property OnMouseUp;
  111. end;
  112. {$IFDEF FPC}procedure Register;{$ENDIF}
  113. implementation
  114. uses BCTools;
  115. {$IFDEF FPC}
  116. procedure Register;
  117. begin
  118. RegisterComponents('BGRA Controls', [TBGRAShape]);
  119. end;
  120. {$ENDIF}
  121. { TBGRAShape }
  122. procedure TBGRAShape.SetBorderColor(const AValue: TColor);
  123. begin
  124. if FBorderColor = AValue then
  125. exit;
  126. FBorderColor := AValue;
  127. Invalidate;
  128. end;
  129. procedure TBGRAShape.SetBorderGradient(const AValue: TBCGradient);
  130. begin
  131. if FBorderGradient = AValue then
  132. exit;
  133. FBorderGradient.Assign(AValue);
  134. Invalidate;
  135. end;
  136. procedure TBGRAShape.SetAngle(const AValue: single);
  137. begin
  138. if FAngle = AValue then
  139. exit;
  140. FAngle := AValue;
  141. Invalidate;
  142. end;
  143. procedure TBGRAShape.SetBorderOpacity(const AValue: byte);
  144. begin
  145. if FBorderOpacity = AValue then
  146. exit;
  147. FBorderOpacity := AValue;
  148. Invalidate;
  149. end;
  150. procedure TBGRAShape.SetBorderStyle(const AValue: TPenStyle);
  151. begin
  152. if FBorderStyle = AValue then
  153. exit;
  154. FBorderStyle := AValue;
  155. Invalidate;
  156. end;
  157. procedure TBGRAShape.SetBorderWidth(AValue: integer);
  158. begin
  159. if AValue < 0 then
  160. AValue := 0;
  161. if FBorderWidth = AValue then
  162. exit;
  163. FBorderWidth := AValue;
  164. Invalidate;
  165. end;
  166. procedure TBGRAShape.SetFillColor(const AValue: TColor);
  167. begin
  168. if FFillColor = AValue then
  169. exit;
  170. FFillColor := AValue;
  171. Invalidate;
  172. end;
  173. procedure TBGRAShape.SetFillGradient(const AValue: TBCGradient);
  174. begin
  175. if FFillGradient = AValue then
  176. exit;
  177. FFillGradient.Assign(AValue);
  178. Invalidate;
  179. end;
  180. procedure TBGRAShape.SetFillOpacity(const AValue: byte);
  181. begin
  182. if FFillOpacity = AValue then
  183. exit;
  184. FFillOpacity := AValue;
  185. Invalidate;
  186. end;
  187. procedure TBGRAShape.SetRatioXY(const AValue: single);
  188. begin
  189. if FRatioXY = AValue then
  190. exit;
  191. FRatioXY := AValue;
  192. Invalidate;
  193. end;
  194. procedure TBGRAShape.SetRoundRadius(AValue: integer);
  195. begin
  196. if AValue < 0 then
  197. AValue := 0;
  198. if FRoundRadius = AValue then
  199. exit;
  200. FRoundRadius := AValue;
  201. Invalidate;
  202. end;
  203. procedure TBGRAShape.SetShapeType(const AValue: TBGRAShapeType);
  204. begin
  205. if FShapeType = AValue then
  206. exit;
  207. FShapeType := AValue;
  208. Invalidate;
  209. end;
  210. procedure TBGRAShape.SetSideCount(AValue: integer);
  211. begin
  212. if AValue < 3 then
  213. AValue := 3;
  214. if FSideCount = AValue then
  215. exit;
  216. FSideCount := AValue;
  217. Invalidate;
  218. end;
  219. procedure TBGRAShape.SetUseBorderGradient(const AValue: boolean);
  220. begin
  221. if FUseBorderGradient = AValue then
  222. exit;
  223. FUseBorderGradient := AValue;
  224. Invalidate;
  225. end;
  226. procedure TBGRAShape.SetUseFillGradient(const AValue: boolean);
  227. begin
  228. if FUseFillGradient = AValue then
  229. exit;
  230. FUseFillGradient := AValue;
  231. Invalidate;
  232. end;
  233. procedure TBGRAShape.SetUseRatioXY(const AValue: boolean);
  234. begin
  235. if FUseRatioXY = AValue then
  236. exit;
  237. FUseRatioXY := AValue;
  238. Invalidate;
  239. end;
  240. procedure TBGRAShape.Paint;
  241. var
  242. cx, cy, rx, ry, curRatio, a: single;
  243. coords: array of TPointF;
  244. minCoord, maxCoord: TPointF;
  245. i: integer;
  246. borderGrad, fillGrad: TBGRACustomScanner;
  247. scaling: Double;
  248. begin
  249. if FBGRA = nil then FBGRA := TBGRABitmap.Create;
  250. scaling := GetCanvasScaleFactor;
  251. FBGRA.SetSize(round(Width*scaling), round(Height*scaling));
  252. FBGRA.FillTransparent;
  253. FBGRA.PenStyle := FBorderStyle;
  254. with FBGRA.Canvas2D do
  255. begin
  256. lineJoin := 'round';
  257. if FUseBorderGradient then
  258. begin
  259. borderGrad := CreateGradient(FBorderGradient, Classes.rect(0, 0, FBGRA.Width, FBGRA.Height));
  260. strokeStyle(borderGrad);
  261. end
  262. else
  263. begin
  264. borderGrad := nil;
  265. strokeStyle(ColorToBGRA(ColorToRGB(FBorderColor), FBorderOpacity));
  266. end;
  267. lineStyle(FBGRA.CustomPenStyle);
  268. lineWidth := FBorderWidth*scaling;
  269. if FUseFillGradient then
  270. begin
  271. fillGrad := CreateGradient(FFillGradient, Classes.rect(0, 0, FBGRA.Width, FBGRA.Height));
  272. fillStyle(fillGrad);
  273. end
  274. else
  275. begin
  276. fillGrad := nil;
  277. fillStyle(ColorToBGRA(ColorToRGB(FFillColor), FFillOpacity));
  278. end;
  279. cx := FBGRA.Width / 2;
  280. cy := FBGRA.Height / 2;
  281. rx := (FBGRA.Width - FBorderWidth*scaling) / 2;
  282. ry := (FBGRA.Height - FBorderWidth*scaling) / 2;
  283. if FUseRatioXY and (ry <> 0) and (FRatioXY <> 0) then
  284. begin
  285. curRatio := rx / ry;
  286. if FRatioXY > curRatio then
  287. ry := ry / (FRatioXY / curRatio)
  288. else
  289. rx := rx / (curRatio / FRatioXY);
  290. end;
  291. if FShapeType = stRegularPolygon then
  292. begin
  293. setlength(coords, FSideCount);
  294. for i := 0 to high(coords) do
  295. begin
  296. a := (i / FSideCount + FAngle / 360) * 2 * Pi;
  297. coords[i] := PointF(sin(a), -cos(a));
  298. end;
  299. minCoord := coords[0];
  300. maxCoord := coords[0];
  301. for i := 1 to high(coords) do
  302. begin
  303. if coords[i].x < minCoord.x then
  304. minCoord.x := coords[i].x;
  305. if coords[i].y < minCoord.y then
  306. minCoord.y := coords[i].y;
  307. if coords[i].x > maxCoord.x then
  308. maxCoord.x := coords[i].x;
  309. if coords[i].y > maxCoord.y then
  310. maxCoord.y := coords[i].y;
  311. end;
  312. for i := 0 to high(coords) do
  313. begin
  314. with (coords[i] - minCoord) do
  315. coords[i] := PointF((x / (maxCoord.x - minCoord.x) - 0.5) *
  316. 2 * rx + cx, (y / (maxCoord.y - minCoord.y) - 0.5) * 2 * ry + cy);
  317. end;
  318. beginPath;
  319. for i := 0 to high(coords) do
  320. begin
  321. lineTo((coords[i] + coords[(i + 1) mod length(coords)]) * (1 / 2));
  322. arcTo(coords[(i + 1) mod length(coords)], coords[(i + 2) mod
  323. length(coords)], FRoundRadius);
  324. end;
  325. closePath;
  326. end
  327. else
  328. begin
  329. save;
  330. translate(cx, cy);
  331. scale(rx, ry);
  332. beginPath;
  333. arc(0, 0, 1, 0, 2 * Pi);
  334. restore;
  335. end;
  336. fill;
  337. if FBorderWidth <> 0 then
  338. stroke;
  339. fillStyle(BGRAWhite);
  340. strokeStyle(BGRABlack);
  341. fillGrad.Free;
  342. borderGrad.Free;
  343. end;
  344. FBGRA.Draw(Self.Canvas, rect(0,0,Width,Height), False);
  345. end;
  346. constructor TBGRAShape.Create(AOwner: TComponent);
  347. begin
  348. inherited Create(AOwner);
  349. with GetControlClassDefaultSize do
  350. SetInitialBounds(0, 0, CX, CY);
  351. FBGRA := nil;
  352. FBorderColor := clWindowText;
  353. FBorderOpacity := 255;
  354. FBorderWidth := 1;
  355. FBorderStyle := psSolid;
  356. FBorderGradient := TBCGradient.Create(Self);
  357. FBorderGradient.Point2XPercent := 100;
  358. FBorderGradient.StartColor := clWhite;
  359. FBorderGradient.EndColor := clBlack;
  360. FFillColor := clWindow;
  361. FFillOpacity := 255;
  362. FFillGradient := TBCGradient.Create(Self);
  363. FRoundRadius := 0;
  364. FSideCount := 4;
  365. FRatioXY := 1;
  366. FUseRatioXY := False;
  367. end;
  368. destructor TBGRAShape.Destroy;
  369. begin
  370. FBGRA.Free;
  371. FFillGradient.Free;
  372. FBorderGradient.Free;
  373. inherited Destroy;
  374. end;
  375. {$IFDEF FPC}
  376. procedure TBGRAShape.SaveToFile(AFileName: string);
  377. var
  378. AStream: TMemoryStream;
  379. begin
  380. AStream := TMemoryStream.Create;
  381. try
  382. WriteComponentAsTextToStream(AStream, Self);
  383. AStream.SaveToFile(AFileName);
  384. finally
  385. AStream.Free;
  386. end;
  387. end;
  388. procedure TBGRAShape.LoadFromFile(AFileName: string);
  389. var
  390. AStream: TMemoryStream;
  391. begin
  392. AStream := TMemoryStream.Create;
  393. try
  394. AStream.LoadFromFile(AFileName);
  395. ReadComponentFromTextStream(AStream, TComponent(Self), OnFindClass);
  396. finally
  397. AStream.Free;
  398. end;
  399. end;
  400. {$ENDIF}
  401. procedure TBGRAShape.OnFindClass(Reader: TReader; const AClassName: string;
  402. var ComponentClass: TComponentClass);
  403. begin
  404. if CompareText(AClassName, 'TBGRAShape') = 0 then
  405. ComponentClass := TBGRAShape;
  406. end;
  407. end.