Компонент поворота изображения PNG - PullRequest
3 голосов
/ 01 декабря 2011

Может ли кто-нибудь помочь мне найти компонент или SDK, который быстро вращает изображения PNG, сохраняя при этом прозрачность?

1 Ответ

3 голосов
/ 01 декабря 2011

У первоначального автора компонента PNGImage (основа нативного компонента Delphi) был форум, на котором он и другие опубликовали фрагменты кода о том, как работать с компонентом PNGImage.

Перед форумомЯ взял копию всех фрагментов кода и разместил их на веб-сайте CodeGear Code Central.

Большинство, если не все, работают с собственными изображениями PNG и поддерживают альфа-канал.

Вот полный список примеров, включенных в пакет:

Smooth rotates a PNG object
Resizes a TPNGObject using a smooth algorithm
Slice one PNG into several smaller ones
Saves an image as either a bitmap or a png.
Sample chunk descendant
Read all tEXt-Chunks and write values into a TStrings object
Display a message box with information extracted from the PNG File
Finds and cuts a block from a PNG image
This method converts the png into a jpeg object
This method converts the png into a bmp object
Overlay one PNG over another
This makes the image half transparent
Flips a png image vertically and saves back
Draws a png image over the desktop

Вот ссылка: Методы CodeCentral PNG

РЕДАКТИРОВАТЬ

Вот высоко оптимизированная версия функции RotatePNG.Технически, он все еще идет попиксельно, но использует ряд методов, таких как линии сканирования, математика указателя и сохранение размеров изображения для достижения приличного увеличения скорости.

Я использовал изображение 2550x3300 пикселей (~ 5 МБ) для тестирования и с использованием полугрубых, но полностью (не) научных расчетов (считая в моей голове) я придумал следующие метрики:

the old routine (mentioned above):~7 seconds
the new routine (code below):~1.5 seconds

Я не могу строго взять кредит на это.Код изначально был взят с веб-сайта EFG , и я решил попробовать свои силы в преобразовании одной из этих подпрограмм для поворота изображений PNG вместо растровых изображений.

Я уверен, что любой, кто знает больше оэтот тип вещей посмотрит на код и предложит несколько советов, чтобы настроить его быстрее.

procedure RotatePNG(
        const PNGOriginal:TPNGImage;//input PNG 
        out   PNGRotated:TPNGImage; //output PNG
    Const Angle : double);
{
  (c) har*GIS L.L.C., 1999
    You are free to use this in any way, but please retain this comment block.
    Please email questions to jim@har-gis.com .
    Doc & Updates: http://www.efg2.com/Lab/ImageProcessing/RotateScanline.htm
    and http://www.efg2.com/Lab/Library/Delphi/Graphics/JimHargis_RotateBitMap.zip
}
{
  Modified by R.J.Mills, 2012 - 
    - Use pointer arithmetic instead of type sub-scripting for faster pixels.  
    - Converted to rotate PNG images June 2012.
}

Type
  TRGBTripleArray = array [0..32767] of TRGBTriple; //allow integer subscript
  pRGBTripleArray = ^TRGBTripleArray;

VAR
  wPng : TPngImage;
  theta:Double;  // rotn angle in radians counterclockwise in windows
  cosTheta       :  Single;   {in windows}
  sinTheta       :  Single;
  i              :  INTEGER;
  iOriginal      :  INTEGER;
  iPrime         :  INTEGER;
  j              :  INTEGER;
  jOriginal      :  INTEGER;
  jPrime         :  INTEGER;
  NewWidth,NewHeight:INTEGER;
  nBytes: Integer;//no. bytes per pixelformat
  Oht,Owi,Rht,Rwi: Integer;//Original and Rotated subscripts to bottom/right
  RowSizeRot : integer;
  RowSizeOrg : integer;
  AlphaSizeRot : integer;
  AlphaSizeOrg : integer;
  RowStartPtr : Pointer;
  AlphaStartPtr : Pointer;

  RowRotatedT: pRGBtripleArray; //3 bytes
  AlphaRowT: pByteArray;    //1 byte
  AlphaRotatedT : pByteArray;   //1 byte

  TransparentT: TRGBTriple;

{=======================================}
 function Mod360( const angle:Double ):Double;
 begin
   result := frac( angle/360 )*360;
   if result < 0 then
     result := result+360;
 end;
{=======================================}

begin
  Theta := -(2*pi* Mod360(angle))/360;
  sinTheta := SIN( theta );
  cosTheta := COS( theta );

    NewWidth  := ABS( ROUND( PNGOriginal.Height*sinTheta) ) + ABS( ROUND( PNGOriginal.Width*cosTheta ) );
    NewHeight := ABS( ROUND( PNGOriginal.Width*sinTheta ) ) + ABS( ROUND( PNGOriginal.Height*cosTheta) );

  if ( ABS(theta)*MAX( PNGOriginal.width,PNGOriginal.height ) ) > 1 then
  begin//non-zero rotation

    wPng := TPngImage.createblank(PNGOriginal.Header.ColorType, 8, NewWidth, NewHeight);
    try

    //local constants for loop, each was hit at least width*height times   1/8/00
      Rwi := NewWidth - 1; //right column index
      Rht := NewHeight - 1;//bottom row index
      Owi := PNGOriginal.Width - 1;    //transp color column index
      Oht := PNGOriginal.Height - 1;   //transp color row  index

      RowRotatedT := wPng.Scanline[ Rht ] ;

      RowStartPtr := PNGOriginal.Scanline[ 0 ];
      RowSizeRot := Integer(wPng.ScanLine[1])-Integer(wPng.ScanLine[0]);
      RowSizeOrg := Integer(PNGOriginal.ScanLine[1])-Integer(PNGOriginal.ScanLine[0]);

      TransparentT := pRGBtripleArray( PNGOriginal.Scanline[ Oht ] )[0];

      if PNGOriginal.Header.ColorType in [COLOR_RGBALPHA] then
      begin
        AlphaRowT := PNGOriginal.AlphaScanline[ Oht ];
        AlphaStartPtr := PNGOriginal.AlphaScanline[ 0 ];
        AlphaRotatedT := wPng.AlphaScanline[ Rht ];
        AlphaSizeRot := Integer(wPng.AlphaScanline[1])-Integer(wPng.AlphaScanline[0]);
        AlphaSizeOrg := Integer(PNGOriginal.AlphaScanline[1])-Integer(PNGOriginal.AlphaScanline[0]);
      end
      else
      begin
        AlphaRowT := nil;
        AlphaStartPtr := nil;
        AlphaRotatedT := nil;
        AlphaSizeRot := 0;
        AlphaSizeOrg := 0;
      end;

      for j := Rht downto 0 DO   //1/8/00
      begin //for j
        jPrime := 2*j - NewHeight + 1 ;
        for i := Rwi downto 0 DO   //1/8/00
        begin //for i
          iPrime := 2*i - NewWidth   + 1;
          iOriginal := ( ROUND( iPrime*CosTheta - jPrime*sinTheta ) + Owi ) div 2;

          if ( iOriginal >= 0 ) and ( iOriginal <= Owi ) then
          begin //inside
            jOriginal := ( ROUND( iPrime*sinTheta + jPrime*cosTheta ) + Oht ) div 2 ;
            if ( jOriginal >= 0 ) and ( jOriginal <= Oht ) then
            begin    //1/8/00
              RowRotatedT[i] := pRGBTripleArray(Integer(RowStartPtr) + (jOriginal * RowSizeOrg))[iOriginal];
              if assigned(AlphaRotatedT) then
                AlphaRotatedT[i] := pByteArray(Integer(AlphaStartPtr) + (jOriginal * AlphaSizeOrg))[iOriginal];
            end
            else
            begin //set Transparent
              if Assigned(AlphaRotatedT) then
                AlphaRotatedT[i] := 0;
              RowRotatedT[i] := TransparentT;
            end;
          end //inside
          else
          begin //Set Transpaarent;
            if Assigned(AlphaRotatedT) then
              AlphaRotatedT[i] := 0;
            RowRotatedT[i] := TransparentT;
          end;
        end; //for i
        Dec(Integer(RowRotatedT), RowSizeRot) ;
        if assigned(AlphaRotatedT) then
          Dec(Integer(AlphaRotatedT), AlphaSizeRot) ;
      end;//for j
      PNGRotated.Assign(wPng);
    finally
      wPng.Free;
    end;
  end //non-zero rotation
  else
  begin //Zero rotation         
    if PNGRotated <> PNGOriginal then
      PNGRotated.Assign(PNGOriginal);
  end;
end; {RotatePNG}
...