1 | unit BGRATransform;
|
---|
2 |
|
---|
3 | {$mode objfpc}
|
---|
4 |
|
---|
5 | interface
|
---|
6 |
|
---|
7 | { This unit contains bitmap transformations as classes and the TAffineMatrix record and functions. }
|
---|
8 |
|
---|
9 | uses
|
---|
10 | Classes, SysUtils, BGRABitmapTypes;
|
---|
11 |
|
---|
12 | type
|
---|
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
|
---|
178 | function AffineMatrix(m11,m12,m13,m21,m22,m23: single): TAffineMatrix; overload;
|
---|
179 | function AffineMatrix(AU,AV: TPointF; ATranslation: TPointF): TAffineMatrix; overload;
|
---|
180 |
|
---|
181 | //matrix multiplication
|
---|
182 | operator *(M,N: TAffineMatrix): TAffineMatrix;
|
---|
183 | operator =(M,N: TAffineMatrix): boolean;
|
---|
184 |
|
---|
185 | //matrix multiplication by a vector (apply transformation to that vector)
|
---|
186 | operator *(M: TAffineMatrix; V: TPointF): TPointF;
|
---|
187 | operator *(M: TAffineMatrix; A: array of TPointF): ArrayOfTPointF;
|
---|
188 | operator *(M: TAffineMatrix; ab: TAffineBox): TAffineBox;
|
---|
189 |
|
---|
190 | //check if matrix is inversible
|
---|
191 | function IsAffineMatrixInversible(M: TAffineMatrix): boolean;
|
---|
192 |
|
---|
193 | //check if the matrix is a translation (including the identity)
|
---|
194 | function IsAffineMatrixTranslation(M: TAffineMatrix): boolean;
|
---|
195 |
|
---|
196 | //check if the matrix is a scaling (including a projection i.e. with factor 0)
|
---|
197 | function IsAffineMatrixScale(M: TAffineMatrix): boolean;
|
---|
198 |
|
---|
199 | //check if the matrix is the identity
|
---|
200 | function IsAffineMatrixIdentity(M: TAffineMatrix): boolean;
|
---|
201 |
|
---|
202 | //compute inverse (check if inversible before)
|
---|
203 | function AffineMatrixInverse(M: TAffineMatrix): TAffineMatrix;
|
---|
204 |
|
---|
205 | //define a translation matrix
|
---|
206 | function AffineMatrixTranslation(OfsX,OfsY: Single): TAffineMatrix;
|
---|
207 |
|
---|
208 | //define a scaling matrix
|
---|
209 | function AffineMatrixScale(sx,sy: single): TAffineMatrix;
|
---|
210 | function AffineMatrixScaledRotation(ASourceVector, ATargetVector: TPointF): TAffineMatrix;
|
---|
211 | function AffineMatrixScaledRotation(ASourcePoint, ATargetPoint, AOrigin: TPointF): TAffineMatrix;
|
---|
212 |
|
---|
213 | function AffineMatrixSkewXDeg(AngleCW: single): TAffineMatrix;
|
---|
214 | function AffineMatrixSkewYDeg(AngleCW: single): TAffineMatrix;
|
---|
215 | function AffineMatrixSkewXRad(AngleCCW: single): TAffineMatrix;
|
---|
216 | function AffineMatrixSkewYRad(AngleCCW: single): TAffineMatrix;
|
---|
217 |
|
---|
218 | //define a linear matrix
|
---|
219 | function AffineMatrixLinear(v1,v2: TPointF): TAffineMatrix; overload;
|
---|
220 | function 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)
|
---|
224 | function AffineMatrixRotationRad(AngleCCW: Single): TAffineMatrix;
|
---|
225 |
|
---|
226 | //Positive degrees are clockwise
|
---|
227 | //(assuming the y-axis is pointing down)
|
---|
228 | function AffineMatrixRotationDeg(AngleCW: Single): TAffineMatrix;
|
---|
229 |
|
---|
230 | //define the identity matrix (that do nothing)
|
---|
231 | function AffineMatrixIdentity: TAffineMatrix;
|
---|
232 |
|
---|
233 | function IsAffineMatrixOrthogonal(M: TAffineMatrix): boolean;
|
---|
234 | function IsAffineMatrixScaledRotation(M: TAffineMatrix): boolean;
|
---|
235 |
|
---|
236 | type
|
---|
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 |
|
---|
258 | type
|
---|
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 |
|
---|
312 | type
|
---|
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 |
|
---|
365 | implementation
|
---|
366 |
|
---|
367 | uses BGRABlend, Math;
|
---|
368 |
|
---|
369 | function AffineMatrix(m11, m12, m13, m21, m22, m23: single): TAffineMatrix;
|
---|
370 | begin
|
---|
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;
|
---|
377 | end;
|
---|
378 |
|
---|
379 | function AffineMatrix(AU, AV: TPointF; ATranslation: TPointF): TAffineMatrix;
|
---|
380 | begin
|
---|
381 | result:= AffineMatrix(AU.x, AV.x, ATranslation.x,
|
---|
382 | AU.y, AV.y, ATranslation.y);
|
---|
383 | end;
|
---|
384 |
|
---|
385 | operator *(M, N: TAffineMatrix): TAffineMatrix;
|
---|
386 | begin
|
---|
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];
|
---|
394 | end;
|
---|
395 |
|
---|
396 | operator=(M, N: TAffineMatrix): boolean;
|
---|
397 | begin
|
---|
398 | result := CompareMem(@M,@N,SizeOf(TAffineMatrix));
|
---|
399 | end;
|
---|
400 |
|
---|
401 | operator*(M: TAffineMatrix; V: TPointF): TPointF;
|
---|
402 | begin
|
---|
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;
|
---|
410 | end;
|
---|
411 |
|
---|
412 | operator*(M: TAffineMatrix; A: array of TPointF): ArrayOfTPointF;
|
---|
413 | var
|
---|
414 | i: NativeInt;
|
---|
415 | ofs: TPointF;
|
---|
416 | begin
|
---|
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];
|
---|
426 | end;
|
---|
427 |
|
---|
428 | operator*(M: TAffineMatrix; ab: TAffineBox): TAffineBox;
|
---|
429 | begin
|
---|
430 | result.TopLeft := M*ab.TopLeft;
|
---|
431 | result.TopRight := M*ab.TopRight;
|
---|
432 | result.BottomLeft := M*ab.BottomLeft;
|
---|
433 | end;
|
---|
434 |
|
---|
435 | function IsAffineMatrixInversible(M: TAffineMatrix): boolean;
|
---|
436 | begin
|
---|
437 | result := M[1,1]*M[2,2]-M[1,2]*M[2,1] <> 0;
|
---|
438 | end;
|
---|
439 |
|
---|
440 | function IsAffineMatrixTranslation(M: TAffineMatrix): boolean;
|
---|
441 | begin
|
---|
442 | result := (m[1,1]=1) and (m[1,2]=0) and (m[2,1] = 0) and (m[2,2]=1);
|
---|
443 | end;
|
---|
444 |
|
---|
445 | function IsAffineMatrixScale(M: TAffineMatrix): boolean;
|
---|
446 | begin
|
---|
447 | result := (M[1,3]=0) and (M[2,3]=0) and
|
---|
448 | (M[1,2]=0) and (M[2,1]=0);
|
---|
449 | end;
|
---|
450 |
|
---|
451 | function IsAffineMatrixIdentity(M: TAffineMatrix): boolean;
|
---|
452 | begin
|
---|
453 | result := IsAffineMatrixTranslation(M) and (M[1,3]=0) and (M[2,3]=0);
|
---|
454 | end;
|
---|
455 |
|
---|
456 | function AffineMatrixInverse(M: TAffineMatrix): TAffineMatrix;
|
---|
457 | var det,f: single;
|
---|
458 | linearInverse: TAffineMatrix;
|
---|
459 | begin
|
---|
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]);
|
---|
467 | end;
|
---|
468 |
|
---|
469 | function AffineMatrixTranslation(OfsX, OfsY: Single): TAffineMatrix;
|
---|
470 | begin
|
---|
471 | result := AffineMatrix(1, 0, OfsX,
|
---|
472 | 0, 1, OfsY);
|
---|
473 | end;
|
---|
474 |
|
---|
475 | function AffineMatrixScale(sx, sy: single): TAffineMatrix;
|
---|
476 | begin
|
---|
477 | result := AffineMatrix(sx, 0, 0,
|
---|
478 | 0, sy, 0);
|
---|
479 | end;
|
---|
480 |
|
---|
481 | function AffineMatrixScaledRotation(ASourceVector, ATargetVector: TPointF): TAffineMatrix;
|
---|
482 | var
|
---|
483 | prevScale, newScale, scale: Single;
|
---|
484 | u1,v1,u2,v2,w: TPointF;
|
---|
485 | begin
|
---|
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;
|
---|
500 | end;
|
---|
501 |
|
---|
502 | function AffineMatrixScaledRotation(ASourcePoint, ATargetPoint, AOrigin: TPointF): TAffineMatrix;
|
---|
503 | begin
|
---|
504 | result := AffineMatrixTranslation(AOrigin.x,AOrigin.y)*
|
---|
505 | AffineMatrixScaledRotation(ASourcePoint-AOrigin, ATargetPoint-AOrigin)*
|
---|
506 | AffineMatrixTranslation(-AOrigin.x,-AOrigin.y);
|
---|
507 | end;
|
---|
508 |
|
---|
509 | function AffineMatrixSkewXDeg(AngleCW: single): TAffineMatrix;
|
---|
510 | begin
|
---|
511 | result := AffineMatrix(1,tan(AngleCW*Pi/180),0,
|
---|
512 | 0, 1, 0);
|
---|
513 | end;
|
---|
514 |
|
---|
515 | function AffineMatrixSkewYDeg(AngleCW: single): TAffineMatrix;
|
---|
516 | begin
|
---|
517 | result := AffineMatrix(1, 0, 0,
|
---|
518 | tan(AngleCW*Pi/180), 1, 0)
|
---|
519 | end;
|
---|
520 |
|
---|
521 | function AffineMatrixSkewXRad(AngleCCW: single): TAffineMatrix;
|
---|
522 | begin
|
---|
523 |
|
---|
524 | result := AffineMatrix(1,tan(-AngleCCW),0,
|
---|
525 | 0, 1, 0);
|
---|
526 | end;
|
---|
527 |
|
---|
528 | function AffineMatrixSkewYRad(AngleCCW: single): TAffineMatrix;
|
---|
529 | begin
|
---|
530 | result := AffineMatrix(1, 0, 0,
|
---|
531 | tan(-angleCCW), 1, 0)
|
---|
532 | end;
|
---|
533 |
|
---|
534 | function AffineMatrixLinear(v1,v2: TPointF): TAffineMatrix;
|
---|
535 | begin
|
---|
536 | result := AffineMatrix(v1.x, v2.x, 0,
|
---|
537 | v1.y, v2.y, 0);
|
---|
538 | end;
|
---|
539 |
|
---|
540 | function AffineMatrixLinear(const AMatrix: TAffineMatrix): TAffineMatrix;
|
---|
541 | begin
|
---|
542 | result := AffineMatrix(AMatrix[1,1],AMatrix[1,2],0,
|
---|
543 | AMatrix[2,1],AMatrix[2,2],0);
|
---|
544 | end;
|
---|
545 |
|
---|
546 | function AffineMatrixRotationRad(AngleCCW: Single): TAffineMatrix;
|
---|
547 | begin
|
---|
548 | result := AffineMatrix(cos(AngleCCW), sin(AngleCCW), 0,
|
---|
549 | -sin(AngleCCW), cos(AngleCCW), 0);
|
---|
550 | end;
|
---|
551 |
|
---|
552 | function AffineMatrixRotationDeg(AngleCW: Single): TAffineMatrix;
|
---|
553 | const DegToRad = -Pi/180;
|
---|
554 | begin
|
---|
555 | result := AffineMatrixRotationRad(AngleCW*DegToRad);
|
---|
556 | end;
|
---|
557 |
|
---|
558 | function AffineMatrixIdentity: TAffineMatrix;
|
---|
559 | begin
|
---|
560 | result := AffineMatrix(1, 0, 0,
|
---|
561 | 0, 1, 0);
|
---|
562 | end;
|
---|
563 |
|
---|
564 | function IsAffineMatrixOrthogonal(M: TAffineMatrix): boolean;
|
---|
565 | begin
|
---|
566 | result := PointF(M[1,1],M[2,1])*PointF(M[1,2],M[2,2]) = 0;
|
---|
567 | end;
|
---|
568 |
|
---|
569 | function IsAffineMatrixScaledRotation(M: TAffineMatrix): boolean;
|
---|
570 | begin
|
---|
571 | result := IsAffineMatrixOrthogonal(M) and
|
---|
572 | (VectLen(PointF(M[1,1],M[2,1]))=VectLen(PointF(M[1,2],M[2,2])));
|
---|
573 | end;
|
---|
574 |
|
---|
575 | { TBGRAVerticalCylinderDeformationScanner }
|
---|
576 |
|
---|
577 | constructor TBGRAVerticalCylinderDeformationScanner.Create(
|
---|
578 | AScanner: IBGRAScanner; ACenterX: single; ARadiusX: single);
|
---|
579 | begin
|
---|
580 | FScanner := AScanner;
|
---|
581 | FScanAtFunc := @FScanner.ScanAt;
|
---|
582 | FCenterX := ACenterX;
|
---|
583 | FRadiusX := ARadiusX;
|
---|
584 | end;
|
---|
585 |
|
---|
586 | function TBGRAVerticalCylinderDeformationScanner.ScanAt(X, Y: Single): TBGRAPixel;
|
---|
587 | var
|
---|
588 | xn,len,fact: Single;
|
---|
589 | begin
|
---|
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;
|
---|
603 | end;
|
---|
604 |
|
---|
605 | { TBGRASphereDeformationScanner }
|
---|
606 |
|
---|
607 | constructor TBGRASphereDeformationScanner.Create(AScanner: IBGRAScanner;
|
---|
608 | ACenter: TPointF; ARadiusX, ARadiusY: single);
|
---|
609 | begin
|
---|
610 | FScanner := AScanner;
|
---|
611 | FScanAtFunc := @FScanner.ScanAt;
|
---|
612 | FCenter := ACenter;
|
---|
613 | FRadiusX := ARadiusX;
|
---|
614 | FRadiusY := ARadiusY;
|
---|
615 | end;
|
---|
616 |
|
---|
617 | function TBGRASphereDeformationScanner.ScanAt(X, Y: Single): TBGRAPixel;
|
---|
618 | var
|
---|
619 | xn, yn, len,fact: Single;
|
---|
620 | begin
|
---|
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;
|
---|
636 | end;
|
---|
637 |
|
---|
638 | { TBGRAExtendedBorderScanner }
|
---|
639 |
|
---|
640 | constructor TBGRAExtendedBorderScanner.Create(ASource: IBGRAScanner;
|
---|
641 | ABounds: TRect);
|
---|
642 | begin
|
---|
643 | FSource := ASource;
|
---|
644 | FBounds := ABounds;
|
---|
645 | end;
|
---|
646 |
|
---|
647 | function TBGRAExtendedBorderScanner.ScanAt(X, Y: Single): TBGRAPixel;
|
---|
648 | begin
|
---|
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);
|
---|
654 | end;
|
---|
655 |
|
---|
656 | { TBGRAScannerOffset }
|
---|
657 |
|
---|
658 | constructor TBGRAScannerOffset.Create(ASource: IBGRAScanner; AOffset: TPoint);
|
---|
659 | begin
|
---|
660 | FSource := ASource;
|
---|
661 | FOffset := AOffset;
|
---|
662 | end;
|
---|
663 |
|
---|
664 | destructor TBGRAScannerOffset.Destroy;
|
---|
665 | begin
|
---|
666 | fillchar(FSource,sizeof(FSource),0);
|
---|
667 | inherited Destroy;
|
---|
668 | end;
|
---|
669 |
|
---|
670 | procedure TBGRAScannerOffset.ScanMoveTo(X, Y: Integer);
|
---|
671 | begin
|
---|
672 | FSource.ScanMoveTo(X-FOffset.X,Y-FOffset.Y);
|
---|
673 | end;
|
---|
674 |
|
---|
675 | function TBGRAScannerOffset.ScanNextPixel: TBGRAPixel;
|
---|
676 | begin
|
---|
677 | Result:=FSource.ScanNextPixel;
|
---|
678 | end;
|
---|
679 |
|
---|
680 | function TBGRAScannerOffset.ScanAt(X, Y: Single): TBGRAPixel;
|
---|
681 | begin
|
---|
682 | Result:=FSource.ScanAt(X - FOffset.X, Y - FOffset.Y);
|
---|
683 | end;
|
---|
684 |
|
---|
685 | function TBGRAScannerOffset.IsScanPutPixelsDefined: boolean;
|
---|
686 | begin
|
---|
687 | Result:=FSource.IsScanPutPixelsDefined;
|
---|
688 | end;
|
---|
689 |
|
---|
690 | procedure TBGRAScannerOffset.ScanPutPixels(pdest: PBGRAPixel; count: integer;
|
---|
691 | mode: TDrawMode);
|
---|
692 | begin
|
---|
693 | FSource.ScanPutPixels(pdest, count, mode);
|
---|
694 | end;
|
---|
695 |
|
---|
696 | { TBGRABitmapScanner }
|
---|
697 |
|
---|
698 | constructor TBGRABitmapScanner.Create(ASource: TBGRACustomBitmap; ARepeatX,
|
---|
699 | ARepeatY: boolean; AOrigin: TPoint);
|
---|
700 | begin
|
---|
701 | FSource := ASource;
|
---|
702 | FRepeatX := ARepeatX;
|
---|
703 | FRepeatY := ARepeatY;
|
---|
704 | FScanline := nil;
|
---|
705 | FOrigin := AOrigin;
|
---|
706 | end;
|
---|
707 |
|
---|
708 | procedure TBGRABitmapScanner.ScanMoveTo(X, Y: Integer);
|
---|
709 | begin
|
---|
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;
|
---|
733 | end;
|
---|
734 |
|
---|
735 | function TBGRABitmapScanner.ScanNextPixel: TBGRAPixel;
|
---|
736 | begin
|
---|
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;
|
---|
760 | end;
|
---|
761 |
|
---|
762 | function TBGRABitmapScanner.ScanAt(X, Y: Single): TBGRAPixel;
|
---|
763 | begin
|
---|
764 | Result := FSource.GetPixelCycle(X+FOrigin.X,Y+FOrigin.Y,rfLinear,FRepeatX,FRepeatY);
|
---|
765 | end;
|
---|
766 |
|
---|
767 | { TBGRATriangleLinearMapping }
|
---|
768 |
|
---|
769 | constructor TBGRATriangleLinearMapping.Create(AScanner: IBGRAScanner; pt1, pt2,
|
---|
770 | pt3: TPointF; tex1, tex2, tex3: TPointF);
|
---|
771 | begin
|
---|
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];
|
---|
786 | end;
|
---|
787 |
|
---|
788 | procedure TBGRATriangleLinearMapping.ScanMoveTo(X, Y: Integer);
|
---|
789 | begin
|
---|
790 | ScanMoveToF(X, Y);
|
---|
791 | end;
|
---|
792 |
|
---|
793 | procedure TBGRATriangleLinearMapping.ScanMoveToF(X, Y: Single);
|
---|
794 | var
|
---|
795 | Cur: TPointF;
|
---|
796 | begin
|
---|
797 | Cur := FMatrix*PointF(X,Y);
|
---|
798 | FCurTexCoord := FTexCoord1+FDiff2*Cur.X+FDiff3*Cur.Y;
|
---|
799 | end;
|
---|
800 |
|
---|
801 | function TBGRATriangleLinearMapping.ScanAt(X, Y: Single): TBGRAPixel;
|
---|
802 | begin
|
---|
803 | ScanMoveToF(X,Y);
|
---|
804 | result := ScanNextPixel;
|
---|
805 | end;
|
---|
806 |
|
---|
807 | function TBGRATriangleLinearMapping.ScanNextPixel: TBGRAPixel;
|
---|
808 | begin
|
---|
809 | result := FScanAtFunc(FCurTexCoord.X,FCurTexCoord.Y);
|
---|
810 | FCurTexCoord += FStep;
|
---|
811 | end;
|
---|
812 |
|
---|
813 | { TBGRAAffineScannerTransform }
|
---|
814 |
|
---|
815 | constructor TBGRAAffineScannerTransform.Create(AScanner: IBGRAScanner);
|
---|
816 | begin
|
---|
817 | FScanner := AScanner;
|
---|
818 | FScanAtFunc := @FScanner.ScanAt;
|
---|
819 | GlobalOpacity := 255;
|
---|
820 | Reset;
|
---|
821 | end;
|
---|
822 |
|
---|
823 | procedure TBGRAAffineScannerTransform.Reset;
|
---|
824 | begin
|
---|
825 | FMatrix := AffineMatrixIdentity;
|
---|
826 | FEmptyMatrix := False;
|
---|
827 | end;
|
---|
828 |
|
---|
829 | procedure TBGRAAffineScannerTransform.Invert;
|
---|
830 | begin
|
---|
831 | if not FEmptyMatrix and IsAffineMatrixInversible(FMatrix) then
|
---|
832 | FMatrix := AffineMatrixInverse(FMatrix) else
|
---|
833 | FEmptyMatrix := True;
|
---|
834 | end;
|
---|
835 |
|
---|
836 | function TBGRAAffineScannerTransform.GetViewMatrix: TAffineMatrix;
|
---|
837 | begin
|
---|
838 | if FEmptyMatrix then
|
---|
839 | result := AffineMatrixIdentity
|
---|
840 | else
|
---|
841 | result := AffineMatrixInverse(FMatrix);
|
---|
842 | end;
|
---|
843 |
|
---|
844 | procedure TBGRAAffineScannerTransform.SetViewMatrix(AValue: TAffineMatrix);
|
---|
845 | begin
|
---|
846 | Matrix := AValue;
|
---|
847 | Invert;
|
---|
848 | end;
|
---|
849 |
|
---|
850 | procedure TBGRAAffineScannerTransform.SetMatrix(AMatrix: TAffineMatrix);
|
---|
851 | begin
|
---|
852 | FEmptyMatrix := False;
|
---|
853 | FMatrix := AMatrix;
|
---|
854 | end;
|
---|
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
|
---|
859 | procedure TBGRAAffineScannerTransform.Translate(OfsX, OfsY: Single);
|
---|
860 | begin
|
---|
861 | MultiplyBy(AffineMatrixTranslation(-OfsX,-OfsY));
|
---|
862 | end;
|
---|
863 |
|
---|
864 | procedure TBGRAAffineScannerTransform.RotateDeg(AngleCW: Single);
|
---|
865 | begin
|
---|
866 | MultiplyBy(AffineMatrixRotationDeg(-AngleCW));
|
---|
867 | end;
|
---|
868 |
|
---|
869 | procedure TBGRAAffineScannerTransform.RotateRad(AngleCCW: Single);
|
---|
870 | begin
|
---|
871 | MultiplyBy(AffineMatrixRotationRad(-AngleCCW));
|
---|
872 | end;
|
---|
873 |
|
---|
874 | procedure TBGRAAffineScannerTransform.MultiplyBy(AMatrix: TAffineMatrix);
|
---|
875 | begin
|
---|
876 | FMatrix *= AMatrix;
|
---|
877 | end;
|
---|
878 |
|
---|
879 | procedure TBGRAAffineScannerTransform.Fit(Origin, HAxis, VAxis: TPointF);
|
---|
880 | begin
|
---|
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);
|
---|
885 | end;
|
---|
886 |
|
---|
887 | procedure TBGRAAffineScannerTransform.Scale(sx, sy: single);
|
---|
888 | begin
|
---|
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));
|
---|
896 | end;
|
---|
897 |
|
---|
898 | procedure TBGRAAffineScannerTransform.Scale(factor: single);
|
---|
899 | begin
|
---|
900 | Scale(factor,factor);
|
---|
901 | end;
|
---|
902 |
|
---|
903 | procedure TBGRAAffineScannerTransform.ScanMoveTo(X, Y: Integer);
|
---|
904 | begin
|
---|
905 | ScanMoveToF(X,Y);
|
---|
906 | end;
|
---|
907 |
|
---|
908 | procedure TBGRAAffineScannerTransform.ScanMoveToF(X, Y: single);
|
---|
909 | Var Cur: TPointF;
|
---|
910 | begin
|
---|
911 | Cur := FMatrix * PointF(X,Y);
|
---|
912 | FCurX := Cur.X;
|
---|
913 | FCurY := Cur.Y;
|
---|
914 | end;
|
---|
915 |
|
---|
916 | function TBGRAAffineScannerTransform.InternalScanCurrentPixel: TBGRAPixel;
|
---|
917 | begin
|
---|
918 | if FEmptyMatrix then
|
---|
919 | begin
|
---|
920 | result := BGRAPixelTransparent;
|
---|
921 | exit;
|
---|
922 | end;
|
---|
923 | result := FScanAtFunc(FCurX,FCurY);
|
---|
924 | end;
|
---|
925 |
|
---|
926 | function TBGRAAffineScannerTransform.ScanNextPixel: TBGRAPixel;
|
---|
927 | begin
|
---|
928 | result := InternalScanCurrentPixel;
|
---|
929 | FCurX += FMatrix[1,1];
|
---|
930 | FCurY += FMatrix[2,1];
|
---|
931 | if GlobalOpacity <> 255 then result.alpha := ApplyOpacity(result.alpha,GlobalOpacity);
|
---|
932 | end;
|
---|
933 |
|
---|
934 | function TBGRAAffineScannerTransform.ScanAt(X, Y: Single): TBGRAPixel;
|
---|
935 | begin
|
---|
936 | ScanMoveToF(X,Y);
|
---|
937 | result := InternalScanCurrentPixel;
|
---|
938 | if GlobalOpacity <> 255 then result.alpha := ApplyOpacity(result.alpha,GlobalOpacity);
|
---|
939 | end;
|
---|
940 |
|
---|
941 | { TBGRAQuadLinearScanner }
|
---|
942 |
|
---|
943 | function TBGRAQuadLinearScanner.GetTexColorAt(u, v: Single; detNeg: boolean
|
---|
944 | ): TBGRAPixel;
|
---|
945 | begin
|
---|
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));
|
---|
964 | end;
|
---|
965 |
|
---|
966 | procedure TBGRAQuadLinearScanner.ScanMoveToF(X, Y: single);
|
---|
967 | begin
|
---|
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;
|
---|
982 | end;
|
---|
983 |
|
---|
984 | procedure TBGRAQuadLinearScanner.SetCulling(AValue: TFaceCulling);
|
---|
985 | begin
|
---|
986 | FShowC1 := AValue in [fcKeepCW,fcNone];
|
---|
987 | FShowC2 := AValue in [fcKeepCCW,fcNone];
|
---|
988 | end;
|
---|
989 |
|
---|
990 | procedure TBGRAQuadLinearScanner.Init(ASource: IBGRAScanner;
|
---|
991 | const APoints: array of TPointF; ATextureInterpolation: boolean);
|
---|
992 | var
|
---|
993 | i: NativeInt;
|
---|
994 | v: TPointF;
|
---|
995 | len: single;
|
---|
996 | begin
|
---|
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);
|
---|
1032 | end;
|
---|
1033 |
|
---|
1034 | function TBGRAQuadLinearScanner.ScanAt(X, Y: Single): TBGRAPixel;
|
---|
1035 | begin
|
---|
1036 | ScanMoveToF(X,Y);
|
---|
1037 | Result:= FScanFunc();
|
---|
1038 | end;
|
---|
1039 |
|
---|
1040 | procedure TBGRAQuadLinearScanner.ScanPutPixels(pdest: PBGRAPixel; count: integer;
|
---|
1041 | mode: TDrawMode);
|
---|
1042 | var
|
---|
1043 | n: NativeInt;
|
---|
1044 | p: PBGRAPixel;
|
---|
1045 | begin
|
---|
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);
|
---|
1063 | end;
|
---|
1064 |
|
---|
1065 | function TBGRAQuadLinearScanner.IsScanPutPixelsDefined: boolean;
|
---|
1066 | begin
|
---|
1067 | result := true;
|
---|
1068 | end;
|
---|
1069 |
|
---|
1070 | procedure TBGRAQuadLinearScanner.ScanMoveTo(X, Y: Integer);
|
---|
1071 | begin
|
---|
1072 | ScanMoveToF(X,Y);
|
---|
1073 | end;
|
---|
1074 |
|
---|
1075 | function TBGRAQuadLinearScanner.ScanNextPixel: TBGRAPixel;
|
---|
1076 | begin
|
---|
1077 | Result:= FScanFunc();
|
---|
1078 | end;
|
---|
1079 |
|
---|
1080 | function TBGRAQuadLinearScanner.ScanGeneral: TBGRAPixel;
|
---|
1081 | var 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 |
|
---|
1100 | begin
|
---|
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;
|
---|
1256 | end;
|
---|
1257 |
|
---|
1258 | function TBGRAQuadLinearScanner.GetCulling: TFaceCulling;
|
---|
1259 | begin
|
---|
1260 | if FShowC1 and FShowC2 then
|
---|
1261 | result := fcNone
|
---|
1262 | else if FShowC1 then
|
---|
1263 | result := fcKeepCW
|
---|
1264 | else
|
---|
1265 | result := fcKeepCCW;
|
---|
1266 | end;
|
---|
1267 |
|
---|
1268 | procedure TBGRAQuadLinearScanner.PrepareScanVert0;
|
---|
1269 | begin
|
---|
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;
|
---|
1282 | end;
|
---|
1283 |
|
---|
1284 | function TBGRAQuadLinearScanner.ScanVert0: TBGRAPixel;
|
---|
1285 | var u: single;
|
---|
1286 | begin
|
---|
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;
|
---|
1310 | end;
|
---|
1311 |
|
---|
1312 | procedure TBGRAQuadLinearScanner.PrepareScanPara;
|
---|
1313 | begin
|
---|
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;
|
---|
1320 | end;
|
---|
1321 |
|
---|
1322 | function TBGRAQuadLinearScanner.ScanPara: TBGRAPixel;
|
---|
1323 | var
|
---|
1324 | u,v,denom: Single;
|
---|
1325 | begin
|
---|
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;
|
---|
1356 | end;
|
---|
1357 |
|
---|
1358 | constructor TBGRAQuadLinearScanner.Create(ASource: IBGRAScanner;
|
---|
1359 | ASourceMatrix: TAffineMatrix; const APoints: array of TPointF;
|
---|
1360 | ATextureInterpolation: boolean);
|
---|
1361 | begin
|
---|
1362 | Init(ASource, APoints, ATextureInterpolation);
|
---|
1363 | FSourceMatrix := ASourceMatrix;
|
---|
1364 | end;
|
---|
1365 |
|
---|
1366 | constructor TBGRAQuadLinearScanner.Create(ASource: IBGRAScanner;
|
---|
1367 | const ATexCoords: array of TPointF; const APoints: array of TPointF;
|
---|
1368 | ATextureInterpolation: boolean);
|
---|
1369 | begin
|
---|
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]);
|
---|
1374 | end;
|
---|
1375 |
|
---|
1376 | destructor TBGRAQuadLinearScanner.Destroy;
|
---|
1377 | begin
|
---|
1378 | freemem(FBuffer);
|
---|
1379 | inherited Destroy;
|
---|
1380 | end;
|
---|
1381 |
|
---|
1382 | { TBGRAAffineBitmapTransform }
|
---|
1383 |
|
---|
1384 | procedure TBGRAAffineBitmapTransform.Init(ABitmap: TBGRACustomBitmap;
|
---|
1385 | ARepeatImageX: Boolean; ARepeatImageY: Boolean;
|
---|
1386 | AResampleFilter: TResampleFilter; AIncludeEdges: boolean = false);
|
---|
1387 | begin
|
---|
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;
|
---|
1398 | end;
|
---|
1399 |
|
---|
1400 | constructor TBGRAAffineBitmapTransform.Create(ABitmap: TBGRACustomBitmap;
|
---|
1401 | ARepeatImage: Boolean; AResampleFilter: TResampleFilter = rfLinear; AIncludeEdges: boolean = false);
|
---|
1402 | begin
|
---|
1403 | Init(ABitmap,ARepeatImage,ARepeatImage,AResampleFilter,AIncludeEdges);
|
---|
1404 | end;
|
---|
1405 |
|
---|
1406 | constructor TBGRAAffineBitmapTransform.Create(ABitmap: TBGRACustomBitmap;
|
---|
1407 | ARepeatImageX: Boolean; ARepeatImageY: Boolean;
|
---|
1408 | AResampleFilter: TResampleFilter; AIncludeEdges: boolean = false);
|
---|
1409 | begin
|
---|
1410 | Init(ABitmap,ARepeatImageX,ARepeatImageY,AResampleFilter,AIncludeEdges);
|
---|
1411 | end;
|
---|
1412 |
|
---|
1413 | destructor TBGRAAffineBitmapTransform.Destroy;
|
---|
1414 | begin
|
---|
1415 | FreeMem(FBuffer);
|
---|
1416 | end;
|
---|
1417 |
|
---|
1418 | function TBGRAAffineBitmapTransform.InternalScanCurrentPixel: TBGRAPixel;
|
---|
1419 | begin
|
---|
1420 | result := FBitmap.GetPixelCycle(FCurX,FCurY,FResampleFilter,FRepeatImageX,FRepeatImageY);
|
---|
1421 | end;
|
---|
1422 |
|
---|
1423 | procedure TBGRAAffineBitmapTransform.ScanPutPixels(pdest: PBGRAPixel;
|
---|
1424 | count: integer; mode: TDrawMode);
|
---|
1425 | const PrecisionShift = {$IFDEF CPU64}24{$ELSE}12{$ENDIF};
|
---|
1426 | Precision = 1 shl PrecisionShift;
|
---|
1427 | var 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;
|
---|
1434 | begin
|
---|
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);
|
---|
1559 | end;
|
---|
1560 |
|
---|
1561 | function TBGRAAffineBitmapTransform.IsScanPutPixelsDefined: boolean;
|
---|
1562 | begin
|
---|
1563 | Result:=true;
|
---|
1564 | end;
|
---|
1565 |
|
---|
1566 | procedure TBGRAAffineBitmapTransform.Fit(Origin, HAxis, VAxis: TPointF);
|
---|
1567 | begin
|
---|
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;
|
---|
1577 | end;
|
---|
1578 |
|
---|
1579 | { TBGRAPerspectiveScannerTransform }
|
---|
1580 |
|
---|
1581 | function TBGRAPerspectiveScannerTransform.GetIncludeOppositePlane: boolean;
|
---|
1582 | begin
|
---|
1583 | if FMatrix = nil then
|
---|
1584 | result := false
|
---|
1585 | else
|
---|
1586 | result := FMatrix.IncludeOppositePlane;
|
---|
1587 | end;
|
---|
1588 |
|
---|
1589 | procedure TBGRAPerspectiveScannerTransform.SetIncludeOppositePlane(
|
---|
1590 | AValue: boolean);
|
---|
1591 | begin
|
---|
1592 | if FMatrix <> nil then
|
---|
1593 | FMatrix.IncludeOppositePlane := AValue;
|
---|
1594 | end;
|
---|
1595 |
|
---|
1596 | constructor TBGRAPerspectiveScannerTransform.Create(texture: IBGRAScanner; texCoord1,texCoord2: TPointF; const quad: array of TPointF);
|
---|
1597 | begin
|
---|
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;
|
---|
1607 | end;
|
---|
1608 |
|
---|
1609 | constructor TBGRAPerspectiveScannerTransform.Create(texture: IBGRAScanner;
|
---|
1610 | const texCoordsQuad: array of TPointF; const quad: array of TPointF);
|
---|
1611 | begin
|
---|
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;
|
---|
1622 | end;
|
---|
1623 |
|
---|
1624 | destructor TBGRAPerspectiveScannerTransform.Destroy;
|
---|
1625 | begin
|
---|
1626 | FMatrix.free;
|
---|
1627 | inherited Destroy;
|
---|
1628 | end;
|
---|
1629 |
|
---|
1630 | procedure TBGRAPerspectiveScannerTransform.ScanMoveTo(X, Y: Integer);
|
---|
1631 | begin
|
---|
1632 | if FMatrix = nil then exit;
|
---|
1633 | FMatrix.ScanMoveTo(X,Y);
|
---|
1634 | end;
|
---|
1635 |
|
---|
1636 | function TBGRAPerspectiveScannerTransform.ScanAt(X, Y: Single): TBGRAPixel;
|
---|
1637 | var ptSource: TPointF;
|
---|
1638 | begin
|
---|
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;
|
---|
1648 | end;
|
---|
1649 |
|
---|
1650 | function TBGRAPerspectiveScannerTransform.ScanNextPixel: TBGRAPixel;
|
---|
1651 | var ptSource: TPointF;
|
---|
1652 | begin
|
---|
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;
|
---|
1662 | end;
|
---|
1663 |
|
---|
1664 | { TPerspectiveTransform }
|
---|
1665 |
|
---|
1666 | procedure TPerspectiveTransform.Init;
|
---|
1667 | begin
|
---|
1668 | FOutsideValue := PointF(0,0);
|
---|
1669 | FIncludeOppositePlane:= True;
|
---|
1670 | end;
|
---|
1671 |
|
---|
1672 | constructor TPerspectiveTransform.Create;
|
---|
1673 | begin
|
---|
1674 | Init;
|
---|
1675 | AssignIdentity;
|
---|
1676 | end;
|
---|
1677 |
|
---|
1678 | constructor TPerspectiveTransform.Create(x1, y1, x2, y2: single;
|
---|
1679 | const quad: array of TPointF);
|
---|
1680 | begin
|
---|
1681 | Init;
|
---|
1682 | MapRectToQuad(x1 ,y1 ,x2 ,y2 ,quad );
|
---|
1683 | end;
|
---|
1684 |
|
---|
1685 | constructor TPerspectiveTransform.Create(const quad: array of TPointF; x1, y1,
|
---|
1686 | x2, y2: single);
|
---|
1687 | begin
|
---|
1688 | Init;
|
---|
1689 | MapQuadToRect(quad, x1,y1,x2,y2);
|
---|
1690 | end;
|
---|
1691 |
|
---|
1692 | constructor TPerspectiveTransform.Create(const srcQuad,
|
---|
1693 | destQuad: array of TPointF);
|
---|
1694 | begin
|
---|
1695 | Init;
|
---|
1696 | MapQuadToQuad(srcQuad,destQuad);
|
---|
1697 | end;
|
---|
1698 |
|
---|
1699 | { Map a quad to quad. First compute quad to square, and then square to quad. }
|
---|
1700 | function TPerspectiveTransform.MapQuadToQuad(const srcQuad,
|
---|
1701 | destQuad: array of TPointF): boolean;
|
---|
1702 | var
|
---|
1703 | p : TPerspectiveTransform;
|
---|
1704 | begin
|
---|
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;
|
---|
1723 | end;
|
---|
1724 |
|
---|
1725 | //Map a rectangle to a quad. Make a polygon for the rectangle, and map it.
|
---|
1726 | function TPerspectiveTransform.MapRectToQuad(x1, y1, x2, y2: single;
|
---|
1727 | const quad: array of TPointF): boolean;
|
---|
1728 | begin
|
---|
1729 | result := MapQuadToQuad([PointF(x1,y1),PointF(x2,y1),PointF(x2,y2),PointF(x1,y2)], quad);
|
---|
1730 | end;
|
---|
1731 |
|
---|
1732 | //Map a quad to a rectangle. Make a polygon for the rectangle, and map the quad into it.
|
---|
1733 | function TPerspectiveTransform.MapQuadToRect(const quad: array of TPointF; x1,
|
---|
1734 | y1, x2, y2: single): boolean;
|
---|
1735 | begin
|
---|
1736 | result := MapQuadToQuad(quad, [PointF(x1,y1),PointF(x2,y1),PointF(x2,y2),PointF(x1,y2)]);
|
---|
1737 | end;
|
---|
1738 |
|
---|
1739 | //Map a square to a quad
|
---|
1740 | function TPerspectiveTransform.MapSquareToQuad(const quad: array of TPointF): boolean;
|
---|
1741 | var
|
---|
1742 | d,d1,d2: TPointF;
|
---|
1743 | den ,u ,v : double;
|
---|
1744 |
|
---|
1745 | begin
|
---|
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 |
|
---|
1803 | end;
|
---|
1804 |
|
---|
1805 | //Map a quad to a square. Compute mapping from square to quad, then invert.
|
---|
1806 | function TPerspectiveTransform.MapQuadToSquare(const quad: array of TPointF): boolean;
|
---|
1807 | begin
|
---|
1808 | if not MapSquareToQuad(quad ) then
|
---|
1809 | result:=false
|
---|
1810 | else
|
---|
1811 | result := Invert;
|
---|
1812 | end;
|
---|
1813 |
|
---|
1814 | procedure TPerspectiveTransform.AssignIdentity;
|
---|
1815 | begin
|
---|
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;
|
---|
1825 | end;
|
---|
1826 |
|
---|
1827 | function TPerspectiveTransform.Invert: boolean;
|
---|
1828 | var
|
---|
1829 | d0, d1, d2, d : double;
|
---|
1830 | copy : TPerspectiveTransform;
|
---|
1831 |
|
---|
1832 | begin
|
---|
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;
|
---|
1870 | end;
|
---|
1871 |
|
---|
1872 | procedure TPerspectiveTransform.Translate(dx, dy: single);
|
---|
1873 | begin
|
---|
1874 | tx:=tx + dx;
|
---|
1875 | ty:=ty + dy;
|
---|
1876 | end;
|
---|
1877 |
|
---|
1878 | procedure TPerspectiveTransform.MultiplyBy(a: TPerspectiveTransform);
|
---|
1879 | var b: TPerspectiveTransform;
|
---|
1880 | begin
|
---|
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;
|
---|
1892 | end;
|
---|
1893 |
|
---|
1894 | procedure TPerspectiveTransform.PremultiplyBy(b: TPerspectiveTransform);
|
---|
1895 | var
|
---|
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;
|
---|
1909 | end;
|
---|
1910 |
|
---|
1911 | function TPerspectiveTransform.Duplicate: TPerspectiveTransform;
|
---|
1912 | begin
|
---|
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;
|
---|
1923 | end;
|
---|
1924 |
|
---|
1925 | function TPerspectiveTransform.Apply(pt: TPointF): TPointF;
|
---|
1926 | var
|
---|
1927 | m : single;
|
---|
1928 | begin
|
---|
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;
|
---|
1938 | end;
|
---|
1939 |
|
---|
1940 | procedure TPerspectiveTransform.ScanMoveTo(x, y: single);
|
---|
1941 | begin
|
---|
1942 | ScanDenom := x * w0 + y * w1 + w2;
|
---|
1943 | ScanNumX := x * sx + y * shx + tx;
|
---|
1944 | scanNumY := x * shy + y * sy + ty;
|
---|
1945 | end;
|
---|
1946 |
|
---|
1947 | function TPerspectiveTransform.ScanNext: TPointF;
|
---|
1948 | var m: single;
|
---|
1949 | begin
|
---|
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;
|
---|
1961 | end;
|
---|
1962 |
|
---|
1963 | { TBGRATwirlScanner }
|
---|
1964 |
|
---|
1965 | constructor TBGRATwirlScanner.Create(AScanner: IBGRAScanner; ACenter: TPoint; ARadius: single; ATurn: single = 1; AExponent: single = 3);
|
---|
1966 | begin
|
---|
1967 | FScanner := AScanner;
|
---|
1968 | FScanAtFunc := @FScanner.ScanAt;
|
---|
1969 | FCenter := ACenter;
|
---|
1970 | FTurn := ATurn;
|
---|
1971 | FRadius := ARadius;
|
---|
1972 | FExponent := AExponent;
|
---|
1973 | end;
|
---|
1974 |
|
---|
1975 | function TBGRATwirlScanner.ScanAt(X, Y: Single): TBGRAPixel;
|
---|
1976 | var p: TPoint;
|
---|
1977 | d: single;
|
---|
1978 | a,cosa,sina: integer;
|
---|
1979 | begin
|
---|
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);
|
---|
1997 | end;
|
---|
1998 |
|
---|
1999 | end.
|
---|
2000 |
|
---|