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