superled.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. // SPDX-License-Identifier: LGPL-3.0-linking-exception
  2. {
  3. Part of BGRA Controls. Made by third party.
  4. For detailed information see readme.txt
  5. Site: https://sourceforge.net/p/bgra-controls/
  6. Wiki: http://wiki.lazarus.freepascal.org/BGRAControls
  7. Forum: http://forum.lazarus.freepascal.org/index.php/board,46.0.html
  8. }
  9. {******************************* CONTRIBUTOR(S) ******************************
  10. - Sandy Ganz | [email protected]
  11. Evolved from BGRAShape, thin wrapper to make a nice looking LED component. Fast
  12. cached drawing and a bunch of shape, size and drawing options. Special thanks
  13. to the BGRA team for nice code in the BGRAShape component that was easily
  14. reused and modified.
  15. Note On Auto Scale -
  16. This component by default has Auto Scale OFF. That means that it will not be
  17. subjected to any LCL Auto Scaling based on DPI or Screen Zoom (as far as I can test).
  18. Auto Scale will ONLY cause a change in the component at run time on items that are scaled
  19. by the Paint procedure. This means that toggling the Auto Scale property will
  20. NOT change the ClientWidth/Height of the component after the initial form is created.
  21. Again, changing the Auto Scale setting will not change the Component width/height
  22. after the component is created. It must be set prior to form create for the scale
  23. of the ClientWidth/Height to be affected. After that, it will not change.
  24. In the case of the SuperLED the only item that currently will be modified is
  25. the 'BorderThickness' IF Auto Scale is set to True. The drawing of the border
  26. will change between 1.0 Scale and what ever the new scale is at run time, but
  27. again the components ClientWidth and Height will not.
  28. ***************************** END CONTRIBUTOR(S) *****************************}
  29. {******************************** CHANGE LOG *********************************
  30. v1.00 - 07-11-2025 Begat sjg by [email protected]
  31. v1.01 - 07-27-2025 Minor Code clean, Comments about Auto Scale
  32. ******************************* END CHANGE LOG *******************************}
  33. unit SuperLED;
  34. {$I bgracontrols.inc}
  35. interface
  36. uses
  37. Classes, SysUtils, {$IFDEF FPC} LResources, {$ENDIF} Forms, Controls, Graphics, Dialogs,
  38. {$IFNDEF FPC} Types, BGRAGraphics, GraphType, FPImage, {$ENDIF}
  39. BCBaseCtrls, BGRABitmap, BGRAShape, BGRABitmapTypes, BGRAGradientScanner, BCTypes;
  40. const
  41. VERSIONSTR = '1.01'; // SLED version, Should ALWAYS show as a delta when merging!
  42. BASELINE_SIZE = 32; // Default size for the LED
  43. MAX_SHAPE_SIDES = 6; // Max sides for a shape, Hexagon is 6
  44. DARKEN_PERCENT = 50; // Darkening Default for the Inactive color
  45. BRIGHTNESS_SCALER = 32767; // Used to make scale sorta 0-100 percent in Brightness where 32767 is 100%
  46. type
  47. TSLEDStyle = (slsFlat, slsShaded);
  48. TSLEDShape = (slshRound, slshSquare, slshTriangle, slshPentagon, slshHexagon);
  49. { TSuperLED }
  50. TSuperLED = class(TBGRAGraphicCtrl)
  51. private
  52. { Private declarations }
  53. FActive: boolean;
  54. FAutoScale: boolean;
  55. FActiveColor: TColor;
  56. FInactiveColor: TColor;
  57. FInactiveBrightness: integer;
  58. FStyle: TSLEDStyle;
  59. FShape: TSLEDShape;
  60. FOnChange: TNotifyEvent;
  61. FActiveBmp: TBGRABitmap;
  62. FInactiveBmp: TBGRABitmap;
  63. FBorderColor: TColor;
  64. FBorderOpacity: byte;
  65. FBorderStyle: TPenStyle;
  66. FBorderThickness: integer;
  67. FRoundRadius: integer;
  68. FFillOpacity: byte;
  69. FBorderGradient: TBCGradient;
  70. FFillGradient: TBCGradient;
  71. FAngle: single;
  72. FScaling: Double;
  73. FDirty: boolean;
  74. function ShapeToCount(AShape: TSLEDShape): integer;
  75. procedure SetActive(AValue: boolean);
  76. procedure SetActiveColor(AValue: TColor);
  77. procedure SetAutoScale(AValue: boolean);
  78. procedure SetInactiveColor(AValue: TColor);
  79. procedure SetInactiveBrightness(AValue: integer);
  80. procedure SetBorderColor(AValue: TColor);
  81. procedure SetBorderOpacity(Avalue: byte);
  82. procedure SetBorderThickness(AValue: integer);
  83. procedure SetBorderStyle(AValue: TPenStyle);
  84. procedure SetStyle(AValue: TSLEDStyle);
  85. procedure SetShape(AValue: TSLEDShape);
  86. procedure SetAngle(AValue: single);
  87. procedure SetRoundRadius(AValue: integer);
  88. procedure SetFillOpacity(AValue: byte);
  89. procedure SetOnChange(AValue: TNotifyEvent);
  90. procedure DoChange;
  91. protected
  92. { Protected declarations }
  93. class function GetControlClassDefaultSize: TSize; override;
  94. procedure Paint; override;
  95. procedure SetColor(AValue: TColor); override;
  96. function GetColor: TColor;
  97. procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
  98. procedure DrawLED;
  99. procedure DrawLEDBmp(LEDBitmap: TBGRABitmap; Active: boolean);
  100. function CreateGradient(AGradient: TBCGradient; ARect: TRect): TBGRAGradientScanner;
  101. function Brightness(Color: TColor; Brightness: integer): TBGRAPixel;
  102. public
  103. { Public declarations }
  104. constructor Create(AOwner: TComponent); override;
  105. destructor Destroy; override;
  106. procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;
  107. const AFromPPI, AToPPI, AOldFormWidth, ANewFormWidth: Integer); override;
  108. public
  109. { Streaming }
  110. {$IFDEF FPC}
  111. procedure SaveToFile(AFileName: string);
  112. procedure LoadFromFile(AFileName: string);
  113. {$ENDIF}
  114. procedure OnFindClass({%H-}Reader: TReader; const AClassName: string;
  115. var ComponentClass: TComponentClass);
  116. published
  117. { Published declarations }
  118. property ActiveColor: TColor read FActiveColor write SetActiveColor default clRed;
  119. property InactiveColor: TColor read FInactiveColor write SetInactiveColor default clBlack;
  120. property InactiveBrightness: integer read FInactiveBrightness write SetInactiveBrightness default DARKEN_PERCENT;
  121. property BorderColor: TColor read FBorderColor write SetBorderColor default clGray;
  122. property BorderOpacity: byte read FBorderOpacity write SetBorderOpacity default 255;
  123. property BorderThickness: integer read FBorderThickness write SetBorderThickness default 1;
  124. property BorderStyle: TPenStyle Read FBorderStyle Write SetBorderStyle default psSolid;
  125. property RoundRadius: integer read FRoundRadius write SetRoundRadius default 0;
  126. property FillOpacity: byte read FFillOpacity write SetFillOpacity default 255;
  127. property Style: TSLEDStyle read FStyle write SetStyle default slsShaded;
  128. property Shape: TSLEDShape read FShape write SetShape default slshRound;
  129. property Angle: single read FAngle write SetAngle default 0;
  130. property Active: boolean read FActive write SetActive default False;
  131. property AutoScale: boolean read FAutoScale write SetAutoScale default False;
  132. property Color: TColor read GetColor write SetColor default clNone; // need to override the ancestor since we need to dirty to update
  133. property Align;
  134. property ShowHint;
  135. property Anchors;
  136. property OnClick;
  137. property OnDblClick;
  138. property OnMouseDown;
  139. property OnMouseEnter;
  140. property OnMouseLeave;
  141. property OnMouseMove;
  142. property OnMouseUp;
  143. // Debug Only TO allow easy reading of Auto Scale Factor
  144. // property ScalingFactor: double read FScaling;
  145. end;
  146. {$IFDEF FPC}procedure Register;{$ENDIF}
  147. implementation
  148. uses BCTools; // possibly get the gradient code into here
  149. {$IFDEF FPC}
  150. procedure Register;
  151. begin
  152. RegisterComponents('BGRA Controls', [TSuperLED]);
  153. end;
  154. {$ENDIF}
  155. { TSuperLED }
  156. procedure TSuperLED.AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;
  157. const AFromPPI, AToPPI, AOldFormWidth, ANewFormWidth: Integer);
  158. begin
  159. // If autoscaling then we will let the system mess with the component size
  160. // otherwise it will just leave it along as the ACTUAL size in the designer
  161. // as 1:1 with no scaling on anything. By not calling AutoAdjustLayout()
  162. // Scaling will be 1:1
  163. //
  164. // Note - that toggling the AutoScale setting will cause a repaint
  165. // but NOT a resize of the Components client area
  166. if FAutoScale then
  167. inherited AutoAdjustLayout(AMode, AFromPPI, AToPPI, AOldFormWidth, ANewFormWidth);
  168. end;
  169. constructor TSuperLED.Create(AOwner: TComponent);
  170. begin
  171. inherited Create(AOwner);
  172. with GetControlClassDefaultSize do
  173. SetInitialBounds(0, 0, CX, CY);
  174. FDirty := True;
  175. FActiveBmp := TBGRABitmap.Create;
  176. FInactiveBmp := TBGRABitmap.Create;
  177. FActiveColor := clRed;
  178. FInactiveColor := clBlack;
  179. FInactiveBrightness := DARKEN_PERCENT; // In percent of brightness, 100 is full on, 0 black
  180. FShape := slshRound;
  181. FStyle := slsShaded;
  182. FBorderColor := clGray;
  183. FBorderOpacity := 255;
  184. FBorderThickness := 1;
  185. FBorderStyle := psSolid;
  186. FRoundRadius := 0;
  187. FFillOpacity := 255;
  188. FAngle := 0;
  189. FScaling := 1.0;
  190. FAutoScale := False;
  191. Color := clNone;
  192. // Border Gradient
  193. FBorderGradient := TBCGradient.Create(Self);
  194. FBorderGradient.Point2XPercent := 100;
  195. FBorderGradient.StartColor := FBorderColor;
  196. FBorderGradient.EndColor := Brightness(FActiveColor, FInactiveBrightness);
  197. // Fill Gradient
  198. FFillGradient := TBCGradient.Create(Self);
  199. FFillGradient.StartColor := FActiveColor;
  200. FFillGradient.EndColor := Brightness(FActiveColor, FInactiveBrightness);
  201. end;
  202. destructor TSuperLED.Destroy;
  203. begin
  204. FActiveBmp.Free;
  205. FInactiveBmp.Free;
  206. FFillGradient.Free;
  207. FBorderGradient.Free;
  208. inherited Destroy;
  209. end;
  210. // Override the base class which has a rectangular dimension
  211. class function TSuperLED.GetControlClassDefaultSize: TSize;
  212. begin
  213. // Set the preferred size of the control. This may be subject to scaling!
  214. Result.CX := BASELINE_SIZE;
  215. Result.CY := BASELINE_SIZE;
  216. end;
  217. procedure TSuperLED.DoSetBounds(ALeft, ATop, AWidth, AHeight: Integer);
  218. begin
  219. inherited;
  220. FDirty := true; // Called on Resize of component
  221. end;
  222. // Original from BCTools.pas
  223. function TSuperLED.CreateGradient(AGradient: TBCGradient; ARect: TRect): TBGRAGradientScanner;
  224. begin
  225. Result := TBGRAGradientScanner.Create(
  226. ColorToBGRA(ColorToRGB(AGradient.StartColor), AGradient.StartColorOpacity),
  227. ColorToBGRA(ColorToRGB(AGradient.EndColor), AGradient.EndColorOpacity),
  228. AGradient.GradientType, PointF(ARect.Left + Round(
  229. ((ARect.Right - ARect.Left) / 100) * AGradient.Point1XPercent),
  230. ARect.Top + Round(((ARect.Bottom - ARect.Top) / 100) * AGradient.Point1YPercent)),
  231. PointF(ARect.Left + Round(((ARect.Right - ARect.Left) / 100) *
  232. AGradient.Point2XPercent), ARect.Top + Round(
  233. ((ARect.Bottom - ARect.Top) / 100) * AGradient.Point2YPercent)),
  234. AGradient.ColorCorrection, AGradient.Sinus);
  235. end;
  236. // sets the brightness for a color. Useful for a single
  237. // color setting and dim or bright changes on that.
  238. // Brightness = 0 Black
  239. // Brightness = 100 Origional Color
  240. // Brightness > 100 your mileage may vary
  241. //
  242. // In precent as indicated above
  243. function TSuperLED.Brightness(Color: TColor; Brightness: integer): TBGRAPixel;
  244. begin
  245. Result := ApplyIntensityFast(ColorToBGRA(ColorToRGB(Color)), Round((Brightness / 100) * BRIGHTNESS_SCALER));
  246. end;
  247. // given a shape type get the number of sides. slshRound is not really
  248. // used but here just in case...
  249. function TSuperLED.ShapeToCount(AShape: TSLEDShape): integer;
  250. begin
  251. // Only allow a few predefined shapes for the LED, so this helper
  252. // Translates to what's needed if a polygon is drawn. slshRound
  253. // is not really used, but left in just because.
  254. //
  255. // TSLEDShape = (slshRound = 0, slshSquare = 4, slshTriangle = 3,
  256. // slshPentagon = 5, slshHexagon = 6)
  257. case AShape of
  258. slshRound: Result := 0;
  259. slshTriangle: Result := 3;
  260. slshSquare: Result := 4;
  261. slshPentagon: Result := 5;
  262. slshHexagon: Result := 6;
  263. else
  264. Result := 0; // slshRound
  265. end;
  266. end;
  267. procedure TSuperLED.SetActive(AValue: boolean);
  268. begin
  269. if FActive = AValue then
  270. Exit;
  271. FActive := AValue;
  272. Invalidate; // don't set the dirty flag since we don't count this as dirty, just a redraw
  273. end;
  274. procedure TSuperLED.SetActiveColor(AValue: TColor);
  275. begin
  276. if FActiveColor = AValue then
  277. Exit;
  278. FActiveColor := AValue;
  279. DoChange;
  280. end;
  281. // The Set/Get color must be overidden since they are in the ancestor class
  282. // and we need to know they changed since the LED needs to see a Dirty flag
  283. // to repaint efficently the way we pre-paint active and inactive bitmaps
  284. function TSuperLED.GetColor: TColor;
  285. begin
  286. Result := inherited Color;
  287. end;
  288. procedure TSuperLED.SetColor(AValue: TColor);
  289. begin
  290. if inherited Color = AValue then
  291. Exit;
  292. inherited SetColor(AValue);
  293. DoChange;
  294. end;
  295. procedure TSuperLED.SetInactiveColor(AValue: TColor);
  296. begin
  297. if FInactiveColor = AValue then
  298. Exit;
  299. FInactiveColor := AValue;
  300. DoChange;
  301. end;
  302. // allows a 0-100% change in brightness for the INACTIVE state as
  303. // well as used for the gradient transistions
  304. procedure TSuperLED.SetInactiveBrightness(AValue: integer);
  305. begin
  306. if (FInactiveBrightness = AValue) or (AValue < 0) or (AValue > 100) then
  307. Exit;
  308. FInactiveBrightness := AValue;
  309. DoChange;
  310. end;
  311. procedure TSuperLED.SetBorderColor(AValue: TColor);
  312. begin
  313. if FBorderColor = AValue then
  314. Exit;
  315. FBorderColor := AValue;
  316. DoChange;
  317. end;
  318. procedure TSuperLED.SetBorderOpacity(Avalue: byte);
  319. begin
  320. if FBorderOpacity = AValue then
  321. Exit;
  322. FBorderOpacity := AValue;
  323. DoChange;
  324. end;
  325. procedure TSuperLED.SetBorderThickness(Avalue: integer);
  326. begin
  327. if (FBorderThickness = AValue) or (AValue < 0) then
  328. Exit;
  329. FBorderThickness := AValue;
  330. DoChange;
  331. end;
  332. procedure TSuperLED.SetBorderStyle(AValue: TPenStyle);
  333. begin
  334. if FBorderStyle = AValue then
  335. Exit;
  336. FBorderStyle := AValue;
  337. DoChange;
  338. end;
  339. procedure TSuperLED.SetStyle(AValue: TSLEDStyle);
  340. begin
  341. if FStyle = AValue then
  342. Exit;
  343. FStyle := AValue;
  344. DoChange;
  345. end;
  346. procedure TSuperLED.SetShape(AValue: TSLEDShape);
  347. begin
  348. if FShape = AValue then
  349. Exit;
  350. FShape := AValue;
  351. DoChange;
  352. end;
  353. procedure TSuperLED.SetAngle(AValue: single);
  354. begin
  355. if FAngle = AValue then
  356. Exit;
  357. FAngle := AValue;
  358. DoChange;
  359. end;
  360. procedure TSuperLED.SetAutoScale(AValue: boolean);
  361. begin
  362. if FAutoScale = AValue then
  363. Exit;
  364. FAutoScale := AValue;
  365. DoChange;
  366. end;
  367. procedure TSuperLED.SetRoundRadius(AValue: integer);
  368. begin
  369. if FRoundRadius = AValue then
  370. Exit;
  371. FRoundRadius := AValue;
  372. DoChange;
  373. end;
  374. procedure TSuperLED.SetFillOpacity(AValue: byte);
  375. begin
  376. if FFillOpacity = AValue then
  377. Exit;
  378. FFillOpacity := AValue;
  379. DoChange;
  380. end;
  381. procedure TSuperLED.SetOnChange(AValue: TNotifyEvent);
  382. begin
  383. FOnChange := AValue;
  384. // this will not dirty it
  385. if Assigned(FOnChange) then
  386. FOnChange(Self);
  387. end;
  388. procedure TSuperLED.DoChange;
  389. begin
  390. FDirty := True;
  391. Invalidate; // if we get here a prop must have changed, mark dirty
  392. if Assigned(FOnChange) then
  393. FOnChange(Self);
  394. end;
  395. procedure TSuperLED.Paint;
  396. begin
  397. inherited Paint;
  398. // Scaling should only affect visuals that are not based on Width/Height
  399. // as they change when scaled, so if based ClientWidth/Height, good, but not all are!
  400. // IF Scaling then this may/will be not 1:1 if the LCL is scaling the form.
  401. // Also note that set properties for objects like Border thickness will need to
  402. // be scaled scaled here.
  403. // Somewhat experimental, seems to work OK
  404. if FAutoScale then
  405. FScaling := ScaleDesignToForm(BASELINE_SIZE)/BASELINE_SIZE // just get the ratio from arbitrary dims
  406. else
  407. FScaling := 1.0; // not scaling the component, so no scale of anything else done
  408. DrawLED;
  409. if FActive then
  410. begin
  411. // draw the Active BMP to the canvas
  412. FActiveBmp.Draw(Canvas, 0, 0, False);
  413. end
  414. else
  415. begin
  416. // Draw the Inactive BMP to the canvas
  417. FInactiveBmp.Draw(Canvas, 0, 0, False);
  418. end;
  419. end;
  420. procedure TSuperLED.DrawLED;
  421. begin
  422. // See if we need to redraw the bitmaps, we always do both
  423. // the Active and Inactive as we need both.
  424. if Not FDirty then
  425. Exit;
  426. FDirty := False;
  427. // Draw the Active then Inactive
  428. FActiveBmp.SetSize(Width, Height);
  429. FInactiveBmp.SetSize(Width, Height);
  430. // Clear bitmap to transparent or background color.
  431. // NOTE we must overide the ancestor class to force a dirty
  432. // flag for design time repaint, see SetColor() code/comments
  433. if Color = clNone then
  434. begin
  435. FActiveBmp.FillTransparent;
  436. FInactiveBmp.FillTransparent;
  437. end
  438. else
  439. begin
  440. FActiveBmp.Fill(Color);
  441. FInactiveBmp.Fill(Color);
  442. end;
  443. // The magic happens in DrawLEDBmp!
  444. DrawLEDBmp(FActiveBmp, True);
  445. DrawLEDBmp(FInactiveBmp, False);
  446. end;
  447. procedure TSuperLED.DrawLEDBmp(LEDBitmap: TBGRABitmap; Active: boolean);
  448. var
  449. cx, cy, rx, ry, a: single;
  450. coords: array[0..MAX_SHAPE_SIDES] of TPointF;
  451. minCoord, maxCoord: TPointF;
  452. i: integer;
  453. borderGrad, fillGrad: TBGRACustomScanner;
  454. sideCnt: integer;
  455. flatFillColor : TColor;
  456. begin
  457. // Basline code from BGRAShape, changed to work a bit better
  458. // with the simplified LED shapes and borders. Also updated to
  459. // support different fill color based on the state and Gradient borders
  460. sideCnt := ShapeToCount(FShape); // get the number of sides for the users shape
  461. LEDBitmap.PenStyle := FBorderStyle;
  462. FFillGradient.EndColor := Brightness(FActiveColor, FInactiveBrightness);
  463. FBorderGradient.Startcolor := FBorderColor;
  464. FBorderGradient.EndColor := Brightness(FBorderColor, FInactiveBrightness);
  465. // set up anything related to the state, mostly color
  466. if Active then
  467. begin
  468. FFillGradient.StartColor := FActiveColor;
  469. flatFillColor := FActiveColor;
  470. end
  471. else
  472. begin
  473. FFillGradient.StartColor := FInactiveColor;
  474. flatFillColor := Brightness(FInactiveColor, FInactiveBrightness); // allow brightness on flat
  475. end;
  476. with LEDBitmap.Canvas2D do
  477. begin
  478. lineJoin := 'round';
  479. // if we are shaded we gradient both fill and border
  480. // if not we draw both flat.
  481. if FStyle = slsShaded then // use Gradient
  482. begin
  483. FBorderGradient.StartColorOpacity := FBorderOpacity; // sjg - Added Opacity to both
  484. FBorderGradient.EndColorOpacity := FBorderOpacity;
  485. borderGrad := CreateGradient(FBorderGradient, Classes.rect(0, 0, LEDBitmap.Width, LEDBitmap.Height));
  486. strokeStyle(borderGrad);
  487. end
  488. else
  489. begin
  490. borderGrad := nil;
  491. strokeStyle(ColorToBGRA(ColorToRGB(FBorderColor), FBorderOpacity));
  492. end;
  493. lineStyle(LEDBitmap.CustomPenStyle);
  494. lineWidth := FBorderThickness * FScaling;
  495. if FStyle = slsShaded then
  496. begin
  497. fillGrad := CreateGradient(FFillGradient, Classes.rect(0, 0, LEDBitmap.Width, LEDBitmap.Height));
  498. fillStyle(fillGrad);
  499. end
  500. else
  501. begin
  502. fillGrad := nil;
  503. fillStyle(ColorToBGRA(ColorToRGB(flatFillColor), FFillOpacity));
  504. end;
  505. cx := LEDBitmap.Width / 2;
  506. cy := LEDBitmap.Height / 2;
  507. rx := (LEDBitmap.Width - FBorderThickness * FScaling) / 2;
  508. ry := (LEDBitmap.Height - FBorderThickness * FScaling) / 2;
  509. // Now Draw a circle or polygon
  510. if FShape = slshRound then
  511. begin
  512. // slshRound - circle
  513. save;
  514. translate(cx, cy);
  515. scale(rx, ry);
  516. beginPath;
  517. arc(0, 0, 1, 0, 2 * Pi);
  518. restore;
  519. end
  520. else
  521. begin
  522. // Polygon drawing all here
  523. for i := 0 to sideCnt - 1 do
  524. begin
  525. a := (i / sideCnt + FAngle / 360) * 2 * Pi;
  526. coords[i] := PointF(sin(a), -cos(a));
  527. end;
  528. minCoord := coords[0];
  529. maxCoord := coords[0];
  530. for i := 1 to sideCnt - 1 do
  531. begin
  532. if coords[i].x < minCoord.x then
  533. minCoord.x := coords[i].x;
  534. if coords[i].y < minCoord.y then
  535. minCoord.y := coords[i].y;
  536. if coords[i].x > maxCoord.x then
  537. maxCoord.x := coords[i].x;
  538. if coords[i].y > maxCoord.y then
  539. maxCoord.y := coords[i].y;
  540. end;
  541. for i := 0 to sideCnt - 1 do
  542. begin
  543. with (coords[i] - minCoord) do
  544. coords[i] := PointF((x / (maxCoord.x - minCoord.x) - 0.5) *
  545. 2 * rx + cx, (y / (maxCoord.y - minCoord.y) - 0.5) * 2 * ry + cy);
  546. end;
  547. beginPath;
  548. for i := 0 to sideCnt - 1 do
  549. begin
  550. lineTo((coords[i] + coords[(i + 1) mod sideCnt]) * (1 / 2));
  551. arcTo(coords[(i + 1) mod sideCnt], coords[(i + 2) mod sideCnt], FRoundRadius);
  552. end;
  553. closePath;
  554. end;
  555. fill;
  556. if FBorderThickness <> 0 then
  557. stroke;
  558. fillStyle(BGRAWhite);
  559. strokeStyle(BGRABlack);
  560. fillGrad.Free;
  561. borderGrad.Free;
  562. end;
  563. end;
  564. {$IFDEF FPC}
  565. procedure TSuperLED.SaveToFile(AFileName: string);
  566. var
  567. AStream: TMemoryStream;
  568. begin
  569. AStream := TMemoryStream.Create;
  570. try
  571. WriteComponentAsTextToStream(AStream, Self);
  572. AStream.SaveToFile(AFileName);
  573. finally
  574. AStream.Free;
  575. end;
  576. end;
  577. procedure TSuperLED.LoadFromFile(AFileName: string);
  578. var
  579. AStream: TMemoryStream;
  580. begin
  581. AStream := TMemoryStream.Create;
  582. try
  583. AStream.LoadFromFile(AFileName);
  584. ReadComponentFromTextStream(AStream, TComponent(Self), OnFindClass);
  585. finally
  586. AStream.Free;
  587. end;
  588. end;
  589. {$ENDIF}
  590. procedure TSuperLED.OnFindClass(Reader: TReader; const AClassName: string;
  591. var ComponentClass: TComponentClass);
  592. begin
  593. if CompareText(AClassName, 'TSuperLED') = 0 then
  594. ComponentClass := TSuperLED;
  595. end;
  596. end.