source: trunk/Packages/Graphics32/GR32_Paths.pas

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