У первоначального автора компонента 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}