123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909 |
- {
- $Id: fpreadpng.pp,v 1.10 2003/10/19 21:09:51 luk Exp $
- 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
- 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
- Chunk : TChunk;
- 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;
- class function InternalSize(Str:TStream): TPoint; 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
- {$IFDEF ENDIAN_LITTLE}
- alength := swap(CLength);
- {$ELSE}
- alength := CLength;
- {$ENDIF}
- 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);
- {$IFDEF ENDIAN_LITTLE}
- l := swap(l xor All1Bits);
- {$ELSE}
- l := l xor All1Bits;
- {$ENDIF}
- 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 8) + a;
- ThePalette[r] := c;
- end;
- end;
- end;
- procedure TransparentGray;
- var a : word;
- begin
- move (chunk.data^[0], a, 2);
- {$IFDEF ENDIAN_LITTLE}
- a := swap (a);
- {$ENDIF}
- 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;
- {$IFDEF ENDIAN_LITTLE}
- r := swap (r);
- g := swap (g);
- b := swap (b);
- {$ENDIF}
- 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 : pbyte;
- begin
- if UsingBitGroup = 0 then
- begin
- Databytes := 0;
- if Header.BitDepth = 16 then
- begin
- b := @Databytes;
- b^ := 0;
- r := 0;
- while (r < ByteWidth-1) do
- begin
- b^ := FCurrentLine^[DataIndex+r+1];
- inc (b);
- b^ := FCurrentLine^[DataIndex+r];
- inc (b);
- inc (r,2);
- end;
- end
- else move (FCurrentLine^[DataIndex], Databytes, bytewidth);
- {$IFDEF ENDIAN_BIG}
- Databytes:=swap(Databytes);
- {$ENDIF}
- 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 $00FF;
- c := c + (c shl 8);
- with result do
- begin
- red := c;
- green := c;
- blue := c;
- c := CD and $FF00;
- alpha := c + (c shr 8);
- end;
- end;
- function TFPReaderPNG.ColorGrayAlpha16 (CD:TColorData) : TFPColor;
- var c : word;
- begin
- {$ifdef FPC_LITTLE_ENDIAN}
- c := CD and $FFFF;
- {$else}
- c := (CD shr 16) and $FFFF;
- {$endif}
- with result do
- begin
- red := c;
- green := c;
- blue := c;
- {$ifdef FPC_LITTLE_ENDIAN}
- alpha := (CD shr 16) and $FFFF;
- {$else}
- alpha := CD and $FFFF;
- {$endif}
- 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);
- CD:=CD shr 8;
- c := CD and $FF;
- green := c + (c shl 8);
- CD:=CD shr 8;
- c := CD and $FF;
- blue := c + (c shl 8);
- alpha := alphaOpaque;
- end;
- end;
- function TFPReaderPNG.ColorColor16 (CD:TColorData) : TFPColor;
- begin
- with result do
- begin
- red := CD and $FFFF;
- CD:=CD shr 16;
- green := CD and $FFFF;
- CD:=CD shr 16;
- blue := CD and $FFFF;
- alpha := alphaOpaque;
- end;
- end;
- function TFPReaderPNG.ColorColorAlpha8 (CD:TColorData) : TFPColor;
- var c : word;
- begin
- with result do
- begin
- c := CD and $FF;
- red := c + (c shl 8);
- CD:=CD shr 8;
- c := CD and $FF;
- green := c + (c shl 8);
- CD:=CD shr 8;
- c := CD and $FF;
- blue := c + (c shl 8);
- CD:=CD shr 8;
- c := CD and $FF;
- alpha := c + (c shl 8);
- end;
- end;
- function TFPReaderPNG.ColorColorAlpha16 (CD:TColorData) : TFPColor;
- begin
- with result do
- begin
- red := CD and $FFFF;
- CD:=CD shr 16;
- green := CD and $FFFF;
- CD:=CD shr 16;
- blue := CD and $FFFF;
- CD:=CD shr 16;
- alpha := CD and $FFFF;
- 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;
- FCountScanlines[0] := Height;
- FScanLineLength[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);
- FCountScanlines[r] := d;
- d := width div delta[r,0];
- if (width mod delta[r,0]) > startpoints[r,0] then
- inc (d);
- FScanLineLength[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;
- if (l>0) then
- begin
- 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;
- 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;
- // NOTE: It is assumed that signature and IDHDR chunk already have been read.
- procedure TFPReaderPNG.InternalRead (Str:TStream; Img:TFPCustomImage);
- begin
- {$ifdef FPC_Debug_Image}
- if Str<>TheStream then
- writeln('WARNING: TFPReaderPNG.InternalRead Str<>TheStream');
- {$endif}
- with Header do
- Img.SetSize (Width, Height);
- ZData := TMemoryStream.Create;
- try
- EndOfFile := false;
- while not EndOfFile do
- begin
- ReadChunk;
- HandleChunk;
- end;
- ZData.position:=0;
- Decompress := TDecompressionStream.Create (ZData);
- try
- DoDecompress;
- finally
- Decompress.Free;
- end;
- finally
- ZData.Free;
- if not img.UsePalette and assigned(FPalette) then
- begin
- FreeAndNil(FPalette);
- end;
- end;
- end;
- class function TFPReaderPNG.InternalSize(Str: TStream): TPoint;
- var
- SigCheck: array[0..7] of byte;
- r: Integer;
- Width, Height: Word;
- StartPos: Int64;
- begin
- Result.X := 0;
- Result.Y := 0;
- StartPos := Str.Position;
- // Check Signature
- Str.Read(SigCheck, SizeOf(SigCheck));
- for r := Low(SigCheck) to High(SigCheck) do
- begin
- If SigCheck[r] <> Signature[r] then
- Exit;
- end;
- if not(
- (Str.Seek(10, soFromCurrent)=StartPos+18)
- and (Str.Read(Width, 2)=2)
- and (Str.Seek(2, soFromCurrent)=StartPos+22)
- and (Str.Read(Height, 2)=2))
- then
- Exit;
- {$IFDEF ENDIAN_LITTLE}
- Width := Swap(Width);
- Height := Swap(Height);
- {$ENDIF}
- Result.X := Width;
- Result.Y := Height;
- end;
- // NOTE: Stream does not rewind here!
- function TFPReaderPNG.InternalCheck (Str:TStream) : boolean;
- var SigCheck : array[0..7] of byte;
- r : integer;
- begin
- Result:=False;
- if Str=Nil then
- exit;
- // Check Signature
- if Str.Read(SigCheck, SizeOf(SigCheck)) <> SizeOf(SigCheck) then
- Exit;
- for r := 0 to 7 do
- begin
- If SigCheck[r] <> Signature[r] then
- Exit;
- end;
- // Check IHDR
- ReadChunk;
- move (chunk.data^, FHeader, sizeof(Header));
- with header do
- begin
- {$IFDEF ENDIAN_LITTLE}
- Width := swap(width);
- height := swap (height);
- {$ENDIF}
- result :=(width > 0) and (height > 0) and (compression = 0)
- and (filter = 0) and (Interlace in [0,1]);
- end;
- end;
- initialization
- ImageHandlers.RegisterImageReader ('Portable Network Graphics', 'png', TFPReaderPNG);
- end.
|