source: trunk/Packages/bgrabitmap/bgratransform.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 55.7 KB
Line 
1unit BGRATransform;
2
3{$mode objfpc}
4
5interface
6
7{ This unit contains bitmap transformations as classes and the TAffineMatrix record and functions. }
8
9uses
10 Classes, SysUtils, BGRABitmapTypes;
11
12type
13 { Contains an affine matrix, i.e. a matrix to transform linearly and translate TPointF coordinates }
14 TAffineMatrix = BGRABitmapTypes.TAffineMatrix;
15 { Contains an affine base and information on the resulting box }
16 TAffineBox = BGRABitmapTypes.TAffineBox;
17
18 { TBGRAAffineScannerTransform allow to transform any scanner. To use it,
19 create this object with a scanner as parameter, call transformation
20 procedures, and finally, use the newly created object as a scanner.
21
22 You can transform a gradient or a bitmap. See TBGRAAffineBitmapTransform
23 for bitmap specific transformation. }
24
25 { TBGRAAffineScannerTransform }
26
27 TBGRAAffineScannerTransform = class(TBGRACustomScanner)
28 protected
29 FScanner: IBGRAScanner;
30 FScanAtFunc: TScanAtFunction;
31 FCurX,FCurY: Single;
32 FEmptyMatrix: Boolean;
33 FMatrix: TAffineMatrix;
34 procedure SetMatrix(AMatrix: TAffineMatrix);
35 function InternalScanCurrentPixel: TBGRAPixel; virtual;
36 function GetViewMatrix: TAffineMatrix;
37 procedure SetViewMatrix(AValue: TAffineMatrix);
38 public
39 GlobalOpacity: Byte;
40 constructor Create(AScanner: IBGRAScanner);
41 procedure Reset;
42 procedure Invert;
43 procedure Translate(OfsX,OfsY: Single);
44 procedure RotateDeg(AngleCW: Single);
45 procedure RotateRad(AngleCCW: Single);
46 procedure MultiplyBy(AMatrix: TAffineMatrix);
47 procedure Fit(Origin,HAxis,VAxis: TPointF); virtual;
48 procedure Scale(sx,sy: single); overload;
49 procedure Scale(factor: single); overload;
50 procedure ScanMoveTo(X, Y: Integer); override;
51 procedure ScanMoveToF(X, Y: single); inline;
52 function ScanNextPixel: TBGRAPixel; override;
53 function ScanAt(X, Y: Single): TBGRAPixel; override;
54 property Matrix: TAffineMatrix read FMatrix write SetMatrix;
55 property ViewMatrix: TAffineMatrix read GetViewMatrix write SetViewMatrix;
56 end;
57
58 { If you don't want the bitmap to repeats itself, or want to specify the
59 resample filter, or want to fit easily the bitmap on axes,
60 use TBGRAAffineBitmapTransform instead of TBGRAAffineScannerTransform }
61
62 { TBGRAAffineBitmapTransform }
63
64 TBGRAAffineBitmapTransform = class(TBGRAAffineScannerTransform)
65 protected
66 FBitmap: TBGRACustomBitmap;
67 FRepeatImageX,FRepeatImageY: boolean;
68 FResampleFilter : TResampleFilter;
69 FBuffer: PBGRAPixel;
70 FBufferSize: Int32or64;
71 FIncludeEdges: boolean;
72 procedure Init(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean= false; ARepeatImageY: Boolean= false; AResampleFilter: TResampleFilter = rfLinear; AIncludeEdges: boolean = false);
73 public
74 constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImage: Boolean= false; AResampleFilter: TResampleFilter = rfLinear; AIncludeEdges: boolean = false); overload;
75 constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean; ARepeatImageY: Boolean; AResampleFilter: TResampleFilter = rfLinear; AIncludeEdges: boolean = false); overload;
76 destructor Destroy; override;
77 function InternalScanCurrentPixel: TBGRAPixel; override;
78 procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override;
79 function IsScanPutPixelsDefined: boolean; override;
80 procedure Fit(Origin, HAxis, VAxis: TPointF); override;
81 end;
82
83 { TBGRAQuadLinearScanner }
84
85 TBGRAQuadLinearScanner = class(TBGRACustomScanner)
86 private
87 FPoints,FVectors: array[0..3] of TPointF;
88 FInvLengths,FDets: array[0..3] of single;
89 FCoeffs: array[0..3] of TPointF;
90 aa,bb0,cc0,inv2aa: double;
91 FSource: IBGRAScanner;
92 FSourceMatrix: TAffineMatrix;
93 FUVVector: TPointF;
94
95 ScanParaBB, ScanParaCC, ScanParaBBInv: double;
96
97 ScanVertV0,ScanVertVStep0,ScanVertDenom0,ScanVertDenomStep0: double;
98
99 FShowC1, FShowC2: boolean;
100 FScanFunc: TScanNextPixelFunction;
101 FCurXF,FCurYF: single;
102 FBuffer: PBGRAPixel;
103 FBufferSize: Int32or64;
104 FTextureInterpolation: Boolean;
105 function GetCulling: TFaceCulling;
106 function ScanGeneral: TBGRAPixel;
107 procedure PrepareScanVert0;
108 function ScanVert0: TBGRAPixel;
109 procedure PrepareScanPara;
110 function ScanPara: TBGRAPixel;
111 function GetTexColorAt(u,v: Single; detNeg: boolean): TBGRAPixel; inline;
112 procedure ScanMoveToF(X,Y: single); inline;
113 procedure SetCulling(AValue: TFaceCulling);
114 procedure Init(ASource: IBGRAScanner; const APoints: array of TPointF;
115 ATextureInterpolation: boolean);
116 public
117 function ScanAt(X, Y: Single): TBGRAPixel; override;
118 procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override;
119 function IsScanPutPixelsDefined: boolean; override;
120 procedure ScanMoveTo(X, Y: Integer); override;
121 function ScanNextPixel: TBGRAPixel; override;
122 constructor Create(ASource: IBGRAScanner;
123 ASourceMatrix: TAffineMatrix; const APoints: array of TPointF;
124 ATextureInterpolation: boolean = true); overload;
125 constructor Create(ASource: IBGRAScanner;
126 const ATexCoords: array of TPointF; const APoints: array of TPointF;
127 ATextureInterpolation: boolean = true); overload;
128 destructor Destroy; override;
129 property Culling: TFaceCulling read GetCulling write SetCulling;
130 end;
131
132 { TBGRABitmapScanner }
133
134 TBGRABitmapScanner = class(TBGRACustomScanner)
135 protected
136 FSource: TBGRACustomBitmap;
137 FRepeatX,FRepeatY: boolean;
138 FScanline: PBGRAPixel;
139 FCurX: integer;
140 FOrigin: TPoint;
141 public
142 constructor Create(ASource: TBGRACustomBitmap; ARepeatX,ARepeatY: boolean; AOrigin: TPoint);
143 procedure ScanMoveTo(X, Y: Integer); override;
144 function ScanNextPixel: TBGRAPixel; override;
145 function ScanAt(X, Y: Single): TBGRAPixel; override;
146 end;
147
148 { TBGRAExtendedBorderScanner }
149
150 TBGRAExtendedBorderScanner = class(TBGRACustomScanner)
151 protected
152 FSource: IBGRAScanner;
153 FBounds: TRect;
154 public
155 constructor Create(ASource: IBGRAScanner; ABounds: TRect);
156 function ScanAt(X,Y: Single): TBGRAPixel; override;
157 end;
158
159 { TBGRAScannerOffset }
160
161 TBGRAScannerOffset = class(TBGRACustomScanner)
162 protected
163 FSource: IBGRAScanner;
164 FOffset: TPoint;
165 public
166 constructor Create(ASource: IBGRAScanner; AOffset: TPoint);
167 destructor Destroy; override;
168 procedure ScanMoveTo(X, Y: Integer); override;
169 function ScanNextPixel: TBGRAPixel; override;
170 function ScanAt(X, Y: Single): TBGRAPixel; override;
171 function IsScanPutPixelsDefined: boolean; override;
172 procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override;
173 end;
174
175
176{---------------------- Affine matrix functions -------------------}
177//fill a matrix
178function AffineMatrix(m11,m12,m13,m21,m22,m23: single): TAffineMatrix; overload;
179function AffineMatrix(AU,AV: TPointF; ATranslation: TPointF): TAffineMatrix; overload;
180
181//matrix multiplication
182operator *(M,N: TAffineMatrix): TAffineMatrix;
183operator =(M,N: TAffineMatrix): boolean;
184
185//matrix multiplication by a vector (apply transformation to that vector)
186operator *(M: TAffineMatrix; V: TPointF): TPointF;
187operator *(M: TAffineMatrix; A: array of TPointF): ArrayOfTPointF;
188operator *(M: TAffineMatrix; ab: TAffineBox): TAffineBox;
189
190//check if matrix is inversible
191function IsAffineMatrixInversible(M: TAffineMatrix): boolean;
192
193//check if the matrix is a translation (including the identity)
194function IsAffineMatrixTranslation(M: TAffineMatrix): boolean;
195
196//check if the matrix is a scaling (including a projection i.e. with factor 0)
197function IsAffineMatrixScale(M: TAffineMatrix): boolean;
198
199//check if the matrix is the identity
200function IsAffineMatrixIdentity(M: TAffineMatrix): boolean;
201
202//compute inverse (check if inversible before)
203function AffineMatrixInverse(M: TAffineMatrix): TAffineMatrix;
204
205//define a translation matrix
206function AffineMatrixTranslation(OfsX,OfsY: Single): TAffineMatrix;
207
208//define a scaling matrix
209function AffineMatrixScale(sx,sy: single): TAffineMatrix;
210function AffineMatrixScaledRotation(ASourceVector, ATargetVector: TPointF): TAffineMatrix;
211function AffineMatrixScaledRotation(ASourcePoint, ATargetPoint, AOrigin: TPointF): TAffineMatrix;
212
213function AffineMatrixSkewXDeg(AngleCW: single): TAffineMatrix;
214function AffineMatrixSkewYDeg(AngleCW: single): TAffineMatrix;
215function AffineMatrixSkewXRad(AngleCCW: single): TAffineMatrix;
216function AffineMatrixSkewYRad(AngleCCW: single): TAffineMatrix;
217
218//define a linear matrix
219function AffineMatrixLinear(v1,v2: TPointF): TAffineMatrix; overload;
220function AffineMatrixLinear(const AMatrix: TAffineMatrix): TAffineMatrix; overload;
221
222//define a rotation matrix (positive radians are counter-clockwise)
223//(assuming the y-axis is pointing down)
224function AffineMatrixRotationRad(AngleCCW: Single): TAffineMatrix;
225
226//Positive degrees are clockwise
227//(assuming the y-axis is pointing down)
228function AffineMatrixRotationDeg(AngleCW: Single): TAffineMatrix;
229
230//define the identity matrix (that do nothing)
231function AffineMatrixIdentity: TAffineMatrix;
232
233function IsAffineMatrixOrthogonal(M: TAffineMatrix): boolean;
234function IsAffineMatrixScaledRotation(M: TAffineMatrix): boolean;
235
236type
237 { TBGRATriangleLinearMapping is a scanner that provides
238 an optimized transformation for linear texture mapping
239 on triangles }
240
241 { TBGRATriangleLinearMapping }
242
243 TBGRATriangleLinearMapping = class(TBGRACustomScanner)
244 protected
245 FScanner: IBGRAScanner;
246 FMatrix: TAffineMatrix;
247 FTexCoord1,FDiff2,FDiff3,FStep: TPointF;
248 FCurTexCoord: TPointF;
249 FScanAtFunc: TScanAtFunction;
250 public
251 constructor Create(AScanner: IBGRAScanner; pt1,pt2,pt3: TPointF; tex1,tex2,tex3: TPointF);
252 procedure ScanMoveTo(X,Y: Integer); override;
253 procedure ScanMoveToF(X,Y: Single);
254 function ScanAt(X,Y: Single): TBGRAPixel; override;
255 function ScanNextPixel: TBGRAPixel; override;
256 end;
257
258type
259 TPerspectiveTransform = class;
260
261 { TBGRAPerspectiveScannerTransform }
262
263 TBGRAPerspectiveScannerTransform = class(TBGRACustomScanner)
264 private
265 FTexture: IBGRAScanner;
266 FMatrix: TPerspectiveTransform;
267 FScanAtProc: TScanAtFunction;
268 function GetIncludeOppositePlane: boolean;
269 procedure SetIncludeOppositePlane(AValue: boolean);
270 public
271 constructor Create(texture: IBGRAScanner; texCoord1,texCoord2: TPointF; const quad: array of TPointF); overload;
272 constructor Create(texture: IBGRAScanner; const texCoordsQuad: array of TPointF; const quad: array of TPointF); overload;
273 destructor Destroy; override;
274 procedure ScanMoveTo(X, Y: Integer); override;
275 function ScanAt(X, Y: Single): TBGRAPixel; override;
276 function ScanNextPixel: TBGRAPixel; override;
277 property IncludeOppositePlane: boolean read GetIncludeOppositePlane write SetIncludeOppositePlane;
278 end;
279
280 { TPerspectiveTransform }
281
282 TPerspectiveTransform = class
283 private
284 sx ,shy ,w0 ,shx ,sy ,w1 ,tx ,ty ,w2 : single;
285 scanDenom,scanNumX,scanNumY: single;
286 FOutsideValue: TPointF;
287 FIncludeOppositePlane: boolean;
288 procedure Init;
289 public
290 constructor Create; overload;
291 constructor Create(x1,y1,x2,y2: single; const quad: array of TPointF); overload;
292 constructor Create(const quad: array of TPointF; x1,y1,x2,y2: single); overload;
293 constructor Create(const srcQuad,destQuad: array of TPointF); overload;
294 function MapQuadToQuad(const srcQuad,destQuad: array of TPointF): boolean;
295 function MapRectToQuad(x1,y1,x2,y2: single; const quad: array of TPointF): boolean;
296 function MapQuadToRect(const quad: array of TPointF; x1,y1,x2,y2: single): boolean;
297 function MapSquareToQuad(const quad: array of TPointF): boolean;
298 function MapQuadToSquare(const quad: array of TPointF): boolean;
299 procedure AssignIdentity;
300 function Invert: boolean;
301 procedure Translate(dx,dy: single);
302 procedure MultiplyBy(a: TPerspectiveTransform);
303 procedure PremultiplyBy(b: TPerspectiveTransform);
304 function Duplicate: TPerspectiveTransform;
305 function Apply(pt: TPointF): TPointF;
306 procedure ScanMoveTo(x,y:single);
307 function ScanNext: TPointF;
308 property OutsideValue: TPointF read FOutsideValue write FOutsideValue;
309 property IncludeOppositePlane: boolean read FIncludeOppositePlane write FIncludeOppositePlane;
310 end;
311
312type
313 { TBGRATwirlScanner applies a twirl transformation.
314
315 Note : this scanner handles integer coordinates only, so
316 any further transformation applied after this one may not
317 render correctly. }
318
319 { TBGRATwirlScanner }
320
321 TBGRATwirlScanner = Class(TBGRACustomScanner)
322 protected
323 FScanner: IBGRAScanner;
324 FScanAtFunc: TScanAtFunction;
325 FCenter: TPoint;
326 FTurn, FRadius, FExponent: Single;
327 public
328 constructor Create(AScanner: IBGRAScanner; ACenter: TPoint; ARadius: single; ATurn: single = 1; AExponent: single = 3);
329 function ScanAt(X, Y: Single): TBGRAPixel; override;
330 property Radius: Single read FRadius;
331 property Center: TPoint read FCenter;
332 property Exponent: Single read FExponent;
333 end;
334
335 { TBGRASphereDeformationScanner }
336
337 TBGRASphereDeformationScanner = Class(TBGRACustomScanner)
338 protected
339 FScanner: IBGRAScanner;
340 FScanAtFunc: TScanAtFunction;
341 FCenter: TPointF;
342 FRadiusX, FRadiusY: Single;
343 public
344 constructor Create(AScanner: IBGRAScanner; ACenter: TPointF; ARadiusX,ARadiusY: single);
345 function ScanAt(X, Y: Single): TBGRAPixel; override;
346 property RadiusX: Single read FRadiusX;
347 property RadiusY: Single read FRadiusY;
348 end;
349
350 { TBGRAVerticalCylinderDeformationScanner }
351
352 TBGRAVerticalCylinderDeformationScanner = Class(TBGRACustomScanner)
353 protected
354 FScanner: IBGRAScanner;
355 FScanAtFunc: TScanAtFunction;
356 FCenterX: single;
357 FRadiusX: Single;
358 public
359 constructor Create(AScanner: IBGRAScanner; ACenterX: single; ARadiusX: single);
360 function ScanAt(X, Y: Single): TBGRAPixel; override;
361 property RadiusX: Single read FRadiusX;
362 end;
363
364
365implementation
366
367uses BGRABlend, Math;
368
369function AffineMatrix(m11, m12, m13, m21, m22, m23: single): TAffineMatrix;
370begin
371 result[1,1] := m11;
372 result[1,2] := m12;
373 result[1,3] := m13;
374 result[2,1] := m21;
375 result[2,2] := m22;
376 result[2,3] := m23;
377end;
378
379function AffineMatrix(AU, AV: TPointF; ATranslation: TPointF): TAffineMatrix;
380begin
381 result:= AffineMatrix(AU.x, AV.x, ATranslation.x,
382 AU.y, AV.y, ATranslation.y);
383end;
384
385operator *(M, N: TAffineMatrix): TAffineMatrix;
386begin
387 result[1,1] := M[1,1]*N[1,1] + M[1,2]*N[2,1];
388 result[1,2] := M[1,1]*N[1,2] + M[1,2]*N[2,2];
389 result[1,3] := M[1,1]*N[1,3] + M[1,2]*N[2,3] + M[1,3];
390
391 result[2,1] := M[2,1]*N[1,1] + M[2,2]*N[2,1];
392 result[2,2] := M[2,1]*N[1,2] + M[2,2]*N[2,2];
393 result[2,3] := M[2,1]*N[1,3] + M[2,2]*N[2,3] + M[2,3];
394end;
395
396operator=(M, N: TAffineMatrix): boolean;
397begin
398 result := CompareMem(@M,@N,SizeOf(TAffineMatrix));
399end;
400
401operator*(M: TAffineMatrix; V: TPointF): TPointF;
402begin
403 if isEmptyPointF(V) then
404 result := EmptyPointF
405 else
406 begin
407 result.X := V.X*M[1,1]+V.Y*M[1,2]+M[1,3];
408 result.Y := V.X*M[2,1]+V.Y*M[2,2]+M[2,3];
409 end;
410end;
411
412operator*(M: TAffineMatrix; A: array of TPointF): ArrayOfTPointF;
413var
414 i: NativeInt;
415 ofs: TPointF;
416begin
417 setlength(result, length(A));
418 if IsAffineMatrixTranslation(M) then
419 begin
420 ofs := PointF(M[1,3],M[2,3]);
421 for i := 0 to high(A) do
422 result[i] := A[i]+ofs;
423 end else
424 for i := 0 to high(A) do
425 result[i] := M*A[i];
426end;
427
428operator*(M: TAffineMatrix; ab: TAffineBox): TAffineBox;
429begin
430 result.TopLeft := M*ab.TopLeft;
431 result.TopRight := M*ab.TopRight;
432 result.BottomLeft := M*ab.BottomLeft;
433end;
434
435function IsAffineMatrixInversible(M: TAffineMatrix): boolean;
436begin
437 result := M[1,1]*M[2,2]-M[1,2]*M[2,1] <> 0;
438end;
439
440function IsAffineMatrixTranslation(M: TAffineMatrix): boolean;
441begin
442 result := (m[1,1]=1) and (m[1,2]=0) and (m[2,1] = 0) and (m[2,2]=1);
443end;
444
445function IsAffineMatrixScale(M: TAffineMatrix): boolean;
446begin
447 result := (M[1,3]=0) and (M[2,3]=0) and
448 (M[1,2]=0) and (M[2,1]=0);
449end;
450
451function IsAffineMatrixIdentity(M: TAffineMatrix): boolean;
452begin
453 result := IsAffineMatrixTranslation(M) and (M[1,3]=0) and (M[2,3]=0);
454end;
455
456function AffineMatrixInverse(M: TAffineMatrix): TAffineMatrix;
457var det,f: single;
458 linearInverse: TAffineMatrix;
459begin
460 det := M[1,1]*M[2,2]-M[1,2]*M[2,1];
461 if det = 0 then
462 raise Exception.Create('Not inversible');
463 f := 1/det;
464 linearInverse := AffineMatrix(M[2,2]*f,-M[1,2]*f,0,
465 -M[2,1]*f,M[1,1]*f,0);
466 result := linearInverse * AffineMatrixTranslation(-M[1,3],-M[2,3]);
467end;
468
469function AffineMatrixTranslation(OfsX, OfsY: Single): TAffineMatrix;
470begin
471 result := AffineMatrix(1, 0, OfsX,
472 0, 1, OfsY);
473end;
474
475function AffineMatrixScale(sx, sy: single): TAffineMatrix;
476begin
477 result := AffineMatrix(sx, 0, 0,
478 0, sy, 0);
479end;
480
481function AffineMatrixScaledRotation(ASourceVector, ATargetVector: TPointF): TAffineMatrix;
482var
483 prevScale, newScale, scale: Single;
484 u1,v1,u2,v2,w: TPointF;
485begin
486 prevScale := VectLen(ASourceVector);
487 newScale := VectLen(ATargetVector);
488 if (prevScale = 0) or (newScale = 0) then
489 result := AffineMatrixIdentity
490 else
491 begin
492 scale := newScale/prevScale;
493 u1 := ASourceVector*(1/prevScale);
494 v1 := PointF(-u1.y,u1.x);
495 w := ATargetVector*(1/newScale);
496 u2 := PointF(w*u1, w*v1);
497 v2 := PointF(-u2.y,u2.x);
498 result := AffineMatrix(scale*u2,scale*v2,PointF(0,0));
499 end;
500end;
501
502function AffineMatrixScaledRotation(ASourcePoint, ATargetPoint, AOrigin: TPointF): TAffineMatrix;
503begin
504 result := AffineMatrixTranslation(AOrigin.x,AOrigin.y)*
505 AffineMatrixScaledRotation(ASourcePoint-AOrigin, ATargetPoint-AOrigin)*
506 AffineMatrixTranslation(-AOrigin.x,-AOrigin.y);
507end;
508
509function AffineMatrixSkewXDeg(AngleCW: single): TAffineMatrix;
510begin
511 result := AffineMatrix(1,tan(AngleCW*Pi/180),0,
512 0, 1, 0);
513end;
514
515function AffineMatrixSkewYDeg(AngleCW: single): TAffineMatrix;
516begin
517 result := AffineMatrix(1, 0, 0,
518 tan(AngleCW*Pi/180), 1, 0)
519end;
520
521function AffineMatrixSkewXRad(AngleCCW: single): TAffineMatrix;
522begin
523
524 result := AffineMatrix(1,tan(-AngleCCW),0,
525 0, 1, 0);
526end;
527
528function AffineMatrixSkewYRad(AngleCCW: single): TAffineMatrix;
529begin
530 result := AffineMatrix(1, 0, 0,
531 tan(-angleCCW), 1, 0)
532end;
533
534function AffineMatrixLinear(v1,v2: TPointF): TAffineMatrix;
535begin
536 result := AffineMatrix(v1.x, v2.x, 0,
537 v1.y, v2.y, 0);
538end;
539
540function AffineMatrixLinear(const AMatrix: TAffineMatrix): TAffineMatrix;
541begin
542 result := AffineMatrix(AMatrix[1,1],AMatrix[1,2],0,
543 AMatrix[2,1],AMatrix[2,2],0);
544end;
545
546function AffineMatrixRotationRad(AngleCCW: Single): TAffineMatrix;
547begin
548 result := AffineMatrix(cos(AngleCCW), sin(AngleCCW), 0,
549 -sin(AngleCCW), cos(AngleCCW), 0);
550end;
551
552function AffineMatrixRotationDeg(AngleCW: Single): TAffineMatrix;
553const DegToRad = -Pi/180;
554begin
555 result := AffineMatrixRotationRad(AngleCW*DegToRad);
556end;
557
558function AffineMatrixIdentity: TAffineMatrix;
559begin
560 result := AffineMatrix(1, 0, 0,
561 0, 1, 0);
562end;
563
564function IsAffineMatrixOrthogonal(M: TAffineMatrix): boolean;
565begin
566 result := PointF(M[1,1],M[2,1])*PointF(M[1,2],M[2,2]) = 0;
567end;
568
569function IsAffineMatrixScaledRotation(M: TAffineMatrix): boolean;
570begin
571 result := IsAffineMatrixOrthogonal(M) and
572 (VectLen(PointF(M[1,1],M[2,1]))=VectLen(PointF(M[1,2],M[2,2])));
573end;
574
575{ TBGRAVerticalCylinderDeformationScanner }
576
577constructor TBGRAVerticalCylinderDeformationScanner.Create(
578 AScanner: IBGRAScanner; ACenterX: single; ARadiusX: single);
579begin
580 FScanner := AScanner;
581 FScanAtFunc := @FScanner.ScanAt;
582 FCenterX := ACenterX;
583 FRadiusX := ARadiusX;
584end;
585
586function TBGRAVerticalCylinderDeformationScanner.ScanAt(X, Y: Single): TBGRAPixel;
587var
588 xn,len,fact: Single;
589begin
590 xn := (x - FCenterX) / FRadiusX;
591 len := abs(xn);
592 if (len <= 1) then
593 begin
594 if (len > 0) then
595 begin
596 fact := 1 / len * arcsin(len) / (Pi / 2);
597 xn *= fact;
598 end;
599 result := FScanAtFunc(xn * FRadiusX + FCenterX, y);
600 end
601 else
602 result := BGRAPixelTransparent;
603end;
604
605{ TBGRASphereDeformationScanner }
606
607constructor TBGRASphereDeformationScanner.Create(AScanner: IBGRAScanner;
608 ACenter: TPointF; ARadiusX, ARadiusY: single);
609begin
610 FScanner := AScanner;
611 FScanAtFunc := @FScanner.ScanAt;
612 FCenter := ACenter;
613 FRadiusX := ARadiusX;
614 FRadiusY := ARadiusY;
615end;
616
617function TBGRASphereDeformationScanner.ScanAt(X, Y: Single): TBGRAPixel;
618var
619 xn, yn, len,fact: Single;
620begin
621 xn := (x - FCenter.X) / FRadiusX;
622 yn := (y - FCenter.Y) / FRadiusY;
623 len := sqrt(sqr(xn) + sqr(yn));
624 if (len <= 1) then
625 begin
626 if (len > 0) then
627 begin
628 fact := 1 / len * arcsin(len) / (Pi / 2);
629 xn *= fact;
630 yn *= fact;
631 end;
632 result := FScanAtFunc(xn * FRadiusX + FCenter.X, yn * FRadiusY + FCenter.Y);
633 end
634 else
635 result := BGRAPixelTransparent;
636end;
637
638{ TBGRAExtendedBorderScanner }
639
640constructor TBGRAExtendedBorderScanner.Create(ASource: IBGRAScanner;
641 ABounds: TRect);
642begin
643 FSource := ASource;
644 FBounds := ABounds;
645end;
646
647function TBGRAExtendedBorderScanner.ScanAt(X, Y: Single): TBGRAPixel;
648begin
649 if x < FBounds.Left then x := FBounds.Left;
650 if y < FBounds.Top then y := FBounds.Top;
651 if x > FBounds.Right-1 then x := FBounds.Right-1;
652 if y > FBounds.Bottom-1 then y := FBounds.Bottom-1;
653 result := FSource.ScanAt(X,Y);
654end;
655
656{ TBGRAScannerOffset }
657
658constructor TBGRAScannerOffset.Create(ASource: IBGRAScanner; AOffset: TPoint);
659begin
660 FSource := ASource;
661 FOffset := AOffset;
662end;
663
664destructor TBGRAScannerOffset.Destroy;
665begin
666 fillchar(FSource,sizeof(FSource),0);
667 inherited Destroy;
668end;
669
670procedure TBGRAScannerOffset.ScanMoveTo(X, Y: Integer);
671begin
672 FSource.ScanMoveTo(X-FOffset.X,Y-FOffset.Y);
673end;
674
675function TBGRAScannerOffset.ScanNextPixel: TBGRAPixel;
676begin
677 Result:=FSource.ScanNextPixel;
678end;
679
680function TBGRAScannerOffset.ScanAt(X, Y: Single): TBGRAPixel;
681begin
682 Result:=FSource.ScanAt(X - FOffset.X, Y - FOffset.Y);
683end;
684
685function TBGRAScannerOffset.IsScanPutPixelsDefined: boolean;
686begin
687 Result:=FSource.IsScanPutPixelsDefined;
688end;
689
690procedure TBGRAScannerOffset.ScanPutPixels(pdest: PBGRAPixel; count: integer;
691 mode: TDrawMode);
692begin
693 FSource.ScanPutPixels(pdest, count, mode);
694end;
695
696{ TBGRABitmapScanner }
697
698constructor TBGRABitmapScanner.Create(ASource: TBGRACustomBitmap; ARepeatX,
699 ARepeatY: boolean; AOrigin: TPoint);
700begin
701 FSource := ASource;
702 FRepeatX := ARepeatX;
703 FRepeatY := ARepeatY;
704 FScanline := nil;
705 FOrigin := AOrigin;
706end;
707
708procedure TBGRABitmapScanner.ScanMoveTo(X, Y: Integer);
709begin
710 if (FSource.NbPixels = 0) then
711 begin
712 FScanline := nil;
713 exit;
714 end;
715 Inc(Y,FOrigin.Y);
716 if FRepeatY then
717 begin
718 Y := Y mod FSource.Height;
719 if Y < 0 then Y += FSource.Height;
720 end;
721 if (Y < 0) or (Y >= FSource.Height) then
722 begin
723 FScanline := nil;
724 exit;
725 end;
726 FScanline := FSource.Scanline[Y];
727 FCurX := X+FOrigin.X;
728 if FRepeatX then
729 begin
730 FCurX := FCurX mod FSource.Width;
731 if FCurX < 0 then FCurX += FSource.Width;
732 end;
733end;
734
735function TBGRABitmapScanner.ScanNextPixel: TBGRAPixel;
736begin
737 if (FScanline = nil) then
738 begin
739 result := BGRAPixelTransparent;
740 exit;
741 end;
742 if FRepeatX then
743 begin
744 result := (FScanline+FCurX)^;
745 inc(FCurX);
746 if FCurX = FSource.Width then FCurX := 0;
747 end else
748 begin
749 if (FCurX >= FSource.Width) then
750 begin
751 result := BGRAPixelTransparent;
752 exit;
753 end;
754 if FCurX < 0 then
755 result := BGRAPixelTransparent
756 else
757 result := (FScanline+FCurX)^;
758 inc(FCurX);
759 end;
760end;
761
762function TBGRABitmapScanner.ScanAt(X, Y: Single): TBGRAPixel;
763begin
764 Result := FSource.GetPixelCycle(X+FOrigin.X,Y+FOrigin.Y,rfLinear,FRepeatX,FRepeatY);
765end;
766
767{ TBGRATriangleLinearMapping }
768
769constructor TBGRATriangleLinearMapping.Create(AScanner: IBGRAScanner; pt1, pt2,
770 pt3: TPointF; tex1, tex2, tex3: TPointF);
771begin
772 FScanner := AScanner;
773 FScanAtFunc := @FScanner.ScanAt;
774
775 FMatrix := AffineMatrix(pt2.X-pt1.X, pt3.X-pt1.X, 0,
776 pt2.Y-pt1.Y, pt3.Y-pt1.Y, 0);
777 if not IsAffineMatrixInversible(FMatrix) then
778 FMatrix := AffineMatrix(0,0,0,0,0,0)
779 else
780 FMatrix := AffineMatrixInverse(FMatrix) * AffineMatrixTranslation(-pt1.x,-pt1.y);
781
782 FTexCoord1 := tex1;
783 FDiff2 := tex2-tex1;
784 FDiff3 := tex3-tex1;
785 FStep := FDiff2*FMatrix[1,1]+FDiff3*FMatrix[2,1];
786end;
787
788procedure TBGRATriangleLinearMapping.ScanMoveTo(X, Y: Integer);
789begin
790 ScanMoveToF(X, Y);
791end;
792
793procedure TBGRATriangleLinearMapping.ScanMoveToF(X, Y: Single);
794var
795 Cur: TPointF;
796begin
797 Cur := FMatrix*PointF(X,Y);
798 FCurTexCoord := FTexCoord1+FDiff2*Cur.X+FDiff3*Cur.Y;
799end;
800
801function TBGRATriangleLinearMapping.ScanAt(X, Y: Single): TBGRAPixel;
802begin
803 ScanMoveToF(X,Y);
804 result := ScanNextPixel;
805end;
806
807function TBGRATriangleLinearMapping.ScanNextPixel: TBGRAPixel;
808begin
809 result := FScanAtFunc(FCurTexCoord.X,FCurTexCoord.Y);
810 FCurTexCoord += FStep;
811end;
812
813{ TBGRAAffineScannerTransform }
814
815constructor TBGRAAffineScannerTransform.Create(AScanner: IBGRAScanner);
816begin
817 FScanner := AScanner;
818 FScanAtFunc := @FScanner.ScanAt;
819 GlobalOpacity := 255;
820 Reset;
821end;
822
823procedure TBGRAAffineScannerTransform.Reset;
824begin
825 FMatrix := AffineMatrixIdentity;
826 FEmptyMatrix := False;
827end;
828
829procedure TBGRAAffineScannerTransform.Invert;
830begin
831 if not FEmptyMatrix and IsAffineMatrixInversible(FMatrix) then
832 FMatrix := AffineMatrixInverse(FMatrix) else
833 FEmptyMatrix := True;
834end;
835
836function TBGRAAffineScannerTransform.GetViewMatrix: TAffineMatrix;
837begin
838 if FEmptyMatrix then
839 result := AffineMatrixIdentity
840 else
841 result := AffineMatrixInverse(FMatrix);
842end;
843
844procedure TBGRAAffineScannerTransform.SetViewMatrix(AValue: TAffineMatrix);
845begin
846 Matrix := AValue;
847 Invert;
848end;
849
850procedure TBGRAAffineScannerTransform.SetMatrix(AMatrix: TAffineMatrix);
851begin
852 FEmptyMatrix := False;
853 FMatrix := AMatrix;
854end;
855
856//transformations are inverted because the effect on the resulting image
857//is the inverse of the transformation. This is due to the fact
858//that the matrix is applied to source coordinates, not destination coordinates
859procedure TBGRAAffineScannerTransform.Translate(OfsX, OfsY: Single);
860begin
861 MultiplyBy(AffineMatrixTranslation(-OfsX,-OfsY));
862end;
863
864procedure TBGRAAffineScannerTransform.RotateDeg(AngleCW: Single);
865begin
866 MultiplyBy(AffineMatrixRotationDeg(-AngleCW));
867end;
868
869procedure TBGRAAffineScannerTransform.RotateRad(AngleCCW: Single);
870begin
871 MultiplyBy(AffineMatrixRotationRad(-AngleCCW));
872end;
873
874procedure TBGRAAffineScannerTransform.MultiplyBy(AMatrix: TAffineMatrix);
875begin
876 FMatrix *= AMatrix;
877end;
878
879procedure TBGRAAffineScannerTransform.Fit(Origin, HAxis, VAxis: TPointF);
880begin
881 SetMatrix(AffineMatrix(HAxis.X-Origin.X, VAxis.X-Origin.X, 0,
882 HAxis.Y-Origin.Y, VAxis.Y-Origin.Y, 0));
883 Invert;
884 Translate(Origin.X,Origin.Y);
885end;
886
887procedure TBGRAAffineScannerTransform.Scale(sx, sy: single);
888begin
889 if (sx=0) or (sy=0) then
890 begin
891 FEmptyMatrix := True;
892 exit;
893 end;
894
895 MultiplyBy(AffineMatrixScale(1/sx,1/sy));
896end;
897
898procedure TBGRAAffineScannerTransform.Scale(factor: single);
899begin
900 Scale(factor,factor);
901end;
902
903procedure TBGRAAffineScannerTransform.ScanMoveTo(X, Y: Integer);
904begin
905 ScanMoveToF(X,Y);
906end;
907
908procedure TBGRAAffineScannerTransform.ScanMoveToF(X, Y: single);
909Var Cur: TPointF;
910begin
911 Cur := FMatrix * PointF(X,Y);
912 FCurX := Cur.X;
913 FCurY := Cur.Y;
914end;
915
916function TBGRAAffineScannerTransform.InternalScanCurrentPixel: TBGRAPixel;
917begin
918 if FEmptyMatrix then
919 begin
920 result := BGRAPixelTransparent;
921 exit;
922 end;
923 result := FScanAtFunc(FCurX,FCurY);
924end;
925
926function TBGRAAffineScannerTransform.ScanNextPixel: TBGRAPixel;
927begin
928 result := InternalScanCurrentPixel;
929 FCurX += FMatrix[1,1];
930 FCurY += FMatrix[2,1];
931 if GlobalOpacity <> 255 then result.alpha := ApplyOpacity(result.alpha,GlobalOpacity);
932end;
933
934function TBGRAAffineScannerTransform.ScanAt(X, Y: Single): TBGRAPixel;
935begin
936 ScanMoveToF(X,Y);
937 result := InternalScanCurrentPixel;
938 if GlobalOpacity <> 255 then result.alpha := ApplyOpacity(result.alpha,GlobalOpacity);
939end;
940
941{ TBGRAQuadLinearScanner }
942
943function TBGRAQuadLinearScanner.GetTexColorAt(u, v: Single; detNeg: boolean
944 ): TBGRAPixel;
945begin
946 if detNeg then
947 begin
948 if not FShowC2 then
949 begin
950 result := BGRAPixelTransparent;
951 exit;
952 end;
953 end else
954 if not FShowC1 then
955 begin
956 result := BGRAPixelTransparent;
957 exit;
958 end;
959 with (FSourceMatrix * PointF(u,v) + FUVVector*(u*v)) do
960 if FTextureInterpolation then
961 result := FSource.ScanAt(x,y)
962 else
963 result := FSource.ScanAtInteger(System.round(x),System.round(y));
964end;
965
966procedure TBGRAQuadLinearScanner.ScanMoveToF(X, Y: single);
967begin
968 FCurXF := X;
969 FCurYF := Y;
970 if (FVectors[0].x = 0) and (FVectors[2].x = 0) then
971 begin
972 PrepareScanVert0;
973 FScanFunc := @ScanVert0;
974 end else
975 if aa = 0 then
976 begin
977 PrepareScanPara;
978 FScanFunc := @ScanPara
979 end
980 else
981 FScanFunc := @ScanGeneral;
982end;
983
984procedure TBGRAQuadLinearScanner.SetCulling(AValue: TFaceCulling);
985begin
986 FShowC1 := AValue in [fcKeepCW,fcNone];
987 FShowC2 := AValue in [fcKeepCCW,fcNone];
988end;
989
990procedure TBGRAQuadLinearScanner.Init(ASource: IBGRAScanner;
991 const APoints: array of TPointF; ATextureInterpolation: boolean);
992var
993 i: NativeInt;
994 v: TPointF;
995 len: single;
996begin
997 if length(APoints)<>4 then
998 raise exception.Create('Expecting 4 points');
999 FTextureInterpolation:= ATextureInterpolation;
1000 FSource := ASource;
1001 FSourceMatrix := AffineMatrixIdentity;
1002 FUVVector := PointF(0,0);
1003 for i := 0 to 3 do
1004 begin
1005 FPoints[i] := APoints[i];
1006 v := APoints[(i+1) mod 4] - APoints[i];
1007 len := sqrt(v*v);
1008 if len > 0 then FInvLengths[i] := 1/len
1009 else FInvLengths[i] := 0;
1010 FVectors[i] := v*FInvLengths[i];
1011 end;
1012
1013 FCoeffs[0] := FPoints[0];
1014 FCoeffs[1] := FPoints[1]-FPoints[0];
1015 FCoeffs[2] := FPoints[3]-FPoints[0];
1016 FCoeffs[3] := FPoints[0]+FPoints[2]-FPoints[1]-FPoints[3];
1017
1018 aa := VectDet(FCoeffs[3],FCoeffs[2]);
1019 bb0 := VectDet(FCoeffs[3],FCoeffs[0]) + VectDet(FCoeffs[1],FCoeffs[2]);
1020 cc0 := VectDet(FCoeffs[1],FCoeffs[0]);
1021 for i := 0 to 3 do
1022 FDets[i] := VectDet(FVectors[i],FVectors[(i+1) mod 4]);
1023 if aa <> 0 then inv2aa := 1/(2*aa) else inv2aa := 1;
1024
1025 FShowC1 := true;
1026 FShowC2 := true;
1027
1028 FBuffer := nil;
1029 FBufferSize := 0;
1030
1031 ScanMoveToF(0,0);
1032end;
1033
1034function TBGRAQuadLinearScanner.ScanAt(X, Y: Single): TBGRAPixel;
1035begin
1036 ScanMoveToF(X,Y);
1037 Result:= FScanFunc();
1038end;
1039
1040procedure TBGRAQuadLinearScanner.ScanPutPixels(pdest: PBGRAPixel; count: integer;
1041 mode: TDrawMode);
1042var
1043 n: NativeInt;
1044 p: PBGRAPixel;
1045begin
1046 if mode = dmSet then
1047 p := pdest
1048 else
1049 begin
1050 if count > FBufferSize then
1051 begin
1052 FBufferSize := count;
1053 ReAllocMem(FBuffer, FBufferSize*sizeof(TBGRAPixel));
1054 end;
1055 p := FBuffer;
1056 end;
1057 for n := count-1 downto 0 do
1058 begin
1059 p^ := FScanFunc();
1060 inc(p);
1061 end;
1062 if mode <> dmSet then PutPixels(pdest,FBuffer,count,mode,255);
1063end;
1064
1065function TBGRAQuadLinearScanner.IsScanPutPixelsDefined: boolean;
1066begin
1067 result := true;
1068end;
1069
1070procedure TBGRAQuadLinearScanner.ScanMoveTo(X, Y: Integer);
1071begin
1072 ScanMoveToF(X,Y);
1073end;
1074
1075function TBGRAQuadLinearScanner.ScanNextPixel: TBGRAPixel;
1076begin
1077 Result:= FScanFunc();
1078end;
1079
1080function TBGRAQuadLinearScanner.ScanGeneral: TBGRAPixel;
1081var u1,u2,v1,v2,x,y: double;
1082 bb,cc,det,delta,denom: double;
1083
1084 procedure ReturnC1C2; inline;
1085 var c1,c2: TBGRAPixel;
1086 begin
1087 with (FSourceMatrix * PointF(u1,v1) + FUVVector*(u1*v1)) do
1088 if FTextureInterpolation then
1089 c1 := FSource.ScanAt(x,y)
1090 else
1091 c1 := FSource.ScanAtInteger(System.round(x),System.round(y));
1092 with (FSourceMatrix * PointF(u2,v2) + FUVVector*(u2*v2)) do
1093 if FTextureInterpolation then
1094 c2 := FSource.ScanAt(x,y)
1095 else
1096 c2 := FSource.ScanAtInteger(System.round(x),System.round(y));
1097 result := MergeBGRA(c1,c2);
1098 end;
1099
1100begin
1101 x := FCurXF;
1102 y := FCurYF;
1103 FCurXF += 1;
1104 if (Y = FPoints[0].y) and (FVectors[0].y = 0) then
1105 begin
1106 if FVectors[0].x = 0 then
1107 begin
1108 result := BGRAPixelTransparent;
1109 exit;
1110 end;
1111 u1 := (X - FPoints[0].x)/(FPoints[1].x-FPoints[0].x);
1112 if (u1 >= 0) and (u1 <= 1) then
1113 begin
1114 result := GetTexColorAt(u1,0,FDets[0]<0);
1115 exit;
1116 end;
1117 end;
1118 if (X = FPoints[1].x) and (FVectors[1].x = 0) then
1119 begin
1120 if FVectors[1].y = 0 then
1121 begin
1122 result := BGRAPixelTransparent;
1123 exit;
1124 end;
1125 v1 := (Y - FPoints[1].y)/(FPoints[2].y-FPoints[1].y);
1126 if (v1 >= 0) and (v1 <= 1) then
1127 begin
1128 result := GetTexColorAt(0,v1,FDets[1]<0);
1129 exit;
1130 end;
1131 end;
1132 if (Y = FPoints[2].y) and (FVectors[2].y = 0) then
1133 begin
1134 if FVectors[2].x = 0 then
1135 begin
1136 result := BGRAPixelTransparent;
1137 exit;
1138 end;
1139 u1 := (X - FPoints[3].x)/(FPoints[2].x-FPoints[3].x);
1140 if (u1 >= 0) and (u1 <= 1) then
1141 begin
1142 result := GetTexColorAt(u1,1,FDets[2]<0);
1143 exit;
1144 end;
1145 end;
1146 if (X = FPoints[3].x) and (FVectors[3].x = 0) then
1147 begin
1148 if FVectors[3].y = 0 then
1149 begin
1150 result := BGRAPixelTransparent;
1151 exit;
1152 end;
1153 v1 := (Y - FPoints[0].y)/(FPoints[3].y-FPoints[0].y);
1154 if (v1 >= 0) and (v1 <= 1) then
1155 begin
1156 result := GetTexColorAt(0,v1,FDets[3]<0);
1157 exit;
1158 end;
1159 end;
1160
1161 bb := bb0 + x*FCoeffs[3].y - y*FCoeffs[3].x;
1162 cc := cc0 + x*FCoeffs[1].y - y*FCoeffs[1].x;
1163 if cc = 0 then
1164 begin
1165 v1 := -bb*2*inv2aa;
1166 denom := FCoeffs[1].x+FCoeffs[3].x*v1;
1167 if denom = 0 then
1168 begin
1169 result := BGRAPixelTransparent;
1170 exit;
1171 end
1172 else
1173 u1 := (x-FCoeffs[0].x-FCoeffs[2].x*v1)/denom;
1174
1175 if (u1>=0) and (u1<=1) and (v1 >= 0) and (v1 <= 1) then
1176 result := GetTexColorAt(u1,v1,bb<0)
1177 else
1178 result := BGRAPixelTransparent;
1179 end else
1180 begin
1181 delta := bb*bb - 4*aa*cc;
1182
1183 if delta < 0 then
1184 begin
1185 result := BGRAPixelTransparent;
1186 exit;
1187 end;
1188 det := sqrt(delta);
1189 v1 := (-bb+det)*inv2aa;
1190 if v1 = 0 then
1191 u1 := (FVectors[0]*FInvLengths[0])*(PointF(x,y)-FPoints[0])
1192 else if v1 = 1 then
1193 u1 := 1 - (FVectors[2]*FInvLengths[2])*(PointF(x,y)-FPoints[2])
1194 else
1195 begin
1196 denom := FCoeffs[1].x+FCoeffs[3].x*v1;
1197 if abs(denom)<1e-6 then
1198 begin
1199 u1 := (bb+det)*inv2aa;
1200 denom := FCoeffs[1].y+FCoeffs[3].y*u1;
1201 if denom = 0 then
1202 begin
1203 result := BGRAPixelTransparent;
1204 exit;
1205 end
1206 else v1 := (y-FCoeffs[0].y-FCoeffs[2].y*u1)/denom;
1207 end
1208 else u1 := (x-FCoeffs[0].x-FCoeffs[2].x*v1)/denom;
1209 end;
1210
1211 v2 := (-bb-det)*inv2aa;
1212 if v2 = 0 then
1213 u2 := (FVectors[0]*FInvLengths[0])*(PointF(x,y)-FPoints[0])
1214 else if v2 = 1 then
1215 u2 := 1 - (FVectors[2]*FInvLengths[2])*(PointF(x,y)-FPoints[2])
1216 else
1217 begin
1218 denom := FCoeffs[1].x+FCoeffs[3].x*v2;
1219 if abs(denom)<1e-6 then
1220 begin
1221 u2 := (bb-det)*inv2aa;
1222 denom := FCoeffs[1].y+FCoeffs[3].y*u2;
1223 if denom = 0 then
1224 begin
1225 result := BGRAPixelTransparent;
1226 exit;
1227 end
1228 else v2 := (y-FCoeffs[0].y-FCoeffs[2].y*u2)/denom;
1229 end
1230 else u2 := (x-FCoeffs[0].x-FCoeffs[2].x*v2)/denom;
1231 end;
1232
1233 if (u1 >= 0) and (u1 <= 1) and (v1 >= 0) and (v1 <= 1) and FShowC1 then
1234 begin
1235 if (u2 >= 0) and (u2 <= 1) and (v2 >= 0) and (v2 <= 1) and FShowC2 then
1236 ReturnC1C2
1237 else
1238 with (FSourceMatrix * PointF(u1,v1) + FUVVector*(u1*v1)) do
1239 if FTextureInterpolation then
1240 result := FSource.ScanAt(x,y)
1241 else
1242 result := FSource.ScanAtInteger(System.round(x),System.round(y));
1243 end
1244 else
1245 if (u2 >= 0) and (u2 <= 1) and (v2 >= 0) and (v2 <= 1) and FShowC2 then
1246 begin
1247 with (FSourceMatrix * PointF(u2,v2) + FUVVector*(u2*v2)) do
1248 if FTextureInterpolation then
1249 result := FSource.ScanAt(x,y)
1250 else
1251 result := FSource.ScanAtInteger(System.round(x),System.round(y));
1252 end
1253 else
1254 result := BGRAPixelTransparent;
1255 end;
1256end;
1257
1258function TBGRAQuadLinearScanner.GetCulling: TFaceCulling;
1259begin
1260 if FShowC1 and FShowC2 then
1261 result := fcNone
1262 else if FShowC1 then
1263 result := fcKeepCW
1264 else
1265 result := fcKeepCCW;
1266end;
1267
1268procedure TBGRAQuadLinearScanner.PrepareScanVert0;
1269begin
1270 if (FVectors[1].x <> 0) then
1271 begin
1272 ScanVertVStep0 := 1/(FPoints[2].x-FPoints[1].x);
1273 ScanVertV0 := (FCurXF-FPoints[1].x)*ScanVertVStep0;
1274 ScanVertDenom0 := (FPoints[1].y-FPoints[0].y)*(1-ScanVertV0) + (FPoints[2].y-FPoints[3].y)*ScanVertV0;
1275 ScanVertDenomStep0 := (FPoints[2].y-FPoints[3].y-FPoints[1].y+FPoints[0].y)*ScanVertVStep0;
1276 end
1277 else
1278 begin
1279 ScanVertV0 := 0;
1280 ScanVertVStep0 := EmptySingle;
1281 end;
1282end;
1283
1284function TBGRAQuadLinearScanner.ScanVert0: TBGRAPixel;
1285var u: single;
1286begin
1287 FCurXF += 1;
1288 if ScanVertVStep0 = EmptySingle then
1289 begin
1290 result := BGRAPixelTransparent;
1291 exit;
1292 end;
1293 if (ScanVertV0 >= 0) and (ScanVertV0 <= 1) then
1294 begin
1295 if ScanVertDenom0 = 0 then
1296 result := BGRAPixelTransparent
1297 else
1298 begin
1299 u := (FCurYF-(FPoints[0].y*(1-ScanVertV0) + FPoints[3].y*ScanVertV0))/ScanVertDenom0;
1300 if (u >= 0) and (u <= 1) then
1301 result := GetTexColorAt(u,ScanVertV0,FDets[0]<0)
1302 else
1303 result := BGRAPixelTransparent;
1304 end;
1305 end else
1306 result := BGRAPixelTransparent;
1307
1308 ScanVertV0 += ScanVertVStep0;
1309 ScanVertDenom0 += ScanVertDenomStep0;
1310end;
1311
1312procedure TBGRAQuadLinearScanner.PrepareScanPara;
1313begin
1314 ScanParaBB := bb0 + FCurXF*FCoeffs[3].y - FCurYF*FCoeffs[3].x;
1315 ScanParaCC := cc0 + FCurXF*FCoeffs[1].y - FCurYF*FCoeffs[1].x;
1316 if ScanParaBB <> 0 then
1317 ScanParaBBInv := 1/ScanParaBB
1318 else
1319 ScanParaBBInv := 1;
1320end;
1321
1322function TBGRAQuadLinearScanner.ScanPara: TBGRAPixel;
1323var
1324 u,v,denom: Single;
1325begin
1326 FCurXF += 1;
1327
1328 if ScanParaBB = 0 then
1329 result := BGRAPixelTransparent
1330 else
1331 begin
1332 v := -ScanParaCC*ScanParaBBInv;
1333 denom := FCoeffs[1].x+FCoeffs[3].x*v;
1334 if denom = 0 then
1335 result := BGRAPixelTransparent
1336 else
1337 begin
1338 u := (FCurXF-1-FCoeffs[0].x-FCoeffs[2].x*v)/denom;
1339
1340 if (u>=0) and (u<=1) and (v >= 0) and (v <= 1) then
1341 result := GetTexColorAt(u,v,FDets[0]<0)
1342 else
1343 result := BGRAPixelTransparent;
1344 end;
1345 end;
1346
1347 if FCoeffs[3].y <> 0 then
1348 begin
1349 ScanParaBB += FCoeffs[3].y;
1350 if ScanParaBB <> 0 then
1351 ScanParaBBInv := 1/ScanParaBB
1352 else
1353 ScanParaBBInv := 1;
1354 end;
1355 ScanParaCC += FCoeffs[1].y;
1356end;
1357
1358constructor TBGRAQuadLinearScanner.Create(ASource: IBGRAScanner;
1359 ASourceMatrix: TAffineMatrix; const APoints: array of TPointF;
1360 ATextureInterpolation: boolean);
1361begin
1362 Init(ASource, APoints, ATextureInterpolation);
1363 FSourceMatrix := ASourceMatrix;
1364end;
1365
1366constructor TBGRAQuadLinearScanner.Create(ASource: IBGRAScanner;
1367 const ATexCoords: array of TPointF; const APoints: array of TPointF;
1368 ATextureInterpolation: boolean);
1369begin
1370 Init(ASource, APoints, ATextureInterpolation);
1371 FSourceMatrix := AffineMatrixTranslation(ATexCoords[0].x,ATexCoords[0].y)*
1372 AffineMatrixLinear(ATexCoords[1]-ATexCoords[0],ATexCoords[3]-ATexCoords[0]);
1373 FUVVector := ATexCoords[2] - (ATexCoords[1]+ATexCoords[3]-ATexCoords[0]);
1374end;
1375
1376destructor TBGRAQuadLinearScanner.Destroy;
1377begin
1378 freemem(FBuffer);
1379 inherited Destroy;
1380end;
1381
1382{ TBGRAAffineBitmapTransform }
1383
1384procedure TBGRAAffineBitmapTransform.Init(ABitmap: TBGRACustomBitmap;
1385 ARepeatImageX: Boolean; ARepeatImageY: Boolean;
1386 AResampleFilter: TResampleFilter; AIncludeEdges: boolean = false);
1387begin
1388 if (ABitmap.Width = 0) or (ABitmap.Height = 0) then
1389 raise Exception.Create('Empty image');
1390 inherited Create(ABitmap);
1391 FBitmap := ABitmap;
1392 FRepeatImageX := ARepeatImageX;
1393 FRepeatImageY := ARepeatImageY;
1394 FResampleFilter:= AResampleFilter;
1395 FBuffer := nil;
1396 FBufferSize:= 0;
1397 FIncludeEdges := AIncludeEdges;
1398end;
1399
1400constructor TBGRAAffineBitmapTransform.Create(ABitmap: TBGRACustomBitmap;
1401 ARepeatImage: Boolean; AResampleFilter: TResampleFilter = rfLinear; AIncludeEdges: boolean = false);
1402begin
1403 Init(ABitmap,ARepeatImage,ARepeatImage,AResampleFilter,AIncludeEdges);
1404end;
1405
1406constructor TBGRAAffineBitmapTransform.Create(ABitmap: TBGRACustomBitmap;
1407 ARepeatImageX: Boolean; ARepeatImageY: Boolean;
1408 AResampleFilter: TResampleFilter; AIncludeEdges: boolean = false);
1409begin
1410 Init(ABitmap,ARepeatImageX,ARepeatImageY,AResampleFilter,AIncludeEdges);
1411end;
1412
1413destructor TBGRAAffineBitmapTransform.Destroy;
1414begin
1415 FreeMem(FBuffer);
1416end;
1417
1418function TBGRAAffineBitmapTransform.InternalScanCurrentPixel: TBGRAPixel;
1419begin
1420 result := FBitmap.GetPixelCycle(FCurX,FCurY,FResampleFilter,FRepeatImageX,FRepeatImageY);
1421end;
1422
1423procedure TBGRAAffineBitmapTransform.ScanPutPixels(pdest: PBGRAPixel;
1424 count: integer; mode: TDrawMode);
1425const PrecisionShift = {$IFDEF CPU64}24{$ELSE}12{$ENDIF};
1426 Precision = 1 shl PrecisionShift;
1427var p: PBGRAPixel;
1428 n: integer;
1429 posXPrecision, posYPrecision: NativeInt;
1430 deltaXPrecision,deltaYPrecision: NativeInt;
1431 ix,iy,shrMask,w,h: NativeInt;
1432 py0: PByte;
1433 deltaRow: NativeInt;
1434begin
1435 w := FBitmap.Width;
1436 h := FBitmap.Height;
1437 if (w = 0) or (h = 0) then exit;
1438
1439 if GlobalOpacity = 0 then
1440 begin
1441 if mode = dmSet then
1442 FillDWord(pdest^, count, DWord(BGRAPixelTransparent));
1443 exit;
1444 end;
1445
1446 posXPrecision := round(FCurX*Precision);
1447 deltaXPrecision:= round(FMatrix[1,1]*Precision);
1448 posYPrecision := round(FCurY*Precision);
1449 deltaYPrecision:= round(FMatrix[2,1]*Precision);
1450 shrMask := -1;
1451 shrMask := shrMask shr PrecisionShift;
1452 shrMask := not shrMask;
1453
1454 if mode = dmSet then
1455 p := pdest
1456 else
1457 begin
1458 if count > FBufferSize then
1459 begin
1460 FBufferSize := count;
1461 ReAllocMem(FBuffer, FBufferSize*sizeof(TBGRAPixel));
1462 end;
1463 p := FBuffer;
1464 end;
1465
1466 if FResampleFilter = rfBox then
1467 begin
1468 posXPrecision += Precision shr 1;
1469 posYPrecision += Precision shr 1;
1470 py0 := PByte(FBitmap.ScanLine[0]);
1471 if FBitmap.LineOrder = riloTopToBottom then
1472 deltaRow := FBitmap.Width*sizeof(TBGRAPixel) else
1473 deltaRow := -FBitmap.Width*sizeof(TBGRAPixel);
1474 if FRepeatImageX or FRepeatImageY then
1475 begin
1476 for n := count-1 downto 0 do
1477 begin
1478 if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift;
1479 if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift;
1480 if FRepeatImageX then ix := PositiveMod(ix,w);
1481 if FRepeatImageY then iy := PositiveMod(iy,h);
1482 if (ix < 0) or (iy < 0) or (ix >= w) or (iy >= h) then
1483 p^ := BGRAPixelTransparent
1484 else
1485 p^ := (PBGRAPixel(py0 + iy*deltaRow)+ix)^;
1486 inc(p);
1487 posXPrecision += deltaXPrecision;
1488 posYPrecision += deltaYPrecision;
1489 end;
1490 end else
1491 begin
1492 for n := count-1 downto 0 do
1493 begin
1494 if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift;
1495 if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift;
1496 if (ix < 0) or (iy < 0) or (ix >= w) or (iy >= h) then
1497 p^ := BGRAPixelTransparent
1498 else
1499 p^ := (PBGRAPixel(py0 + iy*deltaRow)+ix)^;
1500 inc(p);
1501 posXPrecision += deltaXPrecision;
1502 posYPrecision += deltaYPrecision;
1503 end;
1504 end;
1505 end else
1506 begin
1507 if FRepeatImageX and FRepeatImageY then
1508 begin
1509 for n := count-1 downto 0 do
1510 begin
1511 if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift;
1512 if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift;
1513 p^ := FBitmap.GetPixelCycle256(ix,iy, (posXPrecision shr (PrecisionShift-8)) and 255, (posYPrecision shr (PrecisionShift-8)) and 255,FResampleFilter);
1514 inc(p);
1515 posXPrecision += deltaXPrecision;
1516 posYPrecision += deltaYPrecision;
1517 end;
1518 end else
1519 if FRepeatImageX or FRepeatImageY then
1520 begin
1521 for n := count-1 downto 0 do
1522 begin
1523 if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift;
1524 if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift;
1525 p^ := FBitmap.GetPixelCycle256(ix,iy, (posXPrecision shr (PrecisionShift-8)) and 255, (posYPrecision shr (PrecisionShift-8)) and 255,FResampleFilter, FRepeatImageX,FRepeatImageY);
1526 inc(p);
1527 posXPrecision += deltaXPrecision;
1528 posYPrecision += deltaYPrecision;
1529 end;
1530 end else
1531 begin
1532 for n := count-1 downto 0 do
1533 begin
1534 if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift;
1535 if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift;
1536 p^ := FBitmap.GetPixel256(ix,iy, (posXPrecision shr (PrecisionShift-8)) and 255, (posYPrecision shr (PrecisionShift-8)) and 255,FResampleFilter);
1537 inc(p);
1538 posXPrecision += deltaXPrecision;
1539 posYPrecision += deltaYPrecision;
1540 end;
1541 end;
1542 end;
1543
1544 if GlobalOpacity < 255 then
1545 begin
1546 if mode = dmSet then
1547 p := pdest
1548 else
1549 p := FBuffer;
1550 for n := count-1 downto 0 do
1551 begin
1552 p^.alpha := ApplyOpacity(p^.alpha,GlobalOpacity);
1553 if p^.alpha = 0 then p^ := BGRAPixelTransparent;
1554 inc(p);
1555 end;
1556 end;
1557
1558 if mode <> dmSet then PutPixels(pdest,FBuffer,count,mode,255);
1559end;
1560
1561function TBGRAAffineBitmapTransform.IsScanPutPixelsDefined: boolean;
1562begin
1563 Result:=true;
1564end;
1565
1566procedure TBGRAAffineBitmapTransform.Fit(Origin, HAxis, VAxis: TPointF);
1567begin
1568 if (FBitmap.Width = 0) or (FBitmap.Height = 0) then exit;
1569 Matrix := AffineMatrix(HAxis.X-Origin.X, VAxis.X-Origin.X, Origin.X,
1570 HAxis.Y-Origin.Y, VAxis.Y-Origin.Y, Origin.Y);
1571 Invert;
1572 if FIncludeEdges then
1573 begin
1574 Matrix := AffineMatrixTranslation(-0.5,-0.5)*AffineMatrixScale(FBitmap.Width,FBitmap.Height)*Matrix;
1575 end else
1576 Matrix := AffineMatrixScale(FBitmap.Width-1,FBitmap.Height-1)*Matrix;
1577end;
1578
1579{ TBGRAPerspectiveScannerTransform }
1580
1581function TBGRAPerspectiveScannerTransform.GetIncludeOppositePlane: boolean;
1582begin
1583 if FMatrix = nil then
1584 result := false
1585 else
1586 result := FMatrix.IncludeOppositePlane;
1587end;
1588
1589procedure TBGRAPerspectiveScannerTransform.SetIncludeOppositePlane(
1590 AValue: boolean);
1591begin
1592 if FMatrix <> nil then
1593 FMatrix.IncludeOppositePlane := AValue;
1594end;
1595
1596constructor TBGRAPerspectiveScannerTransform.Create(texture: IBGRAScanner; texCoord1,texCoord2: TPointF; const quad: array of TPointF);
1597begin
1598 if DoesQuadIntersect(quad[0],quad[1],quad[2],quad[3]) or not IsConvex(quad,False) or (texCoord1.x = texCoord2.x) or (texCoord1.y = texCoord2.y) then
1599 FMatrix := nil
1600 else
1601 begin
1602 FMatrix := TPerspectiveTransform.Create(quad,texCoord1.x,texCoord1.y,texCoord2.x,texCoord2.y);
1603 FMatrix.OutsideValue := EmptyPointF;
1604 end;
1605 FTexture := texture;
1606 FScanAtProc:= @FTexture.ScanAt;
1607end;
1608
1609constructor TBGRAPerspectiveScannerTransform.Create(texture: IBGRAScanner;
1610 const texCoordsQuad: array of TPointF; const quad: array of TPointF);
1611begin
1612 if DoesQuadIntersect(quad[0],quad[1],quad[2],quad[3]) or not IsConvex(quad,False) or
1613 DoesQuadIntersect(texCoordsQuad[0],texCoordsQuad[1],texCoordsQuad[2],texCoordsQuad[3]) or not IsConvex(texCoordsQuad,False) then
1614 FMatrix := nil
1615 else
1616 begin
1617 FMatrix := TPerspectiveTransform.Create(quad,texCoordsQuad);
1618 FMatrix.OutsideValue := EmptyPointF;
1619 end;
1620 FTexture := texture;
1621 FScanAtProc:= @FTexture.ScanAt;
1622end;
1623
1624destructor TBGRAPerspectiveScannerTransform.Destroy;
1625begin
1626 FMatrix.free;
1627 inherited Destroy;
1628end;
1629
1630procedure TBGRAPerspectiveScannerTransform.ScanMoveTo(X, Y: Integer);
1631begin
1632 if FMatrix = nil then exit;
1633 FMatrix.ScanMoveTo(X,Y);
1634end;
1635
1636function TBGRAPerspectiveScannerTransform.ScanAt(X, Y: Single): TBGRAPixel;
1637var ptSource: TPointF;
1638begin
1639 if FMatrix = nil then
1640 result := BGRAPixelTransparent else
1641 begin
1642 ptSource := FMatrix.Apply(PointF(X,Y));
1643 if ptSource.x = EmptySingle then
1644 result := BGRAPixelTransparent
1645 else
1646 Result:= FScanAtProc(ptSource.X, ptSource.Y);
1647 end;
1648end;
1649
1650function TBGRAPerspectiveScannerTransform.ScanNextPixel: TBGRAPixel;
1651var ptSource: TPointF;
1652begin
1653 if FMatrix = nil then
1654 result := BGRAPixelTransparent else
1655 begin
1656 ptSource := FMatrix.ScanNext;
1657 if ptSource.x = EmptySingle then
1658 result := BGRAPixelTransparent
1659 else
1660 Result:= FScanAtProc(ptSource.X, ptSource.Y);
1661 end;
1662end;
1663
1664{ TPerspectiveTransform }
1665
1666procedure TPerspectiveTransform.Init;
1667begin
1668 FOutsideValue := PointF(0,0);
1669 FIncludeOppositePlane:= True;
1670end;
1671
1672constructor TPerspectiveTransform.Create;
1673begin
1674 Init;
1675 AssignIdentity;
1676end;
1677
1678constructor TPerspectiveTransform.Create(x1, y1, x2, y2: single;
1679 const quad: array of TPointF);
1680begin
1681 Init;
1682 MapRectToQuad(x1 ,y1 ,x2 ,y2 ,quad );
1683end;
1684
1685constructor TPerspectiveTransform.Create(const quad: array of TPointF; x1, y1,
1686 x2, y2: single);
1687begin
1688 Init;
1689 MapQuadToRect(quad, x1,y1,x2,y2);
1690end;
1691
1692constructor TPerspectiveTransform.Create(const srcQuad,
1693 destQuad: array of TPointF);
1694begin
1695 Init;
1696 MapQuadToQuad(srcQuad,destQuad);
1697end;
1698
1699{ Map a quad to quad. First compute quad to square, and then square to quad. }
1700function TPerspectiveTransform.MapQuadToQuad(const srcQuad,
1701 destQuad: array of TPointF): boolean;
1702var
1703 p : TPerspectiveTransform;
1704begin
1705 if not MapQuadToSquare(srcQuad ) then
1706 begin
1707 result:=false;
1708 exit;
1709 end;
1710
1711 p := TPerspectiveTransform.Create;
1712 if not p.MapSquareToQuad(destQuad) then
1713 begin
1714 p.Free;
1715 result:=false;
1716 exit;
1717 end;
1718
1719 //combine both transformations
1720 MultiplyBy(p);
1721 p.Free;
1722 result:=true;
1723end;
1724
1725//Map a rectangle to a quad. Make a polygon for the rectangle, and map it.
1726function TPerspectiveTransform.MapRectToQuad(x1, y1, x2, y2: single;
1727 const quad: array of TPointF): boolean;
1728begin
1729 result := MapQuadToQuad([PointF(x1,y1),PointF(x2,y1),PointF(x2,y2),PointF(x1,y2)], quad);
1730end;
1731
1732//Map a quad to a rectangle. Make a polygon for the rectangle, and map the quad into it.
1733function TPerspectiveTransform.MapQuadToRect(const quad: array of TPointF; x1,
1734 y1, x2, y2: single): boolean;
1735begin
1736 result := MapQuadToQuad(quad, [PointF(x1,y1),PointF(x2,y1),PointF(x2,y2),PointF(x1,y2)]);
1737end;
1738
1739//Map a square to a quad
1740function TPerspectiveTransform.MapSquareToQuad(const quad: array of TPointF): boolean;
1741var
1742 d,d1,d2: TPointF;
1743 den ,u ,v : double;
1744
1745begin
1746 d := quad[0]-quad[1]+quad[2]-quad[3];
1747
1748 if (d.x = 0.0 ) and
1749 (d.y = 0.0 ) then
1750 begin
1751 // Affine case (parallelogram)
1752 sx :=quad[1].x - quad[0].x;
1753 shy:=quad[1].y - quad[0].y;
1754 w0 :=0.0;
1755 shx:=quad[2].x - quad[1].x;
1756 sy :=quad[2].y - quad[1].y;
1757 w1 :=0.0;
1758 tx :=quad[0].x;
1759 ty :=quad[0].y;
1760 w2 :=1.0;
1761
1762 end
1763 else
1764 begin
1765 d1 := quad[1]-quad[2];
1766 d2 := quad[3]-quad[2];
1767 den:=d1.x * d2.y - d2.x * d1.y;
1768
1769 if den = 0.0 then
1770 begin
1771 // Singular case
1772 sx :=0.0;
1773 shy:=0.0;
1774 w0 :=0.0;
1775 shx:=0.0;
1776 sy :=0.0;
1777 w1 :=0.0;
1778 tx :=0.0;
1779 ty :=0.0;
1780 w2 :=0.0;
1781 result:=false;
1782 exit;
1783 end;
1784
1785 // General case
1786 u:=(d.x * d2.y - d.y * d2.x ) / den;
1787 v:=(d.y * d1.x - d.x * d1.y ) / den;
1788
1789 sx :=quad[1].x - quad[0].x + u * quad[1].x;
1790 shy:=quad[1].y - quad[0].y + u * quad[1].y;
1791 w0 :=u;
1792 shx:=quad[3].x - quad[0].x + v * quad[3].x;
1793 sy :=quad[3].y - quad[0].y + v * quad[3].y;
1794 w1 :=v;
1795 tx :=quad[0].x;
1796 ty :=quad[0].y;
1797 w2 :=1.0;
1798
1799 end;
1800
1801 result:=true;
1802
1803end;
1804
1805//Map a quad to a square. Compute mapping from square to quad, then invert.
1806function TPerspectiveTransform.MapQuadToSquare(const quad: array of TPointF): boolean;
1807begin
1808 if not MapSquareToQuad(quad ) then
1809 result:=false
1810 else
1811 result := Invert;
1812end;
1813
1814procedure TPerspectiveTransform.AssignIdentity;
1815begin
1816 sx :=1;
1817 shy:=0;
1818 w0 :=0;
1819 shx:=0;
1820 sy :=1;
1821 w1 :=0;
1822 tx :=0;
1823 ty :=0;
1824 w2 :=1;
1825end;
1826
1827function TPerspectiveTransform.Invert: boolean;
1828var
1829 d0, d1, d2, d : double;
1830 copy : TPerspectiveTransform;
1831
1832begin
1833 d0:= sy * w2 - w1 * ty;
1834 d1:= w0 * ty - shy * w2;
1835 d2:= shy * w1 - w0 * sy;
1836 d := sx * d0 + shx * d1 + tx * d2;
1837
1838 if d = 0.0 then
1839 begin
1840 sx := 0.0;
1841 shy:= 0.0;
1842 w0 := 0.0;
1843 shx:= 0.0;
1844 sy := 0.0;
1845 w1 := 0.0;
1846 tx := 0.0;
1847 ty := 0.0;
1848 w2 := 0.0;
1849 result:= false;
1850 exit;
1851 end;
1852
1853 d:= 1.0 / d;
1854
1855 copy := Duplicate;
1856
1857 sx :=d * d0;
1858 shy:=d * d1;
1859 w0 :=d * d2;
1860 shx:=d * (copy.w1 * copy.tx - copy.shx * copy.w2 );
1861 sy :=d * (copy.sx * copy.w2 - copy.w0 * copy.tx );
1862 w1 :=d * (copy.w0 * copy.shx - copy.sx * copy.w1 );
1863 tx :=d * (copy.shx * copy.ty - copy.sy * copy.tx );
1864 ty :=d * (copy.shy * copy.tx - copy.sx * copy.ty );
1865 w2 :=d * (copy.sx * copy.sy - copy.shy * copy.shx );
1866
1867 copy.free;
1868
1869 result:=true;
1870end;
1871
1872procedure TPerspectiveTransform.Translate(dx, dy: single);
1873begin
1874 tx:=tx + dx;
1875 ty:=ty + dy;
1876end;
1877
1878procedure TPerspectiveTransform.MultiplyBy(a: TPerspectiveTransform);
1879var b: TPerspectiveTransform;
1880begin
1881 b := Duplicate;
1882 sx :=a.sx * b.sx + a.shx * b.shy + a.tx * b.w0;
1883 shx:=a.sx * b.shx + a.shx * b.sy + a.tx * b.w1;
1884 tx :=a.sx * b.tx + a.shx * b.ty + a.tx * b.w2;
1885 shy:=a.shy * b.sx + a.sy * b.shy + a.ty * b.w0;
1886 sy :=a.shy * b.shx + a.sy * b.sy + a.ty * b.w1;
1887 ty :=a.shy * b.tx + a.sy * b.ty + a.ty * b.w2;
1888 w0 :=a.w0 * b.sx + a.w1 * b.shy + a.w2 * b.w0;
1889 w1 :=a.w0 * b.shx + a.w1 * b.sy + a.w2 * b.w1;
1890 w2 :=a.w0 * b.tx + a.w1 * b.ty + a.w2 * b.w2;
1891 b.Free;
1892end;
1893
1894procedure TPerspectiveTransform.PremultiplyBy(b: TPerspectiveTransform);
1895var
1896 a : TPerspectiveTransform;
1897 begin
1898 a := Duplicate;
1899 sx :=a.sx * b.sx + a.shx * b.shy + a.tx * b.w0;
1900 shx:=a.sx * b.shx + a.shx * b.sy + a.tx * b.w1;
1901 tx :=a.sx * b.tx + a.shx * b.ty + a.tx * b.w2;
1902 shy:=a.shy * b.sx + a.sy * b.shy + a.ty * b.w0;
1903 sy :=a.shy * b.shx + a.sy * b.sy + a.ty * b.w1;
1904 ty :=a.shy * b.tx + a.sy * b.ty + a.ty * b.w2;
1905 w0 :=a.w0 * b.sx + a.w1 * b.shy + a.w2 * b.w0;
1906 w1 :=a.w0 * b.shx + a.w1 * b.sy + a.w2 * b.w1;
1907 w2 :=a.w0 * b.tx + a.w1 * b.ty + a.w2 * b.w2;
1908 a.Free;
1909end;
1910
1911function TPerspectiveTransform.Duplicate: TPerspectiveTransform;
1912begin
1913 result := TPerspectiveTransform.Create;
1914 result.sx :=sx;
1915 result.shy:=shy;
1916 result.w0 :=w0;
1917 result.shx:=shx;
1918 result.sy :=sy;
1919 result.w1 :=w1;
1920 result.tx :=tx;
1921 result.ty :=ty;
1922 result.w2 :=w2;
1923end;
1924
1925function TPerspectiveTransform.Apply(pt: TPointF): TPointF;
1926var
1927 m : single;
1928begin
1929 m:= pt.x * w0 + pt.y * w1 + w2;
1930 if (m=0) or (not FIncludeOppositePlane and (m < 0)) then
1931 result := FOutsideValue
1932 else
1933 begin
1934 m := 1/m;
1935 result.x := m * (pt.x * sx + pt.y * shx + tx );
1936 result.y := m * (pt.x * shy + pt.y * sy + ty );
1937 end;
1938end;
1939
1940procedure TPerspectiveTransform.ScanMoveTo(x, y: single);
1941begin
1942 ScanDenom := x * w0 + y * w1 + w2;
1943 ScanNumX := x * sx + y * shx + tx;
1944 scanNumY := x * shy + y * sy + ty;
1945end;
1946
1947function TPerspectiveTransform.ScanNext: TPointF;
1948var m: single;
1949begin
1950 if (ScanDenom = 0) or (not FIncludeOppositePlane and (ScanDenom < 0)) then
1951 result := FOutsideValue
1952 else
1953 begin
1954 m := 1/scanDenom;
1955 result.x := m * ScanNumX;
1956 result.y := m * scanNumY;
1957 end;
1958 ScanDenom += w0;
1959 ScanNumX += sx;
1960 scanNumY += shy;
1961end;
1962
1963{ TBGRATwirlScanner }
1964
1965constructor TBGRATwirlScanner.Create(AScanner: IBGRAScanner; ACenter: TPoint; ARadius: single; ATurn: single = 1; AExponent: single = 3);
1966begin
1967 FScanner := AScanner;
1968 FScanAtFunc := @FScanner.ScanAt;
1969 FCenter := ACenter;
1970 FTurn := ATurn;
1971 FRadius := ARadius;
1972 FExponent := AExponent;
1973end;
1974
1975function TBGRATwirlScanner.ScanAt(X, Y: Single): TBGRAPixel;
1976var p: TPoint;
1977 d: single;
1978 a,cosa,sina: integer;
1979begin
1980 p := Point(Round(X)-FCenter.X,Round(Y)-FCenter.Y);
1981 if (abs(p.x) < FRadius) and (abs(p.Y) < FRadius) then
1982 begin
1983 d := sqrt(p.x*p.x+p.y*p.y);
1984 if d < FRadius then
1985 begin
1986 d := (FRadius-d)/FRadius;
1987 if FExponent <> 1 then d := exp(ln(d)*FExponent);
1988 a := round(d*FTurn*65536);
1989 cosa := Cos65536(a)-32768;
1990 sina := Sin65536(a)-32768;
1991 result := FScanner.ScanAt((p.x*cosa+p.y*sina)/32768 + FCenter.X,
1992 (-p.x*sina+p.y*cosa)/32768 + FCenter.Y);
1993 exit;
1994 end;
1995 end;
1996 result := FScanAtFunc(X,Y);
1997end;
1998
1999end.
2000
Note: See TracBrowser for help on using the repository browser.