德尔福旋转位图 在代码中

前端之家收集整理的这篇文章主要介绍了德尔福旋转位图 在代码中前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
将一个较大的位图旋转90或270度是否比使用反向坐标的嵌套循环更快?

位图为8bpp,通常为2048 * 2400 * 8bpp

目前我简单地用参数反转复制,大致(伪代码

for x = 0 to 2048-1
  for y = 0 to 2048-1
    dest[x][y]=src[y][x];

(实际上,我用指针做了一些更快的速度,但是大致相同)

GDI与大图像相当缓慢,纹理(GF7卡)的GPU加载/存储时间与当前cpu时间大致相同。

任何提示,指针?就地算法甚至会更好,但速度比原位更重要。

目标是德尔福,但它更是一个算法问题。 SSE(2)向量化没有问题,这对我来说是一个足够大的问题,在汇编程序中编码

跟随尼尔斯的回答

>图像2048×2700 – > 2700×2048
>编译器Turbo Explorer 2006与优化。
> Windows:电源方案设置为“始终开启”。 (重要!!!!)
>机器:Core2 6600(2.4 GHz)

时间与旧例程:32ms(步骤1)
时间与步骤8:12ms
时间与步骤16:10ms
时间与步骤32:9ms

同时我还在Athlon 64 X2(5200 iirc)上进行了测试,其速度略高于四分之一(80到19毫秒)。

谢谢你的加速。也许在夏季,我会用SSE(2)版本折磨自己。但是我已经考虑过如何解决这个问题,我想我将用完SSE2寄存器来直接实现:

for n:=0 to 7 do
  begin
    load r0,<source+n*rowsize> 
    shift byte from r0 into r1
    shift byte from r0 into r2
    ..
    shift byte from r0 into r8
  end; 
store r1,<target>   
store r2,<target+1*<rowsize>
..
store r8,<target+7*<rowsize>

所以8×8需要9个寄存器,但32位SSE只有8个。无论如何,这是夏天的一天:-)

注意,指针的东西是我本能的本能,但是实际上可能有一些东西,如果你的维度没有硬编码,那么编译器就不能把这个变成一个转变。虽然现在这样一个人很便宜,但是他们也会产生更多的登记压力。

代码(通过从“naieve”rotate1实现中减去结果来验证):

const stepsize = 32;
procedure rotatealign(Source: tbw8image; Target:tbw8image);

var stepsx,stepsy,restx,resty : Integer;
   RowPitchSource,RowPitchTarget : Integer;
   pSource,pTarget,ps1,ps2 : pchar;
   x,y,i,j: integer;
   rpstep : integer;
begin
  RowPitchSource := source.RowPitch;          // bytes to jump to next line. Can be negative (includes alignment)
  RowPitchTarget := target.RowPitch;        rpstep:=RowPitchTarget*stepsize;
  stepsx:=source.ImageWidth div stepsize;
  stepsy:=source.ImageHeight div stepsize;
  // check if mod 16=0 here for both dimensions,if so -> SSE2.
  for y := 0 to stepsy - 1 do
    begin
      psource:=source.GetImagePointer(0,y*stepsize);    // gets pointer to pixel x,y
      ptarget:=Target.GetImagePointer(target.imagewidth-(y+1)*stepsize,0);
      for x := 0 to stepsx - 1 do
        begin
          for i := 0 to stepsize - 1 do
            begin
              ps1:=@psource[rowpitchsource*i];   // ( 0,i)
              ps2:=@ptarget[stepsize-1-i];       //  (maxx-i,0);
              for j := 0 to stepsize - 1 do
               begin
                 ps2[0]:=ps1[j];
                 inc(ps2,RowPitchTarget);
               end;
            end;
          inc(psource,stepsize);
          inc(ptarget,rpstep);
        end;
    end;
  // 3 more areas to do,with dimensions
  // - stepsy*stepsize * restx        // right most column of restx width
  // - stepsx*stepsize * resty        // bottom row with resty height
  // - restx*resty                    // bottom-right rectangle.
  restx:=source.ImageWidth mod stepsize;   // typically zero because width is 
                                          // typically 1024 or 2048
  resty:=source.Imageheight mod stepsize;
  if restx>0 then
    begin
      // one loop less,since we know this fits in one line of  "blocks"
      psource:=source.GetImagePointer(source.ImageWidth-restx,0);    // gets pointer to pixel x,y
      ptarget:=Target.GetImagePointer(Target.imagewidth-stepsize,Target.imageheight-restx);
      for y := 0 to stepsy - 1 do
        begin
          for i := 0 to stepsize - 1 do
            begin
              ps1:=@psource[rowpitchsource*i];   // ( 0,0);
              for j := 0 to restx - 1 do
               begin
                 ps2[0]:=ps1[j];
                 inc(ps2,RowPitchTarget);
               end;
            end;
         inc(psource,stepsize*RowPitchSource);
         dec(ptarget,stepsize);
       end;
    end;
  if resty>0 then
    begin
      // one loop less,since we know this fits in one line of  "blocks"
      psource:=source.GetImagePointer(0,source.ImageHeight-resty);    // gets pointer to pixel x,y
      ptarget:=Target.GetImagePointer(0,0);
      for x := 0 to stepsx - 1 do
        begin
          for i := 0 to resty- 1 do
            begin
              ps1:=@psource[rowpitchsource*i];   // ( 0,i)
              ps2:=@ptarget[resty-1-i];       //  (maxx-i,stepsize);
         inc(ptarget,rpstep);
       end;
    end;
 if (resty>0) and (restx>0) then
    begin
      // another loop less,since only one block
      psource:=source.GetImagePointer(source.ImageWidth-restx,target.ImageHeight-restx);
      for i := 0 to resty- 1 do
        begin
          ps1:=@psource[rowpitchsource*i];   // ( 0,i)
          ps2:=@ptarget[resty-1-i];       //  (maxx-i,0);
          for j := 0 to restx - 1 do
            begin
              ps2[0]:=ps1[j];
              inc(ps2,RowPitchTarget);
            end;
       end;
    end;
end;

更新2泛型

我试图在Delphi XE中将此代码更新为泛型版本。我因为QC 99703失败了,而且论坛的人已经确认也存在于XE2中。请投票:-)

更新3泛型
现在在XE10工作

解决方法

是的,有更快的方法来做到这一点。

您的简单循环大部分时间都用于缓存未命中。这样会发生,因为你在一个紧张的循环中碰到很多不同的地方的数据。更糟糕的是,你的记忆位置恰好是两分之一的力量。这是缓存性能最差的大小。

如果改善了内存访问的位置,则可以改进此旋转算法。

这样做的一个简单方法是使用与您完整位图相同的代码来旋转其自己的每个8×8像素块,并将另一个循环将图像旋转分成8×8像素。

例如。这样的东西(没有检查,对C代码很抱歉,我的Delphi技能不是最新的):

// this is the outer-loop that breaks your image rotation
 // into chunks of 8x8 pixels each:
 for (int block_x = 0; block_x < 2048; block_x+=8)
 {
    for (int block_y = 0; blocky_y < 2048; block_y+=8)
    { 
       // this is the inner-loop that processes a block
       // of 8x8 pixels.
       for (int x= 0; x<8; x++)
         for (int y=0; y<8; y++)
            dest[x+block_x][y+block_y] = src[y+block_y][x+block_x]
    }
 }

还有其他方法。您可以处理Hilbert-Order或Morton-Order中的数据。这在理论上甚至会更快一点,但代码将会更加复杂。

Btw – 既然你提到SSE是你的选择。请注意,您可以旋转SSE寄存器中的8×8字节块。这是一个有点棘手的工作,但看看SSE矩阵转置代码应该让你开始,因为它是一样的事情。

编辑:

刚刚检查:

具有8×8像素的块大小,代码运行ca.在我的机器上快5倍。 16×16的块大小可以快10倍。

好像想尝试不同的块大小是好主意。

这是我使用的(非常简单的)测试程序:

#include <stdio.h>
#include <windows.h>

char temp1[2048*2048];
char temp2[2048*2048];

void rotate1 (void)
{
  int x,y;
  for (y=0; y<2048; y++)
  for (x=0; x<2048; x++)
    temp2[2048*y+x] = temp1[2048*x+y];
}

void rotate2 (void)
{
  int x,y;
  int bx,by;

  for (by=0; by<2048; by+=8)
  for (bx=0; bx<2048; bx+=8)
  for (y=0; y<8; y++)
  for (x=0; x<8; x++)
    temp2[2048*(y+by)+x+bx] = temp1[2048*(x+bx)+y+by];
}

void rotate3 (void)
{
  int x,by;

  for (by=0; by<2048; by+=16)
  for (bx=0; bx<2048; bx+=16)
  for (y=0; y<16; y++)
  for (x=0; x<16; x++)
    temp2[2048*(y+by)+x+bx] = temp1[2048*(x+bx)+y+by];
}


int main (int argc,char **args)
{
  int i,t1;

  t1 = GetTickCount();
  for (i=0; i<20; i++) rotate1();
  printf ("%d\n",GetTickCount()-t1);

  t1 = GetTickCount();
  for (i=0; i<20; i++) rotate2();
  printf ("%d\n",GetTickCount()-t1);

  t1 = GetTickCount();
  for (i=0; i<20; i++) rotate3();
  printf ("%d\n",GetTickCount()-t1);

}
原文链接:https://www.f2er.com/delphi/103403.html

猜你在找的Delphi相关文章