Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.
/ GR32PNG Public archive

Commit

Permalink
* completed transcoding of interlaced / non-interlaced
Browse files Browse the repository at this point in the history
* tiny performance improvements for interlaced encoded images by skipping code that only performs checks which turn out to be all false.
  • Loading branch information
CWBudde committed Dec 6, 2010
1 parent ec47d5c commit d5bf6dd
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 128 deletions.
29 changes: 14 additions & 15 deletions GR32_Png.pas
Original file line number Diff line number Diff line change
Expand Up @@ -1085,31 +1085,30 @@ procedure TCustomPngAdam7Decoder.DecodeToScanline(
ctTrueColor: RowByteSize := (PixelPerRow * BitDepth * 3) div 8;
ctGrayscaleAlpha: RowByteSize := (PixelPerRow * BitDepth * 2) div 8;
ctTrueColorAlpha: RowByteSize := (PixelPerRow * BitDepth * 4) div 8;
else RowByteSize := 0;
else Continue;
end;

PassRow := CRowStart[CurrentPass];

// clear previous row
FillChar(FRowBuffer[1 - CurrentRow]^[0], RowByteSize, 0);

// check whether there are any bytes to process in this pass.
if RowByteSize > 0 then
while PassRow < FHeader.Height do
begin
// get interlaced row data
if FStream.Read(FRowBuffer[CurrentRow][0], RowByteSize + 1) <> (RowByteSize + 1)
then raise EPngError.Create(RCStrDataIncomplete);
// process pixel
while PassRow < FHeader.Height do
begin
// get interlaced row data
if FStream.Read(FRowBuffer[CurrentRow][0], RowByteSize + 1) <> (RowByteSize + 1)
then raise EPngError.Create(RCStrDataIncomplete);

DecodeFilterRow(TAdaptiveFilterMethod(FRowBuffer[CurrentRow]^[0]), FRowBuffer[CurrentRow], FRowBuffer[1 - CurrentRow], RowByteSize, PixelByteSize);
DecodeFilterRow(TAdaptiveFilterMethod(FRowBuffer[CurrentRow]^[0]), FRowBuffer[CurrentRow], FRowBuffer[1 - CurrentRow], RowByteSize, PixelByteSize);

// transfer and deinterlace image data
TransferData(CurrentPass, @FRowBuffer[CurrentRow][1], ScanLineCallback(Bitmap, PassRow));
// transfer and deinterlace image data
TransferData(CurrentPass, @FRowBuffer[CurrentRow][1], ScanLineCallback(Bitmap, PassRow));

// prepare for the next pass
Inc(PassRow, CRowIncrement[CurrentPass]);
CurrentRow := 1 - CurrentRow;
end;
// prepare for the next pass
Inc(PassRow, CRowIncrement[CurrentPass]);
CurrentRow := 1 - CurrentRow;
end;
end;
end;

Expand Down
236 changes: 140 additions & 96 deletions GR32_PortableNetworkGraphic.pas
Original file line number Diff line number Diff line change
Expand Up @@ -4073,7 +4073,6 @@ procedure TCustomPngTranscoder.EncodeFilterRow(CurrentRow, PreviousRow,
// check if paeth filter is the current best filter
if CurrentSum < BestSum then
begin
CurrentSum := BestSum;
Move(TempBuffer^[1], OutputRow^[1], BytesPerRow + 1);
OutputRow^[0] := 4;
end;
Expand Down Expand Up @@ -4872,7 +4871,7 @@ procedure TPortableNetworkGraphic.InterlaceMethodChanged;
case FImageHeader.InterlaceMethod of
imNone : begin
TranscoderClass := TPngNonInterlacedToAdam7Transcoder;
raise EPngError.Create(RCStrDirectInterlaceMethodSetError);
// raise EPngError.Create(RCStrDirectInterlaceMethodSetError);
end;
imAdam7 : TranscoderClass := TPngAdam7ToNonInterlacedTranscoder;
end;
Expand Down Expand Up @@ -5236,6 +5235,8 @@ procedure TPngNonInterlacedToAdam7Transcoder.Transcode;
Source : PByte;
Destination : PByte;
TempData : PByteArray;
OutputRow : PByteArray;
TempBuffer : PByteArray;
begin
// initialize variables
CurrentRow := 0;
Expand All @@ -5244,6 +5245,11 @@ procedure TPngNonInterlacedToAdam7Transcoder.Transcode;
GetMem(TempData, FHeader.Height * FHeader.BytesPerRow);
Destination := PByte(TempData);
try

/////////////////////////////////////
// decode image (Adam7-interlaced) //
/////////////////////////////////////

for Index := 0 to FHeader.Height - 1 do
begin
// read data from stream
Expand All @@ -5255,11 +5261,8 @@ procedure TPngNonInterlacedToAdam7Transcoder.Transcode;
FRowBuffer[CurrentRow], FRowBuffer[1 - CurrentRow], FHeader.BytesPerRow,
PixelByteSize);

// transfer data from row to image
// TransferData(@FRowBuffer[CurrentRow][1], ScanLineCallback(Bitmap, Index));

// transfer data from row to temp data
Source := @FRowBuffer[CurrentRow][1];

for PassRow := 0 to FHeader.Width - 1 do
begin
// copy bytes per pixels
Expand All @@ -5273,7 +5276,9 @@ procedure TPngNonInterlacedToAdam7Transcoder.Transcode;
CurrentRow := 1 - CurrentRow;
end;

(*
// reset position to zero
FStream.Seek(0, soFromBeginning);

// The Adam7 interlacer uses 7 passes to create the complete image
for CurrentPass := 0 to 6 do
begin
Expand All @@ -5288,73 +5293,78 @@ procedure TPngNonInterlacedToAdam7Transcoder.Transcode;
ctTrueColor : RowByteSize := (PixelPerRow * BitDepth * 3) div 8;
ctGrayscaleAlpha : RowByteSize := (PixelPerRow * BitDepth * 2) div 8;
ctTrueColorAlpha : RowByteSize := (PixelPerRow * BitDepth * 4) div 8;
else RowByteSize := 0;
else Continue;
end;

PassRow := CRowStart[CurrentPass];

// clear previous row
FillChar(FRowBuffer[1 - CurrentRow]^[0], RowByteSize, 0);

// check whether there are any bytes to process in this pass.
if RowByteSize > 0 then
while PassRow < FHeader.Height do
begin
// get interlaced row data
if FStream.Read(FRowBuffer[CurrentRow][0], RowByteSize + 1) <> (RowByteSize + 1)
then raise EPngError.Create(RCStrDataIncomplete);
// check if pre filter is used and eventually calculate pre filter
if FHeader.ColorType <> ctIndexedColor then
begin
GetMem(OutputRow, FHeader.BytesPerRow + 1);
GetMem(TempBuffer, FHeader.BytesPerRow + 1);
try
while PassRow < FHeader.Height do
begin
Index := CColumnStart[CurrentPass];
Source := @TempData[PassRow * FHeader.BytesPerRow + Index * PixelByteSize];
Destination := @FRowBuffer[CurrentRow][1];

repeat
// copy bytes per pixels
Move(Source^, Destination^, PixelByteSize);

Inc(Source, CColumnIncrement[CurrentPass] * PixelByteSize);
Inc(Destination, PixelByteSize);
Inc(Index, CColumnIncrement[CurrentPass]);
until Index >= FHeader.Width;

FilterRow(TAdaptiveFilterMethod(FRowBuffer[CurrentRow]^[0]), FRowBuffer[CurrentRow], FRowBuffer[1 - CurrentRow], RowByteSize, PixelByteSize);
// filter current row
EncodeFilterRow(FRowBuffer[CurrentRow], FRowBuffer[1 - CurrentRow],
OutputRow, TempBuffer, FHeader.BytesPerRow, FHeader.PixelByteSize);

// write data to data stream
FStream.Write(OutputRow[0], RowByteSize + 1);

// prepare for the next pass
Inc(PassRow, CRowIncrement[CurrentPass]);
CurrentRow := 1 - CurrentRow;
end;
finally
Dispose(OutputRow);
Dispose(TempBuffer);
end;
end
else
while PassRow < FHeader.Height do
begin
Index := CColumnStart[CurrentPass];
Source := @FRowBuffer[CurrentRow][1];
Destination := @TempData[PassRow * FHeader.BytesPerRow + Index * PixelByteSize];
Source := @TempData[PassRow * FHeader.BytesPerRow + Index * PixelByteSize];
Destination := @FRowBuffer[CurrentRow][1];

repeat
// copy bytes per pixels
Move(Source^, Destination^, PixelByteSize);

Inc(Source, PixelByteSize);
Inc(Destination, CColumnIncrement[CurrentPass] * PixelByteSize);
Inc(Source, CColumnIncrement[CurrentPass] * PixelByteSize);
Inc(Destination, PixelByteSize);
Inc(Index, CColumnIncrement[CurrentPass]);
until Index >= FHeader.Width;

// set filter method 0
FRowBuffer[CurrentRow][0] := 0;

// write data to data stream
FStream.Write(FRowBuffer[CurrentRow][0], RowByteSize + 1);

// prepare for the next pass
Inc(PassRow, CRowIncrement[CurrentPass]);
CurrentRow := 1 - CurrentRow;
end;
end;
*)

// reset position to zero
FStream.Seek(0, soFromBeginning);

// clear previous row buffer
FillChar(FRowBuffer[1 - CurrentRow]^[0], FHeader.BytesPerRow, 0);
Source := PByte(TempData);

for Index := 0 to FHeader.Height - 1 do
begin
// set filter method to none
FRowBuffer[CurrentRow]^[0] := 0;

Destination := @FRowBuffer[CurrentRow][1];

for PassRow := 0 to FHeader.Width - 1 do
begin
// copy bytes per pixels
Move(Source^, Destination^, PixelByteSize);

Inc(Source, PixelByteSize);
Inc(Destination, PixelByteSize);
end;

// write data to data stream
FStream.Write(FRowBuffer[CurrentRow][0], FHeader.BytesPerRow + 1);

// flip current row used
CurrentRow := 1 - CurrentRow;
end;

finally
Dispose(TempData);
end;
Expand All @@ -5375,14 +5385,21 @@ procedure TPngAdam7ToNonInterlacedTranscoder.Transcode;
Source : PByte;
Destination : PByte;
TempData : PByteArray;
OutputRow : PByteArray;
TempBuffer : PByteArray;
begin
// initialize variables
CurrentRow := 0;
PixelByteSize := FHeader.PixelByteSize;

GetMem(TempData, FHeader.Height * FHeader.BytesPerRow);
try
// The Adam7 interlacer uses 7 passes to create the complete image

/////////////////////////////////////
// decode image (Adam7-interlaced) //
/////////////////////////////////////

// The Adam7 deinterlacer uses 7 passes to create the complete image
for CurrentPass := 0 to 6 do
begin
// calculate some intermediate variables
Expand All @@ -5396,71 +5413,98 @@ procedure TPngAdam7ToNonInterlacedTranscoder.Transcode;
ctTrueColor : RowByteSize := (PixelPerRow * BitDepth * 3) div 8;
ctGrayscaleAlpha : RowByteSize := (PixelPerRow * BitDepth * 2) div 8;
ctTrueColorAlpha : RowByteSize := (PixelPerRow * BitDepth * 4) div 8;
else RowByteSize := 0;
else Continue;
end;

PassRow := CRowStart[CurrentPass];

// clear previous row
FillChar(FRowBuffer[1 - CurrentRow]^[0], RowByteSize, 0);

// check whether there are any bytes to process in this pass.
if RowByteSize > 0 then
while PassRow < FHeader.Height do
begin
// get interlaced row data
if FStream.Read(FRowBuffer[CurrentRow][0], RowByteSize + 1) <> (RowByteSize + 1)
then raise EPngError.Create(RCStrDataIncomplete);

DecodeFilterRow(TAdaptiveFilterMethod(FRowBuffer[CurrentRow]^[0]), FRowBuffer[CurrentRow], FRowBuffer[1 - CurrentRow], RowByteSize, PixelByteSize);

Index := CColumnStart[CurrentPass];
Source := @FRowBuffer[CurrentRow][1];
Destination := @TempData[PassRow * FHeader.BytesPerRow + Index * PixelByteSize];
repeat
// copy bytes per pixels
Move(Source^, Destination^, PixelByteSize);

Inc(Source, PixelByteSize);
Inc(Destination, CColumnIncrement[CurrentPass] * PixelByteSize);
Inc(Index, CColumnIncrement[CurrentPass]);
until Index >= FHeader.Width;

// prepare for the next pass
Inc(PassRow, CRowIncrement[CurrentPass]);
CurrentRow := 1 - CurrentRow;
end;
// process pixels
while PassRow < FHeader.Height do
begin
// get interlaced row data
if FStream.Read(FRowBuffer[CurrentRow][0], RowByteSize + 1) <> (RowByteSize + 1)
then raise EPngError.Create(RCStrDataIncomplete);

DecodeFilterRow(TAdaptiveFilterMethod(FRowBuffer[CurrentRow]^[0]),
FRowBuffer[CurrentRow], FRowBuffer[1 - CurrentRow], RowByteSize, PixelByteSize);

Index := CColumnStart[CurrentPass];
Source := @FRowBuffer[CurrentRow][1];
Destination := @TempData[PassRow * FHeader.BytesPerRow + Index * PixelByteSize];
repeat
// copy bytes per pixels
Move(Source^, Destination^, PixelByteSize);

Inc(Source, PixelByteSize);
Inc(Destination, CColumnIncrement[CurrentPass] * PixelByteSize);
Inc(Index, CColumnIncrement[CurrentPass]);
until Index >= FHeader.Width;

// prepare for the next pass
Inc(PassRow, CRowIncrement[CurrentPass]);
CurrentRow := 1 - CurrentRow;
end;
end;


// reset position to zero
FStream.Seek(0, soFromBeginning);


/////////////////////////////////
// encode image non-interlaced //
/////////////////////////////////

// clear previous row buffer
FillChar(FRowBuffer[1 - CurrentRow]^[0], FHeader.BytesPerRow, 0);
Source := PByte(TempData);

for Index := 0 to FHeader.Height - 1 do
// check if pre filter is used and eventually calculate pre filter
if FHeader.ColorType <> ctIndexedColor then
begin
// set filter method to none
FRowBuffer[CurrentRow]^[0] := 0;
GetMem(OutputRow, FHeader.BytesPerRow + 1);
GetMem(TempBuffer, FHeader.BytesPerRow + 1);
try
for Index := 0 to FHeader.Height - 1 do
begin
// copy bytes per pixels
Move(Source^, FRowBuffer[CurrentRow][1], FHeader.Width * PixelByteSize);
Inc(Source, FHeader.Width * PixelByteSize);

Destination := @FRowBuffer[CurrentRow][1];
// filter current row
EncodeFilterRow(FRowBuffer[CurrentRow], FRowBuffer[1 - CurrentRow],
OutputRow, TempBuffer, FHeader.BytesPerRow, FHeader.PixelByteSize);

for PassRow := 0 to FHeader.Width - 1 do
begin
// copy bytes per pixels
Move(Source^, Destination^, PixelByteSize);
// write data to data stream
FStream.Write(OutputRow[0], FHeader.BytesPerRow + 1);

Inc(Source, PixelByteSize);
Inc(Destination, PixelByteSize);
end;
// flip current row used
CurrentRow := 1 - CurrentRow;
end;
finally
Dispose(OutputRow);
Dispose(TempBuffer);
end;
end
else
for Index := 0 to FHeader.Height - 1 do
begin
// copy bytes per pixels
Move(Source^, FRowBuffer[CurrentRow][1], FHeader.Width * PixelByteSize);
Inc(Source, FHeader.Width * PixelByteSize);

// write data to data stream
FStream.Write(FRowBuffer[CurrentRow][0], FHeader.BytesPerRow + 1);
// set filter method to none
FRowBuffer[CurrentRow][0] := 0;

// flip current row used
CurrentRow := 1 - CurrentRow;
end;
// write data to data stream
FStream.Write(FRowBuffer[CurrentRow][0], FHeader.BytesPerRow + 1);

// flip current row used
CurrentRow := 1 - CurrentRow;
end;

finally
Dispose(TempData);
Expand Down
Loading

0 comments on commit d5bf6dd

Please sign in to comment.