CnPack Forum » CnVCL 组件包 » 旋转位图


2006-7-21 19:17 alblert
旋转位图

没学过线性代数,也不知道什么是插值算法、矩阵,最近在网上看了点资料,补了补,自己写了段二次插值算法旋转位图的代码(纯粹是根据二次插值的原理写的),图象效果还行,就是速度太慢,根本没有实用价值,cnpack的图象处理单元有快速二次插值旋转位图的算法,可惜没看懂,原理网上也有--将浮点运算变为整数运算,但具体实现我做不出来,在此请教cnpack的高手,可否解释一下cnpack那段快速二次插值旋转位图的代码,不甚感激!!!
另外,使用cnBitmap.rotate旋转后,部分图象看不见,如何将整个位图显示出来?

下面是我写的,如何该为快速的旋转方法(如何将浮点运算变为整数运算)

type
  PPixelArray = ^TPixelArray;
  TPixelArray = array[word] of TRGBQuad;

procedure RotateBitmap(Src: TBitmap; out Dst: TBitmap;Center:point; Angle: single);
var
  cosRadians: single;
  sinRadians: single;
  inX: Integer;
  inY: Integer;
  oldX: Double;
  oldY: Double;
  oldRow: PPixelArray;
  RotatedRow: PPixelArray;
  iX, iY: Integer;
  dX, dY: single;
  SrcWidth, SrcHeight: Integer;
  Sx, Sy: single;
begin
  Angle := -(Angle) * PI / 180;
  SrcWidth := Src.Width;
  SrcHeight := Src.Height;
  sinRadians := Sin(Angle);
  cosRadians := Cos(Angle);
  Sx := Abs(SrcWidth * cosRadians) + Abs(SrcHeight * sinRadians);
  Sy := Abs(SrcWidth * sinRadians) + Abs(SrcHeight * cosRadians);
  Dst.Width := round(Sx);
  Dst.Height := round(Sy);
  
    for inY := 0 to Dst.Height - 1 do begin
    RotatedRow := Dst.Scanline[inY];
    for inX := 0 to Dst.Width - 1 do begin
      oldX := (inX - center.x) * cosRadians + (inY - center.Y) * sinRadians + center.x;
      oldY := -(inX - center.x) * sinRadians + (inY - center.Y) * cosRadians + center.Y;
      if (oldX > 0) and (oldX < SrcWidth - 1) and (oldY > 0) and (oldY < SrcHeight - 1) then begin
        iX := trunc(oldX); iY := trunc(oldY);
        dX := frac(oldX); dY := frac(oldY);
        oldRow := Src.Scanline[iY];
        RotatedRow[inX].rgbBlue := round((oldRow[iX].rgbBlue * (1 - dX) + oldRow[iX + 1].rgbBlue * dX) * (1 - dY));
        RotatedRow[inX].rgbGreen := round((oldRow[iX].rgbGreen * (1 - dX) + oldRow[iX + 1].rgbGreen * dX) * (1 - dY));
        RotatedRow[inX].rgbRed := round((oldRow[iX].rgbRed * (1 - dX) + oldRow[iX + 1].rgbRed * dX) * (1 - dY));
        RotatedRow[inX].rgbReserved := round((oldRow[iX].rgbReserved * (1 - dX) + oldRow[iX + 1].rgbReserved * dX) * (1 - dY));
        oldRow := Src.Scanline[iY + 1];
        RotatedRow[inX].rgbBlue := RotatedRow[inX].rgbBlue + round((oldRow[iX].rgbBlue * (1 - dX) + oldRow[iX + 1].rgbBlue * dX) * dY);
        RotatedRow[inX].rgbGreen := RotatedRow[inX].rgbGreen + round((oldRow[iX].rgbGreen * (1 - dX) + oldRow[iX + 1].rgbGreen * dX) * dY);
        RotatedRow[inX].rgbRed := RotatedRow[inX].rgbRed + round((oldRow[iX].rgbRed * (1 - dX) + oldRow[iX + 1].rgbRed * dX) * dY);
        RotatedRow[inX].rgbReserved := RotatedRow[inX].rgbReserved + round((oldRow[iX].rgbReserved * (1 - dX) + oldRow[iX + 1].rgbReserved * dX) * dY);

      end
      else begin
        RotatedRow[inX].rgbBlue := 0;
        RotatedRow[inX].rgbGreen := 0;
        RotatedRow[inX].rgbRed := 0;
        RotatedRow[inX].rgbReserved := 0;
      end;
    end;
  end;
end;

[[i] Last edited by alblert on 2006-7-21 at 19:26 [/i]]

2006-7-24 14:26 alblert
zx := SrcX and $7FFF; //取低15位,既旋转后坐标的小数部分
zy := SrcY and $7FFF; //同上
izy := zy xor $7FFF;  //???
w2 := (zx * izy) shr 15; // 计算加权值
w1 := izy - w2;
w4 := (zx * zy) shr 15;
w3 := zy - w4;
Dst.b := (Col1.b * w1 + Col2.b * w2 + Col3.b * w3 + Col4.b * w4) shr 15;
Dst.g := (Col1.g * w1 + Col2.g * w2 + Col3.g * w3 + Col4.g * w4) shr 15;
Dst.r := (Col1.r * w1 + Col2.r * w2 + Col3.r * w3 + Col4.r * w4) shr 15;

下面都没看懂
izy := zy xor $7FFF;  异或运算后是什么?
...
...

2006-7-24 20:03 zjy
这段代码使用整数模拟小数运算的方法来提高性能。旋转函数的代码没写什么注释,你可以看看缩放函数的代码:SmoothResize,这里面有原始公式及推导和演化,其实主要就是分别计算插值区域四个点的权值。

代码中用 int32 的低 15 位表示小数部分。
izy := zy xor $7FFF; 这行相当于 izy := $8000 - zy; 也就是小数的 1.0 - zy。
(似乎 xor 的速度比减法快,或者可以跟相邻的指令同步执行,原因不太记得了,也可能跟用减法效果一样吧)

这段代码是我根据缩放代码修改而来,当时忘记写详细的文档,现在自己也不太记得了:(

2006-7-25 14:28 alblert
总算想明白了,和斑竹说的差不多
把我的代码修改之后,速度的确快了很多

  Vdst为反向计算原始点的颜色值
  V11,V21,V12,V22为四个邻近点的颜色值
  i水平方向比例系数
  j垂直方向比例系数
  Vdst=(V11*(1-i)+V21*i)*(1-j) + (V12*(1-i)+V21*i)*j
      =V11*(1-i)*(1-j) + V21*i*(1-j) + (V12*(1-i)*j + V21*i*j
  
  zx := SrcX and $7FFF;       // 旋转后的低15bit,即小数部分 => i
  zy := SrcY and $7FFF;       //同上 => j
  izy := zy xor $7FFF;        // 1-j
  w2 := (zx * izy) shr 15;    // i*(1-j)         
  w1 := izy - w2;             //1-j-i*(1-j) => (1-i)*(1-j)
  w4 := (zx * zy) shr 15;     // i*j
  w3 := zy - w4;              //j-i*j => j*(1-i)

[[i] Last edited by alblert on 2006-7-25 at 14:36 [/i]]

页: [1]
查看完整版本: 旋转位图


Powered by Discuz! Archiver 5.0.0  © 2001-2006 Comsenz Inc.