| 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 |
|
|---|