GR32_Paths.pas 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933
  1. unit GR32_Paths;
  2. (* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1 or LGPL 2.1 with linking exception
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * Alternatively, the contents of this file may be used under the terms of the
  16. * Free Pascal modified version of the GNU Lesser General Public License
  17. * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
  18. * of this license are applicable instead of those above.
  19. * Please see the file LICENSE.txt for additional information concerning this
  20. * license.
  21. *
  22. * The Original Code is Vectorial Polygon Rasterizer for Graphics32
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Mattias Andersson <[email protected]>
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2012
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * Contributor(s):
  31. *
  32. * ***** END LICENSE BLOCK ***** *)
  33. interface
  34. {$I GR32.inc}
  35. uses
  36. Classes, SysUtils,
  37. GR32,
  38. GR32_Math,
  39. GR32_Polygons,
  40. GR32_Transforms,
  41. GR32_Brushes,
  42. GR32_Geometry;
  43. const
  44. DefaultCircleSteps = 100;
  45. DefaultBezierTolerance = 0.25;
  46. type
  47. TControlPointOrigin = (cpNone, cpCubic, cpConic);
  48. { TCustomPath }
  49. TCustomPath = class(TThreadPersistent)
  50. private
  51. FCurrentPoint: TFloatPoint;
  52. FLastControlPoint: TFloatPoint;
  53. FControlPointOrigin: TControlPointOrigin;
  54. FChanged: boolean;
  55. protected
  56. procedure AddPoint(const Point: TFloatPoint); virtual;
  57. procedure AssignTo(Dest: TPersistent); override;
  58. procedure DoChanged; virtual;
  59. public
  60. constructor Create; override;
  61. procedure Clear; virtual;
  62. procedure BeginUpdate; override;
  63. procedure EndUpdate; override;
  64. procedure Changed; override;
  65. procedure BeginPath; deprecated 'No longer necessary. Path is started automatically';
  66. procedure EndPath(Close: boolean = False); virtual;
  67. procedure ClosePath; deprecated 'Use EndPath(True) instead';
  68. // Movement
  69. procedure MoveTo(const X, Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  70. procedure MoveTo(const P: TFloatPoint); overload; virtual;
  71. procedure MoveToRelative(const X, Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  72. procedure MoveToRelative(const P: TFloatPoint); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  73. // Lines and Curves
  74. procedure LineTo(const X, Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  75. procedure LineTo(const P: TFloatPoint); overload; virtual;
  76. procedure LineToRelative(const X, Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  77. procedure LineToRelative(const P: TFloatPoint); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  78. procedure HorizontalLineTo(const X: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  79. procedure HorizontalLineToRelative(const X: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  80. procedure VerticalLineTo(const Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  81. procedure VerticalLineToRelative(const Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  82. procedure CurveTo(const X1, Y1, X2, Y2, X, Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  83. procedure CurveTo(const X2, Y2, X, Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  84. procedure CurveTo(const C1, C2, P: TFloatPoint); overload; virtual;
  85. procedure CurveTo(const C2, P: TFloatPoint); overload; virtual;
  86. procedure CurveToRelative(const X1, Y1, X2, Y2, X, Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  87. procedure CurveToRelative(const X2, Y2, X, Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  88. procedure CurveToRelative(const C1, C2, P: TFloatPoint); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  89. procedure CurveToRelative(const C2, P: TFloatPoint); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  90. procedure ConicTo(const X1, Y1, X, Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  91. procedure ConicTo(const P1, P: TFloatPoint); overload; virtual;
  92. procedure ConicTo(const X, Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  93. procedure ConicTo(const P: TFloatPoint); overload; virtual;
  94. procedure ConicToRelative(const X1, Y1, X, Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  95. procedure ConicToRelative(const P1, P: TFloatPoint); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  96. procedure ConicToRelative(const X, Y: TFloat); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  97. procedure ConicToRelative(const P: TFloatPoint); overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  98. // Polylines
  99. procedure Arc(const P: TFloatPoint; StartAngle, EndAngle, Radius: TFloat);
  100. procedure PolyLine(const APoints: TArrayOfFloatPoint); virtual;
  101. procedure PolyPolyLine(const APoints: TArrayOfArrayOfFloatPoint); virtual;
  102. // Closed Polygons
  103. procedure Rectangle(const Rect: TFloatRect); virtual;
  104. procedure RoundRect(const Rect: TFloatRect; const Radius: TFloat); virtual;
  105. procedure Ellipse(Rx, Ry: TFloat; Steps: Integer = DefaultCircleSteps); overload; virtual;
  106. procedure Ellipse(const Cx, Cy, Rx, Ry: TFloat; Steps: Integer = DefaultCircleSteps); overload; virtual;
  107. procedure Circle(const Cx, Cy, Radius: TFloat; Steps: Integer = DefaultCircleSteps); overload; virtual;
  108. procedure Circle(const Center: TFloatPoint; Radius: TFloat; Steps: Integer = DefaultCircleSteps); overload; virtual;
  109. procedure Polygon(const APoints: TArrayOfFloatPoint); virtual;
  110. procedure PolyPolygon(const APoints: TArrayOfArrayOfFloatPoint); virtual;
  111. property CurrentPoint: TFloatPoint read FCurrentPoint write FCurrentPoint;
  112. end;
  113. { TFlattenedPath }
  114. TFlattenedPath = class(TCustomPath)
  115. private
  116. FPath: TArrayOfArrayOfFloatPoint;
  117. FClosed: TBooleanArray;
  118. FClosedCount: integer;
  119. FPoints: TArrayOfFloatPoint;
  120. FPointIndex: Integer;
  121. FOnBeginPath: TNotifyEvent;
  122. FOnEndPath: TNotifyEvent;
  123. protected
  124. function GetPoints: TArrayOfFloatPoint;
  125. protected
  126. procedure AssignTo(Dest: TPersistent); override;
  127. procedure AddPoint(const Point: TFloatPoint); override;
  128. procedure DoBeginPath; virtual;
  129. procedure DoEndPath; virtual;
  130. procedure ClearPoints;
  131. // Points temporarily holds the vertices used to build a path. Cleared after path has been constructed.
  132. property Points: TArrayOfFloatPoint read GetPoints;
  133. property ClosedCount: integer read FClosedCount;
  134. public
  135. procedure Clear; override;
  136. procedure EndPath(Close: boolean = False); override;
  137. // MoveTo* implicitly ends the current path.
  138. procedure MoveTo(const P: TFloatPoint); override;
  139. property Path: TArrayOfArrayOfFloatPoint read FPath;
  140. property PathClosed: TBooleanArray read FClosed;
  141. property OnBeginPath: TNotifyEvent read FOnBeginPath write FOnBeginPath;
  142. property OnEndPath: TNotifyEvent read FOnEndPath write FOnEndPath;
  143. end;
  144. { TCustomCanvas }
  145. TCustomCanvas = class(TFlattenedPath)
  146. private
  147. FTransformation: TTransformation;
  148. protected
  149. procedure SetTransformation(const Value: TTransformation);
  150. protected
  151. procedure AssignTo(Dest: TPersistent); override;
  152. procedure DoChanged; override;
  153. procedure DrawPath(const Path: TFlattenedPath); virtual; abstract;
  154. public
  155. property Transformation: TTransformation read FTransformation write SetTransformation;
  156. function Path: TFlattenedPath; deprecated 'No longer necessary - Just reference the Canvas itself instead';
  157. end;
  158. { TCanvas32 }
  159. TCanvas32 = class(TCustomCanvas)
  160. private
  161. FBitmap: TBitmap32;
  162. FRenderer: TPolygonRenderer32;
  163. FBrushes: TBrushCollection;
  164. protected
  165. function GetRendererClassName: string;
  166. procedure SetRendererClassName(const Value: string);
  167. procedure SetRenderer(ARenderer: TPolygonRenderer32);
  168. protected
  169. procedure AssignTo(Dest: TPersistent); override;
  170. procedure DrawPath(const Path: TFlattenedPath); override;
  171. class function GetPolygonRendererClass: TPolygonRenderer32Class; virtual;
  172. procedure BrushCollectionChangeHandler(Sender: TObject); virtual;
  173. public
  174. constructor Create(ABitmap: TBitmap32); reintroduce; virtual;
  175. destructor Destroy; override;
  176. procedure RenderText(X, Y: TFloat; const Text: string); overload;
  177. procedure RenderText(const DstRect: TFloatRect; const Text: string; Flags: Cardinal); overload;
  178. function MeasureText(const DstRect: TFloatRect; const Text: string; Flags: Cardinal): TFloatRect;
  179. property Bitmap: TBitmap32 read FBitmap;
  180. property Renderer: TPolygonRenderer32 read FRenderer write SetRenderer;
  181. property RendererClassName: string read GetRendererClassName write SetRendererClassName;
  182. property Brushes: TBrushCollection read FBrushes;
  183. end;
  184. var
  185. CBezierTolerance: TFloat = 0.25;
  186. QBezierTolerance: TFloat = 0.25;
  187. type
  188. TAddPointEvent = procedure(const Point: TFloatPoint) of object;
  189. implementation
  190. uses
  191. Math, {$IFDEF FPC}Types, {$ENDIF} {$IFDEF COMPILERXE2_UP}Types, {$ENDIF}
  192. GR32_Backends,
  193. GR32_VectorUtils;
  194. const
  195. VertexBufferSizeLow = 256;
  196. VertexBufferSizeGrow = 128;
  197. function CubicBezierFlatness(const P1, P2, P3, P4: TFloatPoint): TFloat; {$IFDEF USEINLINING} inline; {$ENDIF}
  198. begin
  199. Result :=
  200. Abs(P1.X + P3.X - 2 * P2.X) +
  201. Abs(P1.Y + P3.Y - 2 * P2.Y) +
  202. Abs(P2.X + P4.X - 2 * P3.X) +
  203. Abs(P2.Y + P4.Y - 2 * P3.Y);
  204. end;
  205. function QuadraticBezierFlatness(const P1, P2, P3: TFloatPoint): TFloat; {$IFDEF USEINLINING} inline; {$ENDIF}
  206. begin
  207. Result :=
  208. Abs(P1.x + P3.x - 2 * P2.x) +
  209. Abs(P1.y + P3.y - 2 * P2.y);
  210. end;
  211. procedure CubicBezierCurve(const P1, P2, P3, P4: TFloatPoint; const AddPoint: TAddPointEvent; const Tolerance: TFloat);
  212. var
  213. P12, P23, P34, P123, P234, P1234: TFloatPoint;
  214. begin
  215. if CubicBezierFlatness(P1, P2, P3, P4) < Tolerance then
  216. AddPoint(P1)
  217. else
  218. begin
  219. P12.X := (P1.X + P2.X) * 0.5;
  220. P12.Y := (P1.Y + P2.Y) * 0.5;
  221. P23.X := (P2.X + P3.X) * 0.5;
  222. P23.Y := (P2.Y + P3.Y) * 0.5;
  223. P34.X := (P3.X + P4.X) * 0.5;
  224. P34.Y := (P3.Y + P4.Y) * 0.5;
  225. P123.X := (P12.X + P23.X) * 0.5;
  226. P123.Y := (P12.Y + P23.Y) * 0.5;
  227. P234.X := (P23.X + P34.X) * 0.5;
  228. P234.Y := (P23.Y + P34.Y) * 0.5;
  229. P1234.X := (P123.X + P234.X) * 0.5;
  230. P1234.Y := (P123.Y + P234.Y) * 0.5;
  231. CubicBezierCurve(P1, P12, P123, P1234, AddPoint, Tolerance);
  232. CubicBezierCurve(P1234, P234, P34, P4, AddPoint, Tolerance);
  233. end;
  234. end;
  235. procedure QuadraticBezierCurve(const P1, P2, P3: TFloatPoint; const AddPoint: TAddPointEvent; const Tolerance: TFloat);
  236. var
  237. P12, P23, P123: TFloatPoint;
  238. begin
  239. if QuadraticBezierFlatness(P1, P2, P3) < Tolerance then
  240. AddPoint(P1)
  241. else
  242. begin
  243. P12.X := (P1.X + P2.X) * 0.5;
  244. P12.Y := (P1.Y + P2.Y) * 0.5;
  245. P23.X := (P2.X + P3.X) * 0.5;
  246. P23.Y := (P2.Y + P3.Y) * 0.5;
  247. P123.X := (P12.X + P23.X) * 0.5;
  248. P123.Y := (P12.Y + P23.Y) * 0.5;
  249. QuadraticBezierCurve(P1, P12, P123, AddPoint, Tolerance);
  250. QuadraticBezierCurve(P123, P23, P3, AddPoint, Tolerance);
  251. end;
  252. end;
  253. //============================================================================//
  254. { TCustomPath }
  255. constructor TCustomPath.Create;
  256. begin
  257. inherited;
  258. FControlPointOrigin := cpNone;
  259. end;
  260. procedure TCustomPath.BeginUpdate;
  261. begin
  262. inherited BeginUpdate;
  263. end;
  264. procedure TCustomPath.EndUpdate;
  265. begin
  266. inherited EndUpdate;
  267. if (UpdateCount = 0) and (FChanged) then
  268. begin
  269. FChanged := False;
  270. DoChanged;
  271. end;
  272. end;
  273. procedure TCustomPath.Changed;
  274. begin
  275. BeginUpdate;
  276. FChanged := True;
  277. EndUpdate;
  278. end;
  279. procedure TCustomPath.DoChanged;
  280. begin
  281. // Execute OnChange event
  282. inherited Changed;
  283. end;
  284. procedure TCustomPath.AddPoint(const Point: TFloatPoint);
  285. begin
  286. end;
  287. procedure TCustomPath.Arc(const P: TFloatPoint; StartAngle, EndAngle, Radius: TFloat);
  288. begin
  289. PolyLine(BuildArc(P, StartAngle, EndAngle, Radius));
  290. end;
  291. procedure TCustomPath.AssignTo(Dest: TPersistent);
  292. begin
  293. if (Dest is TCustomPath) then
  294. begin
  295. TCustomPath(Dest).Clear;
  296. TCustomPath(Dest).FCurrentPoint := FCurrentPoint;
  297. TCustomPath(Dest).FLastControlPoint := FLastControlPoint;
  298. TCustomPath(Dest).FControlPointOrigin := FControlPointOrigin;
  299. end else
  300. inherited;
  301. end;
  302. procedure TCustomPath.BeginPath;
  303. begin
  304. end;
  305. procedure TCustomPath.Circle(const Cx, Cy, Radius: TFloat; Steps: Integer);
  306. begin
  307. Polygon(GR32_VectorUtils.Circle(Cx, Cy, Radius, Steps));
  308. end;
  309. procedure TCustomPath.Circle(const Center: TFloatPoint; Radius: TFloat; Steps: Integer);
  310. begin
  311. Polygon(GR32_VectorUtils.Circle(Center.X, Center.Y, Radius, Steps));
  312. end;
  313. procedure TCustomPath.Clear;
  314. begin
  315. FControlPointOrigin := cpNone;
  316. FChanged := False;
  317. end;
  318. procedure TCustomPath.ClosePath;
  319. begin
  320. EndPath(True);
  321. end;
  322. procedure TCustomPath.ConicTo(const P1, P: TFloatPoint);
  323. begin
  324. QuadraticBezierCurve(FCurrentPoint, P1, P, AddPoint, QBezierTolerance);
  325. AddPoint(P);
  326. FCurrentPoint := P;
  327. FLastControlPoint := P1;
  328. FControlPointOrigin := cpConic;
  329. end;
  330. procedure TCustomPath.ConicTo(const X1, Y1, X, Y: TFloat);
  331. begin
  332. ConicTo(FloatPoint(X1, Y1), FloatPoint(X, Y));
  333. end;
  334. procedure TCustomPath.ConicTo(const X, Y: TFloat);
  335. begin
  336. ConicTo(FloatPoint(X, Y));
  337. end;
  338. procedure TCustomPath.ConicTo(const P: TFloatPoint);
  339. var
  340. P1: TFloatPoint;
  341. begin
  342. if FControlPointOrigin = cpConic then
  343. begin
  344. P1.X := FCurrentPoint.X + (FCurrentPoint.X - FLastControlPoint.X);
  345. P1.Y := FCurrentPoint.Y + (FCurrentPoint.Y - FLastControlPoint.Y);
  346. end
  347. else
  348. P1 := FCurrentPoint;
  349. ConicTo(P1, P);
  350. end;
  351. procedure TCustomPath.ConicToRelative(const X, Y: TFloat);
  352. begin
  353. ConicTo(FloatPoint(FCurrentPoint.X + X, FCurrentPoint.Y + Y));
  354. end;
  355. procedure TCustomPath.ConicToRelative(const P: TFloatPoint);
  356. begin
  357. ConicTo(OffsetPoint(P, FCurrentPoint));
  358. end;
  359. procedure TCustomPath.ConicToRelative(const X1, Y1, X, Y: TFloat);
  360. begin
  361. ConicTo(FloatPoint(FCurrentPoint.X + X1, FCurrentPoint.Y + Y1), FloatPoint(FCurrentPoint.X + X, FCurrentPoint.Y + Y));
  362. end;
  363. procedure TCustomPath.ConicToRelative(const P1, P: TFloatPoint);
  364. begin
  365. ConicTo(OffsetPoint(P1, FCurrentPoint), OffsetPoint(P, FCurrentPoint));
  366. end;
  367. procedure TCustomPath.CurveTo(const C1, C2, P: TFloatPoint);
  368. begin
  369. CubicBezierCurve(FCurrentPoint, C1, C2, P, AddPoint, CBezierTolerance);
  370. AddPoint(P);
  371. FCurrentPoint := P;
  372. FLastControlPoint := C2;
  373. FControlPointOrigin := cpCubic;
  374. end;
  375. procedure TCustomPath.CurveTo(const X1, Y1, X2, Y2, X, Y: TFloat);
  376. begin
  377. CurveTo(FloatPoint(X1, Y1), FloatPoint(X2, Y2), FloatPoint(X, Y));
  378. end;
  379. procedure TCustomPath.CurveTo(const X2, Y2, X, Y: TFloat);
  380. begin
  381. CurveTo(FloatPoint(X2, Y2), FloatPoint(X, Y));
  382. end;
  383. procedure TCustomPath.CurveTo(const C2, P: TFloatPoint);
  384. var
  385. C1: TFloatPoint;
  386. begin
  387. if FControlPointOrigin = cpCubic then
  388. begin
  389. C1.X := FCurrentPoint.X - (FLastControlPoint.X - FCurrentPoint.X);
  390. C1.Y := FCurrentPoint.Y - (FLastControlPoint.Y - FCurrentPoint.Y);
  391. end
  392. else
  393. C1 := FCurrentPoint;
  394. CurveTo(C1, C2, P);
  395. end;
  396. procedure TCustomPath.CurveToRelative(const X1, Y1, X2, Y2, X, Y: TFloat);
  397. begin
  398. CurveTo(FloatPoint(FCurrentPoint.X + X1, FCurrentPoint.Y + Y1),
  399. FloatPoint(FCurrentPoint.X + X2, FCurrentPoint.Y + Y2),
  400. FloatPoint(FCurrentPoint.X + X, FCurrentPoint.Y + Y));
  401. end;
  402. procedure TCustomPath.CurveToRelative(const X2, Y2, X, Y: TFloat);
  403. begin
  404. CurveTo(FloatPoint(FCurrentPoint.X + X2, FCurrentPoint.Y + Y2), FloatPoint(FCurrentPoint.X + X, FCurrentPoint.Y + Y));
  405. end;
  406. procedure TCustomPath.CurveToRelative(const C1, C2, P: TFloatPoint);
  407. begin
  408. CurveTo(OffsetPoint(C1, FCurrentPoint), OffsetPoint(C2, FCurrentPoint), OffsetPoint(P, FCurrentPoint));
  409. end;
  410. procedure TCustomPath.CurveToRelative(const C2, P: TFloatPoint);
  411. begin
  412. CurveTo(OffsetPoint(C2, FCurrentPoint), OffsetPoint(P, FCurrentPoint));
  413. end;
  414. procedure TCustomPath.Ellipse(const Cx, Cy, Rx, Ry: TFloat; Steps: Integer);
  415. begin
  416. Polygon(GR32_VectorUtils.Ellipse(Cx, Cy, Rx, Ry, Steps));
  417. end;
  418. procedure TCustomPath.Ellipse(Rx, Ry: TFloat; Steps: Integer);
  419. begin
  420. with FCurrentPoint do Ellipse(X, Y, Rx, Ry);
  421. end;
  422. procedure TCustomPath.EndPath(Close: boolean = False);
  423. begin
  424. end;
  425. procedure TCustomPath.LineTo(const P: TFloatPoint);
  426. begin
  427. AddPoint(P);
  428. FCurrentPoint := P;
  429. FControlPointOrigin := cpNone;
  430. end;
  431. procedure TCustomPath.HorizontalLineTo(const X: TFloat);
  432. begin
  433. LineTo(FloatPoint(X, FCurrentPoint.Y));
  434. end;
  435. procedure TCustomPath.HorizontalLineToRelative(const X: TFloat);
  436. begin
  437. LineTo(FloatPoint(FCurrentPoint.X + X, FCurrentPoint.Y));
  438. end;
  439. procedure TCustomPath.LineTo(const X, Y: TFloat);
  440. begin
  441. LineTo(FloatPoint(X, Y));
  442. end;
  443. procedure TCustomPath.LineToRelative(const X, Y: TFloat);
  444. begin
  445. LineTo(FloatPoint(FCurrentPoint.X + X, FCurrentPoint.Y + Y));
  446. end;
  447. procedure TCustomPath.LineToRelative(const P: TFloatPoint);
  448. begin
  449. LineTo(FloatPoint(FCurrentPoint.X + P.X, FCurrentPoint.Y + P.Y));
  450. end;
  451. procedure TCustomPath.MoveTo(const X, Y: TFloat);
  452. begin
  453. MoveTo(FloatPoint(X, Y));
  454. end;
  455. procedure TCustomPath.MoveToRelative(const X, Y: TFloat);
  456. begin
  457. MoveTo(FloatPoint(FCurrentPoint.X + X, FCurrentPoint.Y + Y));
  458. end;
  459. procedure TCustomPath.MoveToRelative(const P: TFloatPoint);
  460. begin
  461. MoveTo(FloatPoint(FCurrentPoint.X + P.X, FCurrentPoint.Y + P.Y));
  462. end;
  463. procedure TCustomPath.Rectangle(const Rect: TFloatRect);
  464. begin
  465. Polygon(GR32_VectorUtils.Rectangle(Rect));
  466. end;
  467. procedure TCustomPath.RoundRect(const Rect: TFloatRect; const Radius: TFloat);
  468. begin
  469. Polygon(GR32_VectorUtils.RoundRect(Rect, Radius));
  470. end;
  471. procedure TCustomPath.VerticalLineTo(const Y: TFloat);
  472. begin
  473. LineTo(FloatPoint(FCurrentPoint.X, Y));
  474. end;
  475. procedure TCustomPath.VerticalLineToRelative(const Y: TFloat);
  476. begin
  477. LineTo(FloatPoint(FCurrentPoint.X, FCurrentPoint.Y + Y));
  478. end;
  479. procedure TCustomPath.Polygon(const APoints: TArrayOfFloatPoint);
  480. begin
  481. if (Length(APoints) = 0) then
  482. Exit;
  483. BeginUpdate;
  484. MoveTo(APoints[0]); // Implicitly ends any current path
  485. PolyLine(APoints);
  486. EndPath(True);
  487. EndUpdate;
  488. end;
  489. procedure TCustomPath.PolyPolygon(const APoints: TArrayOfArrayOfFloatPoint);
  490. var
  491. i: Integer;
  492. begin
  493. if Length(APoints) = 0 then
  494. Exit;
  495. BeginUpdate;
  496. for i := 0 to High(APoints) do
  497. Polygon(APoints[i]);
  498. EndUpdate;
  499. end;
  500. procedure TCustomPath.PolyLine(const APoints: TArrayOfFloatPoint);
  501. var
  502. i: Integer;
  503. begin
  504. if Length(APoints) = 0 then
  505. Exit;
  506. BeginUpdate;
  507. for i := 0 to High(APoints) do
  508. LineTo(APoints[i]);
  509. EndUpdate;
  510. end;
  511. procedure TCustomPath.PolyPolyline(const APoints: TArrayOfArrayOfFloatPoint);
  512. var
  513. i: Integer;
  514. begin
  515. if Length(APoints) = 0 then
  516. Exit;
  517. BeginUpdate;
  518. for i := 0 to High(APoints) do
  519. begin
  520. if (i > 0) then
  521. EndPath;
  522. Polyline(APoints[i]);
  523. end;
  524. EndUpdate;
  525. end;
  526. procedure TCustomPath.MoveTo(const P: TFloatPoint);
  527. begin
  528. FCurrentPoint := P;
  529. FControlPointOrigin := cpNone;
  530. end;
  531. { TFlattenedPath }
  532. procedure TFlattenedPath.EndPath(Close: boolean = False);
  533. var
  534. n: Integer;
  535. begin
  536. if (FPointIndex = 0) then
  537. exit;
  538. if (Close) then
  539. begin
  540. AddPoint(FPoints[0]);
  541. Inc(FClosedCount);
  542. CurrentPoint := FPoints[0];
  543. end;
  544. // Grow path list
  545. n := Length(FPath);
  546. SetLength(FPath, n + 1);
  547. SetLength(FClosed, n + 1);
  548. // Save vertex buffer in path list
  549. FPath[n] := Copy(FPoints, 0, FPointIndex);
  550. FClosed[n] := Close;
  551. ClearPoints;
  552. DoEndPath;
  553. end;
  554. procedure TFlattenedPath.Clear;
  555. begin
  556. inherited;
  557. // Clear path list
  558. FPath := nil;
  559. FClosed := nil;
  560. FClosedCount := 0;
  561. // ...and vertex buffer
  562. ClearPoints;
  563. end;
  564. procedure TFlattenedPath.ClearPoints;
  565. begin
  566. // Reset vertex counter...
  567. FPointIndex := 0;
  568. // ...but try to be clever about buffer size to minimize
  569. // reallocation and memory waste
  570. if (Length(FPoints) > VertexBufferSizeLow) then
  571. SetLength(FPoints, VertexBufferSizeLow);
  572. // FPoints := nil;
  573. end;
  574. procedure TFlattenedPath.DoBeginPath;
  575. begin
  576. EndPath; //implicitly finish a prior path
  577. if (Assigned(FOnBeginPath)) then
  578. FOnBeginPath(Self);
  579. end;
  580. procedure TFlattenedPath.DoEndPath;
  581. begin
  582. if (Assigned(FOnEndPath)) then
  583. FOnEndPath(Self);
  584. Changed;
  585. end;
  586. procedure TFlattenedPath.MoveTo(const P: TFloatPoint);
  587. begin
  588. EndPath;
  589. inherited;
  590. AddPoint(P);
  591. end;
  592. procedure TFlattenedPath.AddPoint(const Point: TFloatPoint);
  593. var
  594. n: Integer;
  595. begin
  596. if (FPointIndex = 0) then
  597. DoBeginPath;
  598. // Grow buffer if required
  599. n := Length(FPoints);
  600. if (FPointIndex >= n) then
  601. SetLength(FPoints, n + VertexBufferSizeGrow);
  602. // Add vertex to buffer
  603. FPoints[FPointIndex] := Point;
  604. Inc(FPointIndex);
  605. end;
  606. procedure TFlattenedPath.AssignTo(Dest: TPersistent);
  607. var
  608. i: Integer;
  609. begin
  610. if (Dest is TFlattenedPath) then
  611. begin
  612. TFlattenedPath(Dest).BeginUpdate;
  613. try
  614. inherited;
  615. TFlattenedPath(Dest).DoBeginPath;
  616. SetLength(TFlattenedPath(Dest).FPath, Length(FPath));
  617. for i := 0 to High(FPath) do
  618. begin
  619. SetLength(TFlattenedPath(Dest).FPath[i], Length(FPath[i]));
  620. Move(FPath[i, 0], TFlattenedPath(Dest).FPath[i, 0], Length(FPath[i]) * SizeOf(TFloatPoint));
  621. end;
  622. TFlattenedPath(Dest).FClosed := FClosed;
  623. TFlattenedPath(Dest).FClosedCount := FClosedCount;
  624. TFlattenedPath(Dest).DoEndPath;
  625. TFlattenedPath(Dest).Changed;
  626. finally
  627. TFlattenedPath(Dest).EndUpdate;
  628. end;
  629. end else
  630. inherited;
  631. end;
  632. function TFlattenedPath.GetPoints: TArrayOfFloatPoint;
  633. begin
  634. Result := Copy(FPoints, 0, FPointIndex);
  635. end;
  636. { TCustomCanvas }
  637. procedure TCustomCanvas.AssignTo(Dest: TPersistent);
  638. begin
  639. if (Dest is TCustomCanvas) then
  640. begin
  641. TCustomCanvas(Dest).BeginUpdate;
  642. inherited;
  643. TCustomCanvas(Dest).Transformation := FTransformation;
  644. TCustomCanvas(Dest).EndUpdate;
  645. end else
  646. inherited;
  647. end;
  648. procedure TCustomCanvas.DoChanged;
  649. begin
  650. inherited;
  651. DrawPath(Self);
  652. Clear;
  653. end;
  654. function TCustomCanvas.Path: TFlattenedPath;
  655. begin
  656. Result := Self;
  657. end;
  658. procedure TCustomCanvas.SetTransformation(const Value: TTransformation);
  659. begin
  660. if FTransformation <> Value then
  661. begin
  662. FTransformation := Value;
  663. Changed;
  664. end;
  665. end;
  666. { TCanvas32 }
  667. procedure TCanvas32.AssignTo(Dest: TPersistent);
  668. begin
  669. if (Dest is TCanvas32) then
  670. begin
  671. TCanvas32(Dest).BeginUpdate;
  672. inherited;
  673. TCanvas32(Dest).FBitmap := FBitmap; // TODO : Shouldn't this be .FBitmap.Assign(FBitmap)?
  674. TCanvas32(Dest).FRenderer.Assign(FRenderer);
  675. TCanvas32(Dest).FBrushes.Assign(FBrushes);
  676. TCanvas32(Dest).Changed;
  677. TCanvas32(Dest).EndUpdate;
  678. end else
  679. inherited;
  680. end;
  681. procedure TCanvas32.BrushCollectionChangeHandler(Sender: TObject);
  682. begin
  683. Changed;
  684. end;
  685. constructor TCanvas32.Create(ABitmap: TBitmap32);
  686. begin
  687. if (ABitmap = nil) then
  688. raise Exception.Create('Bitmap parameter required');
  689. inherited Create;
  690. FBitmap := ABitmap;
  691. FRenderer := GetPolygonRendererClass.Create;
  692. // No need to set Bitmap here. It's done in DrawPath()
  693. // FRenderer.Bitmap := ABitmap;
  694. FBrushes := TBrushCollection.Create(Self);
  695. FBrushes.OnChange := BrushCollectionChangeHandler;
  696. end;
  697. destructor TCanvas32.Destroy;
  698. begin
  699. FBrushes.Free;
  700. FRenderer.Free;
  701. inherited;
  702. end;
  703. procedure TCanvas32.DrawPath(const Path: TFlattenedPath);
  704. var
  705. ClipRect: TFloatRect;
  706. i: Integer;
  707. begin
  708. if (Length(Path.Path) = 0) then
  709. exit;
  710. ClipRect := FloatRect(Bitmap.ClipRect);
  711. Renderer.Bitmap := Bitmap;
  712. // Simple case: All paths are closed or all paths are open
  713. if (Path.ClosedCount = 0) or (Path.ClosedCount = Length(Path.Path)) then
  714. begin
  715. for i := 0 to FBrushes.Count-1 do
  716. if FBrushes[i].Visible then
  717. FBrushes[i].PolyPolygonFS(Renderer, Path.Path, ClipRect, Transformation, (Path.ClosedCount > 0));
  718. end else
  719. // Not so simple case: Some paths are closed, some are open
  720. begin
  721. for i := 0 to FBrushes.Count-1 do
  722. FBrushes[i].PolyPolygonFS(Renderer, Path.Path, ClipRect, Transformation, Path.PathClosed);
  723. end;
  724. end;
  725. class function TCanvas32.GetPolygonRendererClass: TPolygonRenderer32Class;
  726. begin
  727. Result := DefaultPolygonRendererClass;
  728. end;
  729. function TCanvas32.GetRendererClassName: string;
  730. begin
  731. Result := FRenderer.ClassName;
  732. end;
  733. function TCanvas32.MeasureText(const DstRect: TFloatRect; const Text: string; Flags: Cardinal): TFloatRect;
  734. var
  735. TextToPath: ITextToPathSupport;
  736. begin
  737. if (not Supports(Bitmap.Backend, ITextToPathSupport, TextToPath)) then
  738. raise Exception.Create(RCStrInpropriateBackend);
  739. Result := TextToPath.MeasureText(DstRect, Text, Flags);
  740. end;
  741. procedure TCanvas32.RenderText(const DstRect: TFloatRect; const Text: string; Flags: Cardinal);
  742. var
  743. TextToPath: ITextToPathSupport;
  744. begin
  745. if (not Supports(Bitmap.Backend, ITextToPathSupport, TextToPath)) then
  746. raise Exception.Create(RCStrInpropriateBackend);
  747. TextToPath.TextToPath(Self, DstRect, Text, Flags);
  748. end;
  749. procedure TCanvas32.RenderText(X, Y: TFloat; const Text: string);
  750. var
  751. TextToPath: ITextToPathSupport;
  752. begin
  753. if (not Supports(Bitmap.Backend, ITextToPathSupport, TextToPath)) then
  754. raise Exception.Create(RCStrInpropriateBackend);
  755. TextToPath.TextToPath(Self, X, Y, Text);
  756. end;
  757. procedure TCanvas32.SetRenderer(ARenderer: TPolygonRenderer32);
  758. begin
  759. if (ARenderer <> nil) and (FRenderer <> ARenderer) then
  760. begin
  761. if (FRenderer <> nil) then
  762. FRenderer.Free;
  763. FRenderer := ARenderer;
  764. Changed;
  765. end;
  766. end;
  767. procedure TCanvas32.SetRendererClassName(const Value: string);
  768. var
  769. RendererClass: TPolygonRenderer32Class;
  770. begin
  771. if (Value <> '') and (FRenderer.ClassName <> Value) and (PolygonRendererList <> nil) then
  772. begin
  773. RendererClass := TPolygonRenderer32Class(PolygonRendererList.Find(Value));
  774. if (RendererClass <> nil) then
  775. Renderer := RendererClass.Create;
  776. end;
  777. end;
  778. end.