Ignore:
Timestamp:
Apr 9, 2015, 9:58:36 PM (9 years ago)
Author:
chronos
Message:
  • Fixed: Use csOpaque control style also to Image, PaintBox and OpenGLControl.
  • Modified: Change size of test frame with SpinEdits as delayed using timer.
  • Updated: BRGABitmap package to version 8.1.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • GraphicTest/Packages/bgrabitmap/bgratransform.pas

    r452 r472  
    2424    TopLeft, TopRight,
    2525    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;
    2828    property BottomRight: TPointF read GetBottomRight;
    2929    property IsEmpty: boolean read GetIsEmpty;
     
    5555    procedure Invert;
    5656    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);
    5959    procedure MultiplyBy(AMatrix: TAffineMatrix);
    6060    procedure Fit(Origin,HAxis,VAxis: TPointF); virtual;
     
    7979    FRepeatImageX,FRepeatImageY: boolean;
    8080    FResampleFilter : TResampleFilter;
     81    FBuffer: PBGRAPixel;
     82    FBufferSize: Int32or64;
    8183    procedure Init(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean= false; ARepeatImageY: Boolean= false; AResampleFilter: TResampleFilter = rfLinear);
    8284  public
    8385    constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImage: Boolean= false; AResampleFilter: TResampleFilter = rfLinear);
    8486    constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean; ARepeatImageY: Boolean; AResampleFilter: TResampleFilter = rfLinear);
     87    destructor Destroy; override;
    8588    function InternalScanCurrentPixel: TBGRAPixel; override;
     89    procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override;
     90    function IsScanPutPixelsDefined: boolean; override;
    8691    procedure Fit(Origin, HAxis, VAxis: TPointF); override;
    8792  end;
     
    103108  end;
    104109
     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
    105121  { TBGRAScannerOffset }
    106122
     
    133149function IsAffineMatrixInversible(M: TAffineMatrix): boolean;
    134150
     151//check if the matrix is a translation (including the identity)
     152function IsAffineMatrixTranslation(M: TAffineMatrix): boolean;
     153
     154//check if the matrix is a scaling (including a projection i.e. with factor 0)
     155function IsAffineMatrixScale(M: TAffineMatrix): boolean;
     156
     157//check if the matrix is the identity
     158function IsAffineMatrixIdentity(M: TAffineMatrix): boolean;
     159
    135160//compute inverse (check if inversible before)
    136161function AffineMatrixInverse(M: TAffineMatrix): TAffineMatrix;
     
    145170function AffineMatrixLinear(v1,v2: TPointF): TAffineMatrix;
    146171
    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)
     174function AffineMatrixRotationRad(AngleCCW: Single): TAffineMatrix;
     175
     176//Positive degrees are clockwise
     177//(assuming the y-axis is pointing down)
     178function AffineMatrixRotationDeg(AngleCW: Single): TAffineMatrix;
    152179
    153180//define the identity matrix (that do nothing)
     
    188215    FMatrix: TPerspectiveTransform;
    189216    FScanAtProc: TScanAtFunction;
     217    function GetIncludeOppositePlane: boolean;
     218    procedure SetIncludeOppositePlane(AValue: boolean);
    190219  public
    191220    constructor Create(texture: IBGRAScanner; texCoord1,texCoord2: TPointF; const quad: array of TPointF);
     
    195224    function ScanAt(X, Y: Single): TBGRAPixel; override;
    196225    function ScanNextPixel: TBGRAPixel; override;
     226    property IncludeOppositePlane: boolean read GetIncludeOppositePlane write SetIncludeOppositePlane;
    197227  end;
    198228
     
    203233    sx ,shy ,w0 ,shx ,sy ,w1 ,tx ,ty ,w2 : single;
    204234    scanDenom,scanNumX,scanNumY: single;
     235    FOutsideValue: TPointF;
     236    FIncludeOppositePlane: boolean;
     237    procedure Init;
    205238  public
    206239    constructor Create; overload;
     
    222255    procedure ScanMoveTo(x,y:single);
    223256    function ScanNext: TPointF;
     257    property OutsideValue: TPointF read FOutsideValue write FOutsideValue;
     258    property IncludeOppositePlane: boolean read FIncludeOppositePlane write FIncludeOppositePlane;
    224259  end;
    225260
     
    249284implementation
    250285
    251 uses BGRABlend;
     286uses BGRABlend, GraphType;
    252287
    253288function AffineMatrix(m11, m12, m13, m21, m22, m23: single): TAffineMatrix;
     
    281316begin
    282317  result := M[1,1]*M[2,2]-M[1,2]*M[2,1] <> 0;
     318end;
     319
     320function IsAffineMatrixTranslation(M: TAffineMatrix): boolean;
     321begin
     322  result := (m[1,1]=1) and (m[1,2]=0) and (m[2,1] = 1) and (m[2,2]=0);
     323end;
     324
     325function IsAffineMatrixScale(M: TAffineMatrix): boolean;
     326begin
     327  result := (M[1,3]=0) and (M[2,3]=0) and
     328            (M[1,2]=0) and (M[2,1]=0);
     329end;
     330
     331function IsAffineMatrixIdentity(M: TAffineMatrix): boolean;
     332begin
     333  result := IsAffineMatrixTranslation(M) and (M[1,3]=0) and (M[2,3]=0);
    283334end;
    284335
     
    314365end;
    315366
    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);
     367function AffineMatrixRotationRad(AngleCCW: Single): TAffineMatrix;
     368begin
     369  result := AffineMatrix(cos(AngleCCW),  sin(AngleCCW), 0,
     370                         -sin(AngleCCW), cos(AngleCCW), 0);
     371end;
     372
     373function AffineMatrixRotationDeg(AngleCW: Single): TAffineMatrix;
     374const DegToRad = -Pi/180;
     375begin
     376  result := AffineMatrixRotationRad(AngleCW*DegToRad);
    325377end;
    326378
     
    334386begin
    335387  result := PointF(M[1,1],M[2,1])*PointF(M[1,2],M[2,2]) = 0;
     388end;
     389
     390{ TBGRAExtendedBorderScanner }
     391
     392constructor TBGRAExtendedBorderScanner.Create(ASource: IBGRAScanner;
     393  ABounds: TRect);
     394begin
     395  FSource := ASource;
     396  FBounds := ABounds;
     397end;
     398
     399function TBGRAExtendedBorderScanner.ScanAt(X, Y: Single): TBGRAPixel;
     400begin
     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);
    336406end;
    337407
     
    356426end;
    357427
    358 function TAffineBox.EmptyBox: TAffineBox;
     428class function TAffineBox.EmptyBox: TAffineBox;
    359429begin
    360430  result.TopLeft := EmptyPointF;
     
    363433end;
    364434
    365 function TAffineBox.AffineBox(ATopLeft, ATopRight, ABottomLeft: TPointF): TAffineBox;
     435class function TAffineBox.AffineBox(ATopLeft, ATopRight, ABottomLeft: TPointF): TAffineBox;
    366436begin
    367437  result.TopLeft := ATopLeft;
     
    556626end;
    557627
     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
    558631procedure TBGRAAffineScannerTransform.Translate(OfsX, OfsY: Single);
    559632begin
     
    561634end;
    562635
    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));
     636procedure TBGRAAffineScannerTransform.RotateDeg(AngleCW: Single);
     637begin
     638  MultiplyBy(AffineMatrixRotationDeg(-AngleCW));
     639end;
     640
     641procedure TBGRAAffineScannerTransform.RotateRad(AngleCCW: Single);
     642begin
     643  MultiplyBy(AffineMatrixRotationRad(-AngleCCW));
    571644end;
    572645
     
    651724  FRepeatImageY := ARepeatImageY;
    652725  FResampleFilter:= AResampleFilter;
     726  FBufferSize:= 0;
    653727end;
    654728
     
    666740end;
    667741
     742destructor TBGRAAffineBitmapTransform.Destroy;
     743begin
     744  FreeMem(FBuffer);
     745end;
     746
    668747function TBGRAAffineBitmapTransform.InternalScanCurrentPixel: TBGRAPixel;
    669748begin
    670   if FRepeatImageX or FRepeatImageY then
    671     result := FBitmap.GetPixelCycle(FCurX,FCurY,FResampleFilter,FRepeatImageX,FRepeatImageY)
     749  result := FBitmap.GetPixelCycle(FCurX,FCurY,FResampleFilter,FRepeatImageX,FRepeatImageY);
     750end;
     751
     752procedure TBGRAAffineBitmapTransform.ScanPutPixels(pdest: PBGRAPixel;
     753  count: integer; mode: TDrawMode);
     754var 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;
     761begin
     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
    672776  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);
     865end;
     866
     867function TBGRAAffineBitmapTransform.IsScanPutPixelsDefined: boolean;
     868begin
     869  Result:=true;
    674870end;
    675871
     
    684880{ TBGRAPerspectiveScannerTransform }
    685881
     882function TBGRAPerspectiveScannerTransform.GetIncludeOppositePlane: boolean;
     883begin
     884  if FMatrix = nil then
     885    result := false
     886  else
     887    result := FMatrix.IncludeOppositePlane;
     888end;
     889
     890procedure TBGRAPerspectiveScannerTransform.SetIncludeOppositePlane(
     891  AValue: boolean);
     892begin
     893  if FMatrix <> nil then
     894    FMatrix.IncludeOppositePlane := AValue;
     895end;
     896
    686897constructor TBGRAPerspectiveScannerTransform.Create(texture: IBGRAScanner; texCoord1,texCoord2: TPointF; const quad: array of TPointF);
    687898begin
     
    689900    FMatrix := nil
    690901  else
     902  begin
    691903    FMatrix := TPerspectiveTransform.Create(quad,texCoord1.x,texCoord1.y,texCoord2.x,texCoord2.y);
     904    FMatrix.OutsideValue := EmptyPointF;
     905  end;
    692906  FTexture := texture;
    693907  FScanAtProc:= @FTexture.ScanAt;
     
    701915    FMatrix := nil
    702916  else
     917  begin
    703918    FMatrix := TPerspectiveTransform.Create(quad,texCoordsQuad);
     919    FMatrix.OutsideValue := EmptyPointF;
     920  end;
    704921  FTexture := texture;
    705922  FScanAtProc:= @FTexture.ScanAt;
     
    725942  begin
    726943    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);
    728948  end;
    729949end;
     
    736956  begin
    737957    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);
    739962  end;
    740963end;
     
    742965{ TPerspectiveTransform }
    743966
     967procedure TPerspectiveTransform.Init;
     968begin
     969  FOutsideValue := PointF(0,0);
     970  FIncludeOppositePlane:= True;
     971end;
     972
    744973constructor TPerspectiveTransform.Create;
    745974begin
     975  Init;
    746976  AssignIdentity;
    747977end;
     
    750980  const quad: array of TPointF);
    751981begin
     982  Init;
    752983  MapRectToQuad(x1 ,y1 ,x2 ,y2 ,quad );
    753984end;
     
    756987  x2, y2: single);
    757988begin
     989  Init;
    758990  MapQuadToRect(quad, x1,y1,x2,y2);
    759991end;
     
    762994  destQuad: array of TPointF);
    763995begin
     996  Init;
    764997  MapQuadToQuad(srcQuad,destQuad);
    765998end;
     
    9951228  m : single;
    9961229begin
    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
    10031234  begin
    10041235   m := 1/m;
     
    10181249var m: single;
    10191250begin
    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
    10251254  begin
    10261255   m := 1/scanDenom;
Note: See TracChangeset for help on using the changeset viewer.