source: trunk/Packages/bgrabitmap/bgracanvas2d.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 78.6 KB
Line 
1unit BGRACanvas2D;
2
3{ To do :
4
5 draw text with a different precision if the matrix is scaled
6 drawImage(in image, in double sx, in double sy, in double sw, in double sh, in double dx, in double dy, in double dw, in double dh)
7 -> using FillPoly with texture coordinates
8 linear gradient any transformation
9 clearPath clipping
10 createRadialGradient
11 globalCompositeOperation
12 image data functions
13}
14
15{$mode objfpc}{$H+}
16
17interface
18
19uses
20 Classes, SysUtils, BGRAGraphics, BGRABitmapTypes, BGRATransform,
21 BGRAGradientScanner, BGRAPath, BGRAPen;
22
23type
24 IBGRACanvasTextureProvider2D = interface
25 function getTexture: IBGRAScanner;
26 property texture: IBGRAScanner read GetTexture;
27 end;
28
29 IBGRACanvasGradient2D = interface(IBGRACanvasTextureProvider2D)
30 procedure addColorStop(APosition: single; AColor: TBGRAPixel);
31 procedure addColorStop(APosition: single; AColor: TColor);
32 procedure addColorStop(APosition: single; AColor: string);
33 procedure setColors(ACustomGradient: TBGRACustomGradient);
34 function GetGammaCorrection: boolean;
35 procedure SetGammaCorrection(AValue: boolean);
36 property gammaCorrection: boolean read GetGammaCorrection write SetGammaCorrection;
37 end;
38
39 { TBGRACanvasTextureProvider2D }
40
41 TBGRACanvasTextureProvider2D = class(TInterfacedObject,IBGRACanvasTextureProvider2D)
42 function getTexture: IBGRAScanner; virtual; abstract;
43 end;
44
45 { TBGRACanvasState2D }
46
47 TBGRACanvasState2D = class
48 private
49 FClipMask: TBGRACustomBitmap;
50 FClipMaskOwned: boolean;
51 function GetClipMaskReadWrite: TBGRACustomBitmap;
52 public
53 strokeColor: TBGRAPixel;
54 strokeTextureProvider: IBGRACanvasTextureProvider2D;
55 fillColor: TBGRAPixel;
56 fillMode: TFillMode;
57 fillTextureProvider: IBGRACanvasTextureProvider2D;
58 globalAlpha: byte;
59
60 fontName: string;
61 fontStyle: TFontStyles;
62 fontEmHeight: single;
63 textAlign: TAlignment;
64 textBaseline: string;
65
66 lineWidth: single;
67 penStroker: TBGRAPenStroker;
68
69 shadowOffsetX,shadowOffsetY,shadowBlur: single;
70 shadowColor: TBGRAPixel;
71 shadowFastest: boolean;
72
73 matrix: TAffineMatrix;
74 constructor Create(AMatrix: TAffineMatrix; AClipMask: TBGRACustomBitmap; AClipMaskOwned: boolean);
75 function Duplicate: TBGRACanvasState2D;
76 destructor Destroy; override;
77 procedure SetClipMask(AClipMask: TBGRACustomBitmap; AOwned: boolean);
78 property clipMaskReadOnly: TBGRACustomBitmap read FClipMask;
79 property clipMaskReadWrite: TBGRACustomBitmap read GetClipMaskReadWrite;
80 end;
81
82 TCanvas2dTextSize = record
83 width,height: single;
84 end;
85
86 { TBGRACanvas2D }
87
88 TBGRACanvas2D = class(IBGRAPath)
89 private
90 FSurface: TBGRACustomBitmap;
91 StateStack: TList;
92 currentState: TBGRACanvasState2D;
93 FCanvasOffset: TPointF;
94 FPixelCenteredCoordinates: boolean;
95 FPathPoints: array of TPointF;
96 FPathPointCount: integer;
97 FTextPaths: array of record
98 Text: string;
99 FontName: string;
100 FontMatrix: TAffineMatrix;
101 FontAlign: TAlignment;
102 FontAnchor: TFontVerticalAnchor;
103 FontStyle: TFontStyles;
104 end;
105 FFontRenderer: TBGRACustomFontRenderer;
106 FLastCoord, FStartCoord: TPointF;
107 function GetCurrentPathAsPoints: ArrayOfTPointF;
108 function GetFontName: string;
109 function GetFontRenderer: TBGRACustomFontRenderer;
110 function GetFontEmHeight: single;
111 function GetFontString: string;
112 function GetFontStyle: TFontStyles;
113 function GetGlobalAlpha: single;
114 function GetHasShadow: boolean;
115 function GetHeight: Integer;
116 function GetLineCap: string;
117 function GetLineCapLCL: TPenEndCap;
118 function GetlineJoin: string;
119 function GetlineJoinLCL: TPenJoinStyle;
120 function GetLineWidth: single;
121 function GetMatrix: TAffineMatrix;
122 function GetMiterLimit: single;
123 function GetPixelCenteredCoordinates: boolean;
124 function GetShadowBlur: single;
125 function GetShadowFastest: boolean;
126 function GetShadowOffset: TPointF;
127 function GetShadowOffsetX: single;
128 function GetShadowOffsetY: single;
129 function GetStrokeMatrix: TAffineMatrix;
130 function GetTextAlign: string;
131 function GetTextAlignLCL: TAlignment;
132 function GetTextBaseline: string;
133 function GetFillMode: TFillMode;
134 function GetWidth: Integer;
135 procedure SetFontName(AValue: string);
136 procedure SetFontRenderer(AValue: TBGRACustomFontRenderer);
137 procedure SetFontEmHeight(AValue: single);
138 procedure SetFontString(AValue: string);
139 procedure SetFontStyle(AValue: TFontStyles);
140 procedure SetGlobalAlpha(const AValue: single);
141 procedure SetLineCap(const AValue: string);
142 procedure SetLineCapLCL(AValue: TPenEndCap);
143 procedure SetLineJoin(const AValue: string);
144 procedure FillPoly(const points: array of TPointF);
145 procedure FillStrokePoly(const points: array of TPointF; fillOver: boolean);
146 procedure FillTexts(AErase: boolean);
147 procedure SetLineJoinLCL(AValue: TPenJoinStyle);
148 procedure SetLineWidth(const AValue: single);
149 procedure SetMatrix(AValue: TAffineMatrix);
150 procedure SetMiterLimit(const AValue: single);
151 procedure SetPixelCenteredCoordinates(const AValue: boolean);
152 procedure SetShadowBlur(const AValue: single);
153 procedure SetShadowFastest(AValue: boolean);
154 procedure SetShadowOffset(const AValue: TPointF);
155 procedure SetShadowOffsetX(const AValue: single);
156 procedure SetShadowOffsetY(const AValue: single);
157 procedure SetStrokeMatrix(AValue: TAffineMatrix);
158 procedure SetTextAlign(AValue: string);
159 procedure SetTextAlignLCL(AValue: TAlignment);
160 procedure SetTextBaseine(AValue: string);
161 procedure SetFillMode(mode: TFillMode);
162 procedure StrokePoly(const points: array of TPointF);
163 procedure DrawShadow(const points, points2: array of TPointF; AFillMode: TFillMode = fmWinding);
164 procedure DrawShadowMask(X,Y: integer; AMask: TBGRACustomBitmap; AMaskOwned: boolean);
165 procedure ClearPoly(const points: array of TPointF);
166 function ApplyTransform(const points: array of TPointF; matrix: TAffineMatrix): ArrayOfTPointF; overload;
167 function ApplyTransform(const points: array of TPointF): ArrayOfTPointF; overload;
168 function ApplyTransform(point: TPointF): TPointF; overload;
169 function GetPenPos(defaultX, defaultY: single): TPointF;
170 function GetPenPos(defaultPt: TPointF): TPointF;
171 procedure AddPoint(point: TPointF);
172 procedure AddPoints(const points: array of TPointF);
173 procedure AddPointsRev(const points: array of TPointF);
174 function ApplyGlobalAlpha(color: TBGRAPixel): TBGRAPixel;
175 function GetDrawMode: TDrawMode;
176 procedure copyTo({%H-}dest: IBGRAPath); //IBGRAPath
177 function getPoints: ArrayOfTPointF; //IBGRAPath
178 function getPoints(AMatrix: TAffineMatrix): ArrayOfTPointF; //IBGRAPath
179 function getCursor: TBGRACustomPathCursor; //IBGRAPath
180 public
181 antialiasing, linearBlend, gradientGammaCorrection: boolean;
182 constructor Create(ASurface: TBGRACustomBitmap);
183 destructor Destroy; override;
184
185 function toDataURL(mimeType: string = 'image/png'): string;
186
187 procedure save;
188 procedure restore;
189 procedure scale(x,y: single); overload;
190 procedure scale(factor: single); overload;
191 procedure rotate(angleRadCW: single);
192 procedure translate(x,y: single);
193 procedure skewx(angleRadCW: single);
194 procedure skewy(angleRadCW: single);
195 procedure transform(m11,m21, m12,m22, m13,m23: single); overload;
196 procedure transform(AMatrix: TAffineMatrix); overload;
197 procedure setTransform(m11,m21, m12,m22, m13,m23: single);
198 procedure resetTransform;
199
200 procedure strokeScale(x,y: single);
201 procedure strokeSkewx(angleRadCW: single);
202 procedure strokeSkewy(angleRadCW: single);
203 procedure strokeResetTransform;
204
205 procedure strokeStyle(color: TBGRAPixel); overload;
206 procedure strokeStyle(color: TColor); overload;
207 procedure strokeStyle(color: string); overload;
208 procedure strokeStyle(texture: IBGRAScanner); overload;
209 procedure strokeStyle(provider: IBGRACanvasTextureProvider2D); overload;
210 procedure fillStyle(color: TBGRAPixel); overload;
211 procedure fillStyle(color: TColor); overload;
212 procedure fillStyle(color: string); overload;
213 procedure fillStyle(texture: IBGRAScanner); overload;
214 procedure fillStyle(provider: IBGRACanvasTextureProvider2D); overload;
215 procedure shadowColor(color: TBGRAPixel); overload;
216 procedure shadowColor(color: TColor); overload;
217 procedure shadowColor(color: string); overload;
218 procedure shadowNone;
219 function getShadowColor: TBGRAPixel;
220
221 function createLinearGradient(x0,y0,x1,y1: single): IBGRACanvasGradient2D; overload;
222 function createLinearGradient(p0,p1: TPointF): IBGRACanvasGradient2D; overload;
223 function createLinearGradient(x0,y0,x1,y1: single; Colors: TBGRACustomGradient): IBGRACanvasGradient2D; overload;
224 function createLinearGradient(p0,p1: TPointF; Colors: TBGRACustomGradient): IBGRACanvasGradient2D; overload;
225
226 function createRadialGradient(x0,y0,r0,x1,y1,r1: single; flipGradient: boolean=false): IBGRACanvasGradient2D; overload;
227 function createRadialGradient(p0: TPointF; r0: single; p1: TPointF; r1: single; flipGradient: boolean=false): IBGRACanvasGradient2D; overload;
228 function createRadialGradient(x0,y0,r0,x1,y1,r1: single; Colors: TBGRACustomGradient; flipGradient: boolean=false): IBGRACanvasGradient2D; overload;
229 function createRadialGradient(p0: TPointF; r0: single; p1: TPointF; r1: single; Colors: TBGRACustomGradient; flipGradient: boolean=false): IBGRACanvasGradient2D; overload;
230
231 function createPattern(image: TBGRACustomBitmap; repetition: string): IBGRACanvasTextureProvider2D; overload;
232 function createPattern(texture: IBGRAScanner): IBGRACanvasTextureProvider2D; overload;
233
234 procedure fillRect(x,y,w,h: single);
235 procedure strokeRect(x,y,w,h: single);
236 procedure clearRect(x,y,w,h: single);
237
238 procedure addPath(APath: IBGRAPath); overload;
239 procedure addPath(ASvgPath: string); overload;
240 procedure path(APath: IBGRAPath); overload;
241 procedure path(ASvgPath: string); overload;
242 procedure beginPath;
243 procedure closePath;
244 procedure toSpline(closed: boolean; style: TSplineStyle= ssOutside);
245 procedure moveTo(x,y: single); overload;
246 procedure lineTo(x,y: single); overload;
247 procedure moveTo(constref pt: TPointF); overload;
248 procedure lineTo(constref pt: TPointF); overload;
249 procedure polylineTo(const pts: array of TPointF);
250 procedure quadraticCurveTo(cpx,cpy,x,y: single); overload;
251 procedure quadraticCurveTo(constref cp,pt: TPointF); overload;
252 procedure bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y: single); overload;
253 procedure bezierCurveTo(constref cp1,cp2,pt: TPointF); overload;
254 procedure rect(x,y,w,h: single);
255 procedure roundRect(x,y,w,h,radius: single); overload;
256 procedure roundRect(x,y,w,h,rx,ry: single); overload;
257 procedure openedSpline(const pts: array of TPointF; style: TSplineStyle);
258 procedure closedSpline(const pts: array of TPointF; style: TSplineStyle);
259 procedure spline(const pts: array of TPointF; style: TSplineStyle= ssOutside);
260 procedure splineTo(const pts: array of TPointF; style: TSplineStyle= ssOutside);
261 procedure arc(x, y, radius, startAngleRadCW, endAngleRadCW: single; anticlockwise: boolean); overload;
262 procedure arc(x, y, radius, startAngleRadCW, endAngleRadCW: single); overload;
263 procedure arc(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single; anticlockwise: boolean); overload;
264 procedure arc(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single); overload;
265 procedure arc(constref arcDef: TArcDef); overload;
266 procedure arcTo(x1, y1, x2, y2, radius: single); overload;
267 procedure arcTo(p1,p2: TPointF; radius: single); overload;
268 procedure arcTo(rx, ry, xAngleRadCW: single; largeArc,anticlockwise: boolean; x, y: single);
269 procedure circle(x,y,r: single);
270 procedure ellipse(x,y,rx,ry: single);
271 procedure text(AText: string; x,y: single);
272 procedure fillText(AText: string; x,y: single);
273 procedure strokeText(AText: string; x,y: single);
274 function measureText(AText: string): TCanvas2dTextSize;
275
276 procedure fill;
277 procedure stroke;
278 procedure fillOverStroke;
279 procedure strokeOverFill;
280 procedure clearPath;
281 procedure clip;
282 procedure unclip;
283 function isPointInPath(x,y: single): boolean; overload;
284 function isPointInPath(pt: TPointF): boolean; overload;
285
286 procedure drawImage(image: TBGRACustomBitmap; dx,dy: single); overload;
287 procedure drawImage(image: TBGRACustomBitmap; dx,dy,dw,dh: single); overload;
288
289 function getLineStyle: TBGRAPenStyle;
290 procedure lineStyle(const AValue: array of single); overload;
291 procedure lineStyle(AStyle: TPenStyle); overload;
292
293 property surface: TBGRACustomBitmap read FSurface;
294 property width: Integer read GetWidth;
295 property height: Integer read GetHeight;
296 property pixelCenteredCoordinates: boolean read GetPixelCenteredCoordinates write SetPixelCenteredCoordinates;
297 property globalAlpha: single read GetGlobalAlpha write SetGlobalAlpha;
298 property matrix: TAffineMatrix read GetMatrix write SetMatrix;
299 property strokeMatrix: TAffineMatrix read GetStrokeMatrix write SetStrokeMatrix;
300
301 property lineWidth: single read GetLineWidth write SetLineWidth;
302 property lineCap: string read GetLineCap write SetLineCap;
303 property lineCapLCL: TPenEndCap read GetLineCapLCL write SetLineCapLCL;
304 property lineJoin: string read GetlineJoin write SetLineJoin;
305 property lineJoinLCL: TPenJoinStyle read GetlineJoinLCL write SetLineJoinLCL;
306 property miterLimit: single read GetMiterLimit write SetMiterLimit;
307
308 property shadowOffsetX: single read GetShadowOffsetX write SetShadowOffsetX;
309 property shadowOffsetY: single read GetShadowOffsetY write SetShadowOffsetY;
310 property shadowOffset: TPointF read GetShadowOffset write SetShadowOffset;
311 property shadowBlur: single read GetShadowBlur write SetShadowBlur;
312 property shadowFastest: boolean read GetShadowFastest write SetShadowFastest;
313 property hasShadow: boolean read GetHasShadow;
314
315 property fontName: string read GetFontName write SetFontName;
316 property fontEmHeight: single read GetFontEmHeight write SetFontEmHeight;
317 property fontStyle: TFontStyles read GetFontStyle write SetFontStyle;
318 property font: string read GetFontString write SetFontString;
319 property textAlignLCL: TAlignment read GetTextAlignLCL write SetTextAlignLCL;
320 property textAlign: string read GetTextAlign write SetTextAlign;
321 property textBaseline: string read GetTextBaseline write SetTextBaseine;
322
323 property fillMode: TFillMode read GetFillMode write SetFillMode;
324
325 property currentPath: ArrayOfTPointF read GetCurrentPathAsPoints;
326 property fontRenderer: TBGRACustomFontRenderer read GetFontRenderer write SetFontRenderer;
327
328 protected
329 function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
330 function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
331 function _Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
332 end;
333
334implementation
335
336uses Types, Math, BGRAFillInfo, BGRAPolygon, BGRABlend, FPWriteJPEG, FPWriteBMP, base64;
337
338type
339 TColorStop = record
340 position: single;
341 color: TBGRAPixel;
342 end;
343
344 TGradientArrayOfColors = array of TBGRAPixel;
345 TGradientArrayOfPositions = array of single;
346
347 { TBGRACanvasGradient2D }
348
349 TBGRACanvasGradient2D = class(TBGRACanvasTextureProvider2D, IBGRACanvasGradient2D)
350 private
351 colorStops: array of TColorStop;
352 nbColorStops: integer;
353 FCustomGradient: TBGRACustomGradient;
354 FGammaCorrection: boolean;
355 protected
356 scanner: TBGRAGradientScanner;
357 procedure CreateScanner; virtual; abstract;
358 function getColorArray: TGradientArrayOfColors;
359 function getPositionArray: TGradientArrayOfPositions;
360 procedure GetBGRAGradient(out ABGRAGradient: TBGRACustomGradient; out AOwned: boolean);
361 function GetGammaCorrection: boolean;
362 procedure SetGammaCorrection(AValue: boolean);
363 public
364 constructor Create;
365 function getTexture: IBGRAScanner; override;
366 destructor Destroy; override;
367 procedure addColorStop(APosition: single; AColor: TBGRAPixel);
368 procedure addColorStop(APosition: single; AColor: TColor);
369 procedure addColorStop(APosition: single; AColor: string);
370 procedure setColors(ACustomGradient: TBGRACustomGradient);
371 property texture: IBGRAScanner read GetTexture;
372 property colorStopCount: integer read nbColorStops;
373 property gammaCorrection: boolean read GetGammaCorrection write SetGammaCorrection;
374 end;
375
376 { TBGRACanvasLinearGradient2D }
377
378 TBGRACanvasLinearGradient2D = class(TBGRACanvasGradient2D)
379 protected
380 o1,o2: TPointF;
381 FTransform: TAffineMatrix;
382 procedure CreateScanner; override;
383 public
384 constructor Create(x0,y0,x1,y1: single; transform: TAffineMatrix);
385 constructor Create(p0,p1: TPointF; transform: TAffineMatrix);
386 end;
387
388 { TBGRACanvasRadialGradient2D }
389
390 TBGRACanvasRadialGradient2D = class(TBGRACanvasGradient2D)
391 protected
392 c0,c1: TPointF;
393 cr0,cr1: single;
394 FFlipGradient: boolean;
395 FTransform: TAffineMatrix;
396 procedure CreateScanner; override;
397 public
398 constructor Create(x0,y0,r0,x1,y1,r1: single; transform: TAffineMatrix; flipGradient: boolean=false);
399 constructor Create(p0: TPointF; r0: single; p1: TPointF; r1: single; transform: TAffineMatrix; flipGradient: boolean=false);
400 end;
401
402 { TBGRACanvasPattern2D }
403
404 TBGRACanvasPattern2D = class(TBGRACanvasTextureProvider2D)
405 protected
406 scanner: TBGRACustomScanner;
407 foreignInterface: IBGRAScanner;
408 ownScanner: boolean;
409 public
410 function getTexture: IBGRAScanner; override;
411 constructor Create(source: TBGRACustomBitmap; repeatX,repeatY: boolean; Origin, HAxis, VAxis: TPointF);
412 constructor Create(source: IBGRAScanner; transformation: TAffineMatrix);
413 destructor Destroy; override;
414 end;
415
416{ TBGRACanvasPattern2D }
417
418function TBGRACanvasPattern2D.GetTexture: IBGRAScanner;
419begin
420 if ownScanner then
421 result := scanner
422 else
423 result := foreignInterface;
424end;
425
426constructor TBGRACanvasPattern2D.Create(source: TBGRACustomBitmap; repeatX,
427 repeatY: boolean; Origin, HAxis, VAxis: TPointF);
428var
429 affine: TBGRAAffineBitmapTransform;
430begin
431 if (abs(Origin.X-round(Origin.X)) < 1e-6) and
432 (abs(Origin.Y-round(Origin.Y)) < 1e-6) and
433 (HAxis = Origin+PointF(1,0)) and
434 (VAxis = Origin+PointF(0,1)) then
435 begin
436 if (round(Origin.X)=0) and (round(Origin.Y)=0) and repeatX and repeatY then
437 begin
438 foreignInterface := source;
439 ownScanner:= false;
440 end else
441 begin
442 scanner := TBGRABitmapScanner.Create(source,repeatX,repeatY,Point(round(Origin.X),round(Origin.Y)));
443 ownScanner := true;
444 end;
445 end
446 else
447 begin
448 affine := TBGRAAffineBitmapTransform.Create(source,repeatX,repeatY);
449 affine.Fit(Origin,HAxis,VAxis);
450 scanner := affine;
451 ownScanner:= true;
452 end;
453end;
454
455constructor TBGRACanvasPattern2D.Create(source: IBGRAScanner;
456 transformation: TAffineMatrix);
457var
458 affine : TBGRAAffineScannerTransform;
459begin
460 if (abs(transformation[1,1]-1) < 1e-6) and
461 (abs(transformation[2,2]-1) < 1e-6) and
462 (abs(transformation[1,2]) < 1e-6) and
463 (abs(transformation[2,1]) < 1e-6) and
464 (abs(transformation[1,3]-round(transformation[1,3])) < 1e-6) and
465 (abs(transformation[2,3]-round(transformation[2,3])) < 1e-6) then
466 begin
467 if (abs(transformation[1,3]) < 1e-6) and
468 (abs(transformation[2,3]) < 1e-6) then
469 begin
470 foreignInterface := source;
471 ownScanner := false;
472 end else
473 begin
474 scanner := TBGRAScannerOffset.Create(source,Point(round(transformation[1,3]),round(transformation[2,3])));
475 ownScanner := true;
476 end;
477 end else
478 begin
479 affine := TBGRAAffineScannerTransform.Create(source);
480 affine.Matrix := transformation;
481 affine.Invert;
482 scanner := affine;
483 ownScanner:= true;
484 end;
485end;
486
487destructor TBGRACanvasPattern2D.Destroy;
488begin
489 fillchar(foreignInterface,sizeof(foreignInterface),0);
490 if ownScanner then FreeAndNil(scanner);
491 inherited Destroy;
492end;
493
494{ TBGRACanvasLinearGradient2D }
495
496procedure TBGRACanvasLinearGradient2D.CreateScanner;
497var GradientOwner: boolean;
498 GradientColors: TBGRACustomGradient;
499begin
500 GetBGRAGradient(GradientColors,GradientOwner);
501 scanner := TBGRAGradientScanner.Create(GradientColors,gtLinear,o1,o2,False,GradientOwner);
502 scanner.Transform := FTransform;
503end;
504
505constructor TBGRACanvasLinearGradient2D.Create(x0, y0, x1, y1: single; transform: TAffineMatrix);
506begin
507 o1 := PointF(x0,y0);
508 o2 := PointF(x1,y1);
509 FTransform := transform;
510end;
511
512constructor TBGRACanvasLinearGradient2D.Create(p0, p1: TPointF; transform: TAffineMatrix);
513begin
514 o1 := p0;
515 o2 := p1;
516 FTransform := transform;
517end;
518
519{ TBGRACanvasRadialGradient2D }
520
521procedure TBGRACanvasRadialGradient2D.CreateScanner;
522var GradientOwner: boolean;
523 GradientColors: TBGRACustomGradient;
524begin
525 GetBGRAGradient(GradientColors,GradientOwner);
526 scanner := TBGRAGradientScanner.Create(GradientColors,c0,cr0,c1,cr1,GradientOwner);
527 scanner.FlipGradient := not FFlipGradient;
528 scanner.Transform := FTransform;
529end;
530
531constructor TBGRACanvasRadialGradient2D.Create(x0, y0, r0, x1, y1, r1: single;
532 transform: TAffineMatrix; flipGradient: boolean);
533begin
534 self.c0 := PointF(x0,y0);
535 self.cr0 := r0;
536 self.c1 := PointF(x1,y1);
537 self.cr1 := r1;
538 FTransform := transform;
539 FFlipGradient := flipGradient;
540end;
541
542constructor TBGRACanvasRadialGradient2D.Create(p0: TPointF; r0: single;
543 p1: TPointF; r1: single; transform: TAffineMatrix; flipGradient: boolean);
544begin
545 self.c0 := p0;
546 self.cr0 := r0;
547 self.c1 := p1;
548 self.cr1 := r1;
549 FTransform := transform;
550 FFlipGradient := flipGradient;
551end;
552
553{ TBGRACanvasGradient2D }
554
555function TBGRACanvasGradient2D.getTexture: IBGRAScanner;
556begin
557 if scanner = nil then CreateScanner;
558 result := scanner;
559end;
560
561function TBGRACanvasGradient2D.GetGammaCorrection: boolean;
562begin
563 result := FGammaCorrection;
564end;
565
566procedure TBGRACanvasGradient2D.SetGammaCorrection(AValue: boolean);
567begin
568 FGammaCorrection:= AValue;
569 FreeAndNil(scanner);
570end;
571
572constructor TBGRACanvasGradient2D.Create;
573begin
574 inherited Create;
575 scanner := nil;
576 FGammaCorrection:= false;
577end;
578
579function TBGRACanvasGradient2D.getColorArray: TGradientArrayOfColors;
580var
581 i: Integer;
582begin
583 setlength(result, nbColorStops);
584 for i := 0 to nbColorStops-1 do
585 result[i] := colorStops[i].color;
586end;
587
588function TBGRACanvasGradient2D.getPositionArray: TGradientArrayOfPositions;
589var
590 i: Integer;
591begin
592 setlength(result, nbColorStops);
593 for i := 0 to nbColorStops-1 do
594 result[i] := colorStops[i].position;
595end;
596
597procedure TBGRACanvasGradient2D.GetBGRAGradient(out
598 ABGRAGradient: TBGRACustomGradient; out AOwned: boolean);
599begin
600 if FCustomGradient = nil then
601 begin
602 if (colorStopCount = 2) and (colorStops[0].position = 0) and (colorStops[1].position = 1) then
603 begin
604 if FGammaCorrection then
605 ABGRAGradient := TBGRASimpleGradientWithGammaCorrection.Create(colorStops[0].color, colorStops[1].color)
606 else
607 ABGRAGradient := TBGRASimpleGradientWithoutGammaCorrection.Create(colorStops[0].color, colorStops[1].color);
608 end
609 else
610 ABGRAGradient := TBGRAMultiGradient.Create(getColorArray,getPositionArray,FGammaCorrection,False);
611 AOwned := true;
612 end else
613 begin
614 ABGRAGradient := FCustomGradient;
615 AOwned := false;
616 end;
617end;
618
619destructor TBGRACanvasGradient2D.Destroy;
620begin
621 FreeAndNil(scanner);
622 inherited Destroy;
623end;
624
625procedure TBGRACanvasGradient2D.addColorStop(APosition: single;
626 AColor: TBGRAPixel);
627begin
628 FreeAndNil(scanner);
629 if nbColorStops = length(colorStops) then
630 setlength(colorStops, (length(colorStops)+1)*2);
631
632 with colorStops[nbColorStops] do
633 begin
634 position := APosition;
635 color := AColor;
636 end;
637 inc(nbColorStops);
638end;
639
640procedure TBGRACanvasGradient2D.addColorStop(APosition: single; AColor: TColor
641 );
642begin
643 addColorStop(APosition, ColorToBGRA(ColorToRGB(AColor)));
644end;
645
646procedure TBGRACanvasGradient2D.addColorStop(APosition: single; AColor: string
647 );
648begin
649 addColorStop(APosition, StrToBGRA(AColor));
650end;
651
652procedure TBGRACanvasGradient2D.setColors(ACustomGradient: TBGRACustomGradient
653 );
654begin
655 FCustomGradient := ACustomGradient;
656end;
657
658{ TBGRACanvasState2D }
659
660function TBGRACanvasState2D.GetClipMaskReadWrite: TBGRACustomBitmap;
661begin
662 if not FClipMaskOwned then
663 begin
664 if FClipMask <> nil then
665 FClipMask := FClipMask.Duplicate;
666 FClipMaskOwned := true;
667 end;
668 result := FClipMask;
669end;
670
671constructor TBGRACanvasState2D.Create(AMatrix: TAffineMatrix;
672 AClipMask: TBGRACustomBitmap; AClipMaskOwned: boolean);
673begin
674 strokeColor := BGRABlack;
675 fillColor := BGRABlack;
676 globalAlpha := 255;
677
678 fontName := 'Arial';
679 fontEmHeight := 10;
680 fontStyle := [];
681 textAlign:= taLeftJustify;
682 textBaseline := 'alphabetic';
683
684 lineWidth := 1;
685 penStroker := TBGRAPenStroker.Create;
686 penStroker.LineCap := pecFlat;
687 penStroker.JoinStyle := pjsMiter;
688 penStroker.CustomPenStyle := DuplicatePenStyle(SolidPenStyle);
689 penStroker.MiterLimit := 10;
690 penStroker.StrokeMatrix := AffineMatrixIdentity;
691
692 shadowOffsetX := 0;
693 shadowOffsetY := 0;
694 shadowBlur := 0;
695 shadowColor := BGRAPixelTransparent;
696 shadowFastest:= false;
697
698 matrix := AMatrix;
699 FClipMask := nil;
700 FClipMaskOwned := true;
701 SetClipMask(AClipMask,AClipMaskOwned);
702end;
703
704function TBGRACanvasState2D.Duplicate: TBGRACanvasState2D;
705begin
706 result := TBGRACanvasState2D.Create(matrix,clipMaskReadOnly,false);
707 result.strokeColor := strokeColor;
708 result.strokeTextureProvider := strokeTextureProvider;
709 result.fillColor := fillColor;
710 result.fillMode := fillMode;
711 result.fillTextureProvider := fillTextureProvider;
712 result.globalAlpha := globalAlpha;
713
714 result.fontName:= fontName;
715 result.fontEmHeight := fontEmHeight;
716 result.fontStyle := fontStyle;
717
718 result.lineWidth := lineWidth;
719 result.penStroker.LineCap := penStroker.LineCap;
720 result.penStroker.JoinStyle := penStroker.JoinStyle;
721 result.penStroker.CustomPenStyle := DuplicatePenStyle(penStroker.CustomPenStyle);
722 result.penStroker.MiterLimit := penStroker.MiterLimit;
723 result.penStroker.StrokeMatrix := penStroker.StrokeMatrix;
724
725 result.shadowOffsetX := shadowOffsetX;
726 result.shadowOffsetY := shadowOffsetY;
727 result.shadowBlur := shadowBlur;
728 result.shadowColor := shadowColor;
729 result.shadowFastest := shadowFastest;
730end;
731
732destructor TBGRACanvasState2D.Destroy;
733begin
734 if FClipMaskOwned and Assigned(FClipMask) then
735 FClipMask.Free;
736 penStroker.Free;
737 inherited Destroy;
738end;
739
740procedure TBGRACanvasState2D.SetClipMask(AClipMask: TBGRACustomBitmap;
741 AOwned: boolean);
742begin
743 if FClipMaskOwned and Assigned(FClipMask) then FreeAndNil(FClipMask);
744 FClipMask := AClipMask;
745 FClipMaskOwned := AOwned;
746end;
747
748{ TBGRACanvas2D }
749
750function TBGRACanvas2D.GetHeight: Integer;
751begin
752 if Assigned(surface) then
753 result := Surface.Height
754 else
755 result := 0;
756end;
757
758function TBGRACanvas2D.GetLineCap: string;
759begin
760 case currentState.penStroker.LineCap of
761 pecRound: result := 'round';
762 pecSquare: result := 'square';
763 else result := 'butt';
764 end;
765end;
766
767function TBGRACanvas2D.GetLineCapLCL: TPenEndCap;
768begin
769 result := currentState.penStroker.LineCap;
770end;
771
772function TBGRACanvas2D.GetlineJoin: string;
773begin
774 case currentState.penStroker.JoinStyle of
775 pjsBevel: result := 'bevel';
776 pjsRound: result := 'round';
777 else result := 'miter';
778 end;
779end;
780
781function TBGRACanvas2D.GetlineJoinLCL: TPenJoinStyle;
782begin
783 result := currentState.penStroker.JoinStyle;
784end;
785
786function TBGRACanvas2D.getLineStyle: TBGRAPenStyle;
787begin
788 result := DuplicatePenStyle(currentState.penStroker.CustomPenStyle);
789end;
790
791function TBGRACanvas2D.GetLineWidth: single;
792begin
793 result := currentState.lineWidth;
794end;
795
796function TBGRACanvas2D.GetMatrix: TAffineMatrix;
797begin
798 result := currentState.matrix;
799end;
800
801function TBGRACanvas2D.GetMiterLimit: single;
802begin
803 result := currentState.penStroker.MiterLimit;
804end;
805
806function TBGRACanvas2D.GetPixelCenteredCoordinates: boolean;
807begin
808 result := FPixelCenteredCoordinates;
809end;
810
811function TBGRACanvas2D.GetShadowBlur: single;
812begin
813 result := currentState.shadowBlur;
814end;
815
816function TBGRACanvas2D.GetShadowFastest: boolean;
817begin
818 result := currentState.shadowFastest;
819end;
820
821function TBGRACanvas2D.GetShadowOffset: TPointF;
822begin
823 result := PointF(shadowOffsetX,shadowOffsetY);
824end;
825
826function TBGRACanvas2D.GetShadowOffsetX: single;
827begin
828 result := currentState.shadowOffsetX;
829end;
830
831function TBGRACanvas2D.GetShadowOffsetY: single;
832begin
833 result := currentState.shadowOffsetY;
834end;
835
836function TBGRACanvas2D.GetStrokeMatrix: TAffineMatrix;
837begin
838 result := currentState.penStroker.StrokeMatrix;
839end;
840
841function TBGRACanvas2D.GetTextAlign: string;
842begin
843 case currentState.textAlign of
844 taRightJustify: result := 'right';
845 taCenter: result := 'center';
846 else
847 result := 'left';
848 end;
849end;
850
851function TBGRACanvas2D.GetTextAlignLCL: TAlignment;
852begin
853 result := currentState.textAlign;
854end;
855
856function TBGRACanvas2D.GetTextBaseline: string;
857begin
858 result := currentState.textBaseline;
859end;
860
861function TBGRACanvas2D.GetGlobalAlpha: single;
862begin
863 result := currentState.globalAlpha/255;
864end;
865
866function TBGRACanvas2D.GetCurrentPathAsPoints: ArrayOfTPointF;
867var i: integer;
868begin
869 setlength(result, FPathPointCount);
870 for i := 0 to high(result) do
871 result[i] := FPathPoints[i];
872end;
873
874function TBGRACanvas2D.GetFontName: string;
875begin
876 result := currentState.fontName;
877end;
878
879function TBGRACanvas2D.GetFontRenderer: TBGRACustomFontRenderer;
880var zoom1,zoom2,zoom: single;
881begin
882 if FFontRenderer = nil then
883 begin
884 if FSurface <> nil then
885 result := FSurface.FontRenderer
886 else
887 result := nil;
888 end else
889 result := FFontRenderer;
890 if Assigned(result) then
891 begin
892 result.FontName := currentState.fontName;
893 result.FontStyle := currentState.fontStyle;
894 if antialiasing then
895 result.FontQuality:= fqFineAntialiasing
896 else
897 result.FontQuality := fqSystem;
898 result.FontOrientation := 0;
899 zoom1 := VectLen(currentState.matrix[1,1],currentState.matrix[2,1]);
900 zoom2 := VectLen(currentState.matrix[1,2],currentState.matrix[2,2]);
901 if zoom1>zoom2 then zoom := zoom1 else zoom := zoom2;
902 result.FontEmHeight := round(currentState.fontEmHeight*zoom);
903 end;
904end;
905
906function TBGRACanvas2D.GetFontEmHeight: single;
907begin
908 result := currentState.fontEmHeight;
909end;
910
911function TBGRACanvas2D.GetFontString: string;
912var formats: TFormatSettings;
913begin
914 formats := DefaultFormatSettings;
915 formats.DecimalSeparator := '.';
916
917 result := '';
918 if fsItalic in currentState.fontStyle then
919 result := result+'italic ';
920 if fsBold in currentState.fontStyle then
921 result += 'bold ';
922 result += FloatToStrF(currentState.fontEmHeight,ffGeneral,6,0,formats)+'px ';
923 result += currentState.fontName;
924 result := trim(result);
925end;
926
927function TBGRACanvas2D.GetFontStyle: TFontStyles;
928begin
929 result := currentState.fontStyle;
930end;
931
932function TBGRACanvas2D.GetHasShadow: boolean;
933begin
934 result := (ApplyGlobalAlpha(currentState.shadowColor).alpha <> 0) and
935 ( (currentState.shadowBlur <> 0) or (currentState.shadowOffsetX <> 0)
936 or (currentState.shadowOffsetY <> 0) );
937end;
938
939function TBGRACanvas2D.GetWidth: Integer;
940begin
941 if Assigned(Surface) then
942 result := Surface.Width
943 else
944 result := 0;
945end;
946
947procedure TBGRACanvas2D.SetFontName(AValue: string);
948begin
949 currentState.fontName := AValue;
950end;
951
952procedure TBGRACanvas2D.SetFontRenderer(AValue: TBGRACustomFontRenderer);
953begin
954 if AValue = FFontRenderer then exit;
955 FreeAndNil(FFontRenderer);
956 FFontRenderer := AValue;
957end;
958
959procedure TBGRACanvas2D.SetFontEmHeight(AValue: single);
960begin
961 currentState.fontEmHeight := AValue;
962end;
963
964procedure TBGRACanvas2D.SetFontString(AValue: string);
965var idxSpace,errPos: integer;
966 attrib,u: string;
967 value: single;
968begin
969 currentState.fontStyle := [];
970 currentState.fontEmHeight := 10;
971 currentState.fontName := 'Arial';
972 AValue := trim(AValue);
973 while AValue <> '' do
974 begin
975 while (AValue <> '') and (AValue[1]in [#0..#32]) do delete(AValue,1,1);
976 idxSpace := pos(' ',AValue);
977 if idxSpace = 0 then
978 attrib := AValue
979 else
980 attrib := copy(AValue,1,idxSpace-1);
981 attrib := lowerCase(attrib);
982 if attrib = '' then break;
983 if (attrib = 'normal') or (attrib = 'small-caps') or (attrib = 'lighter') then
984 begin
985 //nothing
986 end else
987 if (attrib = 'italic') or (attrib = 'oblique') then
988 begin
989 currentState.fontStyle += [fsItalic];
990 end else
991 if (attrib = 'bold') or (attrib = 'bolder') then
992 begin
993 currentState.fontStyle += [fsBold];
994 end else
995 if (attrib[1] in ['.','0'..'9']) then
996 begin
997 u := '';
998 while (length(attrib)>0) and (attrib[length(attrib)] in['a'..'z']) do
999 begin
1000 u := attrib[length(attrib)]+u;
1001 delete(attrib,length(attrib),1);
1002 end;
1003 val(attrib,value,errPos);
1004 if errPos = 0 then
1005 begin
1006 if u = '' then //weight
1007 begin
1008 if value >= 600 then currentState.fontStyle += [fsBold];
1009 end else
1010 if u = 'px' then currentState.fontEmHeight := value else
1011 if u = 'pt' then currentState.fontEmHeight:= value/72*96 else
1012 if u = 'in' then currentState.fontEmHeight:= value*96 else
1013 if u = 'mm' then currentState.fontEmHeight:= value/25.4*96 else
1014 if u = 'cm' then currentState.fontEmHeight:= value/2.54*96;
1015 end;
1016 end else
1017 break;
1018 delete(AValue,1,length(attrib)+1);
1019 end;
1020 AValue := trim(AValue);
1021 if AValue <> '' then currentState.fontName := AValue;
1022end;
1023
1024procedure TBGRACanvas2D.SetFontStyle(AValue: TFontStyles);
1025begin
1026 currentState.fontStyle:= AValue;
1027end;
1028
1029procedure TBGRACanvas2D.SetGlobalAlpha(const AValue: single);
1030begin
1031 if AValue < 0 then currentState.globalAlpha:= 0 else
1032 if AValue > 1 then currentState.globalAlpha:= 255 else
1033 currentState.globalAlpha:= round(AValue*255);
1034end;
1035
1036procedure TBGRACanvas2D.SetLineCap(const AValue: string);
1037begin
1038 if CompareText(AValue,'round')=0 then
1039 currentState.penStroker.LineCap := pecRound else
1040 if CompareText(AValue,'square')=0 then
1041 currentState.penStroker.LineCap := pecSquare
1042 else
1043 currentState.penStroker.LineCap := pecFlat;
1044end;
1045
1046procedure TBGRACanvas2D.SetLineCapLCL(AValue: TPenEndCap);
1047begin
1048 currentState.penStroker.LineCap := AValue;
1049end;
1050
1051procedure TBGRACanvas2D.SetLineJoin(const AValue: string);
1052begin
1053 if CompareText(AValue,'round')=0 then
1054 currentState.penStroker.JoinStyle := pjsRound else
1055 if CompareText(AValue,'bevel')=0 then
1056 currentState.penStroker.JoinStyle := pjsBevel
1057 else
1058 currentState.penStroker.JoinStyle := pjsMiter;
1059end;
1060
1061procedure TBGRACanvas2D.FillPoly(const points: array of TPointF);
1062var
1063 bfill: boolean;
1064 tempScan: TBGRACustomScanner;
1065begin
1066 if (length(points) = 0) or (surface = nil) then exit;
1067 If hasShadow then DrawShadow(points,[],fillMode);
1068 bfill:= currentState.fillMode = fmWinding;
1069 if currentState.clipMaskReadOnly <> nil then
1070 begin
1071 if currentState.fillTextureProvider <> nil then
1072 tempScan := TBGRATextureMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),currentState.fillTextureProvider.texture,currentState.globalAlpha)
1073 else
1074 tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),ApplyGlobalAlpha(currentState.fillColor));
1075 if self.antialiasing then
1076 BGRAPolygon.FillPolyAntialiasWithTexture(surface, points, tempScan, bfill, linearBlend)
1077 else
1078 BGRAPolygon.FillPolyAliasedWithTexture(surface, points, tempScan, bfill, GetDrawMode);
1079 tempScan.free;
1080 end else
1081 begin
1082 if currentState.fillTextureProvider <> nil then
1083 begin
1084 if currentState.globalAlpha <> 255 then
1085 begin
1086 tempScan := TBGRAOpacityScanner.Create(currentState.fillTextureProvider.texture, currentState.globalAlpha);
1087 if self.antialiasing then
1088 BGRAPolygon.FillPolyAntialiasWithTexture(surface, points, tempScan, bfill, linearBlend)
1089 else
1090 BGRAPolygon.FillPolyAliasedWithTexture(surface, points, tempScan, bfill, GetDrawMode);
1091 tempScan.Free;
1092 end else
1093 begin
1094 if self.antialiasing then
1095 BGRAPolygon.FillPolyAntialiasWithTexture(surface, points, currentState.fillTextureProvider.texture, bfill, linearBlend)
1096 else
1097 BGRAPolygon.FillPolyAliasedWithTexture(surface, points, currentState.fillTextureProvider.texture, bfill, GetDrawMode);
1098 end
1099 end
1100 else
1101 begin
1102 if self.antialiasing then
1103 BGRAPolygon.FillPolyAntialias(surface, points, ApplyGlobalAlpha(currentState.fillColor), false, bfill, linearBlend)
1104 else
1105 BGRAPolygon.FillPolyAliased(surface, points, ApplyGlobalAlpha(currentState.fillColor), false, bfill, GetDrawMode)
1106 end
1107 end;
1108end;
1109
1110procedure TBGRACanvas2D.FillStrokePoly(const points: array of TPointF;
1111 fillOver: boolean);
1112var
1113 tempScan,tempScan2: TBGRACustomScanner;
1114 multi: TBGRAMultishapeFiller;
1115 contour : array of TPointF;
1116 texture: IBGRAScanner;
1117 idxContour: Integer;
1118begin
1119 if (length(points) = 0) or (surface = nil) then exit;
1120 tempScan := nil;
1121 tempScan2 := nil;
1122 multi := TBGRAMultishapeFiller.Create;
1123 multi.FillMode := self.fillMode;
1124 if currentState.clipMaskReadOnly <> nil then
1125 begin
1126 if currentState.fillTextureProvider <> nil then
1127 tempScan := TBGRATextureMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),currentState.fillTextureProvider.texture,currentState.globalAlpha)
1128 else
1129 tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),ApplyGlobalAlpha(currentState.fillColor));
1130 multi.AddPolygon(points, tempScan);
1131 end else
1132 begin
1133 if currentState.fillTextureProvider <> nil then
1134 begin
1135 if currentState.globalAlpha <> 255 then
1136 begin
1137 tempScan := TBGRAOpacityScanner.Create(currentState.fillTextureProvider.texture, currentState.globalAlpha);
1138 multi.AddPolygon(points, tempScan);
1139 end else
1140 multi.AddPolygon(points, currentState.fillTextureProvider.texture)
1141 end
1142 else
1143 multi.AddPolygon(points, ApplyGlobalAlpha(currentState.fillColor));
1144 end;
1145
1146 if currentState.lineWidth > 0 then
1147 begin
1148 contour := currentState.penStroker.ComputePolylineAutocycle(points,currentState.lineWidth);
1149
1150 if currentState.clipMaskReadOnly <> nil then
1151 begin
1152 if currentState.strokeTextureProvider <> nil then
1153 tempScan2 := TBGRATextureMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),currentState.strokeTextureProvider.texture,currentState.globalAlpha)
1154 else
1155 tempScan2 := TBGRASolidColorMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),ApplyGlobalAlpha(currentState.strokeColor));
1156 idxContour := multi.AddPolygon(contour,tempScan);
1157 end else
1158 begin
1159 if currentState.strokeTextureProvider <> nil then
1160 texture := currentState.strokeTextureProvider.texture else
1161 texture := nil;
1162 if texture = nil then
1163 idxContour := multi.AddPolygon(contour,ApplyGlobalAlpha(currentState.strokeColor))
1164 else
1165 idxContour := multi.AddPolygon(contour,texture);
1166 end;
1167 multi.OverrideFillMode(idxContour, fmWinding);
1168 If hasShadow then DrawShadow(points,contour);
1169 end else
1170 If hasShadow then DrawShadow(points,[]);
1171
1172 if fillOver then multi.PolygonOrder := poFirstOnTop else multi.PolygonOrder:= poLastOnTop;
1173 multi.Antialiasing := self.antialiasing;
1174 multi.Draw(surface);
1175 tempScan.free;
1176 tempScan2.free;
1177 multi.Free;
1178end;
1179
1180procedure TBGRACanvas2D.FillTexts(AErase: boolean);
1181var
1182 i,j: Integer;
1183 hy,hx,h: single;
1184 bmp,bmpTransf,shadowBmp: TBGRACustomBitmap;
1185 tempScan: TBGRACustomScanner;
1186 m: TAffineMatrix;
1187 s: TSize;
1188 surfaceBounds, shadowBounds: TRect;
1189 rf: TResampleFilter;
1190 pad: TSize;
1191 p: PBGRAPixel;
1192begin
1193 for i := 0 to High(FTextPaths) do
1194 with FTextPaths[i] do
1195 begin
1196 hx := VectLen(FontMatrix[1,1],FontMatrix[2,1]);
1197 hy := VectLen(FontMatrix[1,2],FontMatrix[2,2]);
1198 h := max(hx,hy);
1199 if self.antialiasing then h := round(h);
1200 if h<=0 then continue;
1201 m := FontMatrix*AffineMatrixScale(hx/sqr(h),hy/sqr(h));
1202 if pixelCenteredCoordinates then m := AffineMatrixTranslation(0.5,0.5)*m;
1203 bmp := BGRABitmapFactory.Create;
1204 try
1205 bmp.FontName := FontName;
1206 bmp.FontStyle:= FontStyle;
1207 bmp.FontHeight:= round(h);
1208 if self.antialiasing then
1209 bmp.FontQuality := fqFineAntialiasing
1210 else
1211 bmp.FontQuality:= fqSystem;
1212
1213 bmp.FontVerticalAnchor:= FontAnchor;
1214 m := m*AffineMatrixTranslation(0,-bmp.FontVerticalAnchorOffset);
1215 bmp.FontVerticalAnchor:= fvaTop;
1216
1217 s := bmp.TextSize(Text);
1218 case FontAlign of
1219 taCenter: m := m*AffineMatrixTranslation(-s.cx/2,0);
1220 taRightJustify: m := m*AffineMatrixTranslation(-s.cx,0);
1221 end;
1222
1223 pad := Size(round(h/3), round(h/3));
1224 m := m*AffineMatrixTranslation(-pad.cx,-pad.cy);
1225 surfaceBounds := surface.GetImageAffineBounds(m, Types.Rect(0,0,s.cx+pad.cx*2,s.cy+pad.cy*2));
1226 if hasShadow then
1227 begin
1228 shadowBounds := surfaceBounds;
1229 shadowBounds.Inflate(ceil(shadowBlur),ceil(shadowBlur));
1230 shadowBounds.Offset(round(shadowOffsetX),round(shadowOffsetY));
1231 shadowBounds.Intersect(surface.ClipRect);
1232 if not IsRectEmpty(shadowBounds) then
1233 begin
1234 shadowBounds.Offset(-round(shadowOffsetX),-round(shadowOffsetY));
1235 UnionRect(surfaceBounds, surfaceBounds, shadowBounds);
1236 end;
1237 end;
1238 if not IsRectEmpty(surfaceBounds) then
1239 begin
1240 bmp.SetSize(s.cx+pad.cx*2,s.cy+pad.cy*2);
1241 bmp.Fill(BGRABlack);
1242 bmp.TextOut(pad.cx,pad.cy,Text,BGRAWhite);
1243 if self.antialiasing then bmp.ConvertToLinearRGB else
1244 begin
1245 p := bmp.Data;
1246 for j := bmp.NbPixels-1 downto 0 do
1247 begin
1248 if p^.green<128 then p^ := BGRABlack else p^ := BGRAWhite;
1249 inc(p);
1250 end;
1251 end;
1252
1253 bmpTransf := BGRABitmapFactory.Create(surfaceBounds.Width,surfaceBounds.Height,BGRABlack);
1254 try
1255 m := AffineMatrixTranslation(-surfaceBounds.Left,-surfaceBounds.Top)*m;
1256 if self.antialiasing then rf:= rfCosine else rf := rfBox;
1257 bmpTransf.PutImageAffine(m, bmp, rf, GetDrawMode);
1258 FreeAndNil(bmp);
1259
1260 if AErase then
1261 surface.EraseMask(surfaceBounds.Left,surfaceBounds.Top, bmpTransf) else
1262 begin
1263 if hasShadow then
1264 begin
1265 shadowBmp := BGRABitmapFactory.Create(bmpTransf.Width,bmpTransf.Height);
1266 shadowBmp.FillMask(0,0, bmpTransf, getShadowColor, GetDrawMode);
1267 DrawShadowMask(surfaceBounds.Left+round(shadowOffsetX),surfaceBounds.Top+round(shadowOffsetY), shadowBmp, true);
1268 end;
1269
1270 if currentState.clipMaskReadOnly <> nil then
1271 begin
1272 if currentState.fillTextureProvider <> nil then
1273 tempScan := TBGRATextureMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),currentState.fillTextureProvider.texture,currentState.globalAlpha)
1274 else
1275 tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),ApplyGlobalAlpha(currentState.fillColor));
1276 surface.FillMask(surfaceBounds.Left,surfaceBounds.Top, bmpTransf, tempScan, GetDrawMode);
1277 tempScan.free;
1278 end else
1279 begin
1280 if currentState.fillTextureProvider <> nil then
1281 begin
1282 if currentState.globalAlpha <> 255 then
1283 begin
1284 tempScan := TBGRAOpacityScanner.Create(currentState.fillTextureProvider.texture, currentState.globalAlpha);
1285 surface.FillMask(surfaceBounds.Left,surfaceBounds.Top, bmpTransf, tempScan, GetDrawMode);
1286 tempScan.Free;
1287 end else
1288 surface.FillMask(surfaceBounds.Left,surfaceBounds.Top, bmpTransf, currentState.fillTextureProvider.texture, GetDrawMode);
1289 end
1290 else
1291 surface.FillMask(surfaceBounds.Left,surfaceBounds.Top, bmpTransf, ApplyGlobalAlpha(currentState.fillColor), GetDrawMode);
1292 end;
1293 end;
1294 finally
1295 bmpTransf.Free;
1296 end;
1297 end;
1298 finally
1299 bmp.Free;
1300 end;
1301 end;
1302end;
1303
1304procedure TBGRACanvas2D.SetLineJoinLCL(AValue: TPenJoinStyle);
1305begin
1306 currentState.penStroker.JoinStyle := AValue;
1307end;
1308
1309procedure TBGRACanvas2D.lineStyle(const AValue: array of single);
1310begin
1311 currentState.penStroker.CustomPenStyle := DuplicatePenStyle(AValue);
1312end;
1313
1314procedure TBGRACanvas2D.lineStyle(AStyle: TPenStyle);
1315begin
1316 case AStyle of
1317 psSolid: lineStyle(SolidPenStyle);
1318 psDash: lineStyle(DashPenStyle);
1319 psDot: lineStyle(DotPenStyle);
1320 psDashDot: lineStyle(DashDotPenStyle);
1321 psDashDotDot: lineStyle(DashDotDotPenStyle);
1322 psClear: lineStyle(ClearPenStyle);
1323 end;
1324end;
1325
1326function TBGRACanvas2D.QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
1327begin
1328 if GetInterface(iid, obj) then
1329 Result := S_OK
1330 else
1331 Result := longint(E_NOINTERFACE);
1332end;
1333
1334{ There is no automatic reference counting, but it is compulsory to define these functions }
1335function TBGRACanvas2D._AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
1336begin
1337 result := 0;
1338end;
1339
1340function TBGRACanvas2D._Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
1341begin
1342 result := 0;
1343end;
1344
1345procedure TBGRACanvas2D.SetLineWidth(const AValue: single);
1346begin
1347 currentState.lineWidth := AValue;
1348end;
1349
1350procedure TBGRACanvas2D.SetMatrix(AValue: TAffineMatrix);
1351begin
1352 currentState.matrix := AValue;
1353end;
1354
1355procedure TBGRACanvas2D.SetMiterLimit(const AValue: single);
1356begin
1357 currentState.penStroker.MiterLimit := AValue;
1358end;
1359
1360procedure TBGRACanvas2D.SetPixelCenteredCoordinates(const AValue: boolean);
1361begin
1362 FPixelCenteredCoordinates:= AValue;
1363 if AValue then
1364 FCanvasOffset := PointF(0,0)
1365 else
1366 FCanvasOffset := PointF(-0.5,-0.5);
1367end;
1368
1369procedure TBGRACanvas2D.SetShadowBlur(const AValue: single);
1370begin
1371 currentState.shadowBlur := AValue;
1372end;
1373
1374procedure TBGRACanvas2D.SetShadowFastest(AValue: boolean);
1375begin
1376 currentState.shadowFastest := AValue;
1377end;
1378
1379procedure TBGRACanvas2D.SetShadowOffset(const AValue: TPointF);
1380begin
1381 shadowOffsetX := AValue.X;
1382 shadowOffsetY := AValue.Y;
1383end;
1384
1385procedure TBGRACanvas2D.SetShadowOffsetX(const AValue: single);
1386begin
1387 currentState.shadowOffsetX := AValue;
1388end;
1389
1390procedure TBGRACanvas2D.SetShadowOffsetY(const AValue: single);
1391begin
1392 currentState.shadowOffsetY := AValue;
1393end;
1394
1395procedure TBGRACanvas2D.SetStrokeMatrix(AValue: TAffineMatrix);
1396begin
1397 currentState.penStroker.strokeMatrix := AValue;
1398end;
1399
1400procedure TBGRACanvas2D.SetTextAlign(AValue: string);
1401begin
1402 AValue := trim(LowerCase(AValue));
1403 if (AValue = 'left') or (AValue = 'start') then
1404 textAlignLCL := taLeftJustify else
1405 if (AValue = 'right') or (AValue = 'end') then
1406 textAlignLCL := taRightJustify else
1407 if AValue = 'center' then
1408 textAlignLCL := taCenter;
1409end;
1410
1411procedure TBGRACanvas2D.SetTextAlignLCL(AValue: TAlignment);
1412begin
1413 currentState.textAlign := AValue;
1414end;
1415
1416procedure TBGRACanvas2D.SetTextBaseine(AValue: string);
1417begin
1418 currentState.textBaseline := trim(lowercase(AValue));
1419end;
1420
1421procedure TBGRACanvas2D.StrokePoly(const points: array of TPointF);
1422var
1423 texture: IBGRAScanner;
1424 tempScan: TBGRACustomScanner;
1425 contour: array of TPointF;
1426begin
1427 if (length(points)= 0) or (currentState.lineWidth = 0) or (surface = nil) then exit;
1428 contour := currentState.penStroker.ComputePolylineAutocycle(points,currentState.lineWidth);
1429
1430 If hasShadow then DrawShadow(contour,[]);
1431 if currentState.clipMaskReadOnly <> nil then
1432 begin
1433 if currentState.strokeTextureProvider <> nil then
1434 tempScan := TBGRATextureMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),currentState.strokeTextureProvider.texture,currentState.globalAlpha)
1435 else
1436 tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),ApplyGlobalAlpha(currentState.strokeColor));
1437 if self.antialiasing then
1438 BGRAPolygon.FillPolyAntialiasWithTexture(Surface,contour,tempScan,True, linearBlend)
1439 else
1440 BGRAPolygon.FillPolyAliasedWithTexture(Surface,contour,tempScan,True,GetDrawMode);
1441 tempScan.free;
1442 end else
1443 begin
1444 if currentState.strokeTextureProvider <> nil then
1445 texture := currentState.strokeTextureProvider.texture else
1446 texture := nil;
1447 if texture = nil then
1448 begin
1449 if self.antialiasing then
1450 BGRAPolygon.FillPolyAntialias(Surface,contour,ApplyGlobalAlpha(currentState.strokeColor),false,True, linearBlend)
1451 else
1452 BGRAPolygon.FillPolyAliased(Surface,contour,ApplyGlobalAlpha(currentState.strokeColor),false,True,GetDrawMode)
1453 end
1454 else
1455 begin
1456 if self.antialiasing then
1457 BGRAPolygon.FillPolyAntialiasWithTexture(Surface,contour,texture,True, linearBlend)
1458 else
1459 BGRAPolygon.FillPolyAliasedWithTexture(Surface,contour,texture,True,GetDrawMode)
1460 end;
1461 end;
1462end;
1463
1464procedure TBGRACanvas2D.DrawShadow(const points, points2: array of TPointF;
1465 AFillMode: TFillMode = fmWinding);
1466var ofsPts,ofsPts2: array of TPointF;
1467 offset: TPointF;
1468 i: Integer;
1469 tempBmp: TBGRACustomBitmap;
1470 maxRect: TRect;
1471 foundRect: TRect;
1472 firstFound: boolean;
1473
1474 procedure AddPt(const coord: TPointF);
1475 var pixRect: TRect;
1476 begin
1477 if isEmptyPointF(coord) then exit;
1478 pixRect := Types.Rect(round(floor(coord.x)),round(floor(coord.y)),round(ceil(coord.x+0.999))+1,round(ceil(coord.y+0.999))+1);
1479 if firstFound then
1480 begin
1481 foundRect := pixRect;
1482 firstFound := false
1483 end
1484 else
1485 begin
1486 if pixRect.left < foundRect.left then foundRect.left := pixRect.Left;
1487 if pixRect.top < foundRect.top then foundRect.top := pixRect.top;
1488 if pixRect.right > foundRect.right then foundRect.right := pixRect.right;
1489 if pixRect.bottom > foundRect.bottom then foundRect.bottom := pixRect.bottom;
1490 end;
1491 end;
1492
1493begin
1494 if not hasShadow or (surface = nil) then exit;
1495 offset := PointF(shadowOffsetX,shadowOffsetY);
1496 setlength(ofsPts, length(points));
1497 for i := 0 to high(ofsPts) do
1498 ofsPts[i] := points[i]+offset;
1499 setlength(ofsPts2, length(points2));
1500 for i := 0 to high(ofsPts2) do
1501 ofsPts2[i] := points2[i]+offset;
1502
1503 maxRect := Types.Rect(0,0,width,height);
1504 if currentState.clipMaskReadOnly <> nil then
1505 foundRect := maxRect
1506 else
1507 begin
1508 firstFound := true;
1509 foundRect := EmptyRect;
1510 for i := 0 to high(ofsPts) do
1511 AddPt(ofsPts[i]);
1512 for i := 0 to high(ofsPts2) do
1513 AddPt(ofsPts2[i]);
1514 if firstFound then exit;
1515 InflateRect(foundRect, ceil(shadowBlur),ceil(shadowBlur));
1516 if not IntersectRect(foundRect, foundRect,maxRect) then exit;
1517 offset := PointF(-foundRect.Left,-foundRect.Top);
1518 for i := 0 to high(ofsPts) do
1519 ofsPts[i] += offset;
1520 for i := 0 to high(ofsPts2) do
1521 ofsPts2[i] += offset;
1522 end;
1523
1524 tempBmp := surface.NewBitmap(foundRect.Right-foundRect.Left,foundRect.Bottom-foundRect.Top,BGRAPixelTransparent);
1525 tempBmp.FillMode := AFillMode;
1526 tempBmp.FillPolyAntialias(ofsPts, getShadowColor);
1527 tempBmp.FillPolyAntialias(ofsPts2, getShadowColor);
1528 DrawShadowMask(foundRect.Left,foundRect.Top, tempBmp, true);
1529end;
1530
1531procedure TBGRACanvas2D.DrawShadowMask(X, Y: integer; AMask: TBGRACustomBitmap; AMaskOwned: boolean);
1532const invSqrt2 = 1/sqrt(2);
1533var
1534 bmp: TBGRACustomBitmap;
1535begin
1536 bmp := AMask;
1537 if shadowBlur > 0 then
1538 begin
1539 if shadowFastest then
1540 begin
1541 if shadowBlur*invSqrt2 >= 0.5 then
1542 bmp := AMask.FilterBlurRadial(round(shadowBlur*invSqrt2),rbBox);
1543 end
1544 else
1545 begin
1546 if (shadowBlur < 5) and (abs(shadowBlur-round(shadowBlur)) > 1e-6) then
1547 bmp := AMask.FilterBlurRadial(round(shadowBlur*10),rbPrecise)
1548 else
1549 bmp := AMask.FilterBlurRadial(round(shadowBlur),rbFast);
1550 end;
1551 end;
1552 if currentState.clipMaskReadOnly <> nil then
1553 begin
1554 if (bmp = AMask) and not AMaskOwned then bmp := AMask.Duplicate;
1555 bmp.ApplyMask(currentState.clipMaskReadOnly);
1556 end;
1557 surface.PutImage(X,Y,bmp,GetDrawMode,currentState.globalAlpha);
1558 if bmp <> AMask then bmp.Free;
1559 if AMaskOwned then AMask.Free;
1560end;
1561
1562procedure TBGRACanvas2D.ClearPoly(const points: array of TPointF);
1563begin
1564 if surface = nil then exit;
1565 if self.antialiasing then
1566 BGRAPolygon.FillPolyAntialias(surface, points, BGRA(0,0,0,255), true, true, linearBlend)
1567 else
1568 BGRAPolygon.FillPolyAliased(surface, points, BGRA(0,0,0,255), true, true, dmSet);
1569end;
1570
1571function TBGRACanvas2D.ApplyTransform(const points: array of TPointF;
1572 matrix: TAffineMatrix): ArrayOfTPointF;
1573var
1574 i: Integer;
1575begin
1576 setlength(result,length(points));
1577 for i := 0 to high(result) do
1578 if isEmptyPointF(points[i]) then
1579 result[i] := EmptyPointF
1580 else
1581 result[i] := matrix*points[i]+FCanvasOffset;
1582end;
1583
1584function TBGRACanvas2D.ApplyTransform(const points: array of TPointF
1585 ): ArrayOfTPointF;
1586var
1587 i: Integer;
1588begin
1589 setlength(result,length(points));
1590 for i := 0 to high(result) do
1591 if isEmptyPointF(points[i]) then
1592 result[i] := EmptyPointF
1593 else
1594 result[i] := currentState.matrix*points[i]+FCanvasOffset;
1595end;
1596
1597function TBGRACanvas2D.ApplyTransform(point: TPointF): TPointF;
1598begin
1599 result := currentState.matrix*point+FCanvasOffset;
1600end;
1601
1602function TBGRACanvas2D.GetPenPos(defaultX,defaultY: single): TPointF;
1603begin
1604 if isEmptyPointF(FLastCoord) then
1605 result := PointF(defaultX,defaultY)
1606 else
1607 result := FLastCoord;
1608end;
1609
1610function TBGRACanvas2D.GetPenPos(defaultPt: TPointF): TPointF;
1611begin
1612 result := GetPenPos(defaultPt.x,defaultPt.y);
1613end;
1614
1615procedure TBGRACanvas2D.AddPoint(point: TPointF);
1616begin
1617 if FPathPointCount = length(FPathPoints) then
1618 setlength(FPathPoints, (length(FPathPoints)+1)*2);
1619 FPathPoints[FPathPointCount] := point;
1620 inc(FPathPointCount);
1621end;
1622
1623procedure TBGRACanvas2D.AddPoints(const points: array of TPointF);
1624var i: integer;
1625begin
1626 if FPathPointCount+length(points) > length(FPathPoints) then
1627 setlength(FPathPoints, max( (length(FPathPoints)+1)*2, FPathPointCount+length(points) ) );
1628 for i := 0 to high(points) do
1629 begin
1630 FPathPoints[FPathPointCount] := points[i];
1631 inc(FPathPointCount);
1632 end;
1633end;
1634
1635procedure TBGRACanvas2D.AddPointsRev(const points: array of TPointF);
1636var i: integer;
1637begin
1638 if FPathPointCount+length(points) > length(FPathPoints) then
1639 setlength(FPathPoints, max( (length(FPathPoints)+1)*2, FPathPointCount+length(points) ) );
1640 for i := high(points) downto 0 do
1641 begin
1642 FPathPoints[FPathPointCount] := points[i];
1643 inc(FPathPointCount);
1644 end;
1645end;
1646
1647function TBGRACanvas2D.ApplyGlobalAlpha(color: TBGRAPixel): TBGRAPixel;
1648begin
1649 result := BGRA(color.red,color.green,color.blue,ApplyOpacity(color.alpha, currentState.globalAlpha));
1650end;
1651
1652function TBGRACanvas2D.GetDrawMode: TDrawMode;
1653begin
1654 if linearBlend then result := dmLinearBlend else result := dmDrawWithTransparency;
1655end;
1656
1657procedure TBGRACanvas2D.copyTo(dest: IBGRAPath);
1658begin
1659 //nothing
1660end;
1661
1662function TBGRACanvas2D.getPoints: ArrayOfTPointF;
1663begin
1664 result := GetCurrentPathAsPoints;
1665end;
1666
1667function TBGRACanvas2D.getPoints(AMatrix: TAffineMatrix): ArrayOfTPointF;
1668begin
1669 result := GetCurrentPathAsPoints;
1670 if not IsAffineMatrixIdentity(AMatrix) then
1671 result := AMatrix*result;
1672end;
1673
1674function TBGRACanvas2D.getCursor: TBGRACustomPathCursor;
1675begin
1676 result := nil;
1677end;
1678
1679constructor TBGRACanvas2D.Create(ASurface: TBGRACustomBitmap);
1680begin
1681 FSurface := ASurface;
1682 StateStack := TList.Create;
1683 FPathPointCount := 0;
1684 FLastCoord := EmptyPointF;
1685 FStartCoord := EmptyPointF;
1686 currentState := TBGRACanvasState2D.Create(AffineMatrixIdentity,nil,true);
1687 pixelCenteredCoordinates := false;
1688 antialiasing := true;
1689 gradientGammaCorrection := false;
1690end;
1691
1692destructor TBGRACanvas2D.Destroy;
1693var
1694 i: Integer;
1695begin
1696 for i := 0 to StateStack.Count-1 do
1697 TObject(StateStack[i]).Free;
1698 StateStack.Free;
1699 currentState.Free;
1700 FreeAndNil(FFontRenderer);
1701 inherited Destroy;
1702end;
1703
1704function TBGRACanvas2D.toDataURL(mimeType: string): string;
1705var
1706 stream: TMemoryStream;
1707 jpegWriter: TFPWriterJPEG;
1708 bmpWriter: TFPWriterBMP;
1709 output: TStringStream;
1710 encode64: TBase64EncodingStream;
1711begin
1712 if surface = nil then exit;
1713 stream := TMemoryStream.Create;
1714 if mimeType='image/jpeg' then
1715 begin
1716 jpegWriter := TFPWriterJPEG.Create;
1717 Surface.SaveToStream(stream,jpegWriter);
1718 jpegWriter.Free;
1719 end else
1720 if mimeType='image/bmp' then
1721 begin
1722 bmpWriter := TFPWriterBMP.Create;
1723 Surface.SaveToStream(stream,bmpWriter);
1724 bmpWriter.Free;
1725 end else
1726 begin
1727 mimeType := 'image/png';
1728 Surface.SaveToStreamAsPng(stream);
1729 end;
1730 output := TStringStream.Create('data:'+mimeType+';base64,');
1731 output.Position := output.size;
1732 stream.Position := 0;
1733 encode64 := TBase64EncodingStream.Create(output);
1734 encode64.CopyFrom(stream,stream.size);
1735 encode64.free;
1736 stream.free;
1737 result := output.DataString;
1738 output.free;
1739end;
1740
1741procedure TBGRACanvas2D.save;
1742var cur: TBGRACanvasState2D;
1743begin
1744 cur := currentState.Duplicate;
1745 StateStack.Add(cur);
1746end;
1747
1748procedure TBGRACanvas2D.restore;
1749begin
1750 if StateStack.Count > 0 then
1751 begin
1752 FreeAndNil(currentState);
1753 currentState := TBGRACanvasState2D(StateStack[StateStack.Count-1]);
1754 StateStack.Delete(StateStack.Count-1);
1755 end;
1756end;
1757
1758procedure TBGRACanvas2D.scale(x, y: single);
1759begin
1760 currentState.matrix *= AffineMatrixScale(x,y);
1761end;
1762
1763procedure TBGRACanvas2D.scale(factor: single);
1764begin
1765 currentState.matrix *= AffineMatrixScale(factor,factor);
1766end;
1767
1768procedure TBGRACanvas2D.rotate(angleRadCW: single);
1769begin
1770 currentState.matrix *= AffineMatrixRotationRad(-angleRadCW);
1771end;
1772
1773procedure TBGRACanvas2D.translate(x, y: single);
1774begin
1775 if (x = 0) and (y = 0) then exit;
1776 currentState.matrix *= AffineMatrixTranslation(x,y);
1777end;
1778
1779procedure TBGRACanvas2D.skewx(angleRadCW: single);
1780begin
1781 currentState.matrix *= AffineMatrixSkewXRad(-angleRadCW);
1782end;
1783
1784procedure TBGRACanvas2D.skewy(angleRadCW: single);
1785begin
1786 currentState.matrix *= AffineMatrixSkewYRad(-angleRadCW);
1787end;
1788
1789procedure TBGRACanvas2D.transform(m11,m21, m12,m22, m13,m23: single);
1790begin
1791 currentState.matrix *= AffineMatrix(m11,m12,m13,
1792 m21,m22,m23);
1793end;
1794
1795procedure TBGRACanvas2D.transform(AMatrix: TAffineMatrix);
1796begin
1797 currentState.matrix *= AMatrix;
1798end;
1799
1800procedure TBGRACanvas2D.setTransform(m11,m21, m12,m22, m13,m23: single);
1801begin
1802 currentState.matrix := AffineMatrix(m11,m12,m13,
1803 m21,m22,m23);
1804end;
1805
1806procedure TBGRACanvas2D.resetTransform;
1807begin
1808 currentState.matrix := AffineMatrixIdentity;
1809end;
1810
1811procedure TBGRACanvas2D.strokeScale(x, y: single);
1812begin
1813 currentState.penStroker.strokeMatrix := currentState.penStroker.strokeMatrix * AffineMatrixScale(x,y);
1814end;
1815
1816procedure TBGRACanvas2D.strokeSkewx(angleRadCW: single);
1817begin
1818 currentState.penStroker.strokeMatrix := currentState.penStroker.strokeMatrix * AffineMatrixSkewXRad(-angleRadCW);
1819end;
1820
1821procedure TBGRACanvas2D.strokeSkewy(angleRadCW: single);
1822begin
1823 currentState.penStroker.strokeMatrix := currentState.penStroker.strokeMatrix * AffineMatrixSkewYRad(-angleRadCW);
1824end;
1825
1826procedure TBGRACanvas2D.strokeResetTransform;
1827begin
1828 currentState.penStroker.strokeMatrix := AffineMatrixIdentity;
1829end;
1830
1831procedure TBGRACanvas2D.strokeStyle(color: TBGRAPixel);
1832begin
1833 currentState.strokeColor := color;
1834 currentState.strokeTextureProvider := nil;
1835end;
1836
1837procedure TBGRACanvas2D.strokeStyle(color: TColor);
1838begin
1839 currentState.strokeColor := ColorToBGRA(ColorToRGB(color));
1840 currentState.strokeTextureProvider := nil;
1841end;
1842
1843procedure TBGRACanvas2D.strokeStyle(color: string);
1844begin
1845 currentState.strokeColor := StrToBGRA(color);
1846 currentState.strokeTextureProvider := nil;
1847end;
1848
1849procedure TBGRACanvas2D.strokeStyle(texture: IBGRAScanner);
1850begin
1851 strokeStyle(createPattern(texture));
1852end;
1853
1854procedure TBGRACanvas2D.strokeStyle(provider: IBGRACanvasTextureProvider2D);
1855begin
1856 currentState.strokeColor := BGRAPixelTransparent;
1857 currentState.strokeTextureProvider := provider;
1858end;
1859
1860function TBGRACanvas2D.GetFillMode: TFillMode;
1861begin
1862 result := currentState.fillMode;
1863end;
1864
1865procedure TBGRACanvas2D.SetFillMode(mode: TFillMode);
1866begin
1867 currentState.fillMode := mode;
1868end;
1869
1870procedure TBGRACanvas2D.fillStyle(color: TBGRAPixel);
1871begin
1872 currentState.fillColor := color;
1873 currentState.fillTextureProvider := nil;
1874end;
1875
1876procedure TBGRACanvas2D.fillStyle(color: TColor);
1877begin
1878 currentState.fillColor := ColorToBGRA(ColorToRGB(color));
1879 currentState.fillTextureProvider := nil;
1880end;
1881
1882procedure TBGRACanvas2D.fillStyle(color: string);
1883begin
1884 currentState.fillColor := StrToBGRA(color);
1885 currentState.fillTextureProvider := nil;
1886end;
1887
1888procedure TBGRACanvas2D.fillStyle(texture: IBGRAScanner);
1889begin
1890 fillStyle(createPattern(texture));
1891end;
1892
1893procedure TBGRACanvas2D.fillStyle(provider: IBGRACanvasTextureProvider2D);
1894begin
1895 currentState.fillColor := BGRAPixelTransparent;
1896 currentState.fillTextureProvider := provider;
1897end;
1898
1899procedure TBGRACanvas2D.shadowColor(color: TBGRAPixel);
1900begin
1901 currentState.shadowColor := color;
1902end;
1903
1904procedure TBGRACanvas2D.shadowColor(color: TColor);
1905begin
1906 shadowColor(ColorToBGRA(ColorToRGB(color)));
1907end;
1908
1909procedure TBGRACanvas2D.shadowColor(color: string);
1910begin
1911 shadowColor(StrToBGRA(color));
1912end;
1913
1914procedure TBGRACanvas2D.shadowNone;
1915begin
1916 shadowColor(BGRAPixelTransparent);
1917end;
1918
1919function TBGRACanvas2D.getShadowColor: TBGRAPixel;
1920begin
1921 result := currentState.shadowColor;
1922end;
1923
1924function TBGRACanvas2D.createLinearGradient(x0, y0, x1, y1: single): IBGRACanvasGradient2D;
1925begin
1926 result := createLinearGradient(PointF(x0,y0), PointF(x1,y1));
1927end;
1928
1929function TBGRACanvas2D.createLinearGradient(p0, p1: TPointF): IBGRACanvasGradient2D;
1930begin
1931 result := TBGRACanvasLinearGradient2D.Create(p0,p1,
1932 AffineMatrixTranslation(FCanvasOffset.x,FCanvasOffset.y)*currentState.matrix);
1933 result.gammaCorrection := gradientGammaCorrection;
1934end;
1935
1936function TBGRACanvas2D.createLinearGradient(x0, y0, x1, y1: single;
1937 Colors: TBGRACustomGradient): IBGRACanvasGradient2D;
1938begin
1939 result := createLinearGradient(x0,y0,x1,y1);
1940 result.setColors(Colors);
1941end;
1942
1943function TBGRACanvas2D.createLinearGradient(p0, p1: TPointF;
1944 Colors: TBGRACustomGradient): IBGRACanvasGradient2D;
1945begin
1946 result := createLinearGradient(p0,p1);
1947 result.setColors(Colors);
1948end;
1949
1950function TBGRACanvas2D.createRadialGradient(x0, y0, r0, x1, y1, r1: single;
1951 flipGradient: boolean): IBGRACanvasGradient2D;
1952begin
1953 result := createRadialGradient(PointF(x0,y0), r0, PointF(x1,y1), r1, flipGradient);
1954end;
1955
1956function TBGRACanvas2D.createRadialGradient(p0: TPointF; r0: single;
1957 p1: TPointF; r1: single; flipGradient: boolean): IBGRACanvasGradient2D;
1958begin
1959 result := TBGRACanvasRadialGradient2D.Create(p0,r0,p1,r1,
1960 AffineMatrixTranslation(FCanvasOffset.x,FCanvasOffset.y)*currentState.matrix,
1961 flipGradient);
1962 result.gammaCorrection := gradientGammaCorrection;
1963end;
1964
1965function TBGRACanvas2D.createRadialGradient(x0, y0, r0, x1, y1, r1: single;
1966 Colors: TBGRACustomGradient; flipGradient: boolean): IBGRACanvasGradient2D;
1967begin
1968 result := createRadialGradient(x0,y0,r0,x1,y1,r1,flipGradient);
1969 result.setColors(Colors);
1970end;
1971
1972function TBGRACanvas2D.createRadialGradient(p0: TPointF; r0: single;
1973 p1: TPointF; r1: single; Colors: TBGRACustomGradient; flipGradient: boolean): IBGRACanvasGradient2D;
1974begin
1975 result := createRadialGradient(p0,r0,p1,r1,flipGradient);
1976 result.setColors(Colors);
1977end;
1978
1979function TBGRACanvas2D.createPattern(image: TBGRACustomBitmap; repetition: string
1980 ): IBGRACanvasTextureProvider2D;
1981var
1982 repeatX,repeatY: boolean;
1983 origin: TPointF;
1984begin
1985 repetition := lowercase(trim(repetition));
1986 repeatX := true;
1987 repeatY := true;
1988 if repetition = 'repeat-x' then repeatY := false else
1989 if repetition = 'repeat-y' then repeatX := false else
1990 if repetition = 'no-repeat' then
1991 begin
1992 repeatX := false;
1993 repeatY := false;
1994 end;
1995 origin := ApplyTransform(PointF(0,0)) + PointF(0.5,0.5);
1996 result := TBGRACanvasPattern2D.Create(image,repeatX,repeatY,
1997 origin, origin+PointF(currentState.matrix[1,1],currentState.matrix[2,1])*image.Width,
1998 origin+PointF(currentState.matrix[1,2],currentState.matrix[2,2])*image.Height);
1999end;
2000
2001function TBGRACanvas2D.createPattern(texture: IBGRAScanner
2002 ): IBGRACanvasTextureProvider2D;
2003var
2004 tempTransform: TAffineMatrix;
2005begin
2006 tempTransform := AffineMatrixTranslation(FCanvasOffset.X+0.5,FCanvasOffset.Y+0.5)*currentState.matrix;
2007 result := TBGRACanvasPattern2D.Create(texture,tempTransform);
2008end;
2009
2010procedure TBGRACanvas2D.fillRect(x, y, w, h: single);
2011begin
2012 if (w=0) or (h=0) then exit;
2013 FillPoly(ApplyTransform([PointF(x,y),PointF(x+w,y),PointF(x+w,y+h),PointF(x,y+h)]));
2014end;
2015
2016procedure TBGRACanvas2D.strokeRect(x, y, w, h: single);
2017begin
2018 if (w=0) or (h=0) then exit;
2019 StrokePoly(ApplyTransform([PointF(x,y),PointF(x+w,y),PointF(x+w,y+h),PointF(x,y+h),PointF(x,y)]));
2020end;
2021
2022procedure TBGRACanvas2D.clearRect(x, y, w, h: single);
2023begin
2024 if (w=0) or (h=0) then exit;
2025 ClearPoly(ApplyTransform([PointF(x,y),PointF(x+w,y),PointF(x+w,y+h),PointF(x,y+h)]));
2026end;
2027
2028procedure TBGRACanvas2D.addPath(APath: IBGRAPath);
2029begin
2030 if (FPathPointCount <> 0) and not isEmptyPointF(FPathPoints[FPathPointCount-1]) then
2031 begin
2032 AddPoint(EmptyPointF);
2033 FLastCoord := EmptyPointF;
2034 FStartCoord := EmptyPointF;
2035 end;
2036 APath.copyTo(self);
2037end;
2038
2039procedure TBGRACanvas2D.addPath(ASvgPath: string);
2040var p: TBGRAPath;
2041begin
2042 p := TBGRAPath.Create(ASvgPath);
2043 addPath(p);
2044 p.Free;
2045end;
2046
2047procedure TBGRACanvas2D.path(APath: IBGRAPath);
2048begin
2049 beginPath;
2050 addPath(APath);
2051end;
2052
2053procedure TBGRACanvas2D.path(ASvgPath: string);
2054begin
2055 beginPath;
2056 addPath(ASvgPath);
2057end;
2058
2059procedure TBGRACanvas2D.beginPath;
2060begin
2061 FPathPointCount := 0;
2062 FLastCoord := EmptyPointF;
2063 FStartCoord := EmptyPointF;
2064 FTextPaths := nil;
2065end;
2066
2067procedure TBGRACanvas2D.closePath;
2068var i: integer;
2069begin
2070 if FPathPointCount > 0 then
2071 begin
2072 i := FPathPointCount-1;
2073 while (i > 0) and not isEmptyPointF(FPathPoints[i-1]) do dec(i);
2074 AddPoint(FPathPoints[i]);
2075 FLastCoord := FStartCoord;
2076 end;
2077end;
2078
2079procedure TBGRACanvas2D.toSpline(closed: boolean; style: TSplineStyle);
2080var i,j: integer;
2081 pts, splinePts: array of TPointF;
2082 nb: integer;
2083begin
2084 if FPathPointCount > 0 then
2085 begin
2086 i := FPathPointCount-1;
2087 while (i > 0) and not isEmptyPointF(FPathPoints[i-1]) do dec(i);
2088 nb := FPathPointCount - i;
2089 setlength(pts,nb);
2090 for j := 0 to nb-1 do
2091 pts[j] := FPathPoints[i+j];
2092 if closed then
2093 splinePts := BGRAPath.ComputeClosedSpline(pts,style)
2094 else
2095 splinePts := BGRAPath.ComputeOpenedSpline(pts,style);
2096 dec(FPathPointCount,nb);
2097 AddPoints(splinePts);
2098 end;
2099end;
2100
2101procedure TBGRACanvas2D.moveTo(x, y: single);
2102begin
2103 moveTo(PointF(x,y));
2104end;
2105
2106procedure TBGRACanvas2D.lineTo(x, y: single);
2107begin
2108 lineTo(PointF(x,y));
2109end;
2110
2111procedure TBGRACanvas2D.moveTo(constref pt: TPointF);
2112begin
2113 if (FPathPointCount <> 0) and not isEmptyPointF(FPathPoints[FPathPointCount-1]) then
2114 AddPoint(EmptyPointF);
2115 AddPoint(ApplyTransform(pt));
2116 FStartCoord := pt;
2117 FLastCoord := pt;
2118end;
2119
2120procedure TBGRACanvas2D.lineTo(constref pt: TPointF);
2121begin
2122 AddPoint(ApplyTransform(pt));
2123 FLastCoord := pt;
2124end;
2125
2126procedure TBGRACanvas2D.polylineTo(const pts: array of TPointF);
2127begin
2128 if length(pts)> 0 then
2129 begin
2130 AddPoints(ApplyTransform(pts));
2131 FLastCoord := pts[high(pts)];
2132 end;
2133end;
2134
2135procedure TBGRACanvas2D.quadraticCurveTo(cpx, cpy, x, y: single);
2136var
2137 curve : TQuadraticBezierCurve;
2138 pts : array of TPointF;
2139begin
2140 curve := BezierCurve(ApplyTransform(GetPenPos(cpx,cpy)),ApplyTransform(PointF(cpx,cpy)),ApplyTransform(PointF(x,y)));
2141 pts := BGRAPath.ComputeBezierCurve(curve);
2142 AddPoints(pts);
2143 FLastCoord := PointF(x,y);
2144end;
2145
2146procedure TBGRACanvas2D.quadraticCurveTo(constref cp, pt: TPointF);
2147begin
2148 quadraticCurveTo(cp.x,cp.y,pt.x,pt.y);
2149end;
2150
2151procedure TBGRACanvas2D.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y: single);
2152var
2153 curve : TCubicBezierCurve;
2154 pts : array of TPointF;
2155begin
2156 curve := BezierCurve(ApplyTransform(GetPenPos(cp1x,cp1y)),ApplyTransform(PointF(cp1x,cp1y)),
2157 ApplyTransform(PointF(cp2x,cp2y)),ApplyTransform(PointF(x,y)));
2158 pts := BGRAPath.ComputeBezierCurve(curve);
2159 AddPoints(pts);
2160 FLastCoord := PointF(x,y);
2161end;
2162
2163procedure TBGRACanvas2D.bezierCurveTo(constref cp1, cp2, pt: TPointF);
2164begin
2165 bezierCurveTo(cp1.x,cp1.y,cp2.x,cp2.y,pt.x,pt.y);
2166end;
2167
2168procedure TBGRACanvas2D.rect(x, y, w, h: single);
2169begin
2170 MoveTo(x,y);
2171 LineTo(x+w,y);
2172 LineTo(x+w,y+h);
2173 LineTo(x,y+h);
2174 closePath;
2175end;
2176
2177procedure TBGRACanvas2D.roundRect(x, y, w, h, radius: single);
2178begin
2179 if radius <= 0 then
2180 begin
2181 rect(x,y,w,h);
2182 exit;
2183 end;
2184 if (w <= 0) or (h <= 0) then exit;
2185 if radius*2 > w then radius := w/2;
2186 if radius*2 > h then radius := h/2;
2187 moveTo(x+radius,y);
2188 arcTo(PointF(x+w,y),PointF(x+w,y+h), radius);
2189 arcTo(PointF(x+w,y+h),PointF(x,y+h), radius);
2190 arcTo(PointF(x,y+h),PointF(x,y), radius);
2191 arcTo(PointF(x,y),PointF(x+w,y), radius);
2192 closePath;
2193end;
2194
2195procedure TBGRACanvas2D.roundRect(x, y, w, h, rx, ry: single);
2196begin
2197 if (w <= 0) or (h <= 0) then exit;
2198 if rx < 0 then rx := 0;
2199 if ry < 0 then ry := 0;
2200 if (rx = 0) and (ry = 0) then
2201 begin
2202 rect(x,y,w,h);
2203 exit;
2204 end;
2205 if rx*2 > w then rx := w/2;
2206 if ry*2 > h then ry := h/2;
2207 moveTo(x+rx,y);
2208 lineTo(x+w-rx,y);
2209 arcTo(rx,ry,0,false,false,x+w,y+ry);
2210 lineTo(x+w,y+h-ry);
2211 arcTo(rx,ry,0,false,false,x+w-rx,y+h);
2212 lineTo(x+rx,y+h);
2213 arcTo(rx,ry,0,false,false,x,y+h-ry);
2214 lineTo(x,y+ry);
2215 arcTo(rx,ry,0,false,false,x+rx,y);
2216 closePath;
2217end;
2218
2219procedure TBGRACanvas2D.openedSpline(const pts: array of TPointF;
2220 style: TSplineStyle);
2221var transf: array of TPointF;
2222begin
2223 if length(pts)=0 then exit;
2224 transf := ApplyTransform(pts);
2225 transf := BGRAPath.ComputeOpenedSpline(transf,style);
2226 AddPoints(transf);
2227 FLastCoord := pts[high(pts)];
2228end;
2229
2230procedure TBGRACanvas2D.closedSpline(const pts: array of TPointF;
2231 style: TSplineStyle);
2232var transf: array of TPointF;
2233begin
2234 if length(pts)=0 then exit;
2235 transf := ApplyTransform(pts);
2236 transf := BGRAPath.ComputeClosedSpline(slice(transf, length(transf)-1),style);
2237 AddPoints(transf);
2238 FLastCoord := pts[high(pts)];
2239end;
2240
2241procedure TBGRACanvas2D.spline(const pts: array of TPointF; style: TSplineStyle);
2242var transf: array of TPointF;
2243begin
2244 if length(pts)=0 then exit;
2245 transf := ApplyTransform(pts);
2246 if (pts[0] = pts[high(pts)]) and (length(pts) > 1) then
2247 transf := BGRAPath.ComputeClosedSpline(slice(transf, length(transf)-1),style)
2248 else
2249 transf := BGRAPath.ComputeOpenedSpline(transf,style);
2250 AddPoints(transf);
2251 FLastCoord := pts[high(pts)];
2252end;
2253
2254procedure TBGRACanvas2D.splineTo(const pts: array of TPointF;
2255 style: TSplineStyle);
2256var transf: array of TPointF;
2257 i: Integer;
2258begin
2259 if length(pts) = 0 then exit;
2260 transf := ApplyTransform(pts);
2261 if FPathPointCount <> 0 then
2262 begin
2263 setlength(transf,length(transf)+1);
2264 for i := high(transf) downto 1 do
2265 transf[i]:= transf[i-1];
2266 transf[0] := ApplyTransform(GetPenPos(pts[0].x,pts[0].y));
2267 end;
2268 transf := BGRAPath.ComputeOpenedSpline(transf,style);
2269 AddPoints(transf);
2270 FLastCoord := pts[high(pts)];
2271end;
2272
2273procedure TBGRACanvas2D.arc(x, y, radius, startAngleRadCW, endAngleRadCW: single;
2274 anticlockwise: boolean);
2275var pts: array of TPointF;
2276 temp: single;
2277 pt: TPointF;
2278 rx,ry: single;
2279 len1,len2: single;
2280 unitAffine: TAffineMatrix;
2281 v1orig,v2orig,v1ortho,v2ortho: TPointF;
2282 startRadCCW,endRadCCW: single;
2283begin
2284 v1orig := PointF(currentState.matrix[1,1],currentState.matrix[2,1]);
2285 v2orig := PointF(currentState.matrix[1,2],currentState.matrix[2,2]);
2286 len1 := VectLen(v1orig);
2287 len2 := VectLen(v2orig);
2288 rx := len1*radius;
2289 ry := len2*radius;
2290 if len1 <> 0 then v1ortho := v1orig * (1/len1) else v1ortho := v1orig;
2291 if len2 <> 0 then v2ortho := v2orig * (1/len2) else v2ortho := v2orig;
2292 pt := currentState.matrix* PointF(x,y);
2293 unitAffine := AffineMatrix(v1ortho.x, v2ortho.x, pt.x,
2294 v1ortho.y, v2ortho.y, pt.y);
2295 startRadCCW := -startAngleRadCW;
2296 endRadCCW := -endAngleRadCW;
2297 if not anticlockwise then
2298 begin
2299 temp := startRadCCW;
2300 startRadCCW := endRadCCW;
2301 endRadCCW:= temp;
2302 pts := BGRAPath.ComputeArcRad(0,0,rx,ry,startRadCCW,endRadCCW);
2303 pts := ApplyTransform(pts,unitAffine);
2304 AddPointsRev(pts);
2305 end else
2306 begin
2307 pts := BGRAPath.ComputeArcRad(0,0,rx,ry,startRadCCW,endRadCCW);
2308 pts := ApplyTransform(pts,unitAffine);
2309 AddPoints(pts);
2310 end;
2311 FLastCoord := ArcEndPoint(ArcDef(x,y,radius,radius,0,startAngleRadCW,endAngleRadCW,anticlockwise));
2312end;
2313
2314procedure TBGRACanvas2D.arc(x, y, radius, startAngleRadCW, endAngleRadCW: single);
2315begin
2316 arc(x,y,radius,startAngleRadCW,endAngleRadCW,false);
2317end;
2318
2319procedure TBGRACanvas2D.arc(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single;
2320 anticlockwise: boolean);
2321begin
2322 arc(ArcDef(cx,cy,rx,ry,xAngleRadCW,startAngleRadCW,endAngleRadCW,anticlockwise))
2323end;
2324
2325procedure TBGRACanvas2D.arc(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single);
2326begin
2327 arc(ArcDef(cx,cy,rx,ry,xAngleRadCW,startAngleRadCW,endAngleRadCW,false))
2328end;
2329
2330procedure TBGRACanvas2D.arc(constref arcDef: TArcDef);
2331var previousMatrix: TAffineMatrix;
2332begin
2333 if (arcDef.radius.x = 0) and (arcDef.radius.y = 0) then
2334 lineTo(arcDef.center) else
2335 begin
2336 previousMatrix := currentState.matrix;
2337 translate(arcDef.center.x,arcDef.center.y);
2338 rotate(arcDef.xAngleRadCW);
2339 scale(arcDef.radius.x,arcDef.radius.y);
2340 arc(0,0,1,arcDef.startAngleRadCW,arcDef.endAngleRadCW,arcDef.anticlockwise);
2341 currentState.matrix := previousMatrix;
2342 FLastCoord := ArcEndPoint(arcDef);
2343 end;
2344end;
2345
2346procedure TBGRACanvas2D.arcTo(x1, y1, x2, y2, radius: single);
2347var p0: TPointF;
2348begin
2349 p0 := GetPenPos(x1,y1);
2350 arc(Html5ArcTo(p0,PointF(x1,y1),PointF(x2,y2),radius));
2351end;
2352
2353procedure TBGRACanvas2D.arcTo(p1, p2: TPointF; radius: single);
2354begin
2355 arcTo(p1.x,p1.y,p2.x,p2.y,radius);
2356end;
2357
2358procedure TBGRACanvas2D.arcTo(rx, ry, xAngleRadCW: single; largeArc,
2359 anticlockwise: boolean; x, y: single);
2360begin
2361 arc(SvgArcTo(GetPenPos(x,y), rx,ry, xAngleRadCW, largeArc, anticlockwise, PointF(x,y)));
2362 FLastCoord := PointF(x,y);
2363end;
2364
2365procedure TBGRACanvas2D.circle(x, y, r: single);
2366begin
2367 arc(x,y,r,0,0);
2368end;
2369
2370procedure TBGRACanvas2D.ellipse(x, y, rx, ry: single);
2371begin
2372 arc(x,y,rx,ry,0,0,0);
2373end;
2374
2375procedure TBGRACanvas2D.text(AText: string; x, y: single);
2376var renderer : TBGRACustomFontRenderer;
2377 previousMatrix: TAffineMatrix;
2378 fva: TFontVerticalAnchor;
2379begin
2380 renderer := fontRenderer;
2381 if renderer = nil then exit;
2382 if renderer.FontEmHeight <= 0 then exit;
2383
2384 case currentState.textBaseline of
2385 'bottom': fva := fvaBottom;
2386 'middle': fva := fvaCenter;
2387 'alphabetic': fva := fvaBaseline;
2388 else {'top','hanging'}
2389 fva := fvaTop;
2390 end;
2391
2392 if renderer.HandlesTextPath then
2393 begin
2394 previousMatrix := currentState.matrix;
2395 translate(x,y);
2396 scale(currentState.fontEmHeight/renderer.FontEmHeight);
2397 if fva <> fvaTop then
2398 with renderer.GetFontPixelMetric do
2399 case fva of
2400 fvaBottom: translate(0,-Lineheight);
2401 fvaCenter: translate(0,-Lineheight/2);
2402 fvaBaseline: translate(0,-baseline);
2403 end;
2404 renderer.CopyTextPathTo(self, 0,0, AText, textAlignLCL);
2405 currentState.matrix := previousMatrix;
2406 end else
2407 begin
2408 setlength(FTextPaths, length(FTextPaths)+1);
2409 FTextPaths[high(FTextPaths)].Text := AText;
2410 FTextPaths[high(FTextPaths)].FontName := fontName;
2411 FTextPaths[high(FTextPaths)].FontMatrix := currentState.matrix*AffineMatrixTranslation(x,y)*AffineMatrixScale(fontEmHeight,fontEmHeight);
2412 FTextPaths[high(FTextPaths)].FontStyle := fontStyle;
2413 FTextPaths[high(FTextPaths)].FontAlign := textAlignLCL;
2414 FTextPaths[high(FTextPaths)].FontAnchor := fva;
2415 end;
2416
2417 FLastCoord := EmptyPointF;
2418 FStartCoord := EmptyPointF;
2419end;
2420
2421procedure TBGRACanvas2D.fillText(AText: string; x, y: single);
2422begin
2423 beginPath;
2424 text(AText,x,y);
2425 fill;
2426 beginPath;
2427end;
2428
2429procedure TBGRACanvas2D.strokeText(AText: string; x, y: single);
2430begin
2431 beginPath;
2432 text(AText,x,y);
2433 stroke;
2434 beginPath;
2435end;
2436
2437function TBGRACanvas2D.measureText(AText: string): TCanvas2dTextSize;
2438var renderer: TBGRACustomFontRenderer;
2439begin
2440 renderer := fontRenderer;
2441 if renderer <> nil then
2442 begin
2443 with renderer.TextSize(AText) do
2444 begin
2445 result.width := cx;
2446 result.height:= cy;
2447 end;
2448 end
2449 else
2450 begin
2451 result.width := 0;
2452 result.height := 0;
2453 end;
2454end;
2455
2456procedure TBGRACanvas2D.fill;
2457begin
2458 if FPathPointCount > 0 then
2459 FillPoly(slice(FPathPoints,FPathPointCount));
2460 FillTexts(false);
2461end;
2462
2463procedure TBGRACanvas2D.stroke;
2464begin
2465 if FPathPointCount > 0 then
2466 StrokePoly(slice(FPathPoints,FPathPointCount));
2467end;
2468
2469procedure TBGRACanvas2D.fillOverStroke;
2470begin
2471 if FPathPointCount > 0 then
2472 FillStrokePoly(slice(FPathPoints,FPathPointCount),true);
2473 FillTexts(false);
2474end;
2475
2476procedure TBGRACanvas2D.strokeOverFill;
2477begin
2478 FillTexts(false);
2479 if FPathPointCount > 0 then
2480 FillStrokePoly(slice(FPathPoints,FPathPointCount),false);
2481end;
2482
2483procedure TBGRACanvas2D.clearPath;
2484begin
2485 if FPathPointCount > 0 then
2486 ClearPoly(slice(FPathPoints,FPathPointCount));
2487 FillTexts(true);
2488end;
2489
2490procedure TBGRACanvas2D.clip;
2491var
2492 tempBmp: TBGRACustomBitmap;
2493begin
2494 if FPathPointCount = 0 then
2495 begin
2496 currentState.clipMaskReadWrite.Fill(BGRABlack);
2497 exit;
2498 end;
2499 if currentState.clipMaskReadOnly = nil then
2500 currentState.SetClipMask(surface.NewBitmap(width,height,BGRAWhite),True);
2501 tempBmp := surface.NewBitmap(width,height,BGRABlack);
2502 if antialiasing then
2503 tempBmp.FillPolyAntialias(slice(FPathPoints,FPathPointCount),BGRAWhite)
2504 else
2505 tempBmp.FillPoly(slice(FPathPoints,FPathPointCount),BGRAWhite,dmSet);
2506 currentState.clipMaskReadWrite.BlendImage(0,0,tempBmp,boDarken);
2507 tempBmp.Free;
2508end;
2509
2510procedure TBGRACanvas2D.unclip;
2511begin
2512 if FPathPointCount = 0 then exit;
2513 if currentState.clipMaskReadOnly = nil then exit;
2514 if antialiasing then
2515 currentState.clipMaskReadWrite.FillPolyAntialias(slice(FPathPoints,FPathPointCount),BGRAWhite)
2516 else
2517 currentState.clipMaskReadWrite.FillPoly(slice(FPathPoints,FPathPointCount),BGRAWhite,dmSet);
2518 if currentState.clipMaskReadOnly.Equals(BGRAWhite) then
2519 currentState.SetClipMask(nil,true);
2520end;
2521
2522function TBGRACanvas2D.isPointInPath(x, y: single): boolean;
2523begin
2524 result := isPointInPath(PointF(x,y));
2525end;
2526
2527function TBGRACanvas2D.isPointInPath(pt: TPointF): boolean;
2528begin
2529 if FPathPointCount <= 2 then
2530 result := false
2531 else
2532 begin
2533 setlength(FPathPoints,FPathPointCount);
2534 result := IsPointInPolygon(FPathPoints,pt+FCanvasOffset, fillMode = fmWinding);
2535 end;
2536end;
2537
2538procedure TBGRACanvas2D.drawImage(image: TBGRACustomBitmap; dx, dy: single);
2539begin
2540 Surface.PutImageAffine(ApplyTransform(PointF(dx,dy))+PointF(0.5,0.5),
2541 ApplyTransform(PointF(dx+image.width,dy))+PointF(0.5,0.5),
2542 ApplyTransform(PointF(dx,dy+image.height))+PointF(0.5,0.5), image, currentState.globalAlpha);
2543end;
2544
2545procedure TBGRACanvas2D.drawImage(image: TBGRACustomBitmap; dx, dy, dw, dh: single);
2546begin
2547 Surface.PutImageAffine(ApplyTransform(PointF(dx,dy))+PointF(0.5,0.5),
2548 ApplyTransform(PointF(dx+dw,dy))+PointF(0.5,0.5),
2549 ApplyTransform(PointF(dx,dy+dh))+PointF(0.5,0.5), image, currentState.globalAlpha);
2550end;
2551
2552end.
2553
Note: See TracBrowser for help on using the repository browser.