Changeset 472 for GraphicTest/Packages/bgrabitmap/bgratransform.pas
- Timestamp:
- Apr 9, 2015, 9:58:36 PM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
GraphicTest/Packages/bgrabitmap/bgratransform.pas
r452 r472 24 24 TopLeft, TopRight, 25 25 BottomLeft: TPointF; 26 function EmptyBox: TAffineBox;27 function AffineBox(ATopLeft, ATopRight, ABottomLeft: TPointF): TAffineBox;26 class function EmptyBox: TAffineBox; 27 class function AffineBox(ATopLeft, ATopRight, ABottomLeft: TPointF): TAffineBox; 28 28 property BottomRight: TPointF read GetBottomRight; 29 29 property IsEmpty: boolean read GetIsEmpty; … … 55 55 procedure Invert; 56 56 procedure Translate(OfsX,OfsY: Single); 57 procedure RotateDeg(Angle : Single);58 procedure RotateRad(Angle : Single);57 procedure RotateDeg(AngleCW: Single); 58 procedure RotateRad(AngleCCW: Single); 59 59 procedure MultiplyBy(AMatrix: TAffineMatrix); 60 60 procedure Fit(Origin,HAxis,VAxis: TPointF); virtual; … … 79 79 FRepeatImageX,FRepeatImageY: boolean; 80 80 FResampleFilter : TResampleFilter; 81 FBuffer: PBGRAPixel; 82 FBufferSize: Int32or64; 81 83 procedure Init(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean= false; ARepeatImageY: Boolean= false; AResampleFilter: TResampleFilter = rfLinear); 82 84 public 83 85 constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImage: Boolean= false; AResampleFilter: TResampleFilter = rfLinear); 84 86 constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean; ARepeatImageY: Boolean; AResampleFilter: TResampleFilter = rfLinear); 87 destructor Destroy; override; 85 88 function InternalScanCurrentPixel: TBGRAPixel; override; 89 procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override; 90 function IsScanPutPixelsDefined: boolean; override; 86 91 procedure Fit(Origin, HAxis, VAxis: TPointF); override; 87 92 end; … … 103 108 end; 104 109 110 { TBGRAExtendedBorderScanner } 111 112 TBGRAExtendedBorderScanner = class(TBGRACustomScanner) 113 protected 114 FSource: IBGRAScanner; 115 FBounds: TRect; 116 public 117 constructor Create(ASource: IBGRAScanner; ABounds: TRect); 118 function ScanAt(X,Y: Single): TBGRAPixel; override; 119 end; 120 105 121 { TBGRAScannerOffset } 106 122 … … 133 149 function IsAffineMatrixInversible(M: TAffineMatrix): boolean; 134 150 151 //check if the matrix is a translation (including the identity) 152 function IsAffineMatrixTranslation(M: TAffineMatrix): boolean; 153 154 //check if the matrix is a scaling (including a projection i.e. with factor 0) 155 function IsAffineMatrixScale(M: TAffineMatrix): boolean; 156 157 //check if the matrix is the identity 158 function IsAffineMatrixIdentity(M: TAffineMatrix): boolean; 159 135 160 //compute inverse (check if inversible before) 136 161 function AffineMatrixInverse(M: TAffineMatrix): TAffineMatrix; … … 145 170 function AffineMatrixLinear(v1,v2: TPointF): TAffineMatrix; 146 171 147 //define a rotation matrix (positive radians are counter clock wise) 148 function AffineMatrixRotationRad(Angle: Single): TAffineMatrix; 149 150 //Positive degrees are clock wise 151 function AffineMatrixRotationDeg(Angle: Single): TAffineMatrix; 172 //define a rotation matrix (positive radians are counter-clockwise) 173 //(assuming the y-axis is pointing down) 174 function AffineMatrixRotationRad(AngleCCW: Single): TAffineMatrix; 175 176 //Positive degrees are clockwise 177 //(assuming the y-axis is pointing down) 178 function AffineMatrixRotationDeg(AngleCW: Single): TAffineMatrix; 152 179 153 180 //define the identity matrix (that do nothing) … … 188 215 FMatrix: TPerspectiveTransform; 189 216 FScanAtProc: TScanAtFunction; 217 function GetIncludeOppositePlane: boolean; 218 procedure SetIncludeOppositePlane(AValue: boolean); 190 219 public 191 220 constructor Create(texture: IBGRAScanner; texCoord1,texCoord2: TPointF; const quad: array of TPointF); … … 195 224 function ScanAt(X, Y: Single): TBGRAPixel; override; 196 225 function ScanNextPixel: TBGRAPixel; override; 226 property IncludeOppositePlane: boolean read GetIncludeOppositePlane write SetIncludeOppositePlane; 197 227 end; 198 228 … … 203 233 sx ,shy ,w0 ,shx ,sy ,w1 ,tx ,ty ,w2 : single; 204 234 scanDenom,scanNumX,scanNumY: single; 235 FOutsideValue: TPointF; 236 FIncludeOppositePlane: boolean; 237 procedure Init; 205 238 public 206 239 constructor Create; overload; … … 222 255 procedure ScanMoveTo(x,y:single); 223 256 function ScanNext: TPointF; 257 property OutsideValue: TPointF read FOutsideValue write FOutsideValue; 258 property IncludeOppositePlane: boolean read FIncludeOppositePlane write FIncludeOppositePlane; 224 259 end; 225 260 … … 249 284 implementation 250 285 251 uses BGRABlend ;286 uses BGRABlend, GraphType; 252 287 253 288 function AffineMatrix(m11, m12, m13, m21, m22, m23: single): TAffineMatrix; … … 281 316 begin 282 317 result := M[1,1]*M[2,2]-M[1,2]*M[2,1] <> 0; 318 end; 319 320 function IsAffineMatrixTranslation(M: TAffineMatrix): boolean; 321 begin 322 result := (m[1,1]=1) and (m[1,2]=0) and (m[2,1] = 1) and (m[2,2]=0); 323 end; 324 325 function IsAffineMatrixScale(M: TAffineMatrix): boolean; 326 begin 327 result := (M[1,3]=0) and (M[2,3]=0) and 328 (M[1,2]=0) and (M[2,1]=0); 329 end; 330 331 function IsAffineMatrixIdentity(M: TAffineMatrix): boolean; 332 begin 333 result := IsAffineMatrixTranslation(M) and (M[1,3]=0) and (M[2,3]=0); 283 334 end; 284 335 … … 314 365 end; 315 366 316 function AffineMatrixRotationRad(Angle: Single): TAffineMatrix; 317 begin 318 result := AffineMatrix(cos(Angle), sin(Angle), 0, 319 -sin(Angle), cos(Angle), 0); 320 end; 321 322 function AffineMatrixRotationDeg(Angle: Single): TAffineMatrix; 323 begin 324 result := AffineMatrixRotationRad(-Angle*Pi/180); 367 function AffineMatrixRotationRad(AngleCCW: Single): TAffineMatrix; 368 begin 369 result := AffineMatrix(cos(AngleCCW), sin(AngleCCW), 0, 370 -sin(AngleCCW), cos(AngleCCW), 0); 371 end; 372 373 function AffineMatrixRotationDeg(AngleCW: Single): TAffineMatrix; 374 const DegToRad = -Pi/180; 375 begin 376 result := AffineMatrixRotationRad(AngleCW*DegToRad); 325 377 end; 326 378 … … 334 386 begin 335 387 result := PointF(M[1,1],M[2,1])*PointF(M[1,2],M[2,2]) = 0; 388 end; 389 390 { TBGRAExtendedBorderScanner } 391 392 constructor TBGRAExtendedBorderScanner.Create(ASource: IBGRAScanner; 393 ABounds: TRect); 394 begin 395 FSource := ASource; 396 FBounds := ABounds; 397 end; 398 399 function TBGRAExtendedBorderScanner.ScanAt(X, Y: Single): TBGRAPixel; 400 begin 401 if x < FBounds.Left then x := FBounds.Left; 402 if y < FBounds.Top then y := FBounds.Top; 403 if x > FBounds.Right-1 then x := FBounds.Right-1; 404 if y > FBounds.Bottom-1 then y := FBounds.Bottom-1; 405 result := FSource.ScanAt(X,Y); 336 406 end; 337 407 … … 356 426 end; 357 427 358 function TAffineBox.EmptyBox: TAffineBox;428 class function TAffineBox.EmptyBox: TAffineBox; 359 429 begin 360 430 result.TopLeft := EmptyPointF; … … 363 433 end; 364 434 365 function TAffineBox.AffineBox(ATopLeft, ATopRight, ABottomLeft: TPointF): TAffineBox;435 class function TAffineBox.AffineBox(ATopLeft, ATopRight, ABottomLeft: TPointF): TAffineBox; 366 436 begin 367 437 result.TopLeft := ATopLeft; … … 556 626 end; 557 627 628 //transformations are inverted because the effect on the resulting image 629 //is the inverse of the transformation. This is due to the fact 630 //that the matrix is applied to source coordinates, not destination coordinates 558 631 procedure TBGRAAffineScannerTransform.Translate(OfsX, OfsY: Single); 559 632 begin … … 561 634 end; 562 635 563 procedure TBGRAAffineScannerTransform.RotateDeg(Angle : Single);564 begin 565 MultiplyBy(AffineMatrixRotationDeg(-Angle ));566 end; 567 568 procedure TBGRAAffineScannerTransform.RotateRad(Angle : Single);569 begin 570 MultiplyBy(AffineMatrixRotationRad(-Angle ));636 procedure TBGRAAffineScannerTransform.RotateDeg(AngleCW: Single); 637 begin 638 MultiplyBy(AffineMatrixRotationDeg(-AngleCW)); 639 end; 640 641 procedure TBGRAAffineScannerTransform.RotateRad(AngleCCW: Single); 642 begin 643 MultiplyBy(AffineMatrixRotationRad(-AngleCCW)); 571 644 end; 572 645 … … 651 724 FRepeatImageY := ARepeatImageY; 652 725 FResampleFilter:= AResampleFilter; 726 FBufferSize:= 0; 653 727 end; 654 728 … … 666 740 end; 667 741 742 destructor TBGRAAffineBitmapTransform.Destroy; 743 begin 744 FreeMem(FBuffer); 745 end; 746 668 747 function TBGRAAffineBitmapTransform.InternalScanCurrentPixel: TBGRAPixel; 669 748 begin 670 if FRepeatImageX or FRepeatImageY then 671 result := FBitmap.GetPixelCycle(FCurX,FCurY,FResampleFilter,FRepeatImageX,FRepeatImageY) 749 result := FBitmap.GetPixelCycle(FCurX,FCurY,FResampleFilter,FRepeatImageX,FRepeatImageY); 750 end; 751 752 procedure TBGRAAffineBitmapTransform.ScanPutPixels(pdest: PBGRAPixel; 753 count: integer; mode: TDrawMode); 754 var p: PBGRAPixel; 755 n: integer; 756 posX4096, posY4096: Int32or64; 757 deltaX4096,deltaY4096: Int32or64; 758 ix,iy,shrMask,w,h: Int32or64; 759 py0: PByte; 760 deltaRow: Int32or64; 761 begin 762 w := FBitmap.Width; 763 h := FBitmap.Height; 764 if (w = 0) or (h = 0) then exit; 765 766 posX4096 := round(FCurX*4096); 767 deltaX4096:= round(FMatrix[1,1]*4096); 768 posY4096 := round(FCurY*4096); 769 deltaY4096:= round(FMatrix[2,1]*4096); 770 shrMask := -1; 771 shrMask := shrMask shr 12; 772 shrMask := not shrMask; 773 774 if mode = dmSet then 775 p := pdest 672 776 else 673 result := FBitmap.GetPixel(FCurX,FCurY,FResampleFilter); 777 begin 778 if count > FBufferSize then 779 begin 780 FBufferSize := count; 781 ReAllocMem(FBuffer, FBufferSize*sizeof(TBGRAPixel)); 782 end; 783 p := FBuffer; 784 end; 785 786 if FResampleFilter = rfBox then 787 begin 788 posX4096 += 2048; 789 posY4096 += 2048; 790 py0 := PByte(FBitmap.ScanLine[0]); 791 if FBitmap.LineOrder = riloTopToBottom then 792 deltaRow := FBitmap.Width*sizeof(TBGRAPixel) else 793 deltaRow := -FBitmap.Width*sizeof(TBGRAPixel); 794 if FRepeatImageX or FRepeatImageY then 795 begin 796 for n := count-1 downto 0 do 797 begin 798 if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12; 799 if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12; 800 if FRepeatImageX then ix := PositiveMod(ix,w); 801 if FRepeatImageY then iy := PositiveMod(iy,h); 802 if (ix < 0) or (iy < 0) or (ix >= w) or (iy >= h) then 803 p^ := BGRAPixelTransparent 804 else 805 p^ := (PBGRAPixel(py0 + iy*deltaRow)+ix)^; 806 inc(p); 807 posX4096 += deltaX4096; 808 posY4096 += deltaY4096; 809 end; 810 end else 811 begin 812 for n := count-1 downto 0 do 813 begin 814 if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12; 815 if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12; 816 if (ix < 0) or (iy < 0) or (ix >= w) or (iy >= h) then 817 p^ := BGRAPixelTransparent 818 else 819 p^ := (PBGRAPixel(py0 + iy*deltaRow)+ix)^; 820 inc(p); 821 posX4096 += deltaX4096; 822 posY4096 += deltaY4096; 823 end; 824 end; 825 end else 826 begin 827 if FRepeatImageX and FRepeatImageY then 828 begin 829 for n := count-1 downto 0 do 830 begin 831 if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12; 832 if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12; 833 p^ := FBitmap.GetPixelCycle256(ix,iy, (posX4096 shr 4) and 255, (posY4096 shr 4) and 255,FResampleFilter); 834 inc(p); 835 posX4096 += deltaX4096; 836 posY4096 += deltaY4096; 837 end; 838 end else 839 if FRepeatImageX or FRepeatImageY then 840 begin 841 for n := count-1 downto 0 do 842 begin 843 if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12; 844 if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12; 845 p^ := FBitmap.GetPixelCycle256(ix,iy, (posX4096 shr 4) and 255, (posY4096 shr 4) and 255,FResampleFilter, FRepeatImageX,FRepeatImageY); 846 inc(p); 847 posX4096 += deltaX4096; 848 posY4096 += deltaY4096; 849 end; 850 end else 851 begin 852 for n := count-1 downto 0 do 853 begin 854 if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12; 855 if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12; 856 p^ := FBitmap.GetPixel256(ix,iy, (posX4096 shr 4) and 255, (posY4096 shr 4) and 255,FResampleFilter); 857 inc(p); 858 posX4096 += deltaX4096; 859 posY4096 += deltaY4096; 860 end; 861 end; 862 end; 863 864 if mode <> dmSet then PutPixels(pdest,FBuffer,count,mode,255); 865 end; 866 867 function TBGRAAffineBitmapTransform.IsScanPutPixelsDefined: boolean; 868 begin 869 Result:=true; 674 870 end; 675 871 … … 684 880 { TBGRAPerspectiveScannerTransform } 685 881 882 function TBGRAPerspectiveScannerTransform.GetIncludeOppositePlane: boolean; 883 begin 884 if FMatrix = nil then 885 result := false 886 else 887 result := FMatrix.IncludeOppositePlane; 888 end; 889 890 procedure TBGRAPerspectiveScannerTransform.SetIncludeOppositePlane( 891 AValue: boolean); 892 begin 893 if FMatrix <> nil then 894 FMatrix.IncludeOppositePlane := AValue; 895 end; 896 686 897 constructor TBGRAPerspectiveScannerTransform.Create(texture: IBGRAScanner; texCoord1,texCoord2: TPointF; const quad: array of TPointF); 687 898 begin … … 689 900 FMatrix := nil 690 901 else 902 begin 691 903 FMatrix := TPerspectiveTransform.Create(quad,texCoord1.x,texCoord1.y,texCoord2.x,texCoord2.y); 904 FMatrix.OutsideValue := EmptyPointF; 905 end; 692 906 FTexture := texture; 693 907 FScanAtProc:= @FTexture.ScanAt; … … 701 915 FMatrix := nil 702 916 else 917 begin 703 918 FMatrix := TPerspectiveTransform.Create(quad,texCoordsQuad); 919 FMatrix.OutsideValue := EmptyPointF; 920 end; 704 921 FTexture := texture; 705 922 FScanAtProc:= @FTexture.ScanAt; … … 725 942 begin 726 943 ptSource := FMatrix.Apply(PointF(X,Y)); 727 Result:= FScanAtProc(ptSource.X, ptSource.Y); 944 if ptSource.x = EmptySingle then 945 result := BGRAPixelTransparent 946 else 947 Result:= FScanAtProc(ptSource.X, ptSource.Y); 728 948 end; 729 949 end; … … 736 956 begin 737 957 ptSource := FMatrix.ScanNext; 738 Result:= FScanAtProc(ptSource.X, ptSource.Y); 958 if ptSource.x = EmptySingle then 959 result := BGRAPixelTransparent 960 else 961 Result:= FScanAtProc(ptSource.X, ptSource.Y); 739 962 end; 740 963 end; … … 742 965 { TPerspectiveTransform } 743 966 967 procedure TPerspectiveTransform.Init; 968 begin 969 FOutsideValue := PointF(0,0); 970 FIncludeOppositePlane:= True; 971 end; 972 744 973 constructor TPerspectiveTransform.Create; 745 974 begin 975 Init; 746 976 AssignIdentity; 747 977 end; … … 750 980 const quad: array of TPointF); 751 981 begin 982 Init; 752 983 MapRectToQuad(x1 ,y1 ,x2 ,y2 ,quad ); 753 984 end; … … 756 987 x2, y2: single); 757 988 begin 989 Init; 758 990 MapQuadToRect(quad, x1,y1,x2,y2); 759 991 end; … … 762 994 destQuad: array of TPointF); 763 995 begin 996 Init; 764 997 MapQuadToQuad(srcQuad,destQuad); 765 998 end; … … 995 1228 m : single; 996 1229 begin 997 m:= pt.x * w0 + pt.y * w1 + w2 ; 998 if m=0 then 999 begin 1000 result.x := 0; 1001 result.y := 0; 1002 end else 1230 m:= pt.x * w0 + pt.y * w1 + w2; 1231 if (m=0) or (not FIncludeOppositePlane and (m < 0)) then 1232 result := FOutsideValue 1233 else 1003 1234 begin 1004 1235 m := 1/m; … … 1018 1249 var m: single; 1019 1250 begin 1020 if ScanDenom = 0 then 1021 begin 1022 result.x := 0; 1023 result.y := 0; 1024 end else 1251 if (ScanDenom = 0) or (not FIncludeOppositePlane and (ScanDenom < 0)) then 1252 result := FOutsideValue 1253 else 1025 1254 begin 1026 1255 m := 1/scanDenom;
Note:
See TracChangeset
for help on using the changeset viewer.