GR32_Paths.pas 25 KB

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