Changeset 494 for GraphicTest/Packages/bgrabitmap/bgrapath.pas
- Timestamp:
- Dec 22, 2016, 8:49:19 PM (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
GraphicTest/Packages/bgrabitmap/bgrapath.pas
r472 r494 4 4 5 5 interface 6 7 //todo: tangent interpolation 6 8 7 9 { There are different conventions for angles. … … 39 41 40 42 type 41 TBGRAPathElementType = (peNone, peMoveTo, peLineTo, peCloseSubPath, peQuadraticBezierTo, peCubicBezierTo, peArc); 42 PBGRAPathElementType = ^TBGRAPathElementType; 43 TBGRAPathElementType = (peNone, peMoveTo, peLineTo, peCloseSubPath, 44 peQuadraticBezierTo, peCubicBezierTo, peArc, peOpenedSpline, 45 peClosedSpline); 46 47 TBGRAPathDrawProc = procedure(const APoints: array of TPointF; AClosed: boolean; AData: Pointer) of object; 48 TBGRAPathFillProc = procedure(const APoints: array of TPointF; AData: pointer) of object; 49 50 TBGRAPath = class; 51 52 { TBGRAPathCursor } 53 54 TBGRAPathCursor = class(TBGRACustomPathCursor) 55 protected 56 FPath: TBGRAPath; 57 FDataPos: IntPtr; 58 FAcceptedDeviation: single; 59 FPathLength: single; 60 FPathLengthComputed: boolean; 61 FBounds: TRectF; 62 FBoundsComputed: boolean; 63 FArcPos: Single; 64 65 FStartCoordinate: TPointF; 66 FEndCoordinate: TPointF; 67 FLoopClosedShapes,FLoopPath: boolean; 68 69 FCurrentElementType: TBGRAPathElementType; 70 FCurrentElement: Pointer; 71 FCurrentElementArcPos, 72 FCurrentElementArcPosScale: single; 73 FCurrentElementStartCoord, 74 FCurrentElementEndCoord: TPointF; 75 FCurrentElementLength: single; 76 FCurrentElementPoints: array of TPointF; 77 FCurrentSegment: NativeInt; 78 FCurrentSegmentPos: single; 79 function GoToNextElement(ACanJump: boolean): boolean; 80 function GoToPreviousElement(ACanJump: boolean): boolean; 81 procedure MoveToEndOfElement; 82 procedure MoveForwardInElement(ADistance: single); 83 procedure MoveBackwardInElement(ADistance: single); 84 function NeedPolygonalApprox: boolean; 85 procedure OnPathFree; virtual; 86 87 function GetLoopClosedShapes: boolean; override; 88 function GetLoopPath: boolean; override; 89 function GetStartCoordinate: TPointF; override; 90 procedure SetLoopClosedShapes(AValue: boolean); override; 91 procedure SetLoopPath(AValue: boolean); override; 92 93 function GetArcPos: single; override; 94 function GetCurrentTangent: TPointF; override; 95 procedure SetArcPos(AValue: single); override; 96 function GetBounds: TRectF; override; 97 function GetPathLength: single; override; 98 procedure PrepareCurrentElement; virtual; 99 function GetCurrentCoord: TPointF; override; 100 function GetPath: TBGRAPath; virtual; 101 public 102 constructor Create(APath: TBGRAPath; AAcceptedDeviation: single = 0.1); 103 function MoveForward(ADistance: single; ACanJump: boolean = true): single; override; 104 function MoveBackward(ADistance: single; ACanJump: boolean = true): single; override; 105 destructor Destroy; override; 106 property CurrentCoordinate: TPointF read GetCurrentCoord; 107 property CurrentTangent: TPointF read GetCurrentTangent; 108 property Position: single read GetArcPos write SetArcPos; 109 property PathLength: single read GetPathLength; 110 property Path: TBGRAPath read GetPath; 111 property Bounds: TRectF read GetBounds; 112 property StartCoordinate: TPointF read GetStartCoordinate; 113 property LoopClosedShapes: boolean read GetLoopClosedShapes write SetLoopClosedShapes; 114 property LoopPath: boolean read GetLoopPath write SetLoopPath; 115 property AcceptedDeviation: single read FAcceptedDeviation; 116 end; 43 117 44 118 { TBGRAPath } 45 119 46 120 TBGRAPath = class(IBGRAPath) 47 private48 function GetSvgString: string;49 procedure SetSvgString(const AValue: string);50 121 protected 51 FData: pbyte; 52 FDataSize: integer; 53 FDataPos: integer; 54 FLastElementType: TBGRAPathElementType; 55 FLastCoord, 56 FStartCoord: TPointF; 57 FExpectedControlPoint: TPointF; 122 FData: PByte; 123 FDataCapacity: PtrInt; 124 FDataPos: PtrInt; 125 FLastSubPathElementType, FLastStoredElementType: TBGRAPathElementType; 126 FLastMoveToDataPos: PtrInt; 127 FLastCoord,FLastTransformedCoord, 128 FSubPathStartCoord, FSubPathTransformedStartCoord: TPointF; 129 FExpectedTransformedControlPoint: TPointF; 58 130 FMatrix: TAffineMatrix; //this matrix must have a base of vectors 59 131 //orthogonal, of same length and with positive 60 132 //orientation in order to preserve arcs 61 133 FScale,FAngleRadCW: single; 134 FCursors: array of TBGRAPathCursor; 135 FInternalDrawOffset: TPointF; 136 procedure OnModify; 137 procedure OnMatrixChange; 62 138 procedure NeedSpace(count: integer); 63 procedure StoreCoord(const pt: TPointF); 64 function ReadCoord: TPointF; 65 procedure StoreElementType(value: TBGRAPathElementType); 66 function ReadElementType: TBGRAPathElementType; 67 function ReadArcDef: TArcDef; 68 procedure RewindFloat; 139 function AllocateElement(AElementType: TBGRAPathElementType; 140 AExtraBytes: PtrInt = 0): Pointer; 69 141 procedure Init; 142 procedure DoClear; 143 function CheckElementType(AElementType: TBGRAPathElementType): boolean; 144 function GoToNextElement(var APos: PtrInt): boolean; 145 function GoToPreviousElement(var APos: PtrInt): boolean; 146 function PeekNextElement(APos: PtrInt): TBGRAPathElementType; 147 function GetElementStartCoord(APos: PtrInt): TPointF; 148 function GetElementEndCoord(APos: PtrInt): TPointF; 149 function GetElementLength(APos: PtrInt; AAcceptedDeviation: single): Single; 150 procedure GetElementAt(APos: PtrInt; 151 out AElementType: TBGRAPathElementType; out AElement: pointer); 152 function GetSvgString: string; virtual; 153 procedure SetSvgString(const AValue: string); virtual; 154 procedure RegisterCursor(ACursor: TBGRAPathCursor); 155 procedure UnregisterCursor(ACursor: TBGRAPathCursor); 156 function SetLastCoord(ACoord: TPointF): TPointF; inline; 157 procedure ClearLastCoord; 158 procedure BezierCurveFromTransformed(tcp1, cp2, pt:TPointF); 159 procedure QuadraticCurveFromTransformed(tcp, pt: TPointF); 160 function LastCoordDefined: boolean; inline; 161 function GetPolygonalApprox(APos: IntPtr; AAcceptedDeviation: single; AIncludeFirstPoint: boolean): ArrayOfTPointF; 162 function getPoints: ArrayOfTPointF; 163 function getPoints(AMatrix: TAffineMatrix): ArrayOfTPointF; 164 function getCursor: TBGRACustomPathCursor; 165 procedure InternalDraw(ADrawProc: TBGRAPathDrawProc; const AMatrix: TAffineMatrix; AAcceptedDeviation: single; AData: pointer); 166 procedure BitmapDrawSubPathProc(const APoints: array of TPointF; AClosed: boolean; AData: pointer); 167 function CorrectAcceptedDeviation(AAcceptedDeviation: single; const AMatrix: TAffineMatrix): single; 70 168 public 71 169 constructor Create; overload; 72 170 constructor Create(ASvgString: string); overload; 171 constructor Create(const APoints: ArrayOfTPointF); overload; 172 constructor Create(APath: IBGRAPath); overload; 73 173 destructor Destroy; override; 74 174 procedure beginPath; 175 procedure beginSubPath; 75 176 procedure closePath; 76 177 procedure translate(x,y: single); … … 85 186 procedure moveTo(const pt: TPointF); overload; 86 187 procedure lineTo(const pt: TPointF); overload; 188 procedure polyline(const pts: array of TPointF); 87 189 procedure polylineTo(const pts: array of TPointF); 190 procedure polygon(const pts: array of TPointF); 88 191 procedure quadraticCurveTo(cpx,cpy,x,y: single); overload; 89 192 procedure quadraticCurveTo(const cp,pt: TPointF); overload; 90 193 procedure quadraticCurve(const curve: TQuadraticBezierCurve); overload; 194 procedure quadraticCurve(p1,cp,p2: TPointF); overload; 91 195 procedure smoothQuadraticCurveTo(x,y: single); overload; 92 196 procedure smoothQuadraticCurveTo(const pt: TPointF); overload; … … 94 198 procedure bezierCurveTo(const cp1,cp2,pt: TPointF); overload; 95 199 procedure bezierCurve(const curve: TCubicBezierCurve); overload; 200 procedure bezierCurve(p1,cp1,cp2,p2: TPointF); overload; 96 201 procedure smoothBezierCurveTo(cp2x,cp2y,x,y: single); overload; 97 202 procedure smoothBezierCurveTo(const cp2,pt: TPointF); overload; … … 105 210 procedure arcTo(const p1,p2: TPointF; radius: single); overload; 106 211 procedure arc(const arcDef: TArcDef); overload; 107 procedure arc(cx, cy, rx,ry ,xAngleRadCW, startAngleRadCW, endAngleRadCW: single); overload;212 procedure arc(cx, cy, rx,ry: single; xAngleRadCW, startAngleRadCW, endAngleRadCW: single); overload; 108 213 procedure arc(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single; anticlockwise: boolean); overload; 109 214 procedure arcTo(rx,ry, xAngleRadCW: single; largeArc, anticlockwise: boolean; x,y:single); … … 111 216 procedure addPath(const AValue: string); overload; 112 217 procedure addPath(source: IBGRAPath); overload; 218 procedure openedSpline(const pts: array of TPointF; style: TSplineStyle); 219 procedure closedSpline(const pts: array of TPointF; style: TSplineStyle); 113 220 property SvgString: string read GetSvgString write SetSvgString; 221 function ComputeLength(AAcceptedDeviation: single = 0.1): single; 222 function ToPoints(AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 223 function ToPoints(AMatrix: TAffineMatrix; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 224 function IsEmpty: boolean; 225 function GetBounds(AAcceptedDeviation: single = 0.1): TRectF; 226 procedure SetPoints(const APoints: ArrayOfTPointF); 227 procedure stroke(ABitmap: TBGRACustomBitmap; AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single = 0.1); 228 procedure stroke(ABitmap: TBGRACustomBitmap; ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single = 0.1); 229 procedure stroke(ABitmap: TBGRACustomBitmap; x,y: single; AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single = 0.1); 230 procedure stroke(ABitmap: TBGRACustomBitmap; x,y: single; ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single = 0.1); 231 procedure stroke(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single = 0.1); 232 procedure stroke(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single = 0.1); 233 procedure stroke(ADrawProc: TBGRAPathDrawProc; const AMatrix: TAffineMatrix; AAcceptedDeviation: single = 0.1; AData: pointer = nil); 234 procedure fill(ABitmap: TBGRACustomBitmap; AColor: TBGRAPixel; AAcceptedDeviation: single = 0.1); 235 procedure fill(ABitmap: TBGRACustomBitmap; ATexture: IBGRAScanner; AAcceptedDeviation: single = 0.1); 236 procedure fill(ABitmap: TBGRACustomBitmap; x,y: single; AColor: TBGRAPixel; AAcceptedDeviation: single = 0.1); 237 procedure fill(ABitmap: TBGRACustomBitmap; x,y: single; ATexture: IBGRAScanner; AAcceptedDeviation: single = 0.1); 238 procedure fill(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; AColor: TBGRAPixel; AAcceptedDeviation: single = 0.1); 239 procedure fill(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; ATexture: IBGRAScanner; AAcceptedDeviation: single = 0.1); 240 procedure fill(AFillProc: TBGRAPathFillProc; const AMatrix: TAffineMatrix; AAcceptedDeviation: single = 0.1; AData: pointer = nil); 241 function CreateCursor(AAcceptedDeviation: single = 0.1): TBGRAPathCursor; 242 procedure Fit(ARect: TRectF; AAcceptedDeviation: single = 0.1); 243 procedure FitInto(ADest: TBGRAPath; ARect: TRectF; AAcceptedDeviation: single = 0.1); 114 244 protected 115 245 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}; … … 121 251 122 252 function SplineVertexToSide(y0, y1, y2, y3: single; t: single): single; 123 function ComputeBezierCurve(const curve: TCubicBezierCurve): ArrayOfTPointF; overload; 124 function ComputeBezierCurve(const curve: TQuadraticBezierCurve): ArrayOfTPointF; overload; 125 function ComputeBezierSpline(const spline: array of TCubicBezierCurve): ArrayOfTPointF; overload; 126 function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve): ArrayOfTPointF; overload; 127 function ComputeClosedSpline(const points: array of TPointF; Style: TSplineStyle): ArrayOfTPointF; 128 function ComputeOpenedSpline(const points: array of TPointF; Style: TSplineStyle; EndCoeff: single = 0.25): ArrayOfTPointF; 253 function ComputeBezierCurve(const curve: TCubicBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 254 function ComputeBezierCurve(const curve: TQuadraticBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 255 function ComputeBezierSpline(const spline: array of TCubicBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 256 function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 257 function ComputeClosedSpline(const points: array of TPointF; Style: TSplineStyle; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; 258 function ComputeOpenedSpline(const points: array of TPointF; Style: TSplineStyle; EndCoeff: single = 0.25; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; 259 function ClosedSplineStartPoint(const points: array of TPointF; Style: TSplineStyle): TPointF; 129 260 130 261 { Compute points to draw an antialiased ellipse } … … 147 278 uses Math, BGRAResample, SysUtils; 148 279 280 type 281 TStrokeData = record 282 Bitmap: TBGRACustomBitmap; 283 Texture: IBGRAScanner; 284 Color: TBGRAPixel; 285 Width: Single; 286 end; 287 288 PPathElementHeader = ^TPathElementHeader; 289 TPathElementHeader = record 290 ElementType: TBGRAPathElementType; 291 PreviousElementType: TBGRAPathElementType; 292 end; 293 PMoveToElement = ^TMoveToElement; 294 TMoveToElement = record 295 StartCoordinate: TPointF; 296 LoopDataPos: PtrInt; //if the path is closed 297 end; 298 PClosePathElement = ^TClosePathElement; 299 TClosePathElement = type TMoveToElement; 300 PQuadraticBezierToElement = ^TQuadraticBezierToElement; 301 TQuadraticBezierToElement = record 302 ControlPoint, Destination: TPointF; 303 end; 304 PCubicBezierToElement = ^TCubicBezierToElement; 305 TCubicBezierToElement = record 306 ControlPoint1, ControlPoint2, Destination: TPointF; 307 end; 308 PArcElement = ^TArcElement; 309 TArcElement = TArcDef; 310 311 PSplineElement = ^TSplineElement; 312 TSplineElement = record 313 SplineStyle: TSplineStyle; 314 NbControlPoints: integer; 315 end; 316 317 const 318 PathElementSize : array[TBGRAPathElementType] of PtrInt = 319 (0, Sizeof(TMoveToElement), Sizeof(TClosePathElement), sizeof(TPointF), 320 sizeof(TQuadraticBezierToElement), sizeof(TCubicBezierToElement), 321 sizeof(TArcElement), sizeof(TSplineElement)+sizeof(integer), 322 sizeof(TSplineElement)+sizeof(integer)); 323 149 324 function SplineVertexToSide(y0, y1, y2, y3: single; t: single): single; 150 325 var … … 160 335 end; 161 336 162 function ComputeCurveP recision(pt1, pt2, pt3, pt4: TPointF): integer;337 function ComputeCurvePartPrecision(pt1, pt2, pt3, pt4: TPointF; AAcceptedDeviation: single = 0.1): integer; 163 338 var 164 339 len: single; … … 167 342 len := max(len, sqr(pt3.x - pt2.x) + sqr(pt3.y - pt2.y)); 168 343 len := max(len, sqr(pt3.x - pt4.x) + sqr(pt3.y - pt4.y)); 169 Result := round(sqrt(sqrt(len) ) * 2);344 Result := round(sqrt(sqrt(len)/AAcceptedDeviation) * 0.9); 170 345 if Result<=0 then Result:=1; 171 346 end; 172 347 173 function ComputeBezierCurve(const curve: TCubicBezierCurve): ArrayOfTPointF; overload; 174 var 175 t,f1,f2,f3,f4: single; 176 i,nb: Integer; 177 begin 178 nb := ComputeCurvePrecision(curve.p1,curve.c1,curve.c2,curve.p2); 179 if nb <= 1 then nb := 2; 180 setlength(result,nb); 181 result[0] := curve.p1; 182 result[nb-1] := curve.p2; 183 for i := 1 to nb-2 do 184 begin 185 t := i/(nb-1); 186 f1 := (1-t); 187 f2 := f1*f1; 188 f1 *= f2; 189 f2 *= t*3; 190 f4 := t*t; 191 f3 := f4*(1-t)*3; 192 f4 *= t; 193 194 result[i] := PointF(f1*curve.p1.x + f2*curve.c1.x + 195 f3*curve.c2.x + f4*curve.p2.x, 196 f1*curve.p1.y + f2*curve.c1.y + 197 f3*curve.c2.y + f4*curve.p2.y); 198 end; 199 end; 200 201 function ComputeBezierCurve(const curve: TQuadraticBezierCurve): ArrayOfTPointF; overload; 202 var 203 t,f1,f2,f3: single; 204 i,nb: Integer; 205 begin 206 nb := ComputeCurvePrecision(curve.p1,curve.c,curve.c,curve.p2); 207 if nb <= 1 then nb := 2; 208 setlength(result,nb); 209 result[0] := curve.p1; 210 result[nb-1] := curve.p2; 211 for i := 1 to nb-2 do 212 begin 213 t := i/(nb-1); 214 f1 := (1-t); 215 f3 := t; 216 f2 := f1*f3*2; 217 f1 *= f1; 218 f3 *= f3; 219 result[i] := PointF(f1*curve.p1.x + f2*curve.c.x + f3*curve.p2.x, 220 f1*curve.p1.y + f2*curve.c.y + f3*curve.p2.y); 221 end; 222 end; 223 224 function ComputeBezierSpline(const spline: array of TCubicBezierCurve): ArrayOfTPointF; 348 function ComputeBezierCurve(const curve: TCubicBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 349 begin 350 result := curve.ToPoints(AAcceptedDeviation); 351 end; 352 353 function ComputeBezierCurve(const curve: TQuadraticBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 354 begin 355 result := curve.ToPoints(AAcceptedDeviation); 356 end; 357 358 function ComputeBezierSpline(const spline: array of TCubicBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; 225 359 var 226 360 curves: array of array of TPointF; … … 250 384 setlength(curves, length(spline)); 251 385 for i := 0 to high(spline) do 252 curves[i] := ComputeBezierCurve(spline[i] );386 curves[i] := ComputeBezierCurve(spline[i],AAcceptedDeviation); 253 387 nb := length(curves[0]); 254 388 lastPt := curves[0][high(curves[0])]; … … 271 405 end; 272 406 273 function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve 274 ): ArrayOfTPointF;407 function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve; 408 AAcceptedDeviation: single = 0.1): ArrayOfTPointF; 275 409 var 276 410 curves: array of array of TPointF; … … 300 434 setlength(curves, length(spline)); 301 435 for i := 0 to high(spline) do 302 curves[i] := ComputeBezierCurve(spline[i] );436 curves[i] := ComputeBezierCurve(spline[i],AAcceptedDeviation); 303 437 nb := length(curves[0]); 304 438 lastPt := curves[0][high(curves[0])]; … … 321 455 end; 322 456 323 function ComputeClosedSpline(const points: array of TPointF; Style: TSplineStyle ): ArrayOfTPointF;457 function ComputeClosedSpline(const points: array of TPointF; Style: TSplineStyle; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; 324 458 var 325 459 i, j, nb, idx, pre: integer; … … 344 478 ptNext := points[(i + 1) mod length(points)]; 345 479 ptNext2 := points[(i + 2) mod length(points)]; 346 nb += ComputeCurveP recision(ptPrev2, ptPrev, ptNext, ptNext2);480 nb += ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2, AAcceptedDeviation); 347 481 end; 348 482 349 483 kernel := CreateInterpolator(style); 350 484 setlength(Result, nb); 485 idx := 0; 351 486 for i := 0 to high(points) do 352 487 begin … … 355 490 ptNext := points[(i + 1) mod length(points)]; 356 491 ptNext2 := points[(i + 2) mod length(points)]; 357 pre := ComputeCurveP recision(ptPrev2, ptPrev, ptNext, ptNext2);492 pre := ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2, AAcceptedDeviation); 358 493 if i=0 then 359 begin 360 j := 0; 361 idx := 0; 362 end else j := 1; 494 j := 0 495 else 496 j := 1; 363 497 while j <= pre do 364 498 begin … … 373 507 end; 374 508 375 function ComputeOpenedSpline(const points: array of TPointF; Style: TSplineStyle; EndCoeff: single ): ArrayOfTPointF;509 function ComputeOpenedSpline(const points: array of TPointF; Style: TSplineStyle; EndCoeff: single; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; 376 510 var 377 511 i, j, nb, idx, pre: integer; … … 403 537 else 404 538 ptNext2 := points[i + 2]; 405 nb += ComputeCurveP recision(ptPrev2, ptPrev, ptNext, ptNext2);539 nb += ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2, AAcceptedDeviation); 406 540 end; 407 541 … … 430 564 else 431 565 ptNext2 := points[i + 2]; 432 pre := ComputeCurveP recision(ptPrev2, ptPrev, ptNext, ptNext2);566 pre := ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2, AAcceptedDeviation); 433 567 if i=0 then 434 568 begin … … 447 581 if Style in[ssInsideWithEnds,ssCrossingWithEnds] then 448 582 result[idx] := points[high(points)]; 583 end; 584 585 function ClosedSplineStartPoint(const points: array of TPointF; 586 Style: TSplineStyle): TPointF; 587 var 588 kernel: TWideKernelFilter; 589 ptPrev2: TPointF; 590 ptPrev: TPointF; 591 ptNext: TPointF; 592 ptNext2: TPointF; 593 begin 594 if length(points) = 0 then 595 result := EmptyPointF 596 else 597 if length(points)<=2 then 598 result := points[0] 599 else 600 begin 601 kernel := CreateInterpolator(style); 602 ptPrev2 := points[high(points)]; 603 ptPrev := points[0]; 604 ptNext := points[1]; 605 ptNext2 := points[2]; 606 result := ptPrev2*kernel.Interpolation(1) + ptPrev*kernel.Interpolation(0) + 607 ptNext*kernel.Interpolation(-1) + ptNext2*kernel.Interpolation(-2); 608 kernel.free; 609 end; 449 610 end; 450 611 … … 707 868 end; 708 869 870 { TBGRAPathCursor } 871 872 function TBGRAPathCursor.GetCurrentCoord: TPointF; 873 begin 874 case FCurrentElementType of 875 peNone: result := EmptyPointF; 876 peMoveTo,peLineTo,peCloseSubPath: 877 if FCurrentElementLength <= 0 then 878 result := FCurrentElementStartCoord 879 else 880 result := FCurrentElementStartCoord + (FCurrentElementEndCoord-FCurrentElementStartCoord)*(FCurrentElementArcPos/FCurrentElementLength); 881 peCubicBezierTo,peQuadraticBezierTo,peArc,peOpenedSpline,peClosedSpline: 882 begin 883 NeedPolygonalApprox; 884 if FCurrentSegment >= high(FCurrentElementPoints) then 885 result := FCurrentElementEndCoord 886 else 887 result := FCurrentElementPoints[FCurrentSegment]+ 888 (FCurrentElementPoints[FCurrentSegment+1]- 889 FCurrentElementPoints[FCurrentSegment])*FCurrentSegmentPos; 890 end; 891 else 892 raise Exception.Create('Unknown element type'); 893 end; 894 end; 895 896 function TBGRAPathCursor.GetPath: TBGRAPath; 897 begin 898 if not Assigned(FPath) then 899 raise exception.Create('Path does not exist'); 900 result := FPath; 901 end; 902 903 procedure TBGRAPathCursor.MoveToEndOfElement; 904 begin 905 FCurrentElementArcPos := FCurrentElementLength; 906 if not NeedPolygonalApprox then exit; 907 if length(FCurrentElementPoints) > 1 then 908 begin 909 FCurrentSegment := high(FCurrentElementPoints)-1; 910 FCurrentSegmentPos := 1; 911 end else 912 begin 913 FCurrentSegment := high(FCurrentElementPoints); 914 FCurrentSegmentPos := 0; 915 end; 916 end; 917 918 procedure TBGRAPathCursor.MoveForwardInElement(ADistance: single); 919 var segLen,rightSpace,remaining: single; 920 begin 921 if not NeedPolygonalApprox then exit; 922 ADistance *= FCurrentElementArcPosScale; 923 remaining := ADistance; 924 while remaining > 0 do 925 begin 926 if FCurrentSegment < high(FCurrentElementPoints) then 927 segLen := VectLen(FCurrentElementPoints[FCurrentSegment+1]-FCurrentElementPoints[FCurrentSegment]) 928 else 929 segLen := 0; 930 rightSpace := segLen*(1-FCurrentSegmentPos); 931 if (segLen > 0) and (remaining <= rightSpace) then 932 begin 933 FCurrentSegmentPos += remaining/segLen; 934 exit; 935 end else 936 begin 937 remaining -= rightSpace; 938 if FCurrentSegment < high(FCurrentElementPoints)-1 then 939 begin 940 inc(FCurrentSegment); 941 FCurrentSegmentPos := 0; 942 end else 943 begin 944 FCurrentSegmentPos := 1; 945 exit; 946 end; 947 end; 948 end; 949 end; 950 951 procedure TBGRAPathCursor.MoveBackwardInElement(ADistance: single); 952 var 953 segLen,leftSpace,remaining: Single; 954 begin 955 if not NeedPolygonalApprox then exit; 956 ADistance *= FCurrentElementArcPosScale; 957 remaining := ADistance; 958 while remaining > 0 do 959 begin 960 if FCurrentSegment < high(FCurrentElementPoints) then 961 segLen := VectLen(FCurrentElementPoints[FCurrentSegment+1]-FCurrentElementPoints[FCurrentSegment]) 962 else 963 segLen := 0; 964 leftSpace := segLen*FCurrentSegmentPos; 965 if (segLen > 0) and (remaining <= leftSpace) then 966 begin 967 FCurrentSegmentPos -= remaining/segLen; 968 exit; 969 end else 970 begin 971 remaining -= leftSpace; 972 if FCurrentSegment > 0 then 973 begin 974 dec(FCurrentSegment); 975 FCurrentSegmentPos := 1; 976 end else 977 begin 978 FCurrentSegmentPos := 0; 979 exit; 980 end; 981 end; 982 end; 983 end; 984 985 function TBGRAPathCursor.NeedPolygonalApprox: boolean; 986 begin 987 if not (FCurrentElementType in[peQuadraticBezierTo,peCubicBezierTo,peArc, 988 peOpenedSpline,peClosedSpline]) 989 then 990 begin 991 result := false; 992 exit; 993 end; 994 result := true; 995 if FCurrentElementPoints = nil then 996 begin 997 FCurrentElementPoints := Path.GetPolygonalApprox(FDataPos, FAcceptedDeviation, True); 998 if FCurrentElementType = peQuadraticBezierTo then 999 begin 1000 if FCurrentElementLength <> 0 then 1001 FCurrentElementArcPosScale := PolylineLen(FCurrentElementPoints)/FCurrentElementLength; 1002 end; 1003 end; 1004 end; 1005 1006 function TBGRAPathCursor.GetArcPos: single; 1007 var pos: PtrInt; 1008 begin 1009 if FArcPos = EmptySingle then 1010 begin 1011 FArcPos := FCurrentElementArcPos; 1012 pos := FDataPos; 1013 while Path.GoToPreviousElement(pos) do 1014 FArcPos += Path.GetElementLength(pos, FAcceptedDeviation); 1015 end; 1016 result := FArcPos; 1017 end; 1018 1019 function TBGRAPathCursor.GetCurrentTangent: TPointF; 1020 var idxStart,idxEnd: integer; 1021 seg: TPointF; 1022 begin 1023 while FCurrentElementLength <= 0 do 1024 begin 1025 if not GoToNextElement(False) then 1026 begin 1027 result := EmptyPointF; 1028 exit; 1029 end; 1030 end; 1031 case FCurrentElementType of 1032 peMoveTo,peLineTo,peCloseSubPath: 1033 result := (FCurrentElementEndCoord-FCurrentElementStartCoord)*(1/FCurrentElementLength); 1034 peCubicBezierTo,peQuadraticBezierTo,peArc,peOpenedSpline,peClosedSpline: 1035 begin 1036 NeedPolygonalApprox; 1037 idxStart := FCurrentSegment; 1038 if idxStart >= high(FCurrentElementPoints) then 1039 idxStart:= high(FCurrentElementPoints)-1; 1040 idxEnd := idxStart+1; 1041 if idxStart < 0 then 1042 begin 1043 result := EmptyPointF; 1044 exit; 1045 end; 1046 seg := FCurrentElementPoints[idxEnd] - FCurrentElementPoints[idxStart]; 1047 while (seg.x = 0) and (seg.y = 0) and (idxEnd < high(FCurrentElementPoints)) do 1048 begin 1049 inc(idxEnd); 1050 seg := FCurrentElementPoints[idxEnd] - FCurrentElementPoints[idxStart]; 1051 end; 1052 while (seg.x = 0) and (seg.y = 0) and (idxStart > 0) do 1053 begin 1054 dec(idxStart); 1055 seg := FCurrentElementPoints[idxEnd] - FCurrentElementPoints[idxStart]; 1056 end; 1057 if (seg.x = 0) and (seg.y = 0) then 1058 result := EmptyPointF 1059 else 1060 result := seg*(1/VectLen(seg)); 1061 end; 1062 else result := EmptyPointF; 1063 end; 1064 end; 1065 1066 procedure TBGRAPathCursor.SetArcPos(AValue: single); 1067 var oldLoopClosedShapes,oldLoopPath: boolean; 1068 begin 1069 if GetArcPos=AValue then Exit; 1070 if (AValue > PathLength) and (PathLength <> 0) then 1071 AValue := AValue - trunc(AValue/PathLength)*PathLength 1072 else if (AValue < 0) then 1073 AValue := AValue + (trunc(-AValue/PathLength)+1)*PathLength; 1074 oldLoopClosedShapes:= LoopClosedShapes; 1075 oldLoopPath:= LoopPath; 1076 LoopClosedShapes:= false; 1077 LoopPath:= false; 1078 MoveForward(AValue-GetArcPos, True); 1079 LoopClosedShapes:= oldLoopClosedShapes; 1080 LoopPath:= oldLoopPath; 1081 end; 1082 1083 function TBGRAPathCursor.GetPathLength: single; 1084 begin 1085 if not FPathLengthComputed then 1086 begin 1087 FPathLength := Path.ComputeLength(FAcceptedDeviation); 1088 FPathLengthComputed := true; 1089 end; 1090 result := FPathLength; 1091 end; 1092 1093 procedure TBGRAPathCursor.OnPathFree; 1094 begin 1095 FPath := nil; 1096 end; 1097 1098 function TBGRAPathCursor.GetLoopClosedShapes: boolean; 1099 begin 1100 result := FLoopClosedShapes; 1101 end; 1102 1103 function TBGRAPathCursor.GetLoopPath: boolean; 1104 begin 1105 result := FLoopPath; 1106 end; 1107 1108 function TBGRAPathCursor.GetStartCoordinate: TPointF; 1109 begin 1110 result := FStartCoordinate; 1111 end; 1112 1113 procedure TBGRAPathCursor.SetLoopClosedShapes(AValue: boolean); 1114 begin 1115 FLoopClosedShapes := AValue; 1116 end; 1117 1118 procedure TBGRAPathCursor.SetLoopPath(AValue: boolean); 1119 begin 1120 FLoopPath := AValue; 1121 end; 1122 1123 procedure TBGRAPathCursor.PrepareCurrentElement; 1124 begin 1125 Path.GetElementAt(FDataPos, FCurrentElementType, FCurrentElement); 1126 FCurrentElementLength := 0; 1127 FCurrentElementArcPos := 0; 1128 FCurrentElementPoints := nil; 1129 FCurrentSegment := 0; 1130 FCurrentSegmentPos := 0; 1131 FCurrentElementArcPosScale := 1; 1132 if FCurrentElementType = peNone then 1133 begin 1134 FCurrentElementStartCoord := EmptyPointF; 1135 FCurrentElementEndCoord := EmptyPointF; 1136 end 1137 else 1138 begin 1139 FCurrentElementStartCoord := Path.GetElementStartCoord(FDataPos); 1140 case FCurrentElementType of 1141 peLineTo, peCloseSubPath: 1142 begin 1143 FCurrentElementEndCoord := PPointF(FCurrentElement)^; 1144 FCurrentElementLength := VectLen(FCurrentElementEndCoord - FCurrentElementStartCoord); 1145 end; 1146 peQuadraticBezierTo: with PQuadraticBezierToElement(FCurrentElement)^ do 1147 begin 1148 FCurrentElementEndCoord := Destination; 1149 FCurrentElementLength := BGRABitmapTypes.BezierCurve(FCurrentElementStartCoord,ControlPoint,Destination).ComputeLength; 1150 end; 1151 peCubicBezierTo,peArc,peOpenedSpline,peClosedSpline: 1152 begin 1153 NeedPolygonalApprox; 1154 FCurrentElementEndCoord := FCurrentElementPoints[high(FCurrentElementPoints)]; 1155 FCurrentElementLength := PolylineLen(FCurrentElementPoints); 1156 end; 1157 else 1158 FCurrentElementEndCoord := FCurrentElementStartCoord; 1159 end; 1160 end; 1161 end; 1162 1163 function TBGRAPathCursor.GetBounds: TRectF; 1164 begin 1165 if not FBoundsComputed then 1166 begin 1167 FBounds:= Path.GetBounds(FAcceptedDeviation); 1168 FBoundsComputed := true; 1169 end; 1170 result := FBounds; 1171 end; 1172 1173 function TBGRAPathCursor.GoToNextElement(ACanJump: boolean): boolean; 1174 begin 1175 if (FCurrentElementType = peCloseSubPath) and 1176 (PClosePathElement(FCurrentElement)^.LoopDataPos <> -1) and 1177 ( FLoopClosedShapes or 1178 (FLoopPath and (PClosePathElement(FCurrentElement)^.LoopDataPos = 0)) 1179 ) then 1180 begin 1181 if PClosePathElement(FCurrentElement)^.LoopDataPos <> FDataPos then 1182 begin 1183 result := true; 1184 FDataPos := PClosePathElement(FCurrentElement)^.LoopDataPos; 1185 FArcPos := EmptySingle; 1186 PrepareCurrentElement; 1187 end else 1188 result := false; 1189 end; 1190 if not ACanJump and ((FCurrentElementType = peCloseSubPath) 1191 or (Path.PeekNextElement(FDataPos) = peMoveTo)) then 1192 begin 1193 result := false; 1194 exit; 1195 end; 1196 if Path.GoToNextElement(FDataPos) then 1197 begin 1198 result := true; 1199 PrepareCurrentElement; 1200 end 1201 else 1202 begin 1203 if ACanJump and FLoopPath and (FDataPos > 0) then 1204 begin 1205 result := true; 1206 FDataPos := 0; 1207 FArcPos := EmptySingle; 1208 PrepareCurrentElement; 1209 end else 1210 result := false; 1211 end; 1212 end; 1213 1214 function TBGRAPathCursor.GoToPreviousElement(ACanJump: boolean): boolean; 1215 var lastElemPos: IntPtr; 1216 begin 1217 if (FCurrentElementType = peMoveTo) and (PMoveToElement(FCurrentElement)^.LoopDataPos <> -1) and 1218 ( FLoopClosedShapes or 1219 (FLoopPath and (FDataPos = 0)) 1220 ) then 1221 with PMoveToElement(FCurrentElement)^ do 1222 begin 1223 if LoopDataPos <> -1 then 1224 begin 1225 result := true; 1226 FDataPos := LoopDataPos; 1227 FArcPos := EmptySingle; 1228 PrepareCurrentElement; 1229 end; 1230 end; 1231 if not ACanJump and (FCurrentElementType = peMoveTo) then 1232 begin 1233 result := false; 1234 exit; 1235 end; 1236 if Path.GoToPreviousElement(FDataPos) then 1237 begin 1238 result := true; 1239 PrepareCurrentElement; 1240 end 1241 else 1242 begin 1243 if FLoopPath then 1244 begin 1245 lastElemPos := FPath.FDataPos; 1246 if (lastElemPos > 0) and FPath.GoToPreviousElement(lastElemPos) then 1247 begin 1248 if lastElemPos > 0 then 1249 begin 1250 result := true; 1251 FDataPos := lastElemPos; 1252 PrepareCurrentElement; 1253 FArcPos := EmptySingle; 1254 exit; 1255 end; 1256 end; 1257 end; 1258 result := false; 1259 end; 1260 end; 1261 1262 constructor TBGRAPathCursor.Create(APath: TBGRAPath; AAcceptedDeviation: single); 1263 begin 1264 FPath := APath; 1265 FPathLengthComputed := false; 1266 FBoundsComputed:= false; 1267 FDataPos := 0; 1268 FArcPos:= 0; 1269 FAcceptedDeviation:= AAcceptedDeviation; 1270 Path.RegisterCursor(self); 1271 PrepareCurrentElement; 1272 1273 FStartCoordinate := FCurrentElementStartCoord; 1274 if isEmptyPointF(FStartCoordinate) then 1275 raise exception.Create('Path does not has a starting coordinate'); 1276 FEndCoordinate := Path.FLastTransformedCoord; 1277 if isEmptyPointF(FEndCoordinate) then 1278 raise exception.Create('Path does not has an ending coordinate'); 1279 end; 1280 1281 function TBGRAPathCursor.MoveForward(ADistance: single; ACanJump: boolean): single; 1282 var newArcPos,step,remaining: single; 1283 begin 1284 if ADistance < 0 then 1285 begin 1286 result := -MoveBackward(-ADistance, ACanJump); 1287 exit; 1288 end; 1289 result := 0; 1290 remaining := ADistance; 1291 while remaining > 0 do 1292 begin 1293 newArcPos := FCurrentElementArcPos + remaining; 1294 if newArcPos > FCurrentElementLength then 1295 begin 1296 step := FCurrentElementLength - FCurrentElementArcPos; 1297 result += step; 1298 remaining -= step; 1299 if not GoToNextElement(ACanJump) then 1300 begin 1301 MoveForwardInElement(step); 1302 FCurrentElementArcPos := FCurrentElementLength; 1303 FArcPos := PathLength; 1304 exit; 1305 end; 1306 end else 1307 begin 1308 MoveForwardInElement(remaining); 1309 FCurrentElementArcPos := newArcPos; 1310 result := ADistance; 1311 break; 1312 end; 1313 end; 1314 if FArcPos <> EmptySingle then 1315 FArcPos += result; 1316 end; 1317 1318 function TBGRAPathCursor.MoveBackward(ADistance: single; ACanJump: boolean = true): single; 1319 var 1320 remaining: Single; 1321 newArcPos: Single; 1322 step: Single; 1323 begin 1324 if ADistance = 0 then 1325 begin 1326 result := 0; 1327 exit; 1328 end; 1329 if ADistance < 0 then 1330 begin 1331 result := -MoveForward(-ADistance, ACanJump); 1332 exit; 1333 end; 1334 result := 0; 1335 remaining := ADistance; 1336 while remaining > 0 do 1337 begin 1338 newArcPos := FCurrentElementArcPos - remaining; 1339 if newArcPos < 0 then 1340 begin 1341 step := FCurrentElementArcPos; 1342 result += step; 1343 remaining -= step; 1344 if not GoToPreviousElement(ACanJump) then 1345 begin 1346 MoveBackwardInElement(step); 1347 FCurrentElementArcPos := 0; 1348 FArcPos := 0; 1349 exit; 1350 end else 1351 MoveToEndOfElement; 1352 end else 1353 begin 1354 MoveBackwardInElement(remaining); 1355 FCurrentElementArcPos := newArcPos; 1356 result := ADistance; 1357 break; 1358 end; 1359 end; 1360 if FArcPos <> EmptySingle then 1361 FArcPos -= result; 1362 end; 1363 1364 destructor TBGRAPathCursor.Destroy; 1365 begin 1366 if Assigned(FPath) then 1367 begin 1368 FPath.UnregisterCursor(self); 1369 end; 1370 inherited Destroy; 1371 end; 1372 709 1373 { TBGRAPath } 1374 1375 function TBGRAPath.ComputeLength(AAcceptedDeviation: single): single; 1376 var pos: PtrInt; 1377 begin 1378 pos := 0; 1379 result := 0; 1380 repeat 1381 result += GetElementLength(pos, AAcceptedDeviation); 1382 until not GoToNextElement(pos); 1383 end; 1384 1385 function TBGRAPath.ToPoints(AAcceptedDeviation: single): ArrayOfTPointF; 1386 var sub: array of ArrayOfTPointF; 1387 temp: ArrayOfTPointF; 1388 nbSub,nbPts,curPt,curSub: NativeInt; 1389 startPos,pos: PtrInt; 1390 elemType: TBGRAPathElementType; 1391 elem: pointer; 1392 begin 1393 pos := 0; 1394 nbSub := 0; 1395 repeat 1396 GetElementAt(pos, elemType, elem); 1397 if elem = nil then break; 1398 case elemType of 1399 peMoveTo,peLineTo,peCloseSubPath: begin 1400 inc(nbSub); 1401 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 1402 GoToNextElement(pos); 1403 end; 1404 peQuadraticBezierTo, peCubicBezierTo, peArc, peOpenedSpline, peClosedSpline: inc(nbSub); 1405 end; 1406 until not GoToNextElement(pos); 1407 1408 pos := 0; 1409 setlength(sub, nbSub); 1410 curSub := 0; 1411 repeat 1412 GetElementAt(pos, elemType, elem); 1413 if elem = nil then break; 1414 case elemType of 1415 peMoveTo,peLineTo,peCloseSubPath: begin 1416 startPos := pos; 1417 if (elemType = peMoveTo) and (curSub > 0) then 1418 nbPts := 2 1419 else 1420 nbPts := 1; 1421 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 1422 begin 1423 GoToNextElement(pos); 1424 inc(nbPts); 1425 end; 1426 setlength(temp, nbPts); 1427 pos := startPos; 1428 if (elemType = peMoveTo) and (curSub > 0) then 1429 begin 1430 temp[0] := EmptyPointF; 1431 temp[1] := PPointF(elem)^; 1432 curPt := 2; 1433 end else 1434 begin 1435 temp[0] := PPointF(elem)^; 1436 curPt := 1; 1437 end; 1438 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 1439 begin 1440 GoToNextElement(pos); 1441 GetElementAt(pos, elemType, elem); 1442 temp[curPt] := PPointF(elem)^; 1443 inc(curPt); 1444 end; 1445 sub[curSub] := temp; 1446 inc(curSub); 1447 temp := nil; 1448 end; 1449 peQuadraticBezierTo,peCubicBezierTo,peArc, 1450 peOpenedSpline, peClosedSpline: 1451 begin 1452 sub[curSub] := GetPolygonalApprox(pos, AAcceptedDeviation, False); 1453 inc(curSub); 1454 end; 1455 end; 1456 until not GoToNextElement(pos) or (curSub = nbSub); 1457 result := ConcatPointsF(sub); 1458 end; 1459 1460 function TBGRAPath.ToPoints(AMatrix: TAffineMatrix; AAcceptedDeviation: single): ArrayOfTPointF; 1461 begin 1462 AAcceptedDeviation:= CorrectAcceptedDeviation(AAcceptedDeviation,AMatrix); 1463 result := ToPoints(AAcceptedDeviation); 1464 if not IsAffineMatrixIdentity(AMatrix) then 1465 result := AMatrix*result; 1466 end; 1467 1468 function TBGRAPath.IsEmpty: boolean; 1469 begin 1470 result := FDataPos = 0; 1471 end; 1472 1473 function TBGRAPath.GetBounds(AAcceptedDeviation: single): TRectF; 1474 var empty: boolean; 1475 pos: PtrInt; 1476 elemType: TBGRAPathElementType; 1477 elem: pointer; 1478 temp: array of TPointF; 1479 i: integer; 1480 1481 procedure Include(pt: TPointF); 1482 begin 1483 if empty then 1484 begin 1485 result.TopLeft := pt; 1486 result.BottomRight := pt; 1487 empty := false; 1488 end else 1489 begin 1490 if pt.x < result.Left then result.Left := pt.x 1491 else if pt.x > result.Right then result.Right := pt.x; 1492 if pt.y < result.Top then result.Top := pt.y 1493 else if pt.y > result.Bottom then result.Bottom := pt.y; 1494 end; 1495 end; 1496 1497 procedure IncludeRect(r: TRectF); 1498 begin 1499 Include(r.TopLeft); 1500 Include(r.BottomRight); 1501 end; 1502 1503 begin 1504 empty := true; 1505 result := RectF(0,0,0,0); 1506 pos := 0; 1507 repeat 1508 GetElementAt(pos, elemType, elem); 1509 if elem = nil then break; 1510 case elemType of 1511 peMoveTo,peLineTo,peCloseSubPath: begin 1512 Include(PPointF(elem)^); 1513 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 1514 begin 1515 GoToNextElement(pos); 1516 GetElementAt(pos, elemType, elem); 1517 Include(PPointF(elem)^); 1518 end; 1519 end; 1520 peCubicBezierTo: 1521 with PCubicBezierToElement(elem)^ do 1522 IncludeRect(BGRABitmapTypes.BezierCurve(GetElementStartCoord(pos),ControlPoint1,ControlPoint2,Destination).GetBounds); 1523 peQuadraticBezierTo: 1524 with PQuadraticBezierToElement(elem)^ do 1525 IncludeRect(BGRABitmapTypes.BezierCurve(GetElementStartCoord(pos),ControlPoint,Destination).GetBounds); 1526 peArc, peOpenedSpline, peClosedSpline: 1527 begin 1528 temp := GetPolygonalApprox(pos, AAcceptedDeviation, False); 1529 for i := 0 to high(temp) do 1530 Include(temp[i]); 1531 end; 1532 end; 1533 until not GoToNextElement(pos); 1534 if empty then raise exception.Create('Path is empty'); 1535 end; 1536 1537 procedure TBGRAPath.SetPoints(const APoints: ArrayOfTPointF); 1538 var i: integer; 1539 nextIsMoveTo: boolean; 1540 startPoint: TPointF; 1541 begin 1542 beginPath; 1543 if length(APoints) = 0 then exit; 1544 NeedSpace((sizeof(TPathElementHeader)+sizeof(TPointF))*length(APoints)); 1545 nextIsMoveTo:= true; 1546 startPoint := EmptyPointF; 1547 for i := 0 to high(APoints) do 1548 begin 1549 if isEmptyPointF(APoints[i]) then 1550 nextIsMoveTo:= true 1551 else 1552 if nextIsMoveTo then 1553 begin 1554 startPoint := APoints[i]; 1555 moveTo(startPoint); 1556 nextIsMoveTo:= false; 1557 end 1558 else 1559 begin 1560 with APoints[i] do 1561 if (x = startPoint.x) and (y = startPoint.y) then 1562 closePath 1563 else 1564 lineTo(APoints[i]); 1565 end; 1566 end; 1567 end; 1568 1569 procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; AColor: TBGRAPixel; 1570 AWidth: single; AAcceptedDeviation: single); 1571 begin 1572 stroke(ABitmap,AffineMatrixIdentity,AColor,AWidth,AAcceptedDeviation); 1573 end; 1574 1575 procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; ATexture: IBGRAScanner; 1576 AWidth: single; AAcceptedDeviation: single); 1577 begin 1578 stroke(ABitmap,AffineMatrixIdentity,ATexture,AWidth,AAcceptedDeviation); 1579 end; 1580 1581 procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; x, y: single; 1582 AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single); 1583 begin 1584 stroke(ABitmap,AffineMatrixTranslation(x,y),AColor,AWidth,AAcceptedDeviation); 1585 end; 1586 1587 procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; x, y: single; 1588 ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single); 1589 begin 1590 stroke(ABitmap,AffineMatrixTranslation(x,y),ATexture,AWidth,AAcceptedDeviation); 1591 end; 1592 1593 procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; 1594 AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single); 1595 var data: TStrokeData; 1596 begin 1597 data.Bitmap := ABitmap; 1598 data.Texture := nil; 1599 data.Color := AColor; 1600 data.Width := AWidth; 1601 InternalDraw(@BitmapDrawSubPathProc, AMatrix, AAcceptedDeviation, @data); 1602 end; 1603 1604 procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; 1605 ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single); 1606 var data: TStrokeData; 1607 begin 1608 data.Bitmap := ABitmap; 1609 data.Texture := ATexture; 1610 data.Color := BGRAPixelTransparent; 1611 data.Width := AWidth; 1612 InternalDraw(@BitmapDrawSubPathProc, AMatrix, AAcceptedDeviation, @data); 1613 end; 1614 1615 procedure TBGRAPath.stroke(ADrawProc: TBGRAPathDrawProc; 1616 const AMatrix: TAffineMatrix; AAcceptedDeviation: single; AData: pointer); 1617 begin 1618 InternalDraw(ADrawProc,AMatrix,AAcceptedDeviation,AData); 1619 end; 1620 1621 procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; AColor: TBGRAPixel; 1622 AAcceptedDeviation: single); 1623 begin 1624 fill(ABitmap,AffineMatrixIdentity,AColor,AAcceptedDeviation); 1625 end; 1626 1627 procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; ATexture: IBGRAScanner; 1628 AAcceptedDeviation: single); 1629 begin 1630 fill(ABitmap,AffineMatrixIdentity,ATexture,AAcceptedDeviation); 1631 end; 1632 1633 procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; x, y: single; 1634 AColor: TBGRAPixel; AAcceptedDeviation: single); 1635 begin 1636 fill(ABitmap,AffineMatrixTranslation(x,y),AColor,AAcceptedDeviation); 1637 end; 1638 1639 procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; x, y: single; 1640 ATexture: IBGRAScanner; AAcceptedDeviation: single); 1641 begin 1642 fill(ABitmap,AffineMatrixTranslation(x,y),ATexture,AAcceptedDeviation); 1643 end; 1644 1645 procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; 1646 AColor: TBGRAPixel; AAcceptedDeviation: single); 1647 begin 1648 ABitmap.FillPolyAntialias(ToPoints(AMatrix,AAcceptedDeviation), AColor); 1649 end; 1650 1651 procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; 1652 ATexture: IBGRAScanner; AAcceptedDeviation: single); 1653 begin 1654 ABitmap.FillPolyAntialias(ToPoints(AMatrix,AAcceptedDeviation), ATexture); 1655 end; 1656 1657 procedure TBGRAPath.fill(AFillProc: TBGRAPathFillProc; const AMatrix: TAffineMatrix; 1658 AAcceptedDeviation: single; AData: pointer); 1659 begin 1660 AFillProc(ToPoints(AMatrix,AAcceptedDeviation), AData); 1661 end; 1662 1663 function TBGRAPath.CreateCursor(AAcceptedDeviation: single): TBGRAPathCursor; 1664 begin 1665 result := TBGRAPathCursor.Create(self, AAcceptedDeviation); 1666 end; 1667 1668 procedure TBGRAPath.Fit(ARect: TRectF; AAcceptedDeviation: single); 1669 var 1670 temp: TBGRAPath; 1671 begin 1672 temp := TBGRAPath.Create; 1673 copyTo(temp); 1674 temp.FitInto(self, ARect, AAcceptedDeviation); 1675 temp.Free; 1676 end; 1677 1678 procedure TBGRAPath.FitInto(ADest: TBGRAPath; ARect: TRectF; 1679 AAcceptedDeviation: single); 1680 var bounds: TRectF; 1681 zoomX,zoomY: single; 1682 begin 1683 bounds := GetBounds(AAcceptedDeviation); 1684 ADest.beginPath; 1685 ADest.translate((ARect.Left+ARect.Right)*0.5, (ARect.Bottom+ARect.Top)*0.5); 1686 if bounds.Right-bounds.Left <> 0 then 1687 begin 1688 zoomX := (ARect.Right-ARect.Left)/(bounds.Right-bounds.Left); 1689 if bounds.Bottom-bounds.Top > 0 then 1690 begin 1691 zoomY := (ARect.Bottom-ARect.Top)/(bounds.Bottom-bounds.Top); 1692 if zoomY < zoomX then ADest.scale(zoomY) else ADest.scale(zoomX); 1693 end else 1694 ADest.scale(zoomX); 1695 end else 1696 if bounds.Bottom-bounds.Top > 0 then 1697 begin 1698 zoomY := (ARect.Bottom-ARect.Top)/(bounds.Bottom-bounds.Top); 1699 ADest.scale(zoomY); 1700 end; 1701 ADest.translate(-(bounds.Left+bounds.Right)*0.5, -(bounds.Bottom+bounds.Top)*0.5); 1702 copyTo(ADest); 1703 ADest.resetTransform; 1704 end; 710 1705 711 1706 function TBGRAPath.GetSvgString: string; 712 1707 const RadToDeg = 180/Pi; 713 var savedPos: integer; 714 a: TArcDef; 715 formats: TFormatSettings; 716 lastPos,p1: TPointF; 717 implicitCommand: char; 1708 var 1709 formats: TFormatSettings; 1710 lastPosF: TPointF; 1711 implicitCommand: char; 718 1712 719 1713 function FloatToString(value: single): string; … … 724 1718 function CoordToString(const pt: TPointF): string; 725 1719 begin 726 lastPos := pt;1720 lastPosF := pt; 727 1721 result := FloatToString(pt.x)+FloatToString(pt.y); 728 1722 end; … … 745 1739 end; 746 1740 747 var param: string; 748 1741 var elemType: TBGRAPathElementType; 1742 elem: pointer; 1743 a: PArcElement; 1744 Pos: PtrInt; 1745 p1: TPointF; 1746 pts: array of TPointF; 1747 i: integer; 749 1748 begin 750 1749 formats := DefaultFormatSettings; … … 752 1751 753 1752 result := ''; 754 savedPos:= FDataPos; 755 FDataPos := 0; 756 lastPos := EmptyPointF; 1753 Pos := 0; 1754 lastPosF := EmptyPointF; 757 1755 implicitCommand := #0; 758 while FDataPos < savedPos do 759 begin 760 case ReadElementType of 761 peMoveTo: addCommand('M',CoordToString(ReadCoord)); 762 peLineTo: addCommand('L',CoordToString(ReadCoord)); 763 peCloseSubPath: addCommand('z',''); 764 peQuadraticBezierTo: 765 begin 766 param := CoordToString(ReadCoord); 767 param += CoordToString(ReadCoord); 768 addCommand('Q',param); 769 end; 770 peCubicBezierTo: 771 begin 772 param := CoordToString(ReadCoord); 773 param += CoordToString(ReadCoord); 774 param += CoordToString(ReadCoord); 775 addCommand('C',param); 776 end; 777 peArc: 778 begin 779 a := ReadArcDef; 780 p1 := ArcStartPoint(a); 781 if isEmptyPointF(lastPos) or (p1 <> lastPos) then 782 addCommand('L',CoordToString(p1)); 783 param := CoordToString(a.radius); 784 param += FloatToString(a.xAngleRadCW*RadToDeg); 785 param += BoolToString(IsLargeArc(a)); 786 param += BoolToString(not a.anticlockwise); 787 param += CoordToString(ArcEndPoint(a)); 788 addCommand('A',param); 789 end; 790 end; 791 end; 792 FDataPos := savedPos; 1756 repeat 1757 GetElementAt(Pos, elemType, elem); 1758 if elem = nil then break; 1759 case elemType of 1760 peMoveTo: addCommand('M',CoordToString(PPointF(elem)^)); 1761 peLineTo: addCommand('L',CoordToString(PPointF(elem)^)); 1762 peCloseSubPath: addCommand('z',''); 1763 peQuadraticBezierTo: 1764 with PQuadraticBezierToElement(elem)^ do 1765 addCommand('Q',CoordToString(ControlPoint)+CoordToString(Destination)); 1766 peCubicBezierTo: 1767 with PCubicBezierToElement(elem)^ do 1768 addCommand('C',CoordToString(ControlPoint1)+ 1769 CoordToString(ControlPoint2)+CoordToString(Destination)); 1770 peArc: 1771 begin 1772 a := PArcElement(elem); 1773 p1 := ArcStartPoint(a^); 1774 if isEmptyPointF(lastPosF) or (p1 <> lastPosF) then 1775 addCommand('L',CoordToString(p1)); 1776 addCommand('A',CoordToString(a^.radius)+ 1777 FloatToString(a^.xAngleRadCW*RadToDeg)+ 1778 BoolToString(IsLargeArc(a^))+ 1779 BoolToString(not a^.anticlockwise)+ 1780 CoordToString(ArcEndPoint(a^))); 1781 end; 1782 peOpenedSpline, peClosedSpline: 1783 begin 1784 pts := GetPolygonalApprox(Pos, 0.1,True); 1785 for i := 0 to high(pts) do 1786 begin 1787 if isEmptyPointF(lastPosF) then 1788 addCommand('M',CoordToString(pts[i])) 1789 else 1790 addCommand('L',CoordToString(pts[i])); 1791 end; 1792 end; 1793 end; 1794 until not GoToNextElement(Pos); 793 1795 end; 794 1796 … … 800 1802 end; 801 1803 1804 procedure TBGRAPath.RegisterCursor(ACursor: TBGRAPathCursor); 1805 begin 1806 setlength(FCursors, length(FCursors)+1); 1807 FCursors[high(FCursors)] := ACursor; 1808 end; 1809 1810 procedure TBGRAPath.UnregisterCursor(ACursor: TBGRAPathCursor); 1811 var 1812 i,j: Integer; 1813 begin 1814 for i := high(FCursors) downto 0 do 1815 if FCursors[i] = ACursor then 1816 begin 1817 for j := i to high(FCursors)-1 do 1818 FCursors[j] := FCursors[j+1]; 1819 setlength(FCursors, length(FCursors)-1); 1820 exit; 1821 end; 1822 end; 1823 1824 function TBGRAPath.SetLastCoord(ACoord: TPointF): TPointF; 1825 begin 1826 FLastCoord := ACoord; 1827 FLastTransformedCoord := FMatrix*ACoord; 1828 result := FLastTransformedCoord; 1829 end; 1830 1831 procedure TBGRAPath.ClearLastCoord; 1832 begin 1833 FLastCoord := EmptyPointF; 1834 FLastTransformedCoord := EmptyPointF; 1835 end; 1836 1837 procedure TBGRAPath.BezierCurveFromTransformed(tcp1, cp2, pt: TPointF); 1838 begin 1839 with PCubicBezierToElement(AllocateElement(peCubicBezierTo))^ do 1840 begin 1841 ControlPoint1 := tcp1; 1842 ControlPoint2 := FMatrix*cp2; 1843 Destination := SetLastCoord(pt); 1844 FExpectedTransformedControlPoint := Destination + (Destination-ControlPoint2); 1845 end; 1846 end; 1847 1848 procedure TBGRAPath.QuadraticCurveFromTransformed(tcp, pt: TPointF); 1849 begin 1850 with PQuadraticBezierToElement(AllocateElement(peQuadraticBezierTo))^ do 1851 begin 1852 ControlPoint := tcp; 1853 Destination := SetLastCoord(pt); 1854 FExpectedTransformedControlPoint := Destination+(Destination-ControlPoint); 1855 end; 1856 end; 1857 1858 function TBGRAPath.LastCoordDefined: boolean; 1859 begin 1860 result := not isEmptyPointF(FLastTransformedCoord); 1861 end; 1862 1863 function TBGRAPath.GetPolygonalApprox(APos: IntPtr; AAcceptedDeviation: single; AIncludeFirstPoint: boolean): ArrayOfTPointF; 1864 var pts: ArrayOfTPointF; 1865 elemType: TBGRAPathElementType; 1866 elem: pointer; 1867 pt : TPointF; 1868 i: NativeInt; 1869 begin 1870 GetElementAt(APos, elemType, elem); 1871 case elemType of 1872 peQuadraticBezierTo: 1873 with PQuadraticBezierToElement(elem)^ do 1874 result := BGRABitmapTypes.BezierCurve(GetElementStartCoord(APos),ControlPoint,Destination).ToPoints(AAcceptedDeviation, AIncludeFirstPoint); 1875 peCubicBezierTo: 1876 with PCubicBezierToElement(elem)^ do 1877 result := BGRABitmapTypes.BezierCurve(GetElementStartCoord(APos),ControlPoint1,ControlPoint2,Destination).ToPoints(AAcceptedDeviation, AIncludeFirstPoint); 1878 peArc: 1879 begin 1880 result := ComputeArc(PArcElement(elem)^, 0.1/AAcceptedDeviation); 1881 pt := GetElementStartCoord(APos); 1882 if pt <> result[0] then 1883 begin 1884 setlength(result, length(result)+1); 1885 for i := high(result) downto 1 do 1886 result[i] := result[i-1]; 1887 result[0] := pt; 1888 end; 1889 end; 1890 peOpenedSpline, peClosedSpline: 1891 with PSplineElement(elem)^ do 1892 begin 1893 setlength(pts, NbControlPoints); 1894 move(Pointer(PSplineElement(elem)+1)^, pts[0], NbControlPoints*sizeof(TPointF)); 1895 if elemType = peOpenedSpline then 1896 result := ComputeOpenedSpline(pts, SplineStyle, 0.25, AAcceptedDeviation) 1897 else 1898 result := ComputeClosedSpline(pts, SplineStyle, AAcceptedDeviation); 1899 end; 1900 end; 1901 end; 1902 1903 function TBGRAPath.getPoints: ArrayOfTPointF; 1904 begin 1905 result := ToPoints; 1906 end; 1907 1908 function TBGRAPath.getPoints(AMatrix: TAffineMatrix): ArrayOfTPointF; 1909 begin 1910 result := ToPoints(AMatrix); 1911 end; 1912 1913 function TBGRAPath.getCursor: TBGRACustomPathCursor; 1914 begin 1915 result := CreateCursor; 1916 end; 1917 1918 procedure TBGRAPath.InternalDraw(ADrawProc: TBGRAPathDrawProc; 1919 const AMatrix: TAffineMatrix; AAcceptedDeviation: single; AData: pointer); 1920 var 1921 nbSub: NativeInt; 1922 1923 procedure OutputSub(subPathStartPos, subPathEndPos: IntPtr); 1924 var 1925 sub: array of ArrayOfTPointF; 1926 temp: ArrayOfTPointF; 1927 startPos,pos,nbPts,curPt,curSub: NativeInt; 1928 elemType: TBGRAPathElementType; 1929 elem: pointer; 1930 begin 1931 pos := subPathStartPos; 1932 setlength(sub, nbSub); 1933 curSub := 0; 1934 while (pos <= subPathEndPos) and (curSub < nbSub) do 1935 begin 1936 GetElementAt(pos, elemType, elem); 1937 if elem = nil then break; 1938 case elemType of 1939 peMoveTo,peLineTo,peCloseSubPath: begin 1940 startPos := pos; 1941 if (elemType = peMoveTo) and (curSub > 0) then 1942 nbPts := 2 1943 else 1944 nbPts := 1; 1945 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 1946 begin 1947 GoToNextElement(pos); 1948 inc(nbPts); 1949 end; 1950 setlength(temp, nbPts); 1951 pos := startPos; 1952 if (elemType = peMoveTo) and (curSub > 0) then 1953 begin 1954 temp[0] := EmptyPointF; 1955 temp[1] := PPointF(elem)^; 1956 curPt := 2; 1957 end else 1958 begin 1959 temp[0] := PPointF(elem)^; 1960 curPt := 1; 1961 end; 1962 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 1963 begin 1964 GoToNextElement(pos); 1965 GetElementAt(pos, elemType, elem); 1966 temp[curPt] := PPointF(elem)^; 1967 inc(curPt); 1968 end; 1969 sub[curSub] := temp; 1970 inc(curSub); 1971 temp := nil; 1972 end; 1973 peQuadraticBezierTo,peCubicBezierTo,peArc, 1974 peOpenedSpline, peClosedSpline: 1975 begin 1976 sub[curSub] := GetPolygonalApprox(pos, AAcceptedDeviation, False); 1977 inc(curSub); 1978 end; 1979 end; 1980 GoToNextElement(pos); 1981 end; 1982 temp := ConcatPointsF(sub); 1983 if not IsAffineMatrixIdentity(AMatrix) then 1984 temp := AMatrix*temp; 1985 if (elemType = peCloseSubPath) or ((curSub = 2) and (elemType = peClosedSpline)) then 1986 ADrawProc(temp, True, AData) 1987 else 1988 ADrawProc(temp, False, AData); 1989 end; 1990 1991 var 1992 subPathStartPos: IntPtr; 1993 prevPos,pos: PtrInt; 1994 elemType: TBGRAPathElementType; 1995 elem: pointer; 1996 begin 1997 AAcceptedDeviation := CorrectAcceptedDeviation(AAcceptedDeviation, AMatrix); 1998 pos := 0; 1999 nbSub := 0; 2000 subPathStartPos := pos; 2001 repeat 2002 prevPos := pos; 2003 GetElementAt(pos, elemType, elem); 2004 if elem = nil then 2005 begin 2006 pos := prevPos; 2007 break; 2008 end; 2009 if (elemType = peMoveTo) and (nbSub > 0) then 2010 begin 2011 OutputSub(subPathStartPos,prevPos); 2012 nbSub := 0; 2013 subPathStartPos := pos; 2014 end; 2015 case elemType of 2016 peMoveTo,peLineTo,peCloseSubPath: begin 2017 inc(nbSub); 2018 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 2019 GoToNextElement(pos); 2020 end; 2021 peQuadraticBezierTo, peCubicBezierTo, peArc, peOpenedSpline, peClosedSpline: inc(nbSub); 2022 end; 2023 until not GoToNextElement(pos); 2024 if nbSub > 0 then OutputSub(subPathStartPos,pos); 2025 end; 2026 802 2027 procedure TBGRAPath.addPath(const AValue: string); 803 2028 var p: integer; 804 2029 numberError: boolean; 2030 startCoord,lastCoord: TPointF; 805 2031 806 2032 function parseFloat: single; … … 812 2038 if (p <= length(AValue)) and (AValue[p] in['+','-']) then inc(p); 813 2039 while (p <= length(AValue)) and (AValue[p] in['0'..'9','.']) do inc(p); 814 if (p <= length(AValue)) and (AValue[p] in['e','E']) then inc(p); 815 if (p <= length(AValue)) and (AValue[p] in['+','-']) then inc(p); 816 while (p <= length(AValue)) and (AValue[p] in['0'..'9','.']) do inc(p); 2040 if (p <= length(AValue)) and (AValue[p] in['e','E']) then 2041 begin 2042 inc(p); 2043 if (p <= length(AValue)) and (AValue[p] in['+','-']) then inc(p); 2044 while (p <= length(AValue)) and (AValue[p] in['0'..'9','.']) do inc(p); 2045 end; 817 2046 val(copy(AValue,numberStart,p-numberStart),result,errPos); 818 2047 if errPos <> 0 then numberError := true; … … 821 2050 function parseCoord(relative: boolean): TPointF; 822 2051 begin 823 result := PointF(parseFloat,parseFloat); 824 if relative and not isEmptyPointF(FLastCoord) then result += FLastCoord; 2052 result.x := parseFloat; 2053 result.y := parseFloat; 2054 if relative and not isEmptyPointF(lastCoord) then result += lastCoord; 2055 if isEmptyPointF(lastCoord) then startCoord := result; 825 2056 end; 826 2057 … … 832 2063 largeArc: boolean; 833 2064 begin 834 FLastCoord := EmptyPointF; 835 FStartCoord := EmptyPointF; 2065 BeginSubPath; 2066 lastCoord := EmptyPointF; 2067 startCoord := EmptyPointF; 836 2068 p := 1; 837 2069 implicitCommand:= #0; … … 853 2085 closePath; 854 2086 implicitCommand:= #0; 2087 lastCoord := startCoord; 855 2088 end; 856 2089 'M': begin 857 2090 p1 := parseCoord(relative); 858 if not numberError then moveTo(p1); 2091 if not numberError then 2092 begin 2093 moveTo(p1); 2094 lastCoord := p1; 2095 end; 859 2096 if relative then implicitCommand:= 'l' else 860 2097 implicitCommand:= 'L'; … … 862 2099 'L': begin 863 2100 p1 := parseCoord(relative); 864 if not numberError then lineTo(p1); 2101 if not numberError then 2102 begin 2103 lineTo(p1); 2104 lastCoord := p1; 2105 end; 865 2106 end; 866 2107 'H': begin 867 if not isEmptyPointF(FLastCoord) then p1 := FLastCoord 868 else p1 := PointF(0,0); 869 if relative then p1.x += parseFloat 870 else p1.x := parseFloat; 871 if not numberError then lineTo(p1); 2108 if not isEmptyPointF(lastCoord) then 2109 begin 2110 p1 := lastCoord; 2111 if relative then p1.x += parseFloat 2112 else p1.x := parseFloat; 2113 end else 2114 begin 2115 p1 := PointF(parseFloat,0); 2116 lastCoord := p1; 2117 startCoord := p1; 2118 end; 2119 if not numberError then 2120 begin 2121 lineTo(p1); 2122 lastCoord := p1; 2123 end; 872 2124 end; 873 2125 'V': begin 874 if not isEmptyPointF(FLastCoord) then p1 := FLastCoord 875 else p1 := PointF(0,0); 876 if relative then p1.y += parseFloat 877 else p1.y := parseFloat; 878 if not numberError then lineTo(p1); 2126 if not isEmptyPointF(lastCoord) then 2127 begin 2128 p1 := lastCoord; 2129 if relative then p1.y += parseFloat 2130 else p1.y := parseFloat; 2131 end else 2132 begin 2133 p1 := PointF(0,parseFloat); 2134 lastCoord := p1; 2135 startCoord := p1; 2136 end; 2137 if not numberError then 2138 begin 2139 lineTo(p1); 2140 lastCoord := p1; 2141 end; 879 2142 end; 880 2143 'C': begin … … 882 2145 c2 := parseCoord(relative); 883 2146 p1 := parseCoord(relative); 884 if not numberError then bezierCurveTo(c1,c2,p1); 2147 if not numberError then 2148 begin 2149 bezierCurveTo(c1,c2,p1); 2150 lastCoord := p1; 2151 end; 885 2152 end; 886 2153 'S': begin 887 2154 c2 := parseCoord(relative); 888 2155 p1 := parseCoord(relative); 889 if not numberError then smoothBezierCurveTo(c2,p1); 2156 if not numberError then 2157 begin 2158 smoothBezierCurveTo(c2,p1); 2159 lastCoord := p1; 2160 end; 890 2161 end; 891 2162 'Q': begin 892 2163 c1 := parseCoord(relative); 893 2164 p1 := parseCoord(relative); 894 if not numberError then quadraticCurveTo(c1,p1); 2165 if not numberError then 2166 begin 2167 quadraticCurveTo(c1,p1); 2168 lastCoord := p1; 2169 end; 895 2170 end; 896 2171 'T': begin 897 2172 p1 := parseCoord(relative); 898 if not numberError then smoothQuadraticCurveTo(p1); 899 end; 900 'A': begin 901 a.radius := parseCoord(false); 2173 if not numberError then 2174 begin 2175 smoothQuadraticCurveTo(p1); 2176 lastCoord := p1; 2177 end; 2178 end; 2179 'A': 2180 begin 2181 a.radius.x := parseFloat; 2182 a.radius.y := parseFloat; 902 2183 a.xAngleRadCW := parseFloat*Pi/180; 903 2184 largeArc := parseFloat<>0; 904 2185 a.anticlockwise:= parseFloat=0; 905 2186 p1 := parseCoord(relative); 906 arcTo(a.radius.x,a.radius.y,a.xAngleRadCW,largeArc,a.anticlockwise,p1.x,p1.y); 2187 if not numberError then 2188 begin 2189 arcTo(a.radius.x,a.radius.y,a.xAngleRadCW,largeArc,a.anticlockwise,p1.x,p1.y); 2190 lastCoord := p1; 2191 end; 907 2192 end; 908 2193 end; … … 915 2200 end; 916 2201 2202 procedure TBGRAPath.openedSpline(const pts: array of TPointF; 2203 style: TSplineStyle); 2204 var elem: PSplineElement; 2205 i: NativeInt; 2206 p: PPointF; 2207 begin 2208 if length(pts) <= 2 then 2209 begin 2210 polyline(pts); 2211 exit; 2212 end; 2213 if not LastCoordDefined then moveTo(pts[0]); 2214 elem := AllocateElement(peOpenedSpline, length(pts)*sizeof(TPointF)); 2215 elem^.NbControlPoints := length(pts); 2216 elem^.SplineStyle := style; 2217 p := PPointF(elem+1); 2218 for i := 0 to high(pts)-1 do 2219 begin 2220 p^ := FMatrix*pts[i]; 2221 inc(p); 2222 end; 2223 p^ := SetLastCoord(pts[high(pts)]); 2224 inc(p); 2225 PInteger(p)^ := length(pts); 2226 end; 2227 2228 procedure TBGRAPath.closedSpline(const pts: array of TPointF; 2229 style: TSplineStyle); 2230 var elem: PSplineElement; 2231 i: NativeInt; 2232 p: PPointF; 2233 begin 2234 if length(pts) = 0 then exit; 2235 if not LastCoordDefined then moveTo(ClosedSplineStartPoint(pts, style)); 2236 if length(pts) <= 2 then exit; 2237 elem := AllocateElement(peClosedSpline, length(pts)*sizeof(TPointF)); 2238 elem^.NbControlPoints := length(pts); 2239 elem^.SplineStyle := style; 2240 p := PPointF(elem+1); 2241 for i := 0 to high(pts) do 2242 begin 2243 p^ := FMatrix*pts[i]; 2244 inc(p); 2245 end; 2246 PInteger(p)^ := length(pts); 2247 end; 2248 2249 procedure TBGRAPath.BitmapDrawSubPathProc(const APoints: array of TPointF; 2250 AClosed: boolean; AData: pointer); 2251 begin 2252 with TStrokeData(AData^) do 2253 if AClosed then 2254 begin 2255 if Texture <> nil then 2256 Bitmap.DrawPolygonAntialias(APoints, Texture, Width) 2257 else 2258 Bitmap.DrawPolygonAntialias(APoints, Color, Width); 2259 end else 2260 begin 2261 if Texture <> nil then 2262 Bitmap.DrawPolyLineAntialiasAutocycle(APoints, Texture, Width) 2263 else 2264 Bitmap.DrawPolyLineAntialiasAutocycle(APoints, Color, Width); 2265 end; 2266 end; 2267 2268 function TBGRAPath.CorrectAcceptedDeviation(AAcceptedDeviation: single; 2269 const AMatrix: TAffineMatrix): single; 2270 var maxZoom: single; 2271 begin 2272 //determine the zoom of the matrix 2273 maxZoom := Max(VectLen(PointF(AMatrix[1,1],AMatrix[2,1])), 2274 VectLen(PointF(AMatrix[1,2],AMatrix[2,2]))); 2275 //make the accepted deviation smaller if the matrix zooms to avoid that 2276 // curves would look angular 2277 if maxZoom = 0 then 2278 result:= 1e10 2279 else 2280 result := AAcceptedDeviation / maxZoom; 2281 end; 2282 2283 procedure TBGRAPath.OnModify; 2284 begin 2285 if length(FCursors)> 0 then 2286 raise Exception.Create('You cannot modify the path when there are cursors'); 2287 end; 2288 2289 procedure TBGRAPath.OnMatrixChange; 2290 begin 2291 //transformed coord are not changed, 2292 //but original coords are lost in the process. 2293 //this has a consequence when using 2294 //arc functions that rely on the previous 2295 //coordinate 2296 FLastCoord := EmptyPointF; 2297 FSubPathStartCoord := EmptyPointF; 2298 end; 2299 917 2300 procedure TBGRAPath.NeedSpace(count: integer); 918 2301 begin 919 if FDataPos + count > FDataSize then 920 begin 921 FDataSize := FDataSize*2+8; 922 ReAllocMem(FData, FDataSize); 923 end; 924 end; 925 926 procedure TBGRAPath.StoreCoord(const pt: TPointF); 927 begin 928 NeedSpace(sizeof(single)*2); 929 with FMatrix*pt do 930 begin 931 PSingle(FData+FDataPos)^ := x; 932 PSingle(FData+FDataPos+sizeof(single))^ := y; 933 end; 934 Inc(FDataPos, sizeof(single)*2); 935 FLastCoord := pt; 936 end; 937 938 function TBGRAPath.ReadCoord: TPointF; 939 begin 940 result := PPointF(FData+FDataPos)^; 941 inc(FDataPos,sizeof(TPointF)); 942 end; 943 944 procedure TBGRAPath.StoreElementType(value: TBGRAPathElementType); 945 begin 946 NeedSpace(sizeof(TBGRAPathElementType)); 947 PBGRAPathElementType(FData+FDataPos)^ := value; 948 Inc(FDataPos, sizeof(TBGRAPathElementType)); 949 FLastElementType:= value; 950 end; 951 952 function TBGRAPath.ReadElementType: TBGRAPathElementType; 953 begin 954 result := PBGRAPathElementType(FData+FDataPos)^; 955 inc(FDataPos,sizeof(TBGRAPathElementType)); 956 end; 957 958 function TBGRAPath.ReadArcDef: TArcDef; 959 begin 960 result := PArcDef(FData+FDataPos)^; 961 inc(FDataPos,sizeof(TArcDef)); 962 end; 963 964 procedure TBGRAPath.RewindFloat; 965 begin 966 if FDataPos >= sizeof(single) then dec(FDataPos, sizeof(Single)); 2302 OnModify; 2303 if FDataPos + count > FDataCapacity then 2304 begin 2305 FDataCapacity := (FDataCapacity shl 1)+8; 2306 if FDataPos + count + 8 > FDataCapacity then 2307 FDataCapacity := FDataPos + count + 8; 2308 ReAllocMem(FData, FDataCapacity); 2309 end; 2310 end; 2311 2312 function TBGRAPath.AllocateElement(AElementType: TBGRAPathElementType; 2313 AExtraBytes: PtrInt): Pointer; 2314 var t: PtrInt; 2315 begin 2316 if not (AElementType in [succ(peNone)..high(TBGRAPathElementType)]) then 2317 raise exception.Create('Invalid element type'); 2318 OnModify; 2319 t := PathElementSize[AElementType]+AExtraBytes; 2320 NeedSpace(SizeOf(TPathElementHeader)+t); 2321 with PPathElementHeader(FData+FDataPos)^ do 2322 begin 2323 ElementType:= AElementType; 2324 PreviousElementType := FLastStoredElementType; 2325 end; 2326 result := FData+(FDataPos+SizeOf(TPathElementHeader)); 2327 FLastSubPathElementType:= AElementType; 2328 FLastStoredElementType:= AElementType; 2329 Inc(FDataPos, sizeof(TPathElementHeader)+t); 967 2330 end; 968 2331 … … 970 2333 begin 971 2334 FData := nil; 972 FDataSize := 0; 973 FDataPos := 0; 974 FLastElementType := peNone; 975 FLastCoord := EmptyPointF; 976 FStartCoord := EmptyPointF; 977 FExpectedControlPoint := EmptyPointF; 2335 FDataCapacity := 0; 2336 FLastMoveToDataPos := -1; 2337 beginPath; 978 2338 resetTransform; 2339 end; 2340 2341 function TBGRAPath.GoToNextElement(var APos: PtrInt): boolean; 2342 var newPos: PtrInt; 2343 p: PSplineElement; 2344 elemType: TBGRAPathElementType; 2345 begin 2346 if APos >= FDataPos then 2347 result := false 2348 else 2349 begin 2350 elemType := PPathElementHeader(FData+APos)^.ElementType; 2351 newPos := APos + sizeof(TPathElementHeader) + PathElementSize[elemType]; 2352 if elemType in[peOpenedSpline,peClosedSpline] then 2353 begin 2354 p := PSplineElement(FData+(APos+sizeof(TPathElementHeader))); 2355 newPos += p^.NbControlPoints * sizeof(TPointF); //extra 2356 end; 2357 if newPos < FDataPos then 2358 begin 2359 result := true; 2360 APos := newPos; 2361 if not CheckElementType(PPathElementHeader(FData+APos)^.ElementType) or 2362 not CheckElementType(PPathElementHeader(FData+APos)^.PreviousElementType) then 2363 raise exception.Create('Internal structure error'); 2364 end 2365 else 2366 result := false; 2367 end; 2368 end; 2369 2370 function TBGRAPath.GoToPreviousElement(var APos: PtrInt): boolean; 2371 var lastElemType: TBGRAPathElementType; 2372 begin 2373 if APos <= 0 then 2374 result := false 2375 else 2376 begin 2377 result := true; 2378 if (APos = FDataPos) then 2379 lastElemType := FLastStoredElementType 2380 else 2381 lastElemType := PPathElementHeader(FData+APos)^.PreviousElementType; 2382 2383 if lastElemType in [peOpenedSpline,peClosedSpline] then 2384 dec(APos, (PInteger(FData+APos)-1)^ *sizeof(TPointF)); //extra 2385 dec(APos, sizeof(TPathElementHeader) + PathElementSize[lastElemType]); 2386 2387 if not CheckElementType(PPathElementHeader(FData+APos)^.ElementType) or 2388 not CheckElementType(PPathElementHeader(FData+APos)^.PreviousElementType) then 2389 raise exception.Create('Internal structure error'); 2390 end; 2391 end; 2392 2393 function TBGRAPath.PeekNextElement(APos: PtrInt): TBGRAPathElementType; 2394 begin 2395 if not GoToNextElement(APos) then 2396 result := peNone 2397 else 2398 result := PPathElementHeader(FData+APos)^.ElementType; 2399 end; 2400 2401 function TBGRAPath.GetElementStartCoord(APos: PtrInt): TPointF; 2402 var 2403 elemType: TBGRAPathElementType; 2404 elem: pointer; 2405 begin 2406 GetElementAt(APos, elemType, elem); 2407 case elemType of 2408 peNone: raise exception.Create('No element'); 2409 peMoveTo: result := PPointF(elem)^; 2410 else 2411 begin 2412 if not GoToPreviousElement(APos) then 2413 raise exception.Create('No previous element') 2414 else 2415 begin 2416 result := GetElementEndCoord(APos); 2417 end; 2418 end; 2419 end; 2420 end; 2421 2422 function TBGRAPath.GetElementEndCoord(APos: PtrInt): TPointF; 2423 var elemType: TBGRAPathElementType; 2424 elem: pointer; 2425 begin 2426 GetElementAt(APos, elemType, elem); 2427 case elemType of 2428 peMoveTo,peLineTo,peCloseSubPath: result := PPointF(elem)^; 2429 peQuadraticBezierTo: result := PQuadraticBezierToElement(elem)^.Destination; 2430 peCubicBezierTo: result := PCubicBezierToElement(elem)^.Destination; 2431 peArc: result := ArcEndPoint(PArcElement(elem)^); 2432 peClosedSpline: result := PPointF(PSplineElement(elem)+1)^; 2433 peOpenedSpline: result := (PPointF(PSplineElement(elem)+1)+(PSplineElement(elem)^.NbControlPoints-1))^; 2434 else 2435 result := EmptyPointF; 2436 end; 2437 end; 2438 2439 function TBGRAPath.GetElementLength(APos: PtrInt; AAcceptedDeviation: single): Single; 2440 var elemType: TBGRAPathElementType; 2441 elem: pointer; 2442 pts: array of TPointF; 2443 begin 2444 GetElementAt(APos, elemType, elem); 2445 case elemType of 2446 peMoveTo: result := 0; 2447 peLineTo,peCloseSubPath: result := VectLen(PPointF(elem)^ - GetElementStartCoord(APos))*FScale; 2448 peQuadraticBezierTo: with PQuadraticBezierToElement(elem)^ do 2449 result := BGRABitmapTypes.BezierCurve(GetElementStartCoord(APos),ControlPoint,Destination).ComputeLength; 2450 peCubicBezierTo: with PCubicBezierToElement(elem)^ do 2451 result := BGRABitmapTypes.BezierCurve(GetElementStartCoord(APos),ControlPoint1,ControlPoint2,Destination).ComputeLength(AAcceptedDeviation); 2452 peArc: begin 2453 result := VectLen(ArcStartPoint(PArcElement(elem)^) - GetElementStartCoord(APos)); 2454 result += PolylineLen(ComputeArc(PArcElement(elem)^, 0.1/AAcceptedDeviation)); 2455 end; 2456 peClosedSpline,peOpenedSpline: 2457 begin 2458 pts := GetPolygonalApprox(APos, AAcceptedDeviation, true); 2459 result := PolylineLen(pts) + VectLen(pts[0]-GetElementStartCoord(APos)); 2460 end 2461 else 2462 result := 0; 2463 end; 2464 end; 2465 2466 procedure TBGRAPath.GetElementAt(APos: PtrInt; out 2467 AElementType: TBGRAPathElementType; out AElement: pointer); 2468 begin 2469 if APos >= FDataPos then 2470 begin 2471 AElementType := peNone; 2472 AElement := nil; 2473 end else 2474 begin 2475 AElementType:= PPathElementHeader(FData+APos)^.ElementType; 2476 AElement := FData+(APos+sizeof(TPathElementHeader)); 2477 end; 979 2478 end; 980 2479 … … 990 2489 end; 991 2490 2491 constructor TBGRAPath.Create(const APoints: ArrayOfTPointF); 2492 begin 2493 Init; 2494 SetPoints(APoints); 2495 end; 2496 2497 constructor TBGRAPath.Create(APath: IBGRAPath); 2498 begin 2499 Init; 2500 APath.copyTo(self); 2501 end; 2502 992 2503 destructor TBGRAPath.Destroy; 993 begin 2504 var i: integer; 2505 begin 2506 for I := 0 to high(FCursors) do 2507 FCursors[i].OnPathFree; 994 2508 if Assigned(FData) then 995 2509 begin … … 1002 2516 procedure TBGRAPath.beginPath; 1003 2517 begin 2518 DoClear; 2519 end; 2520 2521 procedure TBGRAPath.beginSubPath; 2522 begin 2523 OnModify; 2524 FLastSubPathElementType := peNone; 2525 ClearLastCoord; 2526 FSubPathStartCoord := EmptyPointF; 2527 FExpectedTransformedControlPoint := EmptyPointF; 2528 end; 2529 2530 procedure TBGRAPath.DoClear; 2531 begin 2532 OnModify; 1004 2533 FDataPos := 0; 2534 BeginSubPath; 2535 end; 2536 2537 function TBGRAPath.CheckElementType(AElementType: TBGRAPathElementType): boolean; 2538 begin 2539 result := AElementType <= high(TBGRAPathElementType); 1005 2540 end; 1006 2541 1007 2542 procedure TBGRAPath.closePath; 1008 begin 1009 if (FLastElementType <> peNone) and (FLastElementType <> peCloseSubPath) then 1010 begin 1011 StoreElementType(peCloseSubPath); 1012 FLastCoord := FStartCoord; 2543 var 2544 moveToType: TBGRAPathElementType; 2545 moveToElem: pointer; 2546 begin 2547 if (FLastSubPathElementType <> peNone) and (FLastSubPathElementType <> peCloseSubPath) then 2548 begin 2549 with PClosePathElement(AllocateElement(peCloseSubPath))^ do 2550 begin 2551 StartCoordinate := FSubPathTransformedStartCoord; 2552 LoopDataPos := FLastMoveToDataPos; 2553 end; 2554 if FLastMoveToDataPos <> -1 then 2555 begin 2556 GetElementAt(FLastMoveToDataPos,moveToType,moveToElem); 2557 PMoveToElement(moveToElem)^.LoopDataPos := FDataPos; 2558 FLastMoveToDataPos:= -1; 2559 end; 2560 FLastCoord := FSubPathStartCoord; 2561 FLastTransformedCoord := FSubPathTransformedStartCoord; 1013 2562 end; 1014 2563 end; … … 1016 2565 procedure TBGRAPath.translate(x, y: single); 1017 2566 begin 2567 OnMatrixChange; 1018 2568 FMatrix *= AffineMatrixTranslation(x,y); 1019 2569 end; … … 1021 2571 procedure TBGRAPath.resetTransform; 1022 2572 begin 2573 OnMatrixChange; 1023 2574 FMatrix := AffineMatrixIdentity; 1024 2575 FAngleRadCW := 0; … … 1028 2579 procedure TBGRAPath.rotate(angleRadCW: single); 1029 2580 begin 2581 OnMatrixChange; 1030 2582 FMatrix *= AffineMatrixRotationRad(-angleRadCW); 1031 2583 FAngleRadCW += angleRadCW; … … 1054 2606 procedure TBGRAPath.scale(factor: single); 1055 2607 begin 2608 OnMatrixChange; 1056 2609 FMatrix *= AffineMatrixScale(factor,factor); 1057 2610 FScale *= factor; … … 1070 2623 procedure TBGRAPath.moveTo(const pt: TPointF); 1071 2624 begin 1072 if FLastElementType <> peMoveTo then 1073 begin 1074 StoreElementType(peMoveTo); 1075 StoreCoord(pt); 2625 if FLastSubPathElementType <> peMoveTo then 2626 begin 2627 FLastMoveToDataPos:= FDataPos; 2628 with PMoveToElement(AllocateElement(peMoveTo))^ do 2629 begin 2630 StartCoordinate := SetLastCoord(pt); 2631 LoopDataPos := -1; 2632 end 1076 2633 end else 1077 begin 1078 RewindFloat; 1079 RewindFloat; 1080 StoreCoord(pt); 1081 end; 1082 FLastCoord := pt; 1083 FStartCoord := FLastCoord; 2634 PMoveToElement(FData+(FDataPos-Sizeof(TMoveToElement)))^.StartCoordinate := SetLastCoord(pt); 2635 FSubPathStartCoord := FLastCoord; 2636 FSubPathTransformedStartCoord := FLastTransformedCoord; 1084 2637 end; 1085 2638 1086 2639 procedure TBGRAPath.lineTo(const pt: TPointF); 1087 begin 1088 if not isEmptyPointF(FLastCoord) then 1089 begin 1090 StoreElementType(peLineTo); 1091 StoreCoord(pt); 1092 FLastCoord := pt; 2640 var lastTransfCoord, newTransfCoord: TPointF; 2641 begin 2642 if LastCoordDefined then 2643 begin 2644 lastTransfCoord := FLastTransformedCoord; 2645 newTransfCoord := SetLastCoord(pt); 2646 if newTransfCoord <> lastTransfCoord then 2647 PPointF(AllocateElement(peLineTo))^ := newTransfCoord; 1093 2648 end else 1094 2649 moveTo(pt); 1095 2650 end; 1096 2651 2652 procedure TBGRAPath.polyline(const pts: array of TPointF); 2653 var i: integer; 2654 begin 2655 if length(pts) = 0 then exit; 2656 NeedSpace((sizeof(TPathElementHeader)+sizeof(TPointF))*length(pts)); 2657 moveTo(pts[0]); 2658 for i := 1 to high(pts) do lineTo(pts[i]); 2659 end; 2660 1097 2661 procedure TBGRAPath.polylineTo(const pts: array of TPointF); 1098 2662 var i: integer; 1099 2663 begin 1100 NeedSpace((sizeof(TBGRAPathElementType)+2*sizeof(single))*length(pts)); 1101 for i := 0 to high(pts) do with pts[i] do lineTo(x,y); 2664 NeedSpace((sizeof(TPathElementHeader)+sizeof(TPointF))*length(pts)); 2665 for i := 0 to high(pts) do lineTo(pts[i]); 2666 end; 2667 2668 procedure TBGRAPath.polygon(const pts: array of TPointF); 2669 var lastPt: integer; 2670 begin 2671 if length(pts) = 0 then exit; 2672 lastPt := high(pts); 2673 while (lastPt > 1) and (pts[lastPt] = pts[0]) do dec(lastPt); 2674 if lastPt <> high(pts) then 2675 polyline(slice(pts,lastPt+1)) 2676 else 2677 polyline(pts); 2678 closePath; 1102 2679 end; 1103 2680 … … 1109 2686 procedure TBGRAPath.quadraticCurveTo(const cp, pt: TPointF); 1110 2687 begin 1111 if not isEmptyPointF(FLastCoord) then 1112 begin 1113 StoreElementType(peQuadraticBezierTo); 1114 StoreCoord(cp); 1115 StoreCoord(pt); 1116 FLastCoord := pt; 1117 end else 2688 if LastCoordDefined then 2689 QuadraticCurveFromTransformed(FMatrix*cp, pt) else 2690 begin 1118 2691 lineTo(pt); 1119 FExpectedControlPoint := pt+(pt-cp); 2692 FExpectedTransformedControlPoint := FMatrix*(pt+(pt-cp)); 2693 end; 1120 2694 end; 1121 2695 … … 1127 2701 procedure TBGRAPath.bezierCurveTo(const cp1, cp2, pt: TPointF); 1128 2702 begin 1129 if isEmptyPointF(FLastCoord) then moveTo(cp1); 1130 StoreElementType(peCubicBezierTo); 1131 StoreCoord(cp1); 1132 StoreCoord(cp2); 1133 StoreCoord(pt); 1134 FLastCoord := pt; 1135 FExpectedControlPoint := pt + (pt-cp2); 2703 if not LastCoordDefined then moveTo(cp1); 2704 BezierCurveFromTransformed(FMatrix*cp1, cp2, pt); 1136 2705 end; 1137 2706 … … 1142 2711 end; 1143 2712 2713 procedure TBGRAPath.bezierCurve(p1, cp1, cp2, p2: TPointF); 2714 begin 2715 moveTo(p1); 2716 bezierCurveTo(cp1,cp2,p2); 2717 end; 2718 1144 2719 procedure TBGRAPath.smoothBezierCurveTo(cp2x, cp2y, x, y: single); 1145 2720 begin … … 1149 2724 procedure TBGRAPath.smoothBezierCurveTo(const cp2, pt: TPointF); 1150 2725 begin 1151 if (FLast ElementType = peCubicBezierTo) and not isEmptyPointF(FExpectedControlPoint) then1152 bezierCurveTo(FExpectedControlPoint,cp2,pt)1153 else if not isEmptyPointF(FLastCoord)then1154 bezierCurveTo(FLastCoord,cp2,pt)2726 if (FLastSubPathElementType = peCubicBezierTo) and not isEmptyPointF(FExpectedTransformedControlPoint) then 2727 BezierCurveFromTransformed(FExpectedTransformedControlPoint,cp2,pt) 2728 else if LastCoordDefined then 2729 BezierCurveFromTransformed(FLastTransformedCoord,cp2,pt) 1155 2730 else 1156 2731 bezierCurveTo(cp2,cp2,pt); … … 1163 2738 end; 1164 2739 2740 procedure TBGRAPath.quadraticCurve(p1, cp, p2: TPointF); 2741 begin 2742 moveTo(p1); 2743 quadraticCurveTo(cp,p2); 2744 end; 2745 1165 2746 procedure TBGRAPath.smoothQuadraticCurveTo(x, y: single); 1166 2747 begin … … 1170 2751 procedure TBGRAPath.smoothQuadraticCurveTo(const pt: TPointF); 1171 2752 begin 1172 if (FLast ElementType = peQuadraticBezierTo) and not isEmptyPointF(FExpectedControlPoint) then1173 quadraticCurveTo(FExpectedControlPoint,pt)1174 else if not isEmptyPointF(FLastCoord)then1175 quadraticCurveTo(FLastCoord,pt)2753 if (FLastSubPathElementType = peQuadraticBezierTo) and not isEmptyPointF(FExpectedTransformedControlPoint) then 2754 QuadraticCurveFromTransformed(FExpectedTransformedControlPoint,pt) 2755 else if LastCoordDefined then 2756 QuadraticCurveFromTransformed(FLastTransformedCoord,pt) 1176 2757 else 1177 2758 quadraticCurveTo(pt,pt); … … 1237 2818 var p0 : TPointF; 1238 2819 begin 1239 if isEmptyPointF(FLastCoord) then2820 if IsEmptyPointF(FLastCoord) then 1240 2821 p0 := p1 else p0 := FLastCoord; 1241 2822 arc(Html5ArcTo(p0,p1,p2,radius)); … … 1243 2824 1244 2825 procedure TBGRAPath.arc(const arcDef: TArcDef); 1245 var transformedArc: TArc Def;2826 var transformedArc: TArcElement; 1246 2827 begin 1247 2828 if (arcDef.radius.x = 0) and (arcDef.radius.y = 0) then … … 1249 2830 else 1250 2831 begin 1251 if isEmptyPointF(FLastCoord)then2832 if not LastCoordDefined then 1252 2833 moveTo(ArcStartPoint(arcDef)); 1253 StoreElementType(peArc);1254 NeedSpace(sizeof(TArcDef));1255 2834 transformedArc.anticlockwise := arcDef.anticlockwise; 1256 2835 transformedArc.startAngleRadCW := arcDef.startAngleRadCW; … … 1259 2838 transformedArc.radius := arcDef.radius*FScale; 1260 2839 transformedArc.xAngleRadCW := arcDef.xAngleRadCW+FAngleRadCW; 1261 PArcDef(FData+FDataPos)^ := transformedArc; 1262 inc(FDataPos, sizeof(TArcDef)); 1263 FLastCoord := ArcEndPoint(arcDef); 1264 end; 1265 end; 1266 1267 procedure TBGRAPath.arc(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW, 2840 PArcElement(AllocateElement(peArc))^ := transformedArc; 2841 {$PUSH}{$OPTIMIZATION OFF} 2842 SetLastCoord(ArcEndPoint(arcDef)); 2843 {$POP} 2844 end; 2845 end; 2846 2847 procedure TBGRAPath.arc(cx, cy, rx, ry: single; xAngleRadCW, startAngleRadCW, 1268 2848 endAngleRadCW: single); 1269 2849 begin … … 1280 2860 anticlockwise: boolean; x, y: single); 1281 2861 begin 1282 if isEmptyPointF(FLastCoord) then2862 if IsEmptyPointF(FLastCoord) then 1283 2863 moveTo(x,y) 1284 2864 else … … 1287 2867 1288 2868 procedure TBGRAPath.copyTo(dest: IBGRAPath); 1289 var savedPos: integer; 1290 cp1,cp2,p1: TPointF; 1291 begin 1292 savedPos:= FDataPos; 1293 FDataPos := 0; 1294 while FDataPos < savedPos do 1295 begin 1296 case ReadElementType of 1297 peMoveTo: dest.moveTo(ReadCoord); 1298 peLineTo: dest.lineTo(ReadCoord); 1299 peCloseSubPath: dest.closePath; 1300 peQuadraticBezierTo: 1301 begin 1302 cp1 := ReadCoord; 1303 p1 := ReadCoord; 1304 dest.quadraticCurveTo(cp1,p1); 1305 end; 1306 peCubicBezierTo: 1307 begin 1308 cp1 := ReadCoord; 1309 cp2 := ReadCoord; 1310 p1 := ReadCoord; 1311 dest.bezierCurveTo(cp1,cp2,p1); 1312 end; 1313 peArc: dest.arc(ReadArcDef); 1314 end; 1315 end; 1316 FDataPos := savedPos; 2869 var pos: IntPtr; 2870 elemType: TBGRAPathElementType; 2871 elem: Pointer; 2872 pts: array of TPointF; 2873 begin 2874 pos := 0; 2875 repeat 2876 GetElementAt(pos, elemType, elem); 2877 if elem = nil then break; 2878 case elemType of 2879 peMoveTo: dest.moveTo(PPointF(elem)^); 2880 peLineTo: dest.lineTo(PPointF(elem)^); 2881 peCloseSubPath: dest.closePath; 2882 peQuadraticBezierTo: 2883 with PQuadraticBezierToElement(elem)^ do 2884 dest.quadraticCurveTo(ControlPoint,Destination); 2885 peCubicBezierTo: 2886 with PCubicBezierToElement(elem)^ do 2887 dest.bezierCurveTo(ControlPoint1,ControlPoint2,Destination); 2888 peArc: dest.arc(PArcElement(elem)^); 2889 peOpenedSpline, peClosedSpline: 2890 begin 2891 with PSplineElement(elem)^ do 2892 begin 2893 setlength(pts, NbControlPoints); 2894 move(Pointer(PSplineElement(elem)+1)^, pts[0], NbControlPoints*sizeof(TPointF)); 2895 if elemType = peOpenedSpline then 2896 dest.openedSpline(pts, SplineStyle) 2897 else 2898 dest.closedSpline(pts, SplineStyle); 2899 pts := nil; 2900 end; 2901 end; 2902 end; 2903 until not GoToNextElement(pos); 1317 2904 end; 1318 2905
Note:
See TracChangeset
for help on using the changeset viewer.