Есть ли более быстрый способ поворота большого растрового изображения на 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) в той же величине, что и текущее время процессора.
Любые советы, указатели? Алгоритм на месте был бы даже лучше, но скорость важнее, чем быть на месте.
Цель - Delphi, но это скорее алгоритмический вопрос. SSE (2) векторизация не проблема, для меня достаточно большая проблема, чтобы закодировать ее в ассемблере
Следуйте за ответом Нилса
- Изображение 2048x2700 → 2700x2048
- Компилятор Turbo Explorer 2006 с оптимизацией.
- Windows: схема питания установлена на "Always on". (<Б > важно!!!!)
- Машина: Core2 6600 (2,4 ГГц)
время со старой рутиной: 32 мс (шаг 1)
время с шагом 8: 12ms
время с шагом 16: 10 мс
время с шагом 32+: 9 мс
Между тем я также тестировал 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>   
Таким образом, 8x8 нуждается в 9 регистрах, но 32-разрядный SSE имеет только 8. Во всяком случае, это что-то для летних месяцев: -)
Обратите внимание, что вещь-указатель - это то, что я делаю из инстинкта, но это может быть что-то для нее, если ваши измерения не жестко закодированы, компилятор не может превратить мул в сдвиг. В то время как muls an sich дешево в наши дни, они также генерируют большее давление afaik.
Код (подтвержденный вычитанием результата из реализации "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:[email protected][rowpitchsource*i];   // ( 0,i)
              ps2:[email protected][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:[email protected][rowpitchsource*i];   // ( 0,i)
              ps2:[email protected][stepsize-1-i];       //  (maxx-i,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:[email protected][rowpitchsource*i];   // ( 0,i)
              ps2:[email protected][resty-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;
 if (resty>0) and (restx>0) then
    begin
      // another loop less, since only one block
      psource:=source.GetImagePointer(source.ImageWidth-restx,source.ImageHeight-resty);    // gets pointer to pixel x,y
      ptarget:=Target.GetImagePointer(0,target.ImageHeight-restx);
      for i := 0 to resty- 1 do
        begin
          ps1:[email protected][rowpitchsource*i];   // ( 0,i)
          ps2:[email protected][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 Generics
Я попытался обновить этот код до версии generics в Delphi XE. Я потерпел неудачу из-за QC 99703, и люди форума уже подтвердили, что он также существует в XE2. Пожалуйста, проголосуйте за него:-)
Обновление 3 Generics Работает сейчас в XE10
