home *** CD-ROM | disk | FTP | other *** search
- Program DumPing ;
-
- {
- Copyright (c) 1995 by Oliver Fromme -- All Rights Reserved
-
- Address: Oliver Fromme, Leibnizstr. 18-61, 38678 Clausthal, Germany
- Internet: fromme@rz.tu-clausthal.de
- WWW: http://www.tu-clausthal.de/~inof/
-
- Freely distributable, freely usable.
- The original copyright notice may not be modified or omitted.
- }
-
- {$A+,B-,D+,E-,F-,G+,I-,L+,N-,O-,P-,Q-,R-,S-,T-,V+,X+,Y+}
- {$M 16384,0,655360}
-
- Uses Dos,Strings,AltCrt2,CRC32,Adler32,Inflate ;
-
- Const ReadBuffSize = 65528 ;
-
- Type tReadBuff = Array [0..Pred(ReadBuffSize)] Of Byte ;
- pReadBuff = ^tReadBuff ;
-
- Var PNGHead : Record {see the PNG spec, draft #9}
- Width,Height : LongInt ;
- BitsPerSample : Byte ;
- ColorType : Byte ;
- CM,Filter,IL : Byte
- End ;
-
- RunCRC : tCRC ; {see unit CRC32 for details}
- RunAdl : tAdler ; {see unit Adler32 for details}
-
- ChunkHead : Record
- Length : LongInt ;
- Name : Array [0..3] Of Char
- End ;
-
- inname : PathStr ;
- infile : File ;
-
- LineSize : LongInt ; {number of bytes in one row incl. filter byte}
- CurrentOffset : LongInt ; {byte position in current row}
- DecompBytes : LongInt ; {counts decompressed bytes}
- BytesNeeded : LongInt ; {bytes actually decompressed, should be = DecompBytes}
- NumLines : LongInt ; {number of scanlines = number of filter bytes}
- OutputRow : pReadBuff ;
- {Rows larger than 64K are supported, but only the first 64K
- are stored. Actually we need only the filter type byte.}
-
- FilterCount : Array [0..5] Of LongInt ;
- PaletteSize : Word ;
- CurrentPass : Byte ; {Adam7 pass, 0..6}
-
- Procedure Help ;
- Begin
- WriteLn ;
- WriteLn ('DUMPING v0.4 26-Mar-1995') ;
- WriteLn ('Usage: ',GetName(ParamStr(0)),' <filename[.PNG]>') ;
- WriteLn ('Purpose: Verifies the given PNG file and dumps its content to the') ;
- WriteLn (' screen. Complies with the PNG specification, draft #9.') ;
- Halt
- End {Help} ;
-
- Procedure Die (Const msg : String) ;
- Begin
- WriteLn ('*** ',msg) ;
- If IsOpenFile(infile) Then
- Close (infile) ;
- Halt (20)
- End {Die} ;
-
- Procedure Error (Const msg : String) ;
- Const Always : Boolean = False ;
- Var c : Char ;
- Begin
- WriteLn ('!!! ',msg) ;
- If Always Then
- Exit ;
- Write ('Try to continue [Y(es)/n(o)/a(lways)]? ') ;
- Repeat
- c := ReadKey ;
- If c=#0 Then
- If ReadKey=#0 Then
- Until UpCase(c) In ['Y','N','A',#13,#27] ;
- If UpCase(c) In ['N',#27] Then
- Die ('Halted.')
- Else Begin
- Write (#13,EmptyString:39,#13) ;
- If UpCase(c)='A' Then
- Always := True
- End
- End {Error} ;
-
- Procedure CheckIO ;
- Var iores : Integer ;
- Begin
- iores := IOResult ;
- If iores<>0 Then
- If iores=100 Then
- Die ('Premature end of file.')
- Else
- Die ('I/O error #'+IntStr(iores)+' reading from input file.')
- End {CheckIO} ;
-
- {Some read buffer routines for faster access
- on devices with slow response time.}
-
- Var ReadBuff : pReadBuff ;
- rbpos,rbend : Word ;
-
- Procedure ResetReadBuffer ;
- Begin
- rbpos := ReadBuffSize ;
- rbend := ReadBuffSize
- End {ResetReadBuffer} ;
-
- Procedure BufferedRead (Var desti ; c : Word) ;
- Var ec,r : Word ;
- Begin
- If rbpos+LongInt(c)>LongInt(ReadBuffSize) Then Begin
- r := ReadBuffSize-rbpos ;
- If rbpos<ReadBuffSize Then
- Move (ReadBuff^[rbpos],ReadBuff^,r) ;
- BlockRead (infile,ReadBuff^[r],rbpos,ec) ;
- If ec<rbpos Then
- rbend := r+ec ;
- rbpos := 0
- End ;
- Move (ReadBuff^[rbpos],desti,c) ;
- If rbpos+c>rbend Then
- InOutRes := 100
- Else
- Inc (rbpos,c)
- End {BufferedRead} ;
-
- Procedure ReadCheck (Var desti ; c : Word) ;
- Begin
- BufferedRead (desti,c) ;
- CheckIO ;
- Dec (ChunkHead.Length,c) ;
- UpdateCRC32 (RunCRC,desti,c)
- End {ReadCheck} ;
-
- Procedure BufferSkipBack (c : LongInt) ;
- Begin
- If rbpos>=c Then
- Dec (rbpos,c)
- Else Begin
- ResetReadBuffer ;
- Seek (infile,FilePos(infile)-c)
- End
- End {BufferSkipBack} ;
-
- Function MyHeapErrorFunc (Size: Word) : Integer ; Far ;
- Begin
- If Size=0 Then
- MyHeapErrorFUnc := 2 {success}
- Else
- MyHeapErrorFunc := 1 {return NIL}
- End {MyHeapErrorFunc} ;
-
- {Swap a 32 bit variable (MSB<->LSB).}
-
- Procedure Swap32 (Var LongVar : LongInt) ; Assembler ;
- Asm
- les si,LongVar
- mov ax,es:[si]
- mov dx,es:[si+2]
- xchg al,dh
- xchg ah,dl
- mov es:[si],ax
- mov es:[si+2],dx
- End {Swap32} ;
-
- {Swap a 16 bit variable (MSB<->LSB).}
-
- Procedure Swap16 (Var WordVar : Word) ; Assembler ;
- Asm
- les si,WordVar
- mov ax,es:[si]
- xchg al,ah
- mov es:[si],ax
- End {Swap16} ;
-
- Procedure ReadChunkHead ;
- Begin
- BufferedRead (ChunkHead,SizeOf(ChunkHead)) ;
- CheckIO ;
- With ChunkHead Do Begin
- Swap32 (Length) ;
- InitCRC32 (RunCRC) ;
- UpdateCRC32 (RunCRC,Name,4) ;
- WriteLn ('"',Copy(Name,1,4),'"',Length:7,' bytes')
- End
- End {ReadChunkHead} ;
-
- {Skip to the end of the current chunk and check the CRC.}
-
- Procedure SkipChunk ;
- Var CheckCRC : tCRC ;
- b : Byte ;
- Begin
- With ChunkHead Do
- If Length<0 Then Begin
- BufferSkipBack (-Length) ;
- CheckIO
- End
- Else
- While Length>0 Do
- ReadCheck (b,1) ;
- BufferedRead (CheckCRC,4) ;
- CheckIO ;
- Swap32 (CheckCRC) ;
- If FinalCRC32(RunCRC)<>CheckCRC Then
- Error ('Chunk CRC fails.')
- Else
- WriteLn (' Chunk CRC ok.')
- End {SkipChunk} ;
-
- {Callback for inflate: feed an input byte to inflate.}
-
- Function PNG_ReadByte : Byte ; Far ;
- Var CheckCRC : tCRC ;
- b : Byte ;
- Begin
- While ChunkHead.Length=0 Do Begin
- SkipChunk ;
- ReadChunkHead ;
- If ChunkHead.Name<>'IDAT' Then Begin
- Error ('IDAT chunk expected (compressed stream is not complete yet).') ;
- WriteLn (' Assume that this is actually an IDAT chunk.')
- End
- End ;
- ReadCheck (b,1) ;
- PNG_ReadByte := b
- End {PNG_ReadByte} ;
-
- {Apply a filter to a single row of pixels.}
-
- Procedure ApplyFilter ;
- Var f : Byte ;
- Begin {ApplyFilter}
- f := OutputRow^[0] ;
- {Since this is only a checker, there is no filter code.
- Instead, the filter type frequencies are computed.}
- If f In [0..4] Then
- Inc (FilterCount[f])
- Else
- Inc (FilterCount[5]) {Illegal filter type}
- End {ApplyFilter} ;
-
- Function GetLineSize (PixelWidth : LongInt) : LongInt ;
- Begin
- With PNGHead Do
- Case ColorType Of
- 0 : GetLineSize := (BitsPerSample*PixelWidth+7) Shr 3 +1 ;
- 2 : GetLineSize := (BitsPerSample Shr 3)*3*PixelWidth +1 ;
- 3 : GetLineSize := (BitsPerSample*PixelWidth+7) Shr 3 +1 ;
- 4 : GetLineSize := (BitsPerSample Shr 2)*PixelWidth +1 ;
- 6 : GetLineSize := (BitsPerSample Shr 1)*PixelWidth +1
- Else
- GetLineSize := PixelWidth Shl 3 +1
- {should be pretty save if user wants to ignore error}
- End
- End {GetLineSize} ;
-
- {Tables for Adam7 interlacing.}
-
- Const Adam7_StartRow : Array [0..6] Of Byte
- = (0,0,4,0,2,0,1) ;
- Adam7_StartCol : Array [0..6] Of Byte
- = (0,4,0,2,0,1,0) ;
- Adam7_IncrmRow : Array [0..6] Of Byte
- = (8,8,8,4,4,2,2) ;
- Adam7_IncrmCol : Array [0..6] Of Byte
- = (8,8,4,4,2,2,1) ;
-
- Var CurY : LongInt ;
-
- {Number of pixels/row in current pass}
-
- Function PassWidth : LongInt ;
- Begin
- PassWidth :=
- (PNGHead.Width+Adam7_IncrmCol[CurrentPass]-1-Adam7_StartCol[CurrentPass])
- Div Adam7_IncrmCol[CurrentPass]
- End {PassWidth} ;
-
- {Number of rows in current pass}
-
- Function PassHeight : LongInt ;
- Begin
- PassHeight :=
- (PNGHead.Height+Adam7_IncrmRow[CurrentPass]-1-Adam7_StartRow[CurrentPass])
- Div Adam7_IncrmRow[CurrentPass]
- End {PassHeight} ;
-
- {Callback for inflate: provides output data from the sliding window.}
-
- Function PNG_Flush (w : Word) : Integer ; Far ;
- Var CopyOffset,CopyCount,BytesPerLine : Word ;
- Begin
- PNG_Flush := 0 ;
- CopyOffset := 0 ;
- Inc (DecompBytes,w) ;
- UpdateAdler32 (RunAdl,slide^,w) ;
- If CurrentPass>6 Then
- Exit ; {Process_IDAT detects this}
- While w>0 Do Begin
- If PNGHead.IL=1 Then Begin {interlaced}
- {Skip empty passes}
- While ((PassWidth=0) Or (PassHeight=0)) And (CurrentPass<7) Do
- Inc (CurrentPass) ;
- If CurrentPass>6 Then
- Exit ;
- BytesPerLine := GetLineSize(PassWidth)
- End
- Else {non-interlaced}
- BytesPerLine := LineSize ;
- If w>BytesPerLine-CurrentOffset Then
- CopyCount := BytesPerLine-CurrentOffset
- Else
- CopyCount := w ;
- If CurrentOffset+CopyCount<=65528 Then
- Move (slide^[CopyOffset],OutputRow^[CurrentOffset],CopyCount) ;
- Dec (w,CopyCount) ;
- Inc (CopyOffset,CopyCount) ;
- Inc (CurrentOffset,CopyCount) ;
- If CurrentOffset>=BytesPerLine Then Begin {next row}
- ApplyFilter ;
- CurrentOffset := 0 ;
- If PNGHead.IL=1 Then Begin {interlaced}
- Inc (CurY,Adam7_IncrmRow[CurrentPass]) ;
- If CurY>=PNGHead.Height Then Begin
- Inc (CurrentPass) ;
- If CurrentPass>6 Then
- Exit ;
- CurY := Adam7_StartRow[CurrentPass]
- End
- End
- Else Begin {non-interlaced}
- Inc (CurY) ;
- If CurY>=PNGHead.Height Then
- Exit
- End
- End
- End
- End {PNG_Flush} ;
-
- Procedure Process_IHDR ; Far ; Forward ;
- Procedure Process_PLTE ; Far ; Forward ;
- Procedure Process_IDAT ; Far ; Forward ;
- Procedure Process_IEND ; Far ; Forward ;
- Procedure Process_GAMA ; Far ; Forward ;
- Procedure Process_SBIT ; Far ; Forward ;
- Procedure Process_CHRM ; Far ; Forward ;
- Procedure Process_TRNS ; Far ; Forward ;
- Procedure Process_BKGD ; Far ; Forward ;
- Procedure Process_HIST ; Far ; Forward ;
- Procedure Process_TEXT ; Far ; Forward ;
- Procedure Process_ZTXT ; Far ; Forward ;
- Procedure Process_PHYS ; Far ; Forward ;
- Procedure Process_OFFS ; Far ; Forward ;
- Procedure Process_TIME ; Far ; Forward ;
-
- Const NumChunks = 15 ;
- Chunks : Array [1..NumChunks] Of
- Record
- Name : Array [0..3] Of Char ;
- Process : Procedure ;
- HaveIt : Boolean {True = chunk has appeared}
- End
- = ((Name: 'IHDR'; Process: Process_IHDR; HaveIt: False),
- (Name: 'PLTE'; Process: Process_PLTE; HaveIt: False),
- (Name: 'IDAT'; Process: Process_IDAT; HaveIt: False),
- (Name: 'IEND'; Process: Process_IEND; HaveIt: False),
- (Name: 'gAMA'; Process: Process_GAMA; HaveIt: False),
- (Name: 'sBIT'; Process: Process_SBIT; HaveIt: False),
- (Name: 'cHRM'; Process: Process_CHRM; HaveIt: False),
- (Name: 'tRNS'; Process: Process_TRNS; HaveIt: False),
- (Name: 'bKGD'; Process: Process_BKGD; HaveIt: False),
- (Name: 'hIST'; Process: Process_HIST; HaveIt: False),
- (Name: 'tEXt'; Process: Process_TEXT; HaveIt: False),
- (Name: 'zTXt'; Process: Process_ZTXT; HaveIt: False),
- (Name: 'pHYs'; Process: Process_PHYS; HaveIt: False),
- (Name: 'oFFs'; Process: Process_OFFS; HaveIt: False),
- (Name: 'tIME'; Process: Process_TIME; HaveIt: False)) ;
-
- Function FindChunk (c : String) : Integer ;
- Var i : Integer ;
- Begin
- FindChunk := -1 ;
- For i:=1 To NumChunks Do
- If c=Chunks[i].Name Then Begin
- FindChunk := i ;
- Break
- End
- End {FindChunk} ;
-
- Function CheckLength (l : LongInt) : LongInt ; {returns actual length}
- Begin
- If ChunkHead.Length<>l Then
- Error ('Illegal length of '+ChunkHead.Name+' chunk, must be '+
- LongStr(l)+' bytes.') ;
- CheckLength := ChunkHead.Length
- End {CheckLength} ;
-
- Procedure CheckMulti ;
- Begin
- If Chunks[FindChunk(ChunkHead.Name)].HaveIt Then
- Error ('This chunk may not appear more than once.')
- End {CheckMulti} ;
-
- Procedure Process_IHDR ;
- Type ByteSet = Set Of Byte ;
- Var w : Word ;
-
- Procedure CheckTypeDepth (allowed : ByteSet) ;
- Begin {CheckTypeDepth}
- If Not (PNGHead.BitsPerSample In allowed) Then
- Error ('Illegal color type / bit depth combination.')
- End {CheckTypeDepth} ;
-
- Begin {Process_IHDR}
- CheckMulti ;
- CheckLength (SizeOf(PNGHead)) ;
- FillChar (PNGHead,SizeOf(PNGHead),0) ;
- ReadCheck (PNGHead,SizeOf(PNGHead)) ;
- With PNGHead Do Begin
- Swap32 (Width) ;
- Swap32 (Height) ;
- WriteLn (' Width: ',Width:5) ;
- WriteLn (' Height:',Height:5) ;
- WriteLn (' Bit depth: ',BitsPerSample,' (max. ',
- LongInt(1) Shl BitsPerSample,' values/sample)') ;
- If Not (BitsPerSample In [1,2,4,8,16]) Then
- Error ('Illegal bit depth.') ;
- Write (' Color type: ',ColorType,' (') ;
- Case ColorType Of
- 0 : Begin
- WriteLn ('greyscale)') ;
- CheckTypeDepth ([1,2,4,8,16])
- End ;
- 2 : Begin
- WriteLn ('RGB)') ;
- CheckTypeDepth ([8,16])
- End ;
- 3 : Begin
- WriteLn ('color mapped)') ;
- CheckTypeDepth ([1,2,4,8])
- End ;
- 4 : Begin
- WriteLn ('greyscale+alpha)') ;
- CheckTypeDepth ([8,16])
- End ;
- 6 : Begin
- WriteLn ('RGB+alpha)') ;
- CheckTypeDepth ([8,16])
- End
- Else
- WriteLn ('unknown)') ;
- Error ('Illegal color type.')
- End ;
- LineSize := GetLineSize(Width) ;
- Write (' Compression method: ',CM,' (') ;
- If CM=0 Then
- WriteLn ('deflate/32K)')
- Else Begin
- WriteLn ('unknown)') ;
- Error ('Illegal compression method.')
- End ;
- Write (' Filter type: ',Filter,' (') ;
- If Filter=0 Then
- WriteLn ('adaptive/5)')
- Else Begin
- WriteLn ('unknown)') ;
- Error ('Illegal filter type.')
- End ;
- Write (' Interlace type: ',IL,' (') ;
- Case IL Of
- 0 : WriteLn ('none)') ;
- 1 : WriteLn ('Adam7)')
- Else
- WriteLn ('unknown)') ;
- Error ('Illegal interlace type.')
- End
- End ;
- If LineSize>65528 Then
- w := 65528
- Else
- w := LineSize ;
- GetMem (OutputRow,w) ;
- If OutputRow=NIL Then
- Die ('Not enough memory for output row ('+WordStr(w)+' bytes).') ;
- SkipChunk
- End {Process_IHDR} ;
-
- Procedure Process_PLTE ;
- Begin
- CheckMulti ;
- If Chunks[FindChunk('tRNS')].HaveIt Then
- Error ('Must be before tRNS chunk.') ;
- If Chunks[FindChunk('bKGD')].HaveIt Then
- Error ('Must be before bKGD chunk.') ;
- If Chunks[FindChunk('hIST')].HaveIt Then
- Error ('Must be before hIST chunk.') ;
- With ChunkHead Do Begin
- PaletteSize := Length Div 3 ;
- If Length<3 Then
- Error ('Palette smaller than 3 bytes.')
- Else If Length Mod 3 <>0 Then
- Error ('Palette size not divisible by 3.')
- Else If (PNGHead.ColorType And 1 <>0) And
- (PaletteSize > Word(1) Shl PNGHead.BitsPerSample) Then
- Error ('Palette larger than bits per index allows.')
- Else If PaletteSize>256 Then
- Error ('Palette contains more than 256 entries.')
- Else
- WriteLn (' ',PaletteSize,' colors defined.')
- End ;
- SkipChunk
- End {Process_PLTE} ;
-
- Procedure Process_IDAT ;
- Var AdlerCheck : LongInt ;
- Result : Integer ;
- w : Word ;
- Begin
- If Chunks[FindChunk(ChunkHead.Name)].HaveIt Then Begin
- Error ('Image is complete, no more IDAT chunks allowed.') ;
- SkipChunk ;
- Exit
- End ;
- If (PNGHead.ColorType=3) And Not Chunks[FindChunk('PLTE')].HaveIt Then
- Error ('PLTE chunk must precede IDAT for colormapped images.') ;
- w := PNG_ReadByte ;
- w := (w Shl 8) Or PNG_ReadByte ;
- WriteLn (' CMF/FLG: 0x',Hex4(w)) ;
- If w Mod 31 <>0 Then
- Error ('CMF/FLG mod 31 check fails.') ;
- If Hi(w) And $f<>8 Then
- Error ('CMF: illegal compression method, must be 8.')
- Else Begin
- If Hi(w) Shr 4>7 Then
- Error ('CMF: unsupported sliding window size, must be <=7 (32K).') ;
- Case Lo(w) Shr 6 Of
- 0 : WriteLn (' fastest compression') ;
- 1 : WriteLn (' fast compression') ;
- 2 : WriteLn (' default compression') ;
- 3 : WriteLn (' maximum compression')
- End
- End ;
- If w And 32 <>0 Then
- Error ('Bit 5 (reserved) in FLG is set.') ;
- InitAdler32 (RunAdl) ;
- DecompBytes := 0 ;
- With PNGHead Do
- If IL=1 Then Begin {interlaced}
- BytesNeeded := 0 ;
- NumLines := 0 ;
- For CurrentPass:=0 To 6 Do Begin
- Inc (BytesNeeded,GetLineSize(PassWidth)*PassHeight) ;
- Inc (NumLines,PassHeight)
- End
- End
- Else Begin {non-interlaced}
- BytesNeeded := LineSize*Height ;
- NumLines := Height
- End ;
- CurrentOffset := 0 ;
- CurrentPass := 0 ;
- CurY := 0 ;
- InflateRead := PNG_ReadByte ;
- InflateFlush := PNG_Flush ;
- Result := InflateRun ;
- If Result<>0 Then
- Error ('Inflate returns error code '+IntStr(Result)+'.') ;
- WriteLn (' ',DecompBytes,' bytes decompressed.') ;
- If DecompBytes<>BytesNeeded Then
- Error (LongStr(BytesNeeded)+' bytes expected.') ;
- WriteLn (' Reading Adler32 checksum...') ;
- For w:=1 To 4 Do
- AdlerCheck := (AdlerCheck Shl 8) Or PNG_ReadByte ;
- If FinalAdler32(RunAdl)<>AdlerCheck Then Begin
- WriteLn (' Adler32, file: 0x',Hex8(AdlerCheck),', computed: 0x',
- Hex8(FinalAdler32(RunAdl))) ;
- Error ('Adler32 check on uncompressed data fails.')
- End
- Else
- WriteLn (' Adler32 check ok.') ;
- If ChunkHead.Length<0 Then
- Error ('Too few bytes in IDAT chunks ('+
- LongStr(-ChunkHead.Length)+' bytes missing).') ;
- If ChunkHead.Length>0 Then
- Error ('Too many bytes in IDAT chunks ('+
- LongStr(ChunkHead.Length)+' bytes remaining).') ;
- SkipChunk
- End {Process_IDATs} ;
-
- Procedure Process_IEND ;
- Begin
- CheckLength (0) ;
- SkipChunk
- End {Process_IEND} ;
-
- Procedure Process_GAMA ;
- Var gamma : LongInt ;
- Begin
- CheckMulti ;
- If Chunks[FindChunk('IDAT')].HaveIt Then
- Error ('Must be before IDAT chunks.') ;
- If Chunks[FindChunk('PLTE')].HaveIt Then
- Error ('Must be before PLTE chunk.') ;
- If CheckLength(4)>=4 Then Begin
- ReadCheck (gamma,4) ;
- Swap32 (gamma) ;
- WriteLn (' Image gamma is ',gamma/100000:4:2,'.')
- End ;
- SkipChunk
- End {Process_GAMA} ;
-
- Procedure Process_SBIT ;
- Var w : Word ;
- bits : Byte ;
- Descript : String[4] ;
- Begin
- CheckMulti ;
- If Chunks[FindChunk('IDAT')].HaveIt Then
- Error ('Must be before IDAT chunks.') ;
- If Chunks[FindChunk('PLTE')].HaveIt Then
- Error ('Must be before PLTE chunk.') ;
- Case PNGHead.ColorType Of
- 0 : Begin
- CheckLength (1) ;
- Descript := 'G'
- End ;
- 2,3 : Begin
- CheckLength (3) ;
- Descript := 'RGB'
- End ;
- 4 : Begin
- CheckLength (2) ;
- Descript := 'GA'
- End ;
- 6 : Begin
- CheckLength (4) ;
- Descript := 'RGBA'
- End
- End ;
- For w:=1 To Length(Descript) Do Begin
- If ChunkHead.Length<=0 Then
- Break ;
- ReadCheck (bits,1) ;
- WriteLn (' Significant bits (',Descript[w],'):',bits:3)
- End ;
- SkipChunk
- End {Process_SBIT} ;
-
- Procedure Process_CHRM ;
- Const ChrmName : Array [0..7] Of PChar
- = ('White Point X','White Point Y',' Red X',' Red Y',
- 'Green X','Green Y',' Blue X',' Blue Y') ;
- Var value : LongInt ;
- w : Word ;
- Begin
- CheckMulti ;
- If Chunks[FindChunk('IDAT')].HaveIt Then
- Error ('Must be before IDAT chunks.') ;
- If Chunks[FindChunk('PLTE')].HaveIt Then
- Error ('Must be before PLTE chunk.') ;
- CheckLength (32) ;
- For w:=0 To 7 Do Begin
- If ChunkHead.Length<4 Then
- Break ;
- ReadCheck (value,4) ;
- Swap32 (value) ;
- WriteLn (' ',ChrmName[w],': ',value/100000:4:2,'.')
- End ;
- SkipChunk
- End {Process_CHRM} ;
-
- Procedure Process_TRNS ;
- Const SDesc : Array [0..2] Of Char = 'RGB' ;
- Var trans : Array [0..2] Of Word ;
- w : Word ;
- b : Byte ;
- Begin
- CheckMulti ;
- If Chunks[FindChunk('IDAT')].HaveIt Then
- Error ('Must be before IDAT chunks.') ;
- If (PNGHead.ColorType=3) And Not Chunks[FindChunk('PLTE')].HaveIt Then
- Error ('Must be after PLTE chunk.') ;
- If PNGHead.ColorType In [4,6] Then
- Error ('tRNS chunk not allowed for full alpha images.') ;
- Case PNGHead.ColorType Of
- 3 : Begin
- If ChunkHead.Length>PaletteSize Then
- Error ('tRNS chunk contains more entries than palette.') ;
- For w:=0 To PaletteSize Do Begin
- If ChunkHead.Length<=0 Then Begin
- If w And 15 <>0 Then
- WriteLn ;
- Break ;
- End ;
- ReadCheck (b,1) ;
- If w And 15 =0 Then
- Write (' ',b:3)
- Else
- Write (',',b:3) ;
- If (w And 15 =15) Or (w=PaletteSize) Then
- WriteLn
- End
- End ;
- 0,4 : If CheckLength(2)>=2 Then Begin
- ReadCheck (trans[0],2) ;
- Swap16 (trans[0]) ;
- WriteLn (' Transparent grey level: ',trans[0]) ;
- If trans[0]>=LongInt(1) Shl PNGHead.BitsPerSample Then
- Error ('tRNS grey level exceeds maximum value.')
- End ;
- 2,6 : If CheckLength(6)>=6 Then Begin
- ReadCheck (trans,6) ;
- For w:=0 To 2 Do Begin
- Swap16 (trans[w]) ;
- WriteLn (' Transparent level (',SDesc[w],'): ',trans[w]) ;
- If trans[w]>=LongInt(1) Shl PNGHead.BitsPerSample Then
- Error ('tRNS level exceeds maximum value.')
- End
- End
- End ;
- SkipChunk
- End {Process_TRNS} ;
-
- Procedure Process_BKGD ;
- Const SDesc : Array [0..2] Of Char = 'RGB' ;
- Var back : Array [0..2] Of Word ;
- w : Word ;
- b : Byte ;
- Begin
- CheckMulti ;
- If Chunks[FindChunk('IDAT')].HaveIt Then
- Error ('Must be before IDAT chunks.') ;
- If (PNGHead.ColorType=3) And Not Chunks[FindChunk('PLTE')].HaveIt Then
- Error ('Must be after PLTE chunk.') ;
- Case PNGHead.ColorType Of
- 3 : If CheckLength(1)>=1 Then Begin
- ReadCheck (b,1) ;
- WriteLn (' Background color index: ',b) ;
- If b>=PaletteSize Then
- Error ('bKGD index exceeds number of palette entries.')
- End ;
- 0,4 : If CheckLength(2)>=2 Then Begin
- ReadCheck (back[0],2) ;
- Swap16 (back[0]) ;
- WriteLn (' Background grey level: ',back[0]) ;
- If back[0]>=LongInt(1) Shl PNGHead.BitsPerSample Then
- Error ('bKGD grey level exceeds maximum value.')
- End ;
- 2,6 : If CheckLength(6)>=6 Then Begin
- ReadCheck (back,6) ;
- For w:=0 To 2 Do Begin
- Swap16 (back[w]) ;
- WriteLn (' Background color (',SDesc[w],'): ',back[w]) ;
- If back[w]>=LongInt(1) Shl PNGHead.BitsPerSample Then
- Error ('bKGD color exceeds maximum value.')
- End
- End
- End ;
- SkipChunk
- End {Process_BKGD} ;
-
- Procedure Process_HIST ;
- Begin
- CheckMulti ;
- If Chunks[FindChunk('IDAT')].HaveIt Then
- Error ('Must be before IDAT chunks.') ;
- If (PNGHead.ColorType=3) And Not Chunks[FindChunk('PLTE')].HaveIt Then
- Error ('Must be after PLTE chunk.') ;
- CheckLength (PaletteSize Shl 1) ;
- SkipChunk
- End {Process_HIST} ;
-
- Procedure Process_TEXT ;
- Begin
- SkipChunk
- End {Process_TEXT} ;
-
- Procedure Process_ZTXT ;
- Begin
- SkipChunk
- End {Process_ZTXT} ;
-
- Procedure Process_PHYS ;
- Var PhysData : Record
- perx,pery : LongInt ;
- unitspec : Byte
- End ;
- Begin
- CheckMulti ;
- If Chunks[FindChunk('IDAT')].HaveIt Then
- Error ('Must be before IDAT chunks.') ;
- If CheckLength(SizeOf(PhysData))>=SizeOf(PhysData) Then Begin
- ReadCheck (PhysData,SizeOf(PhysData)) ;
- With PhysData Do Begin
- Swap32 (perx) ;
- Swap32 (pery) ;
- If unitspec>1 Then
- Error ('Unknown unit specifier '+WordStr(unitspec)+'.') ;
- If unitspec=1 Then Begin
- WriteLn (' X:',perx/100:7:2,' dpcm =',(perx/100)*2.54:7:2,' dpi') ;
- WriteLn (' Y:',pery/100:7:2,' dpcm =',(pery/100)*2.54:7:2,' dpi')
- End
- Else Begin
- WriteLn (' X: ',perx) ;
- WriteLn (' Y: ',pery)
- End ;
- WriteLn (' => X/Y ascpect ratio = ',perx/pery:5:3)
- End
- End ;
- SkipChunk
- End {Process_PHYS} ;
-
- Procedure Process_OFFS ;
- Var OffsData : Record
- ofsx,ofsy : LongInt ;
- unitspec : Byte
- End ;
- Begin
- CheckMulti ;
- If Chunks[FindChunk('IDAT')].HaveIt Then
- Error ('Must be before IDAT chunks.') ;
- If CheckLength(SizeOf(OffsData))>=SizeOf(OffsData) Then Begin
- ReadCheck (OffsData,SizeOf(OffsData)) ;
- With OffsData Do Begin
- Swap32 (ofsx) ;
- Swap32 (ofsy) ;
- If unitspec>1 Then
- Error ('Unknown unit specifier '+WordStr(unitspec)+'.') ;
- Case unitspec Of
- 0 : Begin
- WriteLn (' X offset: ',ofsx,' pixels') ;
- WriteLn (' Y offset: ',ofsy,' pixels')
- End ;
- 1 : Begin
- WriteLn (' X offset: ',ofsx/10000:6:3,' cm =',ofsx/25400:6:3,'"') ;
- WriteLn (' Y offset: ',ofsy/10000:6:3,' cm =',ofsy/25400:6:3,'"')
- End
- Else
- WriteLn (' X offset: ',ofsx) ;
- WriteLn (' Y offset: ',ofsy)
- End
- End
- End ;
- SkipChunk
- End {Process_OFFS} ;
-
- Procedure Process_TIME ;
- Const MonthDesc : Array [0..12] Of String[3]
- = ('???','Jan','Feb','Mar','Apr','May','Jun',
- 'Jul','Aug','Sep','Oct','Nov','Dec') ;
- Var TimeData : Record
- year : Word ;
- month,day,hour,minute,second : Byte
- End ;
- Begin
- CheckMulti ;
- If CheckLength(SizeOf(TimeData))>=SizeOf(TimeData) Then Begin
- ReadCheck (TimeData,SizeOf(TimeData)) ;
- With TimeData Do Begin
- Swap16 (year) ;
- If year<100 Then
- Error ('Illegal year ('+WordStr(year)+').') ;
- {could try to fix, e.g.: Inc (year,1900)}
- If Not (month In [1..12]) Then Begin
- Error ('Illegal month ('+WordStr(month)+').') ;
- month := 0
- End ;
- If Not (day In [1..31]) Then
- Error ('Illegal day ('+WordStr(day)+').') ;
- {We could check for day>29 when month=2 etc., but this
- is already complicated and picky enough.}
- If Not (hour In [0..23]) Then
- Error ('Illegal hour ('+WordStr(hour)+').') ;
- If Not (minute In [0..59]) Then
- Error ('Illegal minute ('+WordStr(minute)+').') ;
- If Not (second In [0..60]) Then
- Error ('Illegal second ('+WordStr(second)+').') ;
- WriteLn (' Time of last modification: ',day,'-',MonthDesc[month],
- '-',year,', ',hour,':',Lead0(minute,2),':',Lead0(second,2))
- End
- End ;
- SkipChunk
- End {Process_TIME} ;
-
- Procedure Main ;
- Const PNG_Magic : Array [0..7] Of Char
- = #137'PNG'#13#10#26#10 ;
- Var BufMag : Array [0..7] Of Char ;
- First : Boolean ; {True = first chunk}
- i : Integer ;
- Begin
- BufferedRead (BufMag,8) ;
- CheckIO ;
- If BufMag<>PNG_Magic Then
- Die ('Not a valid PNG file (PNG magic mismatch in first 8 bytes).') ;
- First := True ;
- With ChunkHead Do
- While Name<>'IEND' Do Begin
- ReadChunkHead ;
- If ChunkHead.Name='IHDR' Then
- Process_IHDR
- Else Begin
- If First Then
- Error ('First chunk is not IHDR.') ;
- i := FindChunk(ChunkHead.Name) ;
- If i>=0 Then Begin
- Chunks[i].Process ;
- Chunks[i].HaveIt := True
- End
- Else Begin
- If Byte(ChunkHead.Name[0]) And 32 =0 Then
- Error ('Unknown critical chunk.')
- Else
- WriteLn (' Unknown ancillary chunk.') ;
- SkipChunk
- End
- End ;
- First := False
- End ;
- If (rbpos<rbend) Or Not EOF(infile) Then
- Error ('File contains data after IEND chunk.')
- Else
- WriteLn ('-EOF-')
- End {Main} ;
-
- Procedure Init ;
- Begin
- If ParamCount<>1 Then
- Help ;
- inname := ExtPath(FExpand(ParamStr(1)),'PNG') ;
- WriteLn ('Input file: ',inname) ;
- Assign (infile,inname) ;
- Reset (infile,1) ;
- If IOResult<>0 Then
- Die ('Input file not found.') ;
- HeapError := @MyHeapErrorFunc ;
- GetMem (slide,WSIZE) ;
- If slide=NIL Then
- Die ('Not enough memory for sliding window ('+WordStr(WSIZE)+' bytes).') ;
- GetMem (ReadBuff,ReadBuffSize) ;
- If ReadBuff=NIL Then
- Die ('Not enough memory for read buffer ('+WordStr(ReadBuffSize)+' bytes).') ;
- ResetReadBuffer ;
- FillByte (FilterCount,SizeOf(FilterCount),0)
- End {Init} ;
-
- Procedure Done ;
- Var w : Word ;
- Const FilterName : Array [0..5] Of String[9]
- = ('(none)','(sub)','(up)','(average)',
- '(paeth)','(illegal)') ;
- Begin
- Close (infile) ;
- WriteLn ('Filter usage statistics:') ;
- For w:=0 To 5 Do Begin
- WriteLn ('Filter #',w,FilterName[w]:10,':',FilterCount[w]:5,
- (FilterCount[w]*100)/NumLines:7:1,'%') ;
- End ;
- If FilterCount[5]<>0 Then
- Error ('File contains illegal filter types.') ;
- WriteLn ('-Ok-')
- End {Done} ;
-
- Begin
- Init ;
- Main ;
- Done
- End.
-