mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-20 06:59:42 +02:00

* removed color conversion routines (except gray) * memory image with size 0,0 trew an exception * creation of a TMemoryImage of size 0,0 will have no color in palette
835 lines
22 KiB
ObjectPascal
835 lines
22 KiB
ObjectPascal
{
|
|
$Id$
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2003 by the Free Pascal development team
|
|
|
|
PNG reader implementation
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
**********************************************************************}
|
|
{$mode objfpc}{$h+}
|
|
unit FPReadPNG;
|
|
|
|
interface
|
|
|
|
uses
|
|
SysUtils,Classes, FPImage, FPImgCmn, PNGComn, ZStream;
|
|
|
|
Type
|
|
|
|
TSetPixelProc = procedure (x,y:integer; CD : TColordata) of object;
|
|
TConvertColorProc = function (CD:TColorData) : TFPColor of object;
|
|
|
|
TFPReaderPNG = class (TFPCustomImageReader)
|
|
private
|
|
Chunk : TChunk;
|
|
FHeader : THeaderChunk;
|
|
ZData : TMemoryStream; // holds compressed data until all blocks are read
|
|
Decompress : TDeCompressionStream; // decompresses the data
|
|
FPltte : boolean; // if palette is used
|
|
FCountScanlines : EightLong; //Number of scanlines to process for each pass
|
|
FScanLineLength : EightLong; //Length of scanline for each pass
|
|
FCurrentPass : byte;
|
|
ByteWidth : byte; // number of bytes to read for pixel information
|
|
BitsUsed : EightLong; // bitmasks to use to split a byte into smaller parts
|
|
BitShift : byte; // shift right to do of the bits extracted with BitsUsed for 1 element
|
|
CountBitsUsed : byte; // number of bit groups (1 pixel) per byte (when bytewidth = 1)
|
|
//CFmt : TColorFormat; // format of the colors to convert from
|
|
StartX,StartY, DeltaX,DeltaY, StartPass,EndPass : integer; // number and format of passes
|
|
FSwitchLine, FCurrentLine, FPreviousLine : pByteArray;
|
|
FPalette : TFPPalette;
|
|
FSetPixel : TSetPixelProc;
|
|
FConvertColor : TConvertColorProc;
|
|
procedure ReadChunk;
|
|
procedure HandleData;
|
|
procedure HandleUnknown;
|
|
function ColorGray1 (CD:TColorData) : TFPColor;
|
|
function ColorGray2 (CD:TColorData) : TFPColor;
|
|
function ColorGray4 (CD:TColorData) : TFPColor;
|
|
function ColorGray8 (CD:TColorData) : TFPColor;
|
|
function ColorGray16 (CD:TColorData) : TFPColor;
|
|
function ColorGrayAlpha8 (CD:TColorData) : TFPColor;
|
|
function ColorGrayAlpha16 (CD:TColorData) : TFPColor;
|
|
function ColorColor8 (CD:TColorData) : TFPColor;
|
|
function ColorColor16 (CD:TColorData) : TFPColor;
|
|
function ColorColorAlpha8 (CD:TColorData) : TFPColor;
|
|
function ColorColorAlpha16 (CD:TColorData) : TFPColor;
|
|
protected
|
|
UseTransparent, EndOfFile : boolean;
|
|
TransparentDataValue : TColorData;
|
|
UsingBitGroup : byte;
|
|
DataIndex : longword;
|
|
DataBytes : TColorData;
|
|
function CurrentLine(x:longword) : byte;
|
|
function PrevSample (x:longword): byte;
|
|
function PreviousLine (x:longword) : byte;
|
|
function PrevLinePrevSample (x:longword): byte;
|
|
procedure HandleChunk; virtual;
|
|
procedure HandlePalette; virtual;
|
|
procedure HandleAlpha; virtual;
|
|
function CalcX (relX:integer) : integer;
|
|
function CalcY (relY:integer) : integer;
|
|
function CalcColor: TColorData;
|
|
procedure HandleScanLine (const y : integer; const ScanLine : PByteArray); virtual;
|
|
procedure DoDecompress; virtual;
|
|
function DoFilter(LineFilter:byte;index:longword; b:byte) : byte; virtual;
|
|
procedure SetPalettePixel (x,y:integer; CD : TColordata);
|
|
procedure SetPalColPixel (x,y:integer; CD : TColordata);
|
|
procedure SetColorPixel (x,y:integer; CD : TColordata);
|
|
procedure SetColorTrPixel (x,y:integer; CD : TColordata);
|
|
function DecideSetPixel : TSetPixelProc; virtual;
|
|
procedure InternalRead (Str:TStream; Img:TFPCustomImage); override;
|
|
function InternalCheck (Str:TStream) : boolean; override;
|
|
//property ColorFormat : TColorformat read CFmt;
|
|
property ConvertColor : TConvertColorProc read FConvertColor;
|
|
property CurrentPass : byte read FCurrentPass;
|
|
property Pltte : boolean read FPltte;
|
|
property ThePalette : TFPPalette read FPalette;
|
|
property Header : THeaderChunk read FHeader;
|
|
property CountScanlines : EightLong read FCountScanlines;
|
|
property ScanLineLength : EightLong read FScanLineLength;
|
|
public
|
|
constructor create; override;
|
|
destructor destroy; override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
|
|
|
|
const StartPoints : array[0..7, 0..1] of word =
|
|
((0,0),(0,0),(4,0),(0,4),(2,0),(0,2),(1,0),(0,1));
|
|
Delta : array[0..7,0..1] of word =
|
|
((1,1),(8,8),(8,8),(4,8),(4,4),(2,4),(2,2),(1,2));
|
|
BitsUsed1Depth : EightLong = ($80,$40,$20,$10,$08,$04,$02,$01);
|
|
BitsUsed2Depth : EightLong = ($C0,$30,$0C,$03,0,0,0,0);
|
|
BitsUsed4Depth : EightLong = ($F0,$0F,0,0,0,0,0,0);
|
|
|
|
constructor TFPReaderPNG.create;
|
|
begin
|
|
inherited;
|
|
chunk.acapacity := 0;
|
|
chunk.data := nil;
|
|
UseTransparent := False;
|
|
end;
|
|
|
|
destructor TFPReaderPNG.destroy;
|
|
begin
|
|
with chunk do
|
|
if acapacity > 0 then
|
|
freemem (data);
|
|
inherited;
|
|
end;
|
|
|
|
procedure TFPReaderPNG.ReadChunk;
|
|
|
|
var ChunkHeader : TChunkHeader;
|
|
readCRC : longword;
|
|
l : longword;
|
|
begin
|
|
TheStream.Read (ChunkHeader,sizeof(ChunkHeader));
|
|
with chunk do
|
|
begin
|
|
// chunk header
|
|
with ChunkHeader do
|
|
begin
|
|
alength := swap(CLength);
|
|
ReadType := CType;
|
|
end;
|
|
aType := low(TChunkTypes);
|
|
while (aType < high(TChunkTypes)) and (ChunkTypes[aType] <> ReadType) do
|
|
inc (aType);
|
|
if alength > MaxChunkLength then
|
|
raise PNGImageException.Create ('Invalid chunklength');
|
|
if alength > acapacity then
|
|
begin
|
|
if acapacity > 0 then
|
|
freemem (data);
|
|
GetMem (data, alength);
|
|
acapacity := alength;
|
|
end;
|
|
l := TheStream.read (data^, alength);
|
|
if l <> alength then
|
|
raise PNGImageException.Create ('Chunk length exceeds stream length');
|
|
TheStream.Read (readCRC, sizeof(ReadCRC));
|
|
l := CalculateCRC (All1Bits, ReadType, sizeOf(ReadType));
|
|
l := CalculateCRC (l, data^, alength);
|
|
l := swap(l xor All1Bits);
|
|
if ReadCRC <> l then
|
|
raise PNGImageException.Create ('CRC check failed');
|
|
end;
|
|
end;
|
|
|
|
procedure TFPReaderPNG.HandleData;
|
|
var OldSize : longword;
|
|
begin
|
|
OldSize := ZData.size;
|
|
ZData.Size := OldSize + Chunk.aLength;
|
|
ZData.Write (chunk.Data^, chunk.aLength);
|
|
end;
|
|
|
|
procedure TFPReaderPNG.HandleAlpha;
|
|
procedure PaletteAlpha;
|
|
var r : integer;
|
|
a : word;
|
|
c : TFPColor;
|
|
begin
|
|
with chunk do
|
|
begin
|
|
if alength > longword(ThePalette.count) then
|
|
raise PNGImageException.create ('To much alpha values for palette');
|
|
for r := 0 to alength-1 do
|
|
begin
|
|
c := ThePalette[r];
|
|
a := data^[r];
|
|
c.alpha := (a shl 16) + a;
|
|
ThePalette[r] := c;
|
|
end;
|
|
end;
|
|
end;
|
|
procedure TransparentGray;
|
|
var a : word;
|
|
begin
|
|
move (chunk.data^[0], a, 2);
|
|
a := swap (a);
|
|
TransparentDataValue := a;
|
|
UseTransparent := True;
|
|
end;
|
|
procedure TransparentColor;
|
|
var d : byte;
|
|
r,g,b : word;
|
|
a : TColorData;
|
|
begin
|
|
with chunk do
|
|
begin
|
|
move (data^[0], r, 2);
|
|
move (data^[2], g, 2);
|
|
move (data^[4], b, 2);
|
|
end;
|
|
r := swap (r);
|
|
g := swap (g);
|
|
b := swap (b);
|
|
d := header.bitdepth;
|
|
a := (TColorData(b) shl d) shl d;
|
|
a := a + (TColorData(g) shl d) + r;
|
|
TransparentDataValue := a;
|
|
UseTransparent := True;
|
|
end;
|
|
begin
|
|
case header.ColorType of
|
|
3 : PaletteAlpha;
|
|
0 : TransparentGray;
|
|
2 : TransparentColor;
|
|
end;
|
|
end;
|
|
|
|
procedure TFPReaderPNG.HandlePalette;
|
|
var r : longword;
|
|
c : TFPColor;
|
|
t : word;
|
|
begin
|
|
if header.colortype = 3 then
|
|
with chunk do
|
|
begin
|
|
if TheImage.UsePalette then
|
|
FPalette := TheImage.Palette
|
|
else
|
|
FPalette := TFPPalette.Create(0);
|
|
c.Alpha := AlphaOpaque;
|
|
if (aLength mod 3) > 0 then
|
|
raise PNGImageException.Create ('Impossible length for PLTE-chunk');
|
|
r := 0;
|
|
ThePalette.count := 0;
|
|
while r < alength do
|
|
begin
|
|
t := data^[r];
|
|
c.red := t + (t shl 8);
|
|
inc (r);
|
|
t := data^[r];
|
|
c.green := t + (t shl 8);
|
|
inc (r);
|
|
t := data^[r];
|
|
c.blue := t + (t shl 8);
|
|
inc (r);
|
|
ThePalette.Add (c);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TFPReaderPNG.SetPalettePixel (x,y:integer; CD : TColordata);
|
|
begin // both PNG and palette have palette
|
|
TheImage.Pixels[x,y] := CD;
|
|
end;
|
|
|
|
procedure TFPReaderPNG.SetPalColPixel (x,y:integer; CD : TColordata);
|
|
begin // PNG with palette, Img without
|
|
TheImage.Colors[x,y] := ThePalette[CD];
|
|
end;
|
|
|
|
procedure TFPReaderPNG.SetColorPixel (x,y:integer; CD : TColordata);
|
|
var c : TFPColor;
|
|
begin // both PNG and Img work without palette, and no transparency colordata
|
|
// c := ConvertColor (CD,CFmt);
|
|
c := ConvertColor (CD);
|
|
TheImage.Colors[x,y] := c;
|
|
end;
|
|
|
|
procedure TFPReaderPNG.SetColorTrPixel (x,y:integer; CD : TColordata);
|
|
var c : TFPColor;
|
|
begin // both PNG and Img work without palette, and there is a transparency colordata
|
|
//c := ConvertColor (CD,CFmt);
|
|
c := ConvertColor (CD);
|
|
if TransparentDataValue = CD then
|
|
c.alpha := alphaTransparent;
|
|
TheImage.Colors[x,y] := c;
|
|
end;
|
|
|
|
function TFPReaderPNG.CurrentLine(x:longword):byte;
|
|
begin
|
|
result := FCurrentLine^[x];
|
|
end;
|
|
|
|
function TFPReaderPNG.PrevSample (x:longword): byte;
|
|
begin
|
|
if x < byteWidth then
|
|
result := 0
|
|
else
|
|
result := FCurrentLine^[x - bytewidth];
|
|
end;
|
|
|
|
function TFPReaderPNG.PreviousLine (x:longword) : byte;
|
|
begin
|
|
result := FPreviousline^[x];
|
|
end;
|
|
|
|
function TFPReaderPNG.PrevLinePrevSample (x:longword): byte;
|
|
begin
|
|
if x < byteWidth then
|
|
result := 0
|
|
else
|
|
result := FPreviousLine^[x - bytewidth];
|
|
end;
|
|
|
|
function TFPReaderPNG.DoFilter(LineFilter:byte;index:longword; b:byte) : byte;
|
|
var diff : byte;
|
|
procedure FilterSub;
|
|
begin
|
|
diff := PrevSample(index);
|
|
end;
|
|
procedure FilterUp;
|
|
begin
|
|
diff := PreviousLine(index);
|
|
end;
|
|
procedure FilterAverage;
|
|
var l, p : word;
|
|
begin
|
|
l := PrevSample(index);
|
|
p := PreviousLine(index);
|
|
diff := (l + p) div 2;
|
|
end;
|
|
procedure FilterPaeth;
|
|
var dl, dp, dlp : word; // index for previous and distances for:
|
|
l, p, lp : byte; // r:predictor, Left, Previous, LeftPrevious
|
|
r : integer;
|
|
begin
|
|
l := PrevSample(index);
|
|
lp := PrevLinePrevSample(index);
|
|
p := PreviousLine(index);
|
|
r := l + p - lp;
|
|
dl := abs (r - l);
|
|
dlp := abs (r - lp);
|
|
dp := abs (r - p);
|
|
if (dl <= dp) and (dl <= dlp) then
|
|
diff := l
|
|
else if dp <= dlp then
|
|
diff := p
|
|
else
|
|
diff := lp;
|
|
end;
|
|
begin
|
|
case LineFilter of
|
|
0 : diff := 0;
|
|
1 : FilterSub;
|
|
2 : FilterUp;
|
|
3 : FilterAverage;
|
|
4 : FilterPaeth;
|
|
end;
|
|
result := (b + diff) mod $100;
|
|
end;
|
|
|
|
function TFPReaderPNG.DecideSetPixel : TSetPixelProc;
|
|
begin
|
|
if Pltte then
|
|
if TheImage.UsePalette then
|
|
result := @SetPalettePixel
|
|
else
|
|
result := @SetPalColPixel
|
|
else
|
|
if UseTransparent then
|
|
result := @SetColorTrPixel
|
|
else
|
|
result := @SetColorPixel;
|
|
end;
|
|
|
|
function TFPReaderPNG.CalcX (relX:integer) : integer;
|
|
begin
|
|
result := StartX + (relX * deltaX);
|
|
end;
|
|
|
|
function TFPReaderPNG.CalcY (relY:integer) : integer;
|
|
begin
|
|
result := StartY + (relY * deltaY);
|
|
end;
|
|
|
|
function TFPReaderPNG.CalcColor: TColorData;
|
|
var cd : longword;
|
|
r : word;
|
|
b : byte;
|
|
begin
|
|
if UsingBitGroup = 0 then
|
|
begin
|
|
Databytes := 0;
|
|
if Header.BitDepth = 16 then
|
|
begin
|
|
r := 1;
|
|
while (r < ByteWidth) do
|
|
begin
|
|
b := FCurrentLine^[Dataindex+r];
|
|
FCurrentLine^[Dataindex+r] := FCurrentLine^[Dataindex+r-1];
|
|
FCurrentLine^[Dataindex+r-1] := b;
|
|
inc (r,2);
|
|
end;
|
|
end;
|
|
move (FCurrentLine^[DataIndex], Databytes, bytewidth);
|
|
inc (DataIndex,bytewidth);
|
|
end;
|
|
if bytewidth = 1 then
|
|
begin
|
|
cd := (Databytes and BitsUsed[UsingBitGroup]);
|
|
result := cd shr ((CountBitsUsed-UsingBitGroup-1) * BitShift);
|
|
inc (UsingBitgroup);
|
|
if UsingBitGroup >= CountBitsUsed then
|
|
UsingBitGroup := 0;
|
|
end
|
|
else
|
|
result := Databytes;
|
|
end;
|
|
|
|
procedure TFPReaderPNG.HandleScanLine (const y : integer; const ScanLine : PByteArray);
|
|
var x, rx : integer;
|
|
c : TColorData;
|
|
begin
|
|
UsingBitGroup := 0;
|
|
DataIndex := 0;
|
|
for rx := 0 to ScanlineLength[CurrentPass]-1 do
|
|
begin
|
|
X := CalcX(rx);
|
|
c := CalcColor;
|
|
FSetPixel (x,y,c);
|
|
end
|
|
end;
|
|
|
|
function TFPReaderPNG.ColorGray1 (CD:TColorDAta) : TFPColor;
|
|
begin
|
|
if CD = 0 then
|
|
result := colBlack
|
|
else
|
|
result := colWhite;
|
|
end;
|
|
|
|
function TFPReaderPNG.ColorGray2 (CD:TColorDAta) : TFPColor;
|
|
var c : word;
|
|
begin
|
|
c := CD and 3;
|
|
c := c + (c shl 2);
|
|
c := c + (c shl 4);
|
|
c := c + (c shl 8);
|
|
with result do
|
|
begin
|
|
red := c;
|
|
green := c;
|
|
blue := c;
|
|
alpha := alphaOpaque;
|
|
end;
|
|
end;
|
|
|
|
function TFPReaderPNG.ColorGray4 (CD:TColorDAta) : TFPColor;
|
|
var c : word;
|
|
begin
|
|
c := CD and $F;
|
|
c := c + (c shl 4);
|
|
c := c + (c shl 8);
|
|
with result do
|
|
begin
|
|
red := c;
|
|
green := c;
|
|
blue := c;
|
|
alpha := alphaOpaque;
|
|
end;
|
|
end;
|
|
|
|
function TFPReaderPNG.ColorGray8 (CD:TColorDAta) : TFPColor;
|
|
var c : word;
|
|
begin
|
|
c := CD and $FF;
|
|
c := c + (c shl 8);
|
|
with result do
|
|
begin
|
|
red := c;
|
|
green := c;
|
|
blue := c;
|
|
alpha := alphaOpaque;
|
|
end;
|
|
end;
|
|
|
|
function TFPReaderPNG.ColorGray16 (CD:TColorDAta) : TFPColor;
|
|
var c : word;
|
|
begin
|
|
c := CD and $FFFF;
|
|
with result do
|
|
begin
|
|
red := c;
|
|
green := c;
|
|
blue := c;
|
|
alpha := alphaOpaque;
|
|
end;
|
|
end;
|
|
|
|
function TFPReaderPNG.ColorGrayAlpha8 (CD:TColorData) : TFPColor;
|
|
var c : word;
|
|
begin
|
|
c := CD and $FF00;
|
|
c := c + (c shr 8);
|
|
with result do
|
|
begin
|
|
red := c;
|
|
green := c;
|
|
blue := c;
|
|
c := CD and $FF;
|
|
alpha := c + (c shl 8);
|
|
end;
|
|
end;
|
|
|
|
function TFPReaderPNG.ColorGrayAlpha16 (CD:TColorData) : TFPColor;
|
|
var c : word;
|
|
begin
|
|
c := (CD and qword($FFFF0000)) shr 16;
|
|
with result do
|
|
begin
|
|
red := c;
|
|
green := c;
|
|
blue := c;
|
|
alpha := CD and $FFFF;
|
|
end;
|
|
end;
|
|
|
|
function TFPReaderPNG.ColorColor8 (CD:TColorData) : TFPColor;
|
|
var c : word;
|
|
begin
|
|
with result do
|
|
begin
|
|
c := CD and $FF;
|
|
red := c + (c shl 8);
|
|
c := CD and $FF00;
|
|
green := c + (c shr 8);
|
|
c := (CD and $FF0000) shr 8;
|
|
blue := c + (c shr 8);
|
|
alpha := alphaOpaque;
|
|
end;
|
|
end;
|
|
|
|
function TFPReaderPNG.ColorColor16 (CD:TColorData) : TFPColor;
|
|
var c : qword;
|
|
begin
|
|
with result do
|
|
begin
|
|
red := CD and $FFFF;
|
|
c := qword($FFFF0000);
|
|
green := (CD and c) shr 16;
|
|
c := c shl 16;
|
|
blue := (CD and c) shr 32;
|
|
alpha := alphaOpaque;
|
|
end;
|
|
end;
|
|
|
|
function TFPReaderPNG.ColorColorAlpha8 (CD:TColorData) : TFPColor;
|
|
var c : qword;
|
|
begin
|
|
with result do
|
|
begin
|
|
c := CD and $FF;
|
|
red := c + (c shl 8);
|
|
c := CD and $FF00;
|
|
green := c + (c shr 8);
|
|
c := (CD and $FF0000) shr 8;
|
|
blue := c + (c shr 8);
|
|
c := (CD and qword($FF000000)) shr 16;
|
|
alpha := c + (c shr 8);
|
|
end;
|
|
end;
|
|
|
|
function TFPReaderPNG.ColorColorAlpha16 (CD:TColorData) : TFPColor;
|
|
var c : qword;
|
|
begin
|
|
with result do
|
|
begin
|
|
red := CD and $FFFF;
|
|
c := qword($FFFF0000);
|
|
green := (CD and c) shr 16;
|
|
c := c shl 16;
|
|
blue := (CD and c) shr 32;
|
|
c := c shl 16;
|
|
alpha := (CD and c) shr 48;
|
|
end;
|
|
end;
|
|
|
|
procedure TFPReaderPNG.DoDecompress;
|
|
|
|
procedure initVars;
|
|
var r,d : integer;
|
|
begin
|
|
with Header do
|
|
begin
|
|
if interlace=0 then
|
|
begin
|
|
StartPass := 0;
|
|
EndPass := 0;
|
|
CountScanlines[0] := Height;
|
|
ScanLineLength[0] := Width;
|
|
end
|
|
else
|
|
begin
|
|
StartPass := 1;
|
|
EndPass := 7;
|
|
for r := 1 to 7 do
|
|
begin
|
|
d := Height div delta[r,1];
|
|
if (height mod delta[r,1]) > startpoints[r,1] then
|
|
inc (d);
|
|
CountScanLines[r] := d;
|
|
d := width div delta[r,0];
|
|
if (width mod delta[r,0]) > startpoints[r,0] then
|
|
inc (d);
|
|
ScanLineLength[r] := d;
|
|
end;
|
|
end;
|
|
Fpltte := (ColorType = 3);
|
|
case colortype of
|
|
0 : case Bitdepth of
|
|
1 : begin
|
|
FConvertColor := @ColorGray1; //CFmt := cfMono;
|
|
ByteWidth := 1;
|
|
end;
|
|
2 : begin
|
|
FConvertColor := @ColorGray2; //CFmt := cfGray2;
|
|
ByteWidth := 1;
|
|
end;
|
|
4 : begin
|
|
FConvertColor := @ColorGray4; //CFmt := cfGray4;
|
|
ByteWidth := 1;
|
|
end;
|
|
8 : begin
|
|
FConvertColor := @ColorGray8; //CFmt := cfGray8;
|
|
ByteWidth := 1;
|
|
end;
|
|
16 : begin
|
|
FConvertColor := @ColorGray16; //CFmt := cfGray16;
|
|
ByteWidth := 2;
|
|
end;
|
|
end;
|
|
2 : if BitDepth = 8 then
|
|
begin
|
|
FConvertColor := @ColorColor8; //CFmt := cfBGR24
|
|
ByteWidth := 3;
|
|
end
|
|
else
|
|
begin
|
|
FConvertColor := @ColorColor16; //CFmt := cfBGR48;
|
|
ByteWidth := 6;
|
|
end;
|
|
3 : if BitDepth = 16 then
|
|
ByteWidth := 2
|
|
else
|
|
ByteWidth := 1;
|
|
4 : if BitDepth = 8 then
|
|
begin
|
|
FConvertColor := @ColorGrayAlpha8; //CFmt := cfGrayA16
|
|
ByteWidth := 2;
|
|
end
|
|
else
|
|
begin
|
|
FConvertColor := @ColorGrayAlpha16; //CFmt := cfGrayA32;
|
|
ByteWidth := 4;
|
|
end;
|
|
6 : if BitDepth = 8 then
|
|
begin
|
|
FConvertColor := @ColorColorAlpha8; //CFmt := cfABGR32
|
|
ByteWidth := 4;
|
|
end
|
|
else
|
|
begin
|
|
FConvertColor := @ColorColorAlpha16; //CFmt := cfABGR64;
|
|
ByteWidth := 8;
|
|
end;
|
|
end;
|
|
//ByteWidth := BytesNeeded[CFmt];
|
|
case BitDepth of
|
|
1 : begin
|
|
CountBitsUsed := 8;
|
|
BitShift := 1;
|
|
BitsUsed := BitsUsed1Depth;
|
|
end;
|
|
2 : begin
|
|
CountBitsUsed := 4;
|
|
BitShift := 2;
|
|
BitsUsed := BitsUsed2Depth;
|
|
end;
|
|
4 : begin
|
|
CountBitsUsed := 2;
|
|
BitShift := 4;
|
|
BitsUsed := BitsUsed4Depth;
|
|
end;
|
|
8 : begin
|
|
CountBitsUsed := 1;
|
|
BitShift := 0;
|
|
BitsUsed[0] := $FF;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure Decode;
|
|
var y, rp, ry, rx, l : integer;
|
|
lf : byte;
|
|
begin
|
|
FSetPixel := DecideSetPixel;
|
|
for rp := StartPass to EndPass do
|
|
begin
|
|
FCurrentPass := rp;
|
|
StartX := StartPoints[rp,0];
|
|
StartY := StartPoints[rp,1];
|
|
DeltaX := Delta[rp,0];
|
|
DeltaY := Delta[rp,1];
|
|
if bytewidth = 1 then
|
|
begin
|
|
l := (ScanLineLength[rp] div CountBitsUsed);
|
|
if (ScanLineLength[rp] mod CountBitsUsed) > 0 then
|
|
inc (l);
|
|
end
|
|
else
|
|
l := ScanLineLength[rp]*ByteWidth;
|
|
GetMem (FPreviousLine, l);
|
|
GetMem (FCurrentLine, l);
|
|
fillchar (FCurrentLine^,l,0);
|
|
try
|
|
for ry := 0 to CountScanlines[rp]-1 do
|
|
begin
|
|
FSwitchLine := FCurrentLine;
|
|
FCurrentLine := FPreviousLine;
|
|
FPreviousLine := FSwitchLine;
|
|
Y := CalcY(ry);
|
|
Decompress.Read (lf, sizeof(lf));
|
|
Decompress.Read (FCurrentLine^, l);
|
|
if lf <> 0 then // Do nothing when there is no filter used
|
|
for rx := 0 to l-1 do
|
|
FCurrentLine^[rx] := DoFilter (lf, rx, FCurrentLine^[rx]);
|
|
HandleScanLine (y, FCurrentLine);
|
|
end;
|
|
finally
|
|
freemem (FPreviousLine);
|
|
freemem (FCurrentLine);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
InitVars;
|
|
DeCode;
|
|
end;
|
|
|
|
procedure TFPReaderPNG.HandleChunk;
|
|
begin
|
|
case chunk.AType of
|
|
ctIHDR : raise PNGImageException.Create ('Second IHDR chunk found');
|
|
ctPLTE : HandlePalette;
|
|
ctIDAT : HandleData;
|
|
ctIEND : EndOfFile := True;
|
|
cttRNS : HandleAlpha;
|
|
else HandleUnknown;
|
|
end;
|
|
end;
|
|
|
|
procedure TFPReaderPNG.HandleUnknown;
|
|
begin
|
|
if (chunk.readtype[0] in ['A'..'Z']) then
|
|
raise PNGImageException.Create('Critical chunk '+chunk.readtype+' not recognized');
|
|
end;
|
|
|
|
procedure TFPReaderPNG.InternalRead (Str:TStream; Img:TFPCustomImage);
|
|
begin
|
|
if Str<>TheStream then
|
|
writeln('WARNING: TFPReaderPNG.InternalRead Str<>TheStream');
|
|
with Header do
|
|
Img.SetSize (Width, Height);
|
|
ZData := TMemoryStream.Create;
|
|
try
|
|
EndOfFile := false;
|
|
while not EndOfFile do
|
|
begin
|
|
ReadChunk;
|
|
HandleChunk;
|
|
end;
|
|
Decompress := TDecompressionStream.Create (ZData);
|
|
try
|
|
Decompress.position := 0;
|
|
DoDecompress;
|
|
finally
|
|
Decompress.Free;
|
|
end;
|
|
finally
|
|
ZData.Free;
|
|
if not img.UsePalette and assigned(FPalette) then
|
|
begin
|
|
FPalette.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TFPReaderPNG.InternalCheck (Str:TStream) : boolean;
|
|
var SigCheck : array[0..7] of byte;
|
|
r : integer;
|
|
begin
|
|
try
|
|
// Check Signature
|
|
Str.Read(SigCheck, SizeOf(SigCheck));
|
|
for r := 0 to 7 do
|
|
begin
|
|
If SigCheck[r] <> Signature[r] then
|
|
raise PNGImageException.Create('This is not PNG-data');
|
|
end;
|
|
// Check IHDR
|
|
ReadChunk;
|
|
move (chunk.data^, FHeader, sizeof(Header));
|
|
with header do
|
|
begin
|
|
Width := swap(width);
|
|
height := swap (height);
|
|
result := (width > 0) and (height > 0) and (compression = 0)
|
|
and (filter = 0) and (Interlace in [0,1]);
|
|
end;
|
|
except
|
|
result := false;
|
|
end;
|
|
end;
|
|
|
|
initialization
|
|
ImageHandlers.RegisterImageReader ('Portable Network Graphics', 'png', TFPReaderPNG);
|
|
end.
|
|
|