📄 gr32_transforms.pas
字号:
unit GR32_Transforms;
(* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Graphics32
*
* The Initial Developer of the Original Code is
* Alex A. Denisov
*
* Portions created by the Initial Developer are Copyright (C) 2000-2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Andre Beckedorf <Andre@metaException.de>
* Mattias Andersson <Mattias@Centaurix.com>
* J. Tulach <tulach@position.cz>
* Michael Hansen <dyster_tid@hotmail.com>
* Peter Larson
*
* ***** END LICENSE BLOCK ***** *)
interface
{$I GR32.inc}
uses
{$IFDEF CLX}
Qt, Types, {$IFDEF LINUX}Libc, {$ENDIF}
{$ELSE}
Windows,
{$ENDIF}
SysUtils, Classes, GR32, GR32_Blend, GR32_VectorMaps, GR32_Rasterizers;
type
ETransformError = class(Exception);
ETransformNotImplemented = class(Exception);
type
TFloatMatrix = array[0..2, 0..2] of TFloat; // 3x3 TFloat precision
TFixedMatrix = array[0..2, 0..2] of TFixed; // 3x3 fixed precision
const
IdentityMatrix: TFloatMatrix = (
(1, 0, 0),
(0, 1, 0),
(0, 0, 1));
type
TVector3f = array[0..2] of TFloat;
TVector3i = array[0..2] of Integer;
// Matrix conversion routines
function FixedMatrix(const FloatMatrix: TFloatMatrix): TFixedMatrix; overload;
function FloatMatrix(const FixedMatrix: TFixedMatrix): TFloatMatrix; overload;
procedure Adjoint(var M: TFloatMatrix);
function Determinant(const M: TFloatMatrix): TFloat;
procedure Scale(var M: TFloatMatrix; Factor: TFloat);
procedure Invert(var M: TFloatMatrix);
function Mult(const M1, M2: TFloatMatrix): TFloatMatrix;
function VectorTransform(const M: TFloatMatrix; const V: TVector3f): TVector3f;
type
TTransformation = class(TNotifiablePersistent)
private
FSrcRect: TFloatRect;
procedure SetSrcRect(const Value: TFloatRect);
protected
TransformValid: Boolean;
procedure PrepareTransform; virtual;
procedure ReverseTransformInt(DstX, DstY: Integer; out SrcX, SrcY: Integer); virtual;
procedure ReverseTransformFixed(DstX, DstY: TFixed; out SrcX, SrcY: TFixed); virtual;
procedure ReverseTransformFloat(DstX, DstY: TFloat; out SrcX, SrcY: TFloat); virtual;
procedure TransformInt(SrcX, SrcY: Integer; out DstX, DstY: Integer); virtual;
procedure TransformFixed(SrcX, SrcY: TFixed; out DstX, DstY: TFixed); virtual;
procedure TransformFloat(SrcX, SrcY: TFloat; out DstX, DstY: TFloat); virtual;
public
procedure Changed; override;
function HasTransformedBounds: Boolean; virtual;
function GetTransformedBounds: TRect; overload;
function GetTransformedBounds(const ASrcRect: TFloatRect): TRect; overload; virtual;
function ReverseTransform(const P: TPoint): TPoint; overload; virtual;
function ReverseTransform(const P: TFixedPoint): TFixedPoint; overload; virtual;
function ReverseTransform(const P: TFloatPoint): TFloatPoint; overload; virtual;
function Transform(const P: TPoint): TPoint; overload; virtual;
function Transform(const P: TFixedPoint): TFixedPoint; overload; virtual;
function Transform(const P: TFloatPoint): TFloatPoint; overload; virtual;
property SrcRect: TFloatRect read FSrcRect write SetSrcRect;
end;
TAffineTransformation = class(TTransformation)
protected
FInverseMatrix: TFloatMatrix;
FFixedMatrix, FInverseFixedMatrix: TFixedMatrix;
procedure PrepareTransform; override;
procedure ReverseTransformFloat(DstX, DstY: TFloat; out SrcX, SrcY: TFloat); override;
procedure ReverseTransformFixed(DstX, DstY: TFixed; out SrcX, SrcY: TFixed); override;
procedure TransformFloat(SrcX, SrcY: TFloat; out DstX, DstY: TFloat); override;
procedure TransformFixed(SrcX, SrcY: TFixed; out DstX, DstY: TFixed); override;
public
Matrix: TFloatMatrix;
constructor Create; virtual;
function GetTransformedBounds(const ASrcRect: TFloatRect): TRect; override;
procedure Clear;
procedure Rotate(Cx, Cy, Alpha: TFloat); // degrees
procedure Skew(Fx, Fy: TFloat);
procedure Scale(Sx, Sy: TFloat);
procedure Translate(Dx, Dy: TFloat);
end;
TProjectiveTransformation = class(TTransformation)
private
Wx0, Wx1, Wx2, Wx3: TFloat;
Wy0, Wy1, Wy2, Wy3: TFloat;
procedure SetX0(Value: TFloat);
procedure SetX1(Value: TFloat);
procedure SetX2(Value: TFloat);
procedure SetX3(Value: TFloat);
procedure SetY0(Value: TFloat);
procedure SetY1(Value: TFloat);
procedure SetY2(Value: TFloat);
procedure SetY3(Value: TFloat);
protected
FMatrix, FInverseMatrix: TFloatMatrix;
FFixedMatrix, FInverseFixedMatrix: TFixedMatrix;
procedure PrepareTransform; override;
procedure ReverseTransformFloat(DstX, DstY: TFloat; out SrcX, SrcY: TFloat); override;
procedure ReverseTransformFixed(DstX, DstY: TFixed; out SrcX, SrcY: TFixed); override;
procedure TransformFloat(SrcX, SrcY: TFloat; out DstX, DstY: TFloat); override;
procedure TransformFixed(SrcX, SrcY: TFixed; out DstX, DstY: TFixed); override;
public
function GetTransformedBounds(const ASrcRect: TFloatRect): TRect; override;
published
property X0: TFloat read Wx0 write SetX0;
property X1: TFloat read Wx1 write SetX1;
property X2: TFloat read Wx2 write SetX2;
property X3: TFloat read Wx3 write SetX3;
property Y0: TFloat read Wy0 write SetY0;
property Y1: TFloat read Wy1 write SetY1;
property Y2: TFloat read Wy2 write SetY2;
property Y3: TFloat read Wy3 write SetY3;
end;
TTwirlTransformation = class(TTransformation)
private
Frx, Fry: TFloat;
FTwirl: TFloat;
procedure SetTwirl(const Value: TFloat);
protected
procedure PrepareTransform; override;
procedure ReverseTransformFloat(DstX, DstY: TFloat; out SrcX, SrcY: TFloat); override;
public
constructor Create; virtual;
function GetTransformedBounds(const ASrcRect: TFloatRect): TRect; override;
published
property Twirl: TFloat read FTwirl write SetTwirl;
end;
TBloatTransformation = class(TTransformation)
private
FBloatPower: TFloat;
FBP: TFloat;
FPiW, FPiH: TFloat;
procedure SetBloatPower(const Value: TFloat);
protected
procedure PrepareTransform; override;
procedure ReverseTransformFloat(DstX, DstY: TFloat; out SrcX, SrcY: TFloat); override;
public
constructor Create; virtual;
published
property BloatPower: TFloat read FBloatPower write SetBloatPower;
end;
TDisturbanceTransformation = class(TTransformation)
private
FDisturbance: TFloat;
procedure SetDisturbance(const Value: TFloat);
protected
procedure ReverseTransformFloat(DstX, DstY: TFloat; out SrcX, SrcY: TFloat); override;
public
function GetTransformedBounds(const ASrcRect: TFloatRect): TRect; override;
published
property Disturbance: TFloat read FDisturbance write SetDisturbance;
end;
TFishEyeTransformation = class(TTransformation)
private
Frx, Fry: TFloat;
Faw, Fsr: TFloat;
Sx, Sy: TFloat;
FMinR: TFloat;
protected
procedure PrepareTransform; override;
procedure ReverseTransformFloat(DstX, DstY: TFloat; out SrcX, SrcY: TFloat); override;
end;
TRemapTransformation = class(TTransformation)
private
FVectorMap : TVectorMap;
FScalingFixed: TFixedVector;
FScalingFloat: TFloatVector;
FCombinedScalingFixed: TFixedVector;
FCombinedScalingFloat: TFloatVector;
FSrcTranslationFixed: TFixedVector;
FSrcScaleFixed: TFixedVector;
FDstTranslationFixed: TFixedVector;
FDstScaleFixed: TFixedVector;
FSrcTranslationFloat: TFloatVector;
FSrcScaleFloat: TFloatVector;
FDstTranslationFloat: TFloatVector;
FDstScaleFloat: TFloatVector;
FOffsetFixed : TFixedVector;
FOffsetInt : TPoint;
FMappingRect: TFloatRect;
FOffset: TFloatVector;
procedure SetMappingRect(Rect: TFloatRect);
procedure SetOffset(const Value: TFloatVector);
protected
procedure PrepareTransform; override;
procedure ReverseTransformInt(DstX, DstY: Integer; out SrcX, SrcY: Integer); override;
procedure ReverseTransformFloat(DstX, DstY: TFloat; out SrcX, SrcY: TFloat); override;
procedure ReverseTransformFixed(DstX, DstY: TFixed; out SrcX, SrcY: TFixed); override;
public
constructor Create; virtual;
destructor Destroy; override;
function HasTransformedBounds: Boolean; override;
function GetTransformedBounds(const ASrcRect: TFloatRect): TRect; override;
procedure Scale(Sx, Sy: TFloat);
//SAARIXX
property MappingRect: TFloatRect read FMappingRect write SetMappingRect;
property Offset: TFloatVector read FOffset write SetOffset;
published
property VectorMap: TVectorMap read FVectorMap write FVectorMap;
end;
function TransformPoints(Points: TArrayOfArrayOfFixedPoint; Transformation: TTransformation): TArrayOfArrayOfFixedPoint;
procedure Transform(Dst, Src: TBitmap32; Transformation: TTransformation); overload;
procedure Transform(Dst, Src: TBitmap32; Transformation: TTransformation;
const DstClip: TRect); overload;
procedure Transform(Dst, Src: TBitmap32; Transformation: TTransformation;
Rasterizer: TRasterizer); overload;
procedure Transform(Dst, Src: TBitmap32; Transformation: TTransformation;
Rasterizer: TRasterizer; const DstClip: TRect); overload;
procedure RasterizeTransformation(Vectormap: TVectormap;
Transformation: TTransformation; DstRect: TRect;
CombineMode: TVectorCombineMode = vcmAdd;
CombineCallback: TVectorCombineEvent = nil);
procedure SetBorderTransparent(ABitmap: TBitmap32; ARect: TRect);
{ FullEdge controls how the bitmap is resampled }
var
FullEdge: Boolean = True;
implementation
uses
GR32_LowLevel, GR32_System, GR32_Resamplers, Math, GR32_Math;
type
{provides access to proctected members of TBitmap32 by typecasting}
TTransformationAccess = class(TTransformation);
TBitmap32Access = class(TBitmap32);
{ A bit of linear algebra }
function _DET(a1, a2, b1, b2: TFloat): TFloat; overload;
begin
Result := a1 * b2 - a2 * b1;
end;
function _DET(a1, a2, a3, b1, b2, b3, c1, c2, c3: TFloat): TFloat; overload;
begin
Result :=
a1 * (b2 * c3 - b3 * c2) -
b1 * (a2 * c3 - a3 * c2) +
c1 * (a2 * b3 - a3 * b2);
end;
procedure Adjoint(var M: TFloatMatrix);
var
a1, a2, a3: TFloat;
b1, b2, b3: TFloat;
c1, c2, c3: TFloat;
begin
a1 := M[0,0]; a2:= M[0,1]; a3 := M[0,2];
b1 := M[1,0]; b2:= M[1,1]; b3 := M[1,2];
c1 := M[2,0]; c2:= M[2,1]; c3 := M[2,2];
M[0,0]:= _DET(b2, b3, c2, c3);
M[0,1]:=-_DET(a2, a3, c2, c3);
M[0,2]:= _DET(a2, a3, b2, b3);
M[1,0]:=-_DET(b1, b3, c1, c3);
M[1,1]:= _DET(a1, a3, c1, c3);
M[1,2]:=-_DET(a1, a3, b1, b3);
M[2,0]:= _DET(b1, b2, c1, c2);
M[2,1]:=-_DET(a1, a2, c1, c2);
M[2,2]:= _DET(a1, a2, b1, b2);
end;
function Determinant(const M: TFloatMatrix): TFloat;
begin
Result := _DET(M[0,0], M[1,0], M[2,0],
M[0,1], M[1,1], M[2,1],
M[0,2], M[1,2], M[2,2]);
end;
procedure Scale(var M: TFloatMatrix; Factor: TFloat);
var
i, j: Integer;
begin
for i := 0 to 2 do
for j := 0 to 2 do
M[i,j] := M[i,j] * Factor;
end;
procedure Invert(var M: TFloatMatrix);
var
Det: TFloat;
begin
Det := Determinant(M);
if Abs(Det) < 1E-5 then M := IdentityMatrix
else
begin
Adjoint(M);
Scale(M, 1 / Det);
end;
end;
function Mult(const M1, M2: TFloatMatrix): TFloatMatrix;
var
i, j: Integer;
begin
for i := 0 to 2 do
for j := 0 to 2 do
Result[i, j] :=
M1[0, j] * M2[i, 0] +
M1[1, j] * M2[i, 1] +
M1[2, j] * M2[i, 2];
end;
function VectorTransform(const M: TFloatMatrix; const V: TVector3f): TVector3f;
begin
Result[0] := M[0,0] * V[0] + M[1,0] * V[1] + M[2,0] * V[2];
Result[1] := M[0,1] * V[0] + M[1,1] * V[1] + M[2,1] * V[2];
Result[2] := M[0,2] * V[0] + M[1,2] * V[1] + M[2,2] * V[2];
end;
{ Transformation functions }
function TransformPoints(Points: TArrayOfArrayOfFixedPoint; Transformation: TTransformation): TArrayOfArrayOfFixedPoint;
var
I, J: Integer;
begin
if Points = nil then
Result := nil
else
begin
SetLength(Result, Length(Points));
Transformation.PrepareTransform;
for I := 0 to High(Result) do
begin
SetLength(Result[I], Length(Points[I]));
if Length(Result[I]) > 0 then
for J := 0 to High(Result[I]) do
Transformation.TransformFixed(Points[I][J].X, Points[I][J].Y, Result[I][J].X, Result[I][J].Y);
end;
end;
end;
procedure Transform(Dst, Src: TBitmap32; Transformation: TTransformation);
var
Rasterizer: TRasterizer;
begin
Rasterizer := DefaultRasterizerClass.Create;
try
Transform(Dst, Src, Transformation, Rasterizer);
finally
Rasterizer.Free;
end;
end;
procedure Transform(Dst, Src: TBitmap32; Transformation: TTransformation; const DstClip: TRect);
var
Rasterizer: TRasterizer;
begin
Rasterizer := DefaultRasterizerClass.Create;
try
Transform(Dst, Src, Transformation, Rasterizer, DstClip);
finally
Rasterizer.Free;
end;
end;
procedure Transform(Dst, Src: TBitmap32; Transformation: TTransformation;
Rasterizer: TRasterizer);
begin
Transform(Dst, Src, Transformation, Rasterizer, Dst.BoundsRect);
end;
procedure Transform(Dst, Src: TBitmap32; Transformation: TTransformation;
Rasterizer: TRasterizer; const DstClip: TRect);
var
DstRect: TRect;
Transformer: TTransformer;
begin
DstRect := Transformation.GetTransformedBounds;
// clip DstRect
IntersectRect(DstRect, DstRect, Dst.ClipRect);
IntersectRect(DstRect, DstRect, DstClip);
if (DstRect.Right < DstRect.Left) or (DstRect.Bottom < DstRect.Top) then Exit;
if not Dst.MeasuringMode then
begin
Transformer := (Src.Resampler as TBitmap32Resampler).TransformerClass.Create(Src.Resampler, Transformation);
try
Rasterizer.Sampler := Transformer;
Rasterizer.Rasterize(Dst, DstRect, Src);
finally
EMMS;
Transformer.Free;
end;
end;
Dst.Changed(DstRect);
end;
procedure SetBorderTransparent(ABitmap: TBitmap32; ARect: TRect);
var
I: Integer;
begin
IntersectRect(ARect, ARect, ABitmap.BoundsRect);
with ARect, ABitmap do
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -