1 | unit 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 |
|
---|
17 | interface
|
---|
18 |
|
---|
19 | uses
|
---|
20 | Classes, SysUtils, BGRAGraphics, BGRABitmapTypes, BGRATransform,
|
---|
21 | BGRAGradientScanner, BGRAPath, BGRAPen;
|
---|
22 |
|
---|
23 | type
|
---|
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 |
|
---|
334 | implementation
|
---|
335 |
|
---|
336 | uses Types, Math, BGRAFillInfo, BGRAPolygon, BGRABlend, FPWriteJPEG, FPWriteBMP, base64;
|
---|
337 |
|
---|
338 | type
|
---|
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 |
|
---|
418 | function TBGRACanvasPattern2D.GetTexture: IBGRAScanner;
|
---|
419 | begin
|
---|
420 | if ownScanner then
|
---|
421 | result := scanner
|
---|
422 | else
|
---|
423 | result := foreignInterface;
|
---|
424 | end;
|
---|
425 |
|
---|
426 | constructor TBGRACanvasPattern2D.Create(source: TBGRACustomBitmap; repeatX,
|
---|
427 | repeatY: boolean; Origin, HAxis, VAxis: TPointF);
|
---|
428 | var
|
---|
429 | affine: TBGRAAffineBitmapTransform;
|
---|
430 | begin
|
---|
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;
|
---|
453 | end;
|
---|
454 |
|
---|
455 | constructor TBGRACanvasPattern2D.Create(source: IBGRAScanner;
|
---|
456 | transformation: TAffineMatrix);
|
---|
457 | var
|
---|
458 | affine : TBGRAAffineScannerTransform;
|
---|
459 | begin
|
---|
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;
|
---|
485 | end;
|
---|
486 |
|
---|
487 | destructor TBGRACanvasPattern2D.Destroy;
|
---|
488 | begin
|
---|
489 | fillchar(foreignInterface,sizeof(foreignInterface),0);
|
---|
490 | if ownScanner then FreeAndNil(scanner);
|
---|
491 | inherited Destroy;
|
---|
492 | end;
|
---|
493 |
|
---|
494 | { TBGRACanvasLinearGradient2D }
|
---|
495 |
|
---|
496 | procedure TBGRACanvasLinearGradient2D.CreateScanner;
|
---|
497 | var GradientOwner: boolean;
|
---|
498 | GradientColors: TBGRACustomGradient;
|
---|
499 | begin
|
---|
500 | GetBGRAGradient(GradientColors,GradientOwner);
|
---|
501 | scanner := TBGRAGradientScanner.Create(GradientColors,gtLinear,o1,o2,False,GradientOwner);
|
---|
502 | scanner.Transform := FTransform;
|
---|
503 | end;
|
---|
504 |
|
---|
505 | constructor TBGRACanvasLinearGradient2D.Create(x0, y0, x1, y1: single; transform: TAffineMatrix);
|
---|
506 | begin
|
---|
507 | o1 := PointF(x0,y0);
|
---|
508 | o2 := PointF(x1,y1);
|
---|
509 | FTransform := transform;
|
---|
510 | end;
|
---|
511 |
|
---|
512 | constructor TBGRACanvasLinearGradient2D.Create(p0, p1: TPointF; transform: TAffineMatrix);
|
---|
513 | begin
|
---|
514 | o1 := p0;
|
---|
515 | o2 := p1;
|
---|
516 | FTransform := transform;
|
---|
517 | end;
|
---|
518 |
|
---|
519 | { TBGRACanvasRadialGradient2D }
|
---|
520 |
|
---|
521 | procedure TBGRACanvasRadialGradient2D.CreateScanner;
|
---|
522 | var GradientOwner: boolean;
|
---|
523 | GradientColors: TBGRACustomGradient;
|
---|
524 | begin
|
---|
525 | GetBGRAGradient(GradientColors,GradientOwner);
|
---|
526 | scanner := TBGRAGradientScanner.Create(GradientColors,c0,cr0,c1,cr1,GradientOwner);
|
---|
527 | scanner.FlipGradient := not FFlipGradient;
|
---|
528 | scanner.Transform := FTransform;
|
---|
529 | end;
|
---|
530 |
|
---|
531 | constructor TBGRACanvasRadialGradient2D.Create(x0, y0, r0, x1, y1, r1: single;
|
---|
532 | transform: TAffineMatrix; flipGradient: boolean);
|
---|
533 | begin
|
---|
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;
|
---|
540 | end;
|
---|
541 |
|
---|
542 | constructor TBGRACanvasRadialGradient2D.Create(p0: TPointF; r0: single;
|
---|
543 | p1: TPointF; r1: single; transform: TAffineMatrix; flipGradient: boolean);
|
---|
544 | begin
|
---|
545 | self.c0 := p0;
|
---|
546 | self.cr0 := r0;
|
---|
547 | self.c1 := p1;
|
---|
548 | self.cr1 := r1;
|
---|
549 | FTransform := transform;
|
---|
550 | FFlipGradient := flipGradient;
|
---|
551 | end;
|
---|
552 |
|
---|
553 | { TBGRACanvasGradient2D }
|
---|
554 |
|
---|
555 | function TBGRACanvasGradient2D.getTexture: IBGRAScanner;
|
---|
556 | begin
|
---|
557 | if scanner = nil then CreateScanner;
|
---|
558 | result := scanner;
|
---|
559 | end;
|
---|
560 |
|
---|
561 | function TBGRACanvasGradient2D.GetGammaCorrection: boolean;
|
---|
562 | begin
|
---|
563 | result := FGammaCorrection;
|
---|
564 | end;
|
---|
565 |
|
---|
566 | procedure TBGRACanvasGradient2D.SetGammaCorrection(AValue: boolean);
|
---|
567 | begin
|
---|
568 | FGammaCorrection:= AValue;
|
---|
569 | FreeAndNil(scanner);
|
---|
570 | end;
|
---|
571 |
|
---|
572 | constructor TBGRACanvasGradient2D.Create;
|
---|
573 | begin
|
---|
574 | inherited Create;
|
---|
575 | scanner := nil;
|
---|
576 | FGammaCorrection:= false;
|
---|
577 | end;
|
---|
578 |
|
---|
579 | function TBGRACanvasGradient2D.getColorArray: TGradientArrayOfColors;
|
---|
580 | var
|
---|
581 | i: Integer;
|
---|
582 | begin
|
---|
583 | setlength(result, nbColorStops);
|
---|
584 | for i := 0 to nbColorStops-1 do
|
---|
585 | result[i] := colorStops[i].color;
|
---|
586 | end;
|
---|
587 |
|
---|
588 | function TBGRACanvasGradient2D.getPositionArray: TGradientArrayOfPositions;
|
---|
589 | var
|
---|
590 | i: Integer;
|
---|
591 | begin
|
---|
592 | setlength(result, nbColorStops);
|
---|
593 | for i := 0 to nbColorStops-1 do
|
---|
594 | result[i] := colorStops[i].position;
|
---|
595 | end;
|
---|
596 |
|
---|
597 | procedure TBGRACanvasGradient2D.GetBGRAGradient(out
|
---|
598 | ABGRAGradient: TBGRACustomGradient; out AOwned: boolean);
|
---|
599 | begin
|
---|
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;
|
---|
617 | end;
|
---|
618 |
|
---|
619 | destructor TBGRACanvasGradient2D.Destroy;
|
---|
620 | begin
|
---|
621 | FreeAndNil(scanner);
|
---|
622 | inherited Destroy;
|
---|
623 | end;
|
---|
624 |
|
---|
625 | procedure TBGRACanvasGradient2D.addColorStop(APosition: single;
|
---|
626 | AColor: TBGRAPixel);
|
---|
627 | begin
|
---|
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);
|
---|
638 | end;
|
---|
639 |
|
---|
640 | procedure TBGRACanvasGradient2D.addColorStop(APosition: single; AColor: TColor
|
---|
641 | );
|
---|
642 | begin
|
---|
643 | addColorStop(APosition, ColorToBGRA(ColorToRGB(AColor)));
|
---|
644 | end;
|
---|
645 |
|
---|
646 | procedure TBGRACanvasGradient2D.addColorStop(APosition: single; AColor: string
|
---|
647 | );
|
---|
648 | begin
|
---|
649 | addColorStop(APosition, StrToBGRA(AColor));
|
---|
650 | end;
|
---|
651 |
|
---|
652 | procedure TBGRACanvasGradient2D.setColors(ACustomGradient: TBGRACustomGradient
|
---|
653 | );
|
---|
654 | begin
|
---|
655 | FCustomGradient := ACustomGradient;
|
---|
656 | end;
|
---|
657 |
|
---|
658 | { TBGRACanvasState2D }
|
---|
659 |
|
---|
660 | function TBGRACanvasState2D.GetClipMaskReadWrite: TBGRACustomBitmap;
|
---|
661 | begin
|
---|
662 | if not FClipMaskOwned then
|
---|
663 | begin
|
---|
664 | if FClipMask <> nil then
|
---|
665 | FClipMask := FClipMask.Duplicate;
|
---|
666 | FClipMaskOwned := true;
|
---|
667 | end;
|
---|
668 | result := FClipMask;
|
---|
669 | end;
|
---|
670 |
|
---|
671 | constructor TBGRACanvasState2D.Create(AMatrix: TAffineMatrix;
|
---|
672 | AClipMask: TBGRACustomBitmap; AClipMaskOwned: boolean);
|
---|
673 | begin
|
---|
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);
|
---|
702 | end;
|
---|
703 |
|
---|
704 | function TBGRACanvasState2D.Duplicate: TBGRACanvasState2D;
|
---|
705 | begin
|
---|
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;
|
---|
730 | end;
|
---|
731 |
|
---|
732 | destructor TBGRACanvasState2D.Destroy;
|
---|
733 | begin
|
---|
734 | if FClipMaskOwned and Assigned(FClipMask) then
|
---|
735 | FClipMask.Free;
|
---|
736 | penStroker.Free;
|
---|
737 | inherited Destroy;
|
---|
738 | end;
|
---|
739 |
|
---|
740 | procedure TBGRACanvasState2D.SetClipMask(AClipMask: TBGRACustomBitmap;
|
---|
741 | AOwned: boolean);
|
---|
742 | begin
|
---|
743 | if FClipMaskOwned and Assigned(FClipMask) then FreeAndNil(FClipMask);
|
---|
744 | FClipMask := AClipMask;
|
---|
745 | FClipMaskOwned := AOwned;
|
---|
746 | end;
|
---|
747 |
|
---|
748 | { TBGRACanvas2D }
|
---|
749 |
|
---|
750 | function TBGRACanvas2D.GetHeight: Integer;
|
---|
751 | begin
|
---|
752 | if Assigned(surface) then
|
---|
753 | result := Surface.Height
|
---|
754 | else
|
---|
755 | result := 0;
|
---|
756 | end;
|
---|
757 |
|
---|
758 | function TBGRACanvas2D.GetLineCap: string;
|
---|
759 | begin
|
---|
760 | case currentState.penStroker.LineCap of
|
---|
761 | pecRound: result := 'round';
|
---|
762 | pecSquare: result := 'square';
|
---|
763 | else result := 'butt';
|
---|
764 | end;
|
---|
765 | end;
|
---|
766 |
|
---|
767 | function TBGRACanvas2D.GetLineCapLCL: TPenEndCap;
|
---|
768 | begin
|
---|
769 | result := currentState.penStroker.LineCap;
|
---|
770 | end;
|
---|
771 |
|
---|
772 | function TBGRACanvas2D.GetlineJoin: string;
|
---|
773 | begin
|
---|
774 | case currentState.penStroker.JoinStyle of
|
---|
775 | pjsBevel: result := 'bevel';
|
---|
776 | pjsRound: result := 'round';
|
---|
777 | else result := 'miter';
|
---|
778 | end;
|
---|
779 | end;
|
---|
780 |
|
---|
781 | function TBGRACanvas2D.GetlineJoinLCL: TPenJoinStyle;
|
---|
782 | begin
|
---|
783 | result := currentState.penStroker.JoinStyle;
|
---|
784 | end;
|
---|
785 |
|
---|
786 | function TBGRACanvas2D.getLineStyle: TBGRAPenStyle;
|
---|
787 | begin
|
---|
788 | result := DuplicatePenStyle(currentState.penStroker.CustomPenStyle);
|
---|
789 | end;
|
---|
790 |
|
---|
791 | function TBGRACanvas2D.GetLineWidth: single;
|
---|
792 | begin
|
---|
793 | result := currentState.lineWidth;
|
---|
794 | end;
|
---|
795 |
|
---|
796 | function TBGRACanvas2D.GetMatrix: TAffineMatrix;
|
---|
797 | begin
|
---|
798 | result := currentState.matrix;
|
---|
799 | end;
|
---|
800 |
|
---|
801 | function TBGRACanvas2D.GetMiterLimit: single;
|
---|
802 | begin
|
---|
803 | result := currentState.penStroker.MiterLimit;
|
---|
804 | end;
|
---|
805 |
|
---|
806 | function TBGRACanvas2D.GetPixelCenteredCoordinates: boolean;
|
---|
807 | begin
|
---|
808 | result := FPixelCenteredCoordinates;
|
---|
809 | end;
|
---|
810 |
|
---|
811 | function TBGRACanvas2D.GetShadowBlur: single;
|
---|
812 | begin
|
---|
813 | result := currentState.shadowBlur;
|
---|
814 | end;
|
---|
815 |
|
---|
816 | function TBGRACanvas2D.GetShadowFastest: boolean;
|
---|
817 | begin
|
---|
818 | result := currentState.shadowFastest;
|
---|
819 | end;
|
---|
820 |
|
---|
821 | function TBGRACanvas2D.GetShadowOffset: TPointF;
|
---|
822 | begin
|
---|
823 | result := PointF(shadowOffsetX,shadowOffsetY);
|
---|
824 | end;
|
---|
825 |
|
---|
826 | function TBGRACanvas2D.GetShadowOffsetX: single;
|
---|
827 | begin
|
---|
828 | result := currentState.shadowOffsetX;
|
---|
829 | end;
|
---|
830 |
|
---|
831 | function TBGRACanvas2D.GetShadowOffsetY: single;
|
---|
832 | begin
|
---|
833 | result := currentState.shadowOffsetY;
|
---|
834 | end;
|
---|
835 |
|
---|
836 | function TBGRACanvas2D.GetStrokeMatrix: TAffineMatrix;
|
---|
837 | begin
|
---|
838 | result := currentState.penStroker.StrokeMatrix;
|
---|
839 | end;
|
---|
840 |
|
---|
841 | function TBGRACanvas2D.GetTextAlign: string;
|
---|
842 | begin
|
---|
843 | case currentState.textAlign of
|
---|
844 | taRightJustify: result := 'right';
|
---|
845 | taCenter: result := 'center';
|
---|
846 | else
|
---|
847 | result := 'left';
|
---|
848 | end;
|
---|
849 | end;
|
---|
850 |
|
---|
851 | function TBGRACanvas2D.GetTextAlignLCL: TAlignment;
|
---|
852 | begin
|
---|
853 | result := currentState.textAlign;
|
---|
854 | end;
|
---|
855 |
|
---|
856 | function TBGRACanvas2D.GetTextBaseline: string;
|
---|
857 | begin
|
---|
858 | result := currentState.textBaseline;
|
---|
859 | end;
|
---|
860 |
|
---|
861 | function TBGRACanvas2D.GetGlobalAlpha: single;
|
---|
862 | begin
|
---|
863 | result := currentState.globalAlpha/255;
|
---|
864 | end;
|
---|
865 |
|
---|
866 | function TBGRACanvas2D.GetCurrentPathAsPoints: ArrayOfTPointF;
|
---|
867 | var i: integer;
|
---|
868 | begin
|
---|
869 | setlength(result, FPathPointCount);
|
---|
870 | for i := 0 to high(result) do
|
---|
871 | result[i] := FPathPoints[i];
|
---|
872 | end;
|
---|
873 |
|
---|
874 | function TBGRACanvas2D.GetFontName: string;
|
---|
875 | begin
|
---|
876 | result := currentState.fontName;
|
---|
877 | end;
|
---|
878 |
|
---|
879 | function TBGRACanvas2D.GetFontRenderer: TBGRACustomFontRenderer;
|
---|
880 | var zoom1,zoom2,zoom: single;
|
---|
881 | begin
|
---|
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;
|
---|
904 | end;
|
---|
905 |
|
---|
906 | function TBGRACanvas2D.GetFontEmHeight: single;
|
---|
907 | begin
|
---|
908 | result := currentState.fontEmHeight;
|
---|
909 | end;
|
---|
910 |
|
---|
911 | function TBGRACanvas2D.GetFontString: string;
|
---|
912 | var formats: TFormatSettings;
|
---|
913 | begin
|
---|
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);
|
---|
925 | end;
|
---|
926 |
|
---|
927 | function TBGRACanvas2D.GetFontStyle: TFontStyles;
|
---|
928 | begin
|
---|
929 | result := currentState.fontStyle;
|
---|
930 | end;
|
---|
931 |
|
---|
932 | function TBGRACanvas2D.GetHasShadow: boolean;
|
---|
933 | begin
|
---|
934 | result := (ApplyGlobalAlpha(currentState.shadowColor).alpha <> 0) and
|
---|
935 | ( (currentState.shadowBlur <> 0) or (currentState.shadowOffsetX <> 0)
|
---|
936 | or (currentState.shadowOffsetY <> 0) );
|
---|
937 | end;
|
---|
938 |
|
---|
939 | function TBGRACanvas2D.GetWidth: Integer;
|
---|
940 | begin
|
---|
941 | if Assigned(Surface) then
|
---|
942 | result := Surface.Width
|
---|
943 | else
|
---|
944 | result := 0;
|
---|
945 | end;
|
---|
946 |
|
---|
947 | procedure TBGRACanvas2D.SetFontName(AValue: string);
|
---|
948 | begin
|
---|
949 | currentState.fontName := AValue;
|
---|
950 | end;
|
---|
951 |
|
---|
952 | procedure TBGRACanvas2D.SetFontRenderer(AValue: TBGRACustomFontRenderer);
|
---|
953 | begin
|
---|
954 | if AValue = FFontRenderer then exit;
|
---|
955 | FreeAndNil(FFontRenderer);
|
---|
956 | FFontRenderer := AValue;
|
---|
957 | end;
|
---|
958 |
|
---|
959 | procedure TBGRACanvas2D.SetFontEmHeight(AValue: single);
|
---|
960 | begin
|
---|
961 | currentState.fontEmHeight := AValue;
|
---|
962 | end;
|
---|
963 |
|
---|
964 | procedure TBGRACanvas2D.SetFontString(AValue: string);
|
---|
965 | var idxSpace,errPos: integer;
|
---|
966 | attrib,u: string;
|
---|
967 | value: single;
|
---|
968 | begin
|
---|
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;
|
---|
1022 | end;
|
---|
1023 |
|
---|
1024 | procedure TBGRACanvas2D.SetFontStyle(AValue: TFontStyles);
|
---|
1025 | begin
|
---|
1026 | currentState.fontStyle:= AValue;
|
---|
1027 | end;
|
---|
1028 |
|
---|
1029 | procedure TBGRACanvas2D.SetGlobalAlpha(const AValue: single);
|
---|
1030 | begin
|
---|
1031 | if AValue < 0 then currentState.globalAlpha:= 0 else
|
---|
1032 | if AValue > 1 then currentState.globalAlpha:= 255 else
|
---|
1033 | currentState.globalAlpha:= round(AValue*255);
|
---|
1034 | end;
|
---|
1035 |
|
---|
1036 | procedure TBGRACanvas2D.SetLineCap(const AValue: string);
|
---|
1037 | begin
|
---|
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;
|
---|
1044 | end;
|
---|
1045 |
|
---|
1046 | procedure TBGRACanvas2D.SetLineCapLCL(AValue: TPenEndCap);
|
---|
1047 | begin
|
---|
1048 | currentState.penStroker.LineCap := AValue;
|
---|
1049 | end;
|
---|
1050 |
|
---|
1051 | procedure TBGRACanvas2D.SetLineJoin(const AValue: string);
|
---|
1052 | begin
|
---|
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;
|
---|
1059 | end;
|
---|
1060 |
|
---|
1061 | procedure TBGRACanvas2D.FillPoly(const points: array of TPointF);
|
---|
1062 | var
|
---|
1063 | bfill: boolean;
|
---|
1064 | tempScan: TBGRACustomScanner;
|
---|
1065 | begin
|
---|
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;
|
---|
1108 | end;
|
---|
1109 |
|
---|
1110 | procedure TBGRACanvas2D.FillStrokePoly(const points: array of TPointF;
|
---|
1111 | fillOver: boolean);
|
---|
1112 | var
|
---|
1113 | tempScan,tempScan2: TBGRACustomScanner;
|
---|
1114 | multi: TBGRAMultishapeFiller;
|
---|
1115 | contour : array of TPointF;
|
---|
1116 | texture: IBGRAScanner;
|
---|
1117 | idxContour: Integer;
|
---|
1118 | begin
|
---|
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;
|
---|
1178 | end;
|
---|
1179 |
|
---|
1180 | procedure TBGRACanvas2D.FillTexts(AErase: boolean);
|
---|
1181 | var
|
---|
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;
|
---|
1192 | begin
|
---|
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;
|
---|
1302 | end;
|
---|
1303 |
|
---|
1304 | procedure TBGRACanvas2D.SetLineJoinLCL(AValue: TPenJoinStyle);
|
---|
1305 | begin
|
---|
1306 | currentState.penStroker.JoinStyle := AValue;
|
---|
1307 | end;
|
---|
1308 |
|
---|
1309 | procedure TBGRACanvas2D.lineStyle(const AValue: array of single);
|
---|
1310 | begin
|
---|
1311 | currentState.penStroker.CustomPenStyle := DuplicatePenStyle(AValue);
|
---|
1312 | end;
|
---|
1313 |
|
---|
1314 | procedure TBGRACanvas2D.lineStyle(AStyle: TPenStyle);
|
---|
1315 | begin
|
---|
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;
|
---|
1324 | end;
|
---|
1325 |
|
---|
1326 | function 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};
|
---|
1327 | begin
|
---|
1328 | if GetInterface(iid, obj) then
|
---|
1329 | Result := S_OK
|
---|
1330 | else
|
---|
1331 | Result := longint(E_NOINTERFACE);
|
---|
1332 | end;
|
---|
1333 |
|
---|
1334 | { There is no automatic reference counting, but it is compulsory to define these functions }
|
---|
1335 | function TBGRACanvas2D._AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
|
---|
1336 | begin
|
---|
1337 | result := 0;
|
---|
1338 | end;
|
---|
1339 |
|
---|
1340 | function TBGRACanvas2D._Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
|
---|
1341 | begin
|
---|
1342 | result := 0;
|
---|
1343 | end;
|
---|
1344 |
|
---|
1345 | procedure TBGRACanvas2D.SetLineWidth(const AValue: single);
|
---|
1346 | begin
|
---|
1347 | currentState.lineWidth := AValue;
|
---|
1348 | end;
|
---|
1349 |
|
---|
1350 | procedure TBGRACanvas2D.SetMatrix(AValue: TAffineMatrix);
|
---|
1351 | begin
|
---|
1352 | currentState.matrix := AValue;
|
---|
1353 | end;
|
---|
1354 |
|
---|
1355 | procedure TBGRACanvas2D.SetMiterLimit(const AValue: single);
|
---|
1356 | begin
|
---|
1357 | currentState.penStroker.MiterLimit := AValue;
|
---|
1358 | end;
|
---|
1359 |
|
---|
1360 | procedure TBGRACanvas2D.SetPixelCenteredCoordinates(const AValue: boolean);
|
---|
1361 | begin
|
---|
1362 | FPixelCenteredCoordinates:= AValue;
|
---|
1363 | if AValue then
|
---|
1364 | FCanvasOffset := PointF(0,0)
|
---|
1365 | else
|
---|
1366 | FCanvasOffset := PointF(-0.5,-0.5);
|
---|
1367 | end;
|
---|
1368 |
|
---|
1369 | procedure TBGRACanvas2D.SetShadowBlur(const AValue: single);
|
---|
1370 | begin
|
---|
1371 | currentState.shadowBlur := AValue;
|
---|
1372 | end;
|
---|
1373 |
|
---|
1374 | procedure TBGRACanvas2D.SetShadowFastest(AValue: boolean);
|
---|
1375 | begin
|
---|
1376 | currentState.shadowFastest := AValue;
|
---|
1377 | end;
|
---|
1378 |
|
---|
1379 | procedure TBGRACanvas2D.SetShadowOffset(const AValue: TPointF);
|
---|
1380 | begin
|
---|
1381 | shadowOffsetX := AValue.X;
|
---|
1382 | shadowOffsetY := AValue.Y;
|
---|
1383 | end;
|
---|
1384 |
|
---|
1385 | procedure TBGRACanvas2D.SetShadowOffsetX(const AValue: single);
|
---|
1386 | begin
|
---|
1387 | currentState.shadowOffsetX := AValue;
|
---|
1388 | end;
|
---|
1389 |
|
---|
1390 | procedure TBGRACanvas2D.SetShadowOffsetY(const AValue: single);
|
---|
1391 | begin
|
---|
1392 | currentState.shadowOffsetY := AValue;
|
---|
1393 | end;
|
---|
1394 |
|
---|
1395 | procedure TBGRACanvas2D.SetStrokeMatrix(AValue: TAffineMatrix);
|
---|
1396 | begin
|
---|
1397 | currentState.penStroker.strokeMatrix := AValue;
|
---|
1398 | end;
|
---|
1399 |
|
---|
1400 | procedure TBGRACanvas2D.SetTextAlign(AValue: string);
|
---|
1401 | begin
|
---|
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;
|
---|
1409 | end;
|
---|
1410 |
|
---|
1411 | procedure TBGRACanvas2D.SetTextAlignLCL(AValue: TAlignment);
|
---|
1412 | begin
|
---|
1413 | currentState.textAlign := AValue;
|
---|
1414 | end;
|
---|
1415 |
|
---|
1416 | procedure TBGRACanvas2D.SetTextBaseine(AValue: string);
|
---|
1417 | begin
|
---|
1418 | currentState.textBaseline := trim(lowercase(AValue));
|
---|
1419 | end;
|
---|
1420 |
|
---|
1421 | procedure TBGRACanvas2D.StrokePoly(const points: array of TPointF);
|
---|
1422 | var
|
---|
1423 | texture: IBGRAScanner;
|
---|
1424 | tempScan: TBGRACustomScanner;
|
---|
1425 | contour: array of TPointF;
|
---|
1426 | begin
|
---|
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;
|
---|
1462 | end;
|
---|
1463 |
|
---|
1464 | procedure TBGRACanvas2D.DrawShadow(const points, points2: array of TPointF;
|
---|
1465 | AFillMode: TFillMode = fmWinding);
|
---|
1466 | var 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 |
|
---|
1493 | begin
|
---|
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);
|
---|
1529 | end;
|
---|
1530 |
|
---|
1531 | procedure TBGRACanvas2D.DrawShadowMask(X, Y: integer; AMask: TBGRACustomBitmap; AMaskOwned: boolean);
|
---|
1532 | const invSqrt2 = 1/sqrt(2);
|
---|
1533 | var
|
---|
1534 | bmp: TBGRACustomBitmap;
|
---|
1535 | begin
|
---|
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;
|
---|
1560 | end;
|
---|
1561 |
|
---|
1562 | procedure TBGRACanvas2D.ClearPoly(const points: array of TPointF);
|
---|
1563 | begin
|
---|
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);
|
---|
1569 | end;
|
---|
1570 |
|
---|
1571 | function TBGRACanvas2D.ApplyTransform(const points: array of TPointF;
|
---|
1572 | matrix: TAffineMatrix): ArrayOfTPointF;
|
---|
1573 | var
|
---|
1574 | i: Integer;
|
---|
1575 | begin
|
---|
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;
|
---|
1582 | end;
|
---|
1583 |
|
---|
1584 | function TBGRACanvas2D.ApplyTransform(const points: array of TPointF
|
---|
1585 | ): ArrayOfTPointF;
|
---|
1586 | var
|
---|
1587 | i: Integer;
|
---|
1588 | begin
|
---|
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;
|
---|
1595 | end;
|
---|
1596 |
|
---|
1597 | function TBGRACanvas2D.ApplyTransform(point: TPointF): TPointF;
|
---|
1598 | begin
|
---|
1599 | result := currentState.matrix*point+FCanvasOffset;
|
---|
1600 | end;
|
---|
1601 |
|
---|
1602 | function TBGRACanvas2D.GetPenPos(defaultX,defaultY: single): TPointF;
|
---|
1603 | begin
|
---|
1604 | if isEmptyPointF(FLastCoord) then
|
---|
1605 | result := PointF(defaultX,defaultY)
|
---|
1606 | else
|
---|
1607 | result := FLastCoord;
|
---|
1608 | end;
|
---|
1609 |
|
---|
1610 | function TBGRACanvas2D.GetPenPos(defaultPt: TPointF): TPointF;
|
---|
1611 | begin
|
---|
1612 | result := GetPenPos(defaultPt.x,defaultPt.y);
|
---|
1613 | end;
|
---|
1614 |
|
---|
1615 | procedure TBGRACanvas2D.AddPoint(point: TPointF);
|
---|
1616 | begin
|
---|
1617 | if FPathPointCount = length(FPathPoints) then
|
---|
1618 | setlength(FPathPoints, (length(FPathPoints)+1)*2);
|
---|
1619 | FPathPoints[FPathPointCount] := point;
|
---|
1620 | inc(FPathPointCount);
|
---|
1621 | end;
|
---|
1622 |
|
---|
1623 | procedure TBGRACanvas2D.AddPoints(const points: array of TPointF);
|
---|
1624 | var i: integer;
|
---|
1625 | begin
|
---|
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;
|
---|
1633 | end;
|
---|
1634 |
|
---|
1635 | procedure TBGRACanvas2D.AddPointsRev(const points: array of TPointF);
|
---|
1636 | var i: integer;
|
---|
1637 | begin
|
---|
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;
|
---|
1645 | end;
|
---|
1646 |
|
---|
1647 | function TBGRACanvas2D.ApplyGlobalAlpha(color: TBGRAPixel): TBGRAPixel;
|
---|
1648 | begin
|
---|
1649 | result := BGRA(color.red,color.green,color.blue,ApplyOpacity(color.alpha, currentState.globalAlpha));
|
---|
1650 | end;
|
---|
1651 |
|
---|
1652 | function TBGRACanvas2D.GetDrawMode: TDrawMode;
|
---|
1653 | begin
|
---|
1654 | if linearBlend then result := dmLinearBlend else result := dmDrawWithTransparency;
|
---|
1655 | end;
|
---|
1656 |
|
---|
1657 | procedure TBGRACanvas2D.copyTo(dest: IBGRAPath);
|
---|
1658 | begin
|
---|
1659 | //nothing
|
---|
1660 | end;
|
---|
1661 |
|
---|
1662 | function TBGRACanvas2D.getPoints: ArrayOfTPointF;
|
---|
1663 | begin
|
---|
1664 | result := GetCurrentPathAsPoints;
|
---|
1665 | end;
|
---|
1666 |
|
---|
1667 | function TBGRACanvas2D.getPoints(AMatrix: TAffineMatrix): ArrayOfTPointF;
|
---|
1668 | begin
|
---|
1669 | result := GetCurrentPathAsPoints;
|
---|
1670 | if not IsAffineMatrixIdentity(AMatrix) then
|
---|
1671 | result := AMatrix*result;
|
---|
1672 | end;
|
---|
1673 |
|
---|
1674 | function TBGRACanvas2D.getCursor: TBGRACustomPathCursor;
|
---|
1675 | begin
|
---|
1676 | result := nil;
|
---|
1677 | end;
|
---|
1678 |
|
---|
1679 | constructor TBGRACanvas2D.Create(ASurface: TBGRACustomBitmap);
|
---|
1680 | begin
|
---|
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;
|
---|
1690 | end;
|
---|
1691 |
|
---|
1692 | destructor TBGRACanvas2D.Destroy;
|
---|
1693 | var
|
---|
1694 | i: Integer;
|
---|
1695 | begin
|
---|
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;
|
---|
1702 | end;
|
---|
1703 |
|
---|
1704 | function TBGRACanvas2D.toDataURL(mimeType: string): string;
|
---|
1705 | var
|
---|
1706 | stream: TMemoryStream;
|
---|
1707 | jpegWriter: TFPWriterJPEG;
|
---|
1708 | bmpWriter: TFPWriterBMP;
|
---|
1709 | output: TStringStream;
|
---|
1710 | encode64: TBase64EncodingStream;
|
---|
1711 | begin
|
---|
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;
|
---|
1739 | end;
|
---|
1740 |
|
---|
1741 | procedure TBGRACanvas2D.save;
|
---|
1742 | var cur: TBGRACanvasState2D;
|
---|
1743 | begin
|
---|
1744 | cur := currentState.Duplicate;
|
---|
1745 | StateStack.Add(cur);
|
---|
1746 | end;
|
---|
1747 |
|
---|
1748 | procedure TBGRACanvas2D.restore;
|
---|
1749 | begin
|
---|
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;
|
---|
1756 | end;
|
---|
1757 |
|
---|
1758 | procedure TBGRACanvas2D.scale(x, y: single);
|
---|
1759 | begin
|
---|
1760 | currentState.matrix *= AffineMatrixScale(x,y);
|
---|
1761 | end;
|
---|
1762 |
|
---|
1763 | procedure TBGRACanvas2D.scale(factor: single);
|
---|
1764 | begin
|
---|
1765 | currentState.matrix *= AffineMatrixScale(factor,factor);
|
---|
1766 | end;
|
---|
1767 |
|
---|
1768 | procedure TBGRACanvas2D.rotate(angleRadCW: single);
|
---|
1769 | begin
|
---|
1770 | currentState.matrix *= AffineMatrixRotationRad(-angleRadCW);
|
---|
1771 | end;
|
---|
1772 |
|
---|
1773 | procedure TBGRACanvas2D.translate(x, y: single);
|
---|
1774 | begin
|
---|
1775 | if (x = 0) and (y = 0) then exit;
|
---|
1776 | currentState.matrix *= AffineMatrixTranslation(x,y);
|
---|
1777 | end;
|
---|
1778 |
|
---|
1779 | procedure TBGRACanvas2D.skewx(angleRadCW: single);
|
---|
1780 | begin
|
---|
1781 | currentState.matrix *= AffineMatrixSkewXRad(-angleRadCW);
|
---|
1782 | end;
|
---|
1783 |
|
---|
1784 | procedure TBGRACanvas2D.skewy(angleRadCW: single);
|
---|
1785 | begin
|
---|
1786 | currentState.matrix *= AffineMatrixSkewYRad(-angleRadCW);
|
---|
1787 | end;
|
---|
1788 |
|
---|
1789 | procedure TBGRACanvas2D.transform(m11,m21, m12,m22, m13,m23: single);
|
---|
1790 | begin
|
---|
1791 | currentState.matrix *= AffineMatrix(m11,m12,m13,
|
---|
1792 | m21,m22,m23);
|
---|
1793 | end;
|
---|
1794 |
|
---|
1795 | procedure TBGRACanvas2D.transform(AMatrix: TAffineMatrix);
|
---|
1796 | begin
|
---|
1797 | currentState.matrix *= AMatrix;
|
---|
1798 | end;
|
---|
1799 |
|
---|
1800 | procedure TBGRACanvas2D.setTransform(m11,m21, m12,m22, m13,m23: single);
|
---|
1801 | begin
|
---|
1802 | currentState.matrix := AffineMatrix(m11,m12,m13,
|
---|
1803 | m21,m22,m23);
|
---|
1804 | end;
|
---|
1805 |
|
---|
1806 | procedure TBGRACanvas2D.resetTransform;
|
---|
1807 | begin
|
---|
1808 | currentState.matrix := AffineMatrixIdentity;
|
---|
1809 | end;
|
---|
1810 |
|
---|
1811 | procedure TBGRACanvas2D.strokeScale(x, y: single);
|
---|
1812 | begin
|
---|
1813 | currentState.penStroker.strokeMatrix := currentState.penStroker.strokeMatrix * AffineMatrixScale(x,y);
|
---|
1814 | end;
|
---|
1815 |
|
---|
1816 | procedure TBGRACanvas2D.strokeSkewx(angleRadCW: single);
|
---|
1817 | begin
|
---|
1818 | currentState.penStroker.strokeMatrix := currentState.penStroker.strokeMatrix * AffineMatrixSkewXRad(-angleRadCW);
|
---|
1819 | end;
|
---|
1820 |
|
---|
1821 | procedure TBGRACanvas2D.strokeSkewy(angleRadCW: single);
|
---|
1822 | begin
|
---|
1823 | currentState.penStroker.strokeMatrix := currentState.penStroker.strokeMatrix * AffineMatrixSkewYRad(-angleRadCW);
|
---|
1824 | end;
|
---|
1825 |
|
---|
1826 | procedure TBGRACanvas2D.strokeResetTransform;
|
---|
1827 | begin
|
---|
1828 | currentState.penStroker.strokeMatrix := AffineMatrixIdentity;
|
---|
1829 | end;
|
---|
1830 |
|
---|
1831 | procedure TBGRACanvas2D.strokeStyle(color: TBGRAPixel);
|
---|
1832 | begin
|
---|
1833 | currentState.strokeColor := color;
|
---|
1834 | currentState.strokeTextureProvider := nil;
|
---|
1835 | end;
|
---|
1836 |
|
---|
1837 | procedure TBGRACanvas2D.strokeStyle(color: TColor);
|
---|
1838 | begin
|
---|
1839 | currentState.strokeColor := ColorToBGRA(ColorToRGB(color));
|
---|
1840 | currentState.strokeTextureProvider := nil;
|
---|
1841 | end;
|
---|
1842 |
|
---|
1843 | procedure TBGRACanvas2D.strokeStyle(color: string);
|
---|
1844 | begin
|
---|
1845 | currentState.strokeColor := StrToBGRA(color);
|
---|
1846 | currentState.strokeTextureProvider := nil;
|
---|
1847 | end;
|
---|
1848 |
|
---|
1849 | procedure TBGRACanvas2D.strokeStyle(texture: IBGRAScanner);
|
---|
1850 | begin
|
---|
1851 | strokeStyle(createPattern(texture));
|
---|
1852 | end;
|
---|
1853 |
|
---|
1854 | procedure TBGRACanvas2D.strokeStyle(provider: IBGRACanvasTextureProvider2D);
|
---|
1855 | begin
|
---|
1856 | currentState.strokeColor := BGRAPixelTransparent;
|
---|
1857 | currentState.strokeTextureProvider := provider;
|
---|
1858 | end;
|
---|
1859 |
|
---|
1860 | function TBGRACanvas2D.GetFillMode: TFillMode;
|
---|
1861 | begin
|
---|
1862 | result := currentState.fillMode;
|
---|
1863 | end;
|
---|
1864 |
|
---|
1865 | procedure TBGRACanvas2D.SetFillMode(mode: TFillMode);
|
---|
1866 | begin
|
---|
1867 | currentState.fillMode := mode;
|
---|
1868 | end;
|
---|
1869 |
|
---|
1870 | procedure TBGRACanvas2D.fillStyle(color: TBGRAPixel);
|
---|
1871 | begin
|
---|
1872 | currentState.fillColor := color;
|
---|
1873 | currentState.fillTextureProvider := nil;
|
---|
1874 | end;
|
---|
1875 |
|
---|
1876 | procedure TBGRACanvas2D.fillStyle(color: TColor);
|
---|
1877 | begin
|
---|
1878 | currentState.fillColor := ColorToBGRA(ColorToRGB(color));
|
---|
1879 | currentState.fillTextureProvider := nil;
|
---|
1880 | end;
|
---|
1881 |
|
---|
1882 | procedure TBGRACanvas2D.fillStyle(color: string);
|
---|
1883 | begin
|
---|
1884 | currentState.fillColor := StrToBGRA(color);
|
---|
1885 | currentState.fillTextureProvider := nil;
|
---|
1886 | end;
|
---|
1887 |
|
---|
1888 | procedure TBGRACanvas2D.fillStyle(texture: IBGRAScanner);
|
---|
1889 | begin
|
---|
1890 | fillStyle(createPattern(texture));
|
---|
1891 | end;
|
---|
1892 |
|
---|
1893 | procedure TBGRACanvas2D.fillStyle(provider: IBGRACanvasTextureProvider2D);
|
---|
1894 | begin
|
---|
1895 | currentState.fillColor := BGRAPixelTransparent;
|
---|
1896 | currentState.fillTextureProvider := provider;
|
---|
1897 | end;
|
---|
1898 |
|
---|
1899 | procedure TBGRACanvas2D.shadowColor(color: TBGRAPixel);
|
---|
1900 | begin
|
---|
1901 | currentState.shadowColor := color;
|
---|
1902 | end;
|
---|
1903 |
|
---|
1904 | procedure TBGRACanvas2D.shadowColor(color: TColor);
|
---|
1905 | begin
|
---|
1906 | shadowColor(ColorToBGRA(ColorToRGB(color)));
|
---|
1907 | end;
|
---|
1908 |
|
---|
1909 | procedure TBGRACanvas2D.shadowColor(color: string);
|
---|
1910 | begin
|
---|
1911 | shadowColor(StrToBGRA(color));
|
---|
1912 | end;
|
---|
1913 |
|
---|
1914 | procedure TBGRACanvas2D.shadowNone;
|
---|
1915 | begin
|
---|
1916 | shadowColor(BGRAPixelTransparent);
|
---|
1917 | end;
|
---|
1918 |
|
---|
1919 | function TBGRACanvas2D.getShadowColor: TBGRAPixel;
|
---|
1920 | begin
|
---|
1921 | result := currentState.shadowColor;
|
---|
1922 | end;
|
---|
1923 |
|
---|
1924 | function TBGRACanvas2D.createLinearGradient(x0, y0, x1, y1: single): IBGRACanvasGradient2D;
|
---|
1925 | begin
|
---|
1926 | result := createLinearGradient(PointF(x0,y0), PointF(x1,y1));
|
---|
1927 | end;
|
---|
1928 |
|
---|
1929 | function TBGRACanvas2D.createLinearGradient(p0, p1: TPointF): IBGRACanvasGradient2D;
|
---|
1930 | begin
|
---|
1931 | result := TBGRACanvasLinearGradient2D.Create(p0,p1,
|
---|
1932 | AffineMatrixTranslation(FCanvasOffset.x,FCanvasOffset.y)*currentState.matrix);
|
---|
1933 | result.gammaCorrection := gradientGammaCorrection;
|
---|
1934 | end;
|
---|
1935 |
|
---|
1936 | function TBGRACanvas2D.createLinearGradient(x0, y0, x1, y1: single;
|
---|
1937 | Colors: TBGRACustomGradient): IBGRACanvasGradient2D;
|
---|
1938 | begin
|
---|
1939 | result := createLinearGradient(x0,y0,x1,y1);
|
---|
1940 | result.setColors(Colors);
|
---|
1941 | end;
|
---|
1942 |
|
---|
1943 | function TBGRACanvas2D.createLinearGradient(p0, p1: TPointF;
|
---|
1944 | Colors: TBGRACustomGradient): IBGRACanvasGradient2D;
|
---|
1945 | begin
|
---|
1946 | result := createLinearGradient(p0,p1);
|
---|
1947 | result.setColors(Colors);
|
---|
1948 | end;
|
---|
1949 |
|
---|
1950 | function TBGRACanvas2D.createRadialGradient(x0, y0, r0, x1, y1, r1: single;
|
---|
1951 | flipGradient: boolean): IBGRACanvasGradient2D;
|
---|
1952 | begin
|
---|
1953 | result := createRadialGradient(PointF(x0,y0), r0, PointF(x1,y1), r1, flipGradient);
|
---|
1954 | end;
|
---|
1955 |
|
---|
1956 | function TBGRACanvas2D.createRadialGradient(p0: TPointF; r0: single;
|
---|
1957 | p1: TPointF; r1: single; flipGradient: boolean): IBGRACanvasGradient2D;
|
---|
1958 | begin
|
---|
1959 | result := TBGRACanvasRadialGradient2D.Create(p0,r0,p1,r1,
|
---|
1960 | AffineMatrixTranslation(FCanvasOffset.x,FCanvasOffset.y)*currentState.matrix,
|
---|
1961 | flipGradient);
|
---|
1962 | result.gammaCorrection := gradientGammaCorrection;
|
---|
1963 | end;
|
---|
1964 |
|
---|
1965 | function TBGRACanvas2D.createRadialGradient(x0, y0, r0, x1, y1, r1: single;
|
---|
1966 | Colors: TBGRACustomGradient; flipGradient: boolean): IBGRACanvasGradient2D;
|
---|
1967 | begin
|
---|
1968 | result := createRadialGradient(x0,y0,r0,x1,y1,r1,flipGradient);
|
---|
1969 | result.setColors(Colors);
|
---|
1970 | end;
|
---|
1971 |
|
---|
1972 | function TBGRACanvas2D.createRadialGradient(p0: TPointF; r0: single;
|
---|
1973 | p1: TPointF; r1: single; Colors: TBGRACustomGradient; flipGradient: boolean): IBGRACanvasGradient2D;
|
---|
1974 | begin
|
---|
1975 | result := createRadialGradient(p0,r0,p1,r1,flipGradient);
|
---|
1976 | result.setColors(Colors);
|
---|
1977 | end;
|
---|
1978 |
|
---|
1979 | function TBGRACanvas2D.createPattern(image: TBGRACustomBitmap; repetition: string
|
---|
1980 | ): IBGRACanvasTextureProvider2D;
|
---|
1981 | var
|
---|
1982 | repeatX,repeatY: boolean;
|
---|
1983 | origin: TPointF;
|
---|
1984 | begin
|
---|
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);
|
---|
1999 | end;
|
---|
2000 |
|
---|
2001 | function TBGRACanvas2D.createPattern(texture: IBGRAScanner
|
---|
2002 | ): IBGRACanvasTextureProvider2D;
|
---|
2003 | var
|
---|
2004 | tempTransform: TAffineMatrix;
|
---|
2005 | begin
|
---|
2006 | tempTransform := AffineMatrixTranslation(FCanvasOffset.X+0.5,FCanvasOffset.Y+0.5)*currentState.matrix;
|
---|
2007 | result := TBGRACanvasPattern2D.Create(texture,tempTransform);
|
---|
2008 | end;
|
---|
2009 |
|
---|
2010 | procedure TBGRACanvas2D.fillRect(x, y, w, h: single);
|
---|
2011 | begin
|
---|
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)]));
|
---|
2014 | end;
|
---|
2015 |
|
---|
2016 | procedure TBGRACanvas2D.strokeRect(x, y, w, h: single);
|
---|
2017 | begin
|
---|
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)]));
|
---|
2020 | end;
|
---|
2021 |
|
---|
2022 | procedure TBGRACanvas2D.clearRect(x, y, w, h: single);
|
---|
2023 | begin
|
---|
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)]));
|
---|
2026 | end;
|
---|
2027 |
|
---|
2028 | procedure TBGRACanvas2D.addPath(APath: IBGRAPath);
|
---|
2029 | begin
|
---|
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);
|
---|
2037 | end;
|
---|
2038 |
|
---|
2039 | procedure TBGRACanvas2D.addPath(ASvgPath: string);
|
---|
2040 | var p: TBGRAPath;
|
---|
2041 | begin
|
---|
2042 | p := TBGRAPath.Create(ASvgPath);
|
---|
2043 | addPath(p);
|
---|
2044 | p.Free;
|
---|
2045 | end;
|
---|
2046 |
|
---|
2047 | procedure TBGRACanvas2D.path(APath: IBGRAPath);
|
---|
2048 | begin
|
---|
2049 | beginPath;
|
---|
2050 | addPath(APath);
|
---|
2051 | end;
|
---|
2052 |
|
---|
2053 | procedure TBGRACanvas2D.path(ASvgPath: string);
|
---|
2054 | begin
|
---|
2055 | beginPath;
|
---|
2056 | addPath(ASvgPath);
|
---|
2057 | end;
|
---|
2058 |
|
---|
2059 | procedure TBGRACanvas2D.beginPath;
|
---|
2060 | begin
|
---|
2061 | FPathPointCount := 0;
|
---|
2062 | FLastCoord := EmptyPointF;
|
---|
2063 | FStartCoord := EmptyPointF;
|
---|
2064 | FTextPaths := nil;
|
---|
2065 | end;
|
---|
2066 |
|
---|
2067 | procedure TBGRACanvas2D.closePath;
|
---|
2068 | var i: integer;
|
---|
2069 | begin
|
---|
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;
|
---|
2077 | end;
|
---|
2078 |
|
---|
2079 | procedure TBGRACanvas2D.toSpline(closed: boolean; style: TSplineStyle);
|
---|
2080 | var i,j: integer;
|
---|
2081 | pts, splinePts: array of TPointF;
|
---|
2082 | nb: integer;
|
---|
2083 | begin
|
---|
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;
|
---|
2099 | end;
|
---|
2100 |
|
---|
2101 | procedure TBGRACanvas2D.moveTo(x, y: single);
|
---|
2102 | begin
|
---|
2103 | moveTo(PointF(x,y));
|
---|
2104 | end;
|
---|
2105 |
|
---|
2106 | procedure TBGRACanvas2D.lineTo(x, y: single);
|
---|
2107 | begin
|
---|
2108 | lineTo(PointF(x,y));
|
---|
2109 | end;
|
---|
2110 |
|
---|
2111 | procedure TBGRACanvas2D.moveTo(constref pt: TPointF);
|
---|
2112 | begin
|
---|
2113 | if (FPathPointCount <> 0) and not isEmptyPointF(FPathPoints[FPathPointCount-1]) then
|
---|
2114 | AddPoint(EmptyPointF);
|
---|
2115 | AddPoint(ApplyTransform(pt));
|
---|
2116 | FStartCoord := pt;
|
---|
2117 | FLastCoord := pt;
|
---|
2118 | end;
|
---|
2119 |
|
---|
2120 | procedure TBGRACanvas2D.lineTo(constref pt: TPointF);
|
---|
2121 | begin
|
---|
2122 | AddPoint(ApplyTransform(pt));
|
---|
2123 | FLastCoord := pt;
|
---|
2124 | end;
|
---|
2125 |
|
---|
2126 | procedure TBGRACanvas2D.polylineTo(const pts: array of TPointF);
|
---|
2127 | begin
|
---|
2128 | if length(pts)> 0 then
|
---|
2129 | begin
|
---|
2130 | AddPoints(ApplyTransform(pts));
|
---|
2131 | FLastCoord := pts[high(pts)];
|
---|
2132 | end;
|
---|
2133 | end;
|
---|
2134 |
|
---|
2135 | procedure TBGRACanvas2D.quadraticCurveTo(cpx, cpy, x, y: single);
|
---|
2136 | var
|
---|
2137 | curve : TQuadraticBezierCurve;
|
---|
2138 | pts : array of TPointF;
|
---|
2139 | begin
|
---|
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);
|
---|
2144 | end;
|
---|
2145 |
|
---|
2146 | procedure TBGRACanvas2D.quadraticCurveTo(constref cp, pt: TPointF);
|
---|
2147 | begin
|
---|
2148 | quadraticCurveTo(cp.x,cp.y,pt.x,pt.y);
|
---|
2149 | end;
|
---|
2150 |
|
---|
2151 | procedure TBGRACanvas2D.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y: single);
|
---|
2152 | var
|
---|
2153 | curve : TCubicBezierCurve;
|
---|
2154 | pts : array of TPointF;
|
---|
2155 | begin
|
---|
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);
|
---|
2161 | end;
|
---|
2162 |
|
---|
2163 | procedure TBGRACanvas2D.bezierCurveTo(constref cp1, cp2, pt: TPointF);
|
---|
2164 | begin
|
---|
2165 | bezierCurveTo(cp1.x,cp1.y,cp2.x,cp2.y,pt.x,pt.y);
|
---|
2166 | end;
|
---|
2167 |
|
---|
2168 | procedure TBGRACanvas2D.rect(x, y, w, h: single);
|
---|
2169 | begin
|
---|
2170 | MoveTo(x,y);
|
---|
2171 | LineTo(x+w,y);
|
---|
2172 | LineTo(x+w,y+h);
|
---|
2173 | LineTo(x,y+h);
|
---|
2174 | closePath;
|
---|
2175 | end;
|
---|
2176 |
|
---|
2177 | procedure TBGRACanvas2D.roundRect(x, y, w, h, radius: single);
|
---|
2178 | begin
|
---|
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;
|
---|
2193 | end;
|
---|
2194 |
|
---|
2195 | procedure TBGRACanvas2D.roundRect(x, y, w, h, rx, ry: single);
|
---|
2196 | begin
|
---|
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;
|
---|
2217 | end;
|
---|
2218 |
|
---|
2219 | procedure TBGRACanvas2D.openedSpline(const pts: array of TPointF;
|
---|
2220 | style: TSplineStyle);
|
---|
2221 | var transf: array of TPointF;
|
---|
2222 | begin
|
---|
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)];
|
---|
2228 | end;
|
---|
2229 |
|
---|
2230 | procedure TBGRACanvas2D.closedSpline(const pts: array of TPointF;
|
---|
2231 | style: TSplineStyle);
|
---|
2232 | var transf: array of TPointF;
|
---|
2233 | begin
|
---|
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)];
|
---|
2239 | end;
|
---|
2240 |
|
---|
2241 | procedure TBGRACanvas2D.spline(const pts: array of TPointF; style: TSplineStyle);
|
---|
2242 | var transf: array of TPointF;
|
---|
2243 | begin
|
---|
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)];
|
---|
2252 | end;
|
---|
2253 |
|
---|
2254 | procedure TBGRACanvas2D.splineTo(const pts: array of TPointF;
|
---|
2255 | style: TSplineStyle);
|
---|
2256 | var transf: array of TPointF;
|
---|
2257 | i: Integer;
|
---|
2258 | begin
|
---|
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)];
|
---|
2271 | end;
|
---|
2272 |
|
---|
2273 | procedure TBGRACanvas2D.arc(x, y, radius, startAngleRadCW, endAngleRadCW: single;
|
---|
2274 | anticlockwise: boolean);
|
---|
2275 | var 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;
|
---|
2283 | begin
|
---|
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));
|
---|
2312 | end;
|
---|
2313 |
|
---|
2314 | procedure TBGRACanvas2D.arc(x, y, radius, startAngleRadCW, endAngleRadCW: single);
|
---|
2315 | begin
|
---|
2316 | arc(x,y,radius,startAngleRadCW,endAngleRadCW,false);
|
---|
2317 | end;
|
---|
2318 |
|
---|
2319 | procedure TBGRACanvas2D.arc(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single;
|
---|
2320 | anticlockwise: boolean);
|
---|
2321 | begin
|
---|
2322 | arc(ArcDef(cx,cy,rx,ry,xAngleRadCW,startAngleRadCW,endAngleRadCW,anticlockwise))
|
---|
2323 | end;
|
---|
2324 |
|
---|
2325 | procedure TBGRACanvas2D.arc(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single);
|
---|
2326 | begin
|
---|
2327 | arc(ArcDef(cx,cy,rx,ry,xAngleRadCW,startAngleRadCW,endAngleRadCW,false))
|
---|
2328 | end;
|
---|
2329 |
|
---|
2330 | procedure TBGRACanvas2D.arc(constref arcDef: TArcDef);
|
---|
2331 | var previousMatrix: TAffineMatrix;
|
---|
2332 | begin
|
---|
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;
|
---|
2344 | end;
|
---|
2345 |
|
---|
2346 | procedure TBGRACanvas2D.arcTo(x1, y1, x2, y2, radius: single);
|
---|
2347 | var p0: TPointF;
|
---|
2348 | begin
|
---|
2349 | p0 := GetPenPos(x1,y1);
|
---|
2350 | arc(Html5ArcTo(p0,PointF(x1,y1),PointF(x2,y2),radius));
|
---|
2351 | end;
|
---|
2352 |
|
---|
2353 | procedure TBGRACanvas2D.arcTo(p1, p2: TPointF; radius: single);
|
---|
2354 | begin
|
---|
2355 | arcTo(p1.x,p1.y,p2.x,p2.y,radius);
|
---|
2356 | end;
|
---|
2357 |
|
---|
2358 | procedure TBGRACanvas2D.arcTo(rx, ry, xAngleRadCW: single; largeArc,
|
---|
2359 | anticlockwise: boolean; x, y: single);
|
---|
2360 | begin
|
---|
2361 | arc(SvgArcTo(GetPenPos(x,y), rx,ry, xAngleRadCW, largeArc, anticlockwise, PointF(x,y)));
|
---|
2362 | FLastCoord := PointF(x,y);
|
---|
2363 | end;
|
---|
2364 |
|
---|
2365 | procedure TBGRACanvas2D.circle(x, y, r: single);
|
---|
2366 | begin
|
---|
2367 | arc(x,y,r,0,0);
|
---|
2368 | end;
|
---|
2369 |
|
---|
2370 | procedure TBGRACanvas2D.ellipse(x, y, rx, ry: single);
|
---|
2371 | begin
|
---|
2372 | arc(x,y,rx,ry,0,0,0);
|
---|
2373 | end;
|
---|
2374 |
|
---|
2375 | procedure TBGRACanvas2D.text(AText: string; x, y: single);
|
---|
2376 | var renderer : TBGRACustomFontRenderer;
|
---|
2377 | previousMatrix: TAffineMatrix;
|
---|
2378 | fva: TFontVerticalAnchor;
|
---|
2379 | begin
|
---|
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;
|
---|
2419 | end;
|
---|
2420 |
|
---|
2421 | procedure TBGRACanvas2D.fillText(AText: string; x, y: single);
|
---|
2422 | begin
|
---|
2423 | beginPath;
|
---|
2424 | text(AText,x,y);
|
---|
2425 | fill;
|
---|
2426 | beginPath;
|
---|
2427 | end;
|
---|
2428 |
|
---|
2429 | procedure TBGRACanvas2D.strokeText(AText: string; x, y: single);
|
---|
2430 | begin
|
---|
2431 | beginPath;
|
---|
2432 | text(AText,x,y);
|
---|
2433 | stroke;
|
---|
2434 | beginPath;
|
---|
2435 | end;
|
---|
2436 |
|
---|
2437 | function TBGRACanvas2D.measureText(AText: string): TCanvas2dTextSize;
|
---|
2438 | var renderer: TBGRACustomFontRenderer;
|
---|
2439 | begin
|
---|
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;
|
---|
2454 | end;
|
---|
2455 |
|
---|
2456 | procedure TBGRACanvas2D.fill;
|
---|
2457 | begin
|
---|
2458 | if FPathPointCount > 0 then
|
---|
2459 | FillPoly(slice(FPathPoints,FPathPointCount));
|
---|
2460 | FillTexts(false);
|
---|
2461 | end;
|
---|
2462 |
|
---|
2463 | procedure TBGRACanvas2D.stroke;
|
---|
2464 | begin
|
---|
2465 | if FPathPointCount > 0 then
|
---|
2466 | StrokePoly(slice(FPathPoints,FPathPointCount));
|
---|
2467 | end;
|
---|
2468 |
|
---|
2469 | procedure TBGRACanvas2D.fillOverStroke;
|
---|
2470 | begin
|
---|
2471 | if FPathPointCount > 0 then
|
---|
2472 | FillStrokePoly(slice(FPathPoints,FPathPointCount),true);
|
---|
2473 | FillTexts(false);
|
---|
2474 | end;
|
---|
2475 |
|
---|
2476 | procedure TBGRACanvas2D.strokeOverFill;
|
---|
2477 | begin
|
---|
2478 | FillTexts(false);
|
---|
2479 | if FPathPointCount > 0 then
|
---|
2480 | FillStrokePoly(slice(FPathPoints,FPathPointCount),false);
|
---|
2481 | end;
|
---|
2482 |
|
---|
2483 | procedure TBGRACanvas2D.clearPath;
|
---|
2484 | begin
|
---|
2485 | if FPathPointCount > 0 then
|
---|
2486 | ClearPoly(slice(FPathPoints,FPathPointCount));
|
---|
2487 | FillTexts(true);
|
---|
2488 | end;
|
---|
2489 |
|
---|
2490 | procedure TBGRACanvas2D.clip;
|
---|
2491 | var
|
---|
2492 | tempBmp: TBGRACustomBitmap;
|
---|
2493 | begin
|
---|
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;
|
---|
2508 | end;
|
---|
2509 |
|
---|
2510 | procedure TBGRACanvas2D.unclip;
|
---|
2511 | begin
|
---|
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);
|
---|
2520 | end;
|
---|
2521 |
|
---|
2522 | function TBGRACanvas2D.isPointInPath(x, y: single): boolean;
|
---|
2523 | begin
|
---|
2524 | result := isPointInPath(PointF(x,y));
|
---|
2525 | end;
|
---|
2526 |
|
---|
2527 | function TBGRACanvas2D.isPointInPath(pt: TPointF): boolean;
|
---|
2528 | begin
|
---|
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;
|
---|
2536 | end;
|
---|
2537 |
|
---|
2538 | procedure TBGRACanvas2D.drawImage(image: TBGRACustomBitmap; dx, dy: single);
|
---|
2539 | begin
|
---|
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);
|
---|
2543 | end;
|
---|
2544 |
|
---|
2545 | procedure TBGRACanvas2D.drawImage(image: TBGRACustomBitmap; dx, dy, dw, dh: single);
|
---|
2546 | begin
|
---|
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);
|
---|
2550 | end;
|
---|
2551 |
|
---|
2552 | end.
|
---|
2553 |
|
---|