"
COPYRIGHT (c) 1996 by Claus Gittinger
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
ImageReader subclass:#PNGReader
instanceVariableNames:'colorType depth compressionMethod filterMethod interlaceMode
redBytes greenBytes blueBytes'
classVariableNames:''
poolDictionaries:''
category:'Graphics-Images-Support'
!
!PNGReader class methodsFor:'documentation'!
copyright
"
COPYRIGHT (c) 1996 by Claus Gittinger
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
!
documentation
"
this class provides methods for loading PNG pictures.
It is currenty untested.
[See also:]
Image Form Icon
BlitImageReader FaceReader GIFReader JPEGReader PBMReader PCXReader
ST80FormReader SunRasterReader TargaReader TIFFReader WindowsIconReader
XBMReader XPMReader XWDReader
[author:]
Claus Gittinger
"
! !
!PNGReader class methodsFor:'initialization'!
initialize
"install myself in the Image classes fileFormat table
for the `.png' extension."
"/ not yet finished - do not add
"/ MIMETypes defineImageType:'image/x-png' suffix:'png' reader:self.
"Modified: 1.2.1997 / 15:02:47 / cg"
! !
!PNGReader class methodsFor:'testing'!
isValidImageFile:aFileName
"return true, if aFileName contains a PNG image"
|inStream magic|
inStream := self streamReadingFile:aFileName.
inStream isNil ifTrue:[^ false].
inStream binary.
magic := ByteArray new:8.
inStream nextBytes:8 into:magic.
inStream close.
^ (magic = #[137 80 78 71 13 10 26 10])
"Modified: 21.6.1996 / 20:38:46 / cg"
! !
!PNGReader methodsFor:'private'!
getChunk
|len type crc|
inStream atEnd ifTrue:[^ false].
len := inStream nextLongMSB:true.
type := String new:4.
(inStream nextBytes:4 into:type) ~~ 4 ifTrue:[^ false].
"/ 'len: ' print. len print. ' type: ' print. type printCR.
type = 'IEND' ifTrue:[^ false].
(self processChunk:type len:len) ifFalse:[^ false].
inStream skip:4.
"/ crc := inStream nextLongMSB:true.
^ true
"Created: 21.6.1996 / 21:09:36 / cg"
"Modified: 21.6.1996 / 21:20:26 / cg"
!
getIHDRChunk
|len type crc|
len := inStream nextLongMSB:true.
type := String new:4.
inStream nextBytes:4 into:type.
type = 'IHDR' ifFalse:[self halt. ^ false].
len == 13 ifFalse:[self halt. ^ false].
width := inStream nextLongMSB:true.
height := inStream nextLongMSB:true.
(width <= 0 or:[height <= 0]) ifTrue:[
'PNGReader: invalid dimension(s)' infoPrintCR.
^ false.
].
depth := inStream nextByte.
colorType := inStream nextByte.
compressionMethod := inStream nextByte.
filterMethod := inStream nextByte.
interlaceMode := inStream nextByte.
inStream skip:4.
"/ crc := inStream nextLongMSB:true.
^ true
"Modified: 21.6.1996 / 21:38:35 / cg"
!
processBKGDChunkLen:len
inStream skip:len.
^ true
"Created: 21.6.1996 / 21:15:49 / cg"
!
processChunk:type len:len
type = 'gAMA' ifTrue:[^ self processGAMAChunkLen:len].
type = 'sBIT' ifTrue:[^ self processSBITChunkLen:len].
type = 'tEXt' ifTrue:[^ self processTEXTChunkLen:len].
type = 'IDAT' ifTrue:[^ self processIDATChunkLen:len].
type = 'tIME' ifTrue:[^ self processTIMEChunkLen:len].
type = 'bKGD' ifTrue:[^ self processBKGDChunkLen:len].
type = 'zTXt' ifTrue:[^ self processZTXTChunkLen:len].
type = 'PLTE' ifTrue:[^ self processPLTEChunkLen:len].
('PNGReader: unrecognized chunk: ' , type , ' ignored.') infoPrintCR.
inStream skip:len.
^ true.
"Created: 21.6.1996 / 21:10:37 / cg"
"Modified: 21.6.1996 / 21:22:37 / cg"
!
processGAMAChunkLen:len
inStream skip:len.
^ true
"Created: 21.6.1996 / 21:10:52 / cg"
!
processIDATChunkLen:len
inStream skip:len.
^ true
"Created: 21.6.1996 / 21:15:35 / cg"
!
processPLTEChunkLen:len
|n "{ Class: SmallInteger }"|
(len \\ 3) ~~ 0 ifTrue:[
'PNGReader: invalid size of PLTE chunk' infoPrintCR.
^ false
].
n := len // 3.
redBytes := ByteArray new:n.
greenBytes := ByteArray new:n.
blueBytes := ByteArray new:n.
1 to:n do:[:i |
redBytes at:i put:(inStream nextByte).
greenBytes at:i put:(inStream nextByte).
blueBytes at:i put:(inStream nextByte)
].
^ true
"Created: 21.6.1996 / 21:22:28 / cg"
"Modified: 21.6.1996 / 21:43:01 / cg"
!
processSBITChunkLen:len
inStream skip:len.
^ true
"Created: 21.6.1996 / 21:13:09 / cg"
!
processTEXTChunkLen:len
inStream skip:len.
^ true
"Modified: 21.6.1996 / 21:15:27 / cg"
!
processTIMEChunkLen:len
inStream skip:len.
^ true
"Created: 21.6.1996 / 21:15:43 / cg"
"Modified: 21.6.1996 / 21:20:42 / cg"
!
processZTXTChunkLen:len
inStream skip:len.
^ true
"Created: 21.6.1996 / 21:15:58 / cg"
! !
!PNGReader methodsFor:'reading from file'!
fromStream:aStream
"read a stream containing a PNG image.
Leave image description in instance variables."
|header|
inStream := aStream.
aStream binary.
"GIF-files are always msb (network-world)"
byteOrder := #msb.
header := ByteArray new:8.
aStream nextBytes:8 into:header.
header ~= #[137 80 78 71 13 10 26 10] ifTrue:[
'PNGReader: not a png file.' infoPrintCR.
^ nil
].
(self getIHDRChunk) ifFalse:[
'PNGReader: required IHDR chunk missing.' infoPrintCR.
^ nil
].
compressionMethod ~~ 0 ifTrue:[
('PNGReader: compressionMethod ' , compressionMethod printString , ' not supported.') infoPrintCR.
^ nil
].
filterMethod > 0 ifTrue:[
'PNGReader: invalid filterMethod' infoPrintCR.
^ nil.
].
interlaceMode > 1 ifTrue:[
'PNGReader: invalid interlaceMode' infoPrintCR.
^ nil.
].
colorType == 0 ifTrue:[
photometric := #blackIs0.
samplesPerPixel := 1.
bitsPerSample := Array with:depth.
] ifFalse:[ colorType == 2 ifTrue:[
depth < 8 ifTrue:[
'PNGReader: invalid colorType/depth combination' infoPrintCR.
^ nil.
].
photometric := #rgb.
samplesPerPixel := 3.
bitsPerSample := Array with:depth with:depth with:depth.
] ifFalse:[ colorType == 3 ifTrue:[
depth == 16 ifTrue:[
'PNGReader: invalid colorType/depth combination' infoPrintCR.
^ nil.
].
photometric := #palette.
samplesPerPixel := 1.
bitsPerSample := Array with:depth.
] ifFalse:[ colorType == 4 ifTrue:[
depth < 8 ifTrue:[
'PNGReader: invalid colorType/depth combination' infoPrintCR.
^ nil.
].
photometric := #blackIs0.
samplesPerPixel := 2.
bitsPerSample := Array with:depth with:depth.
] ifFalse:[ colorType == 6 ifTrue:[
depth < 8 ifTrue:[
'PNGReader: invalid colorType/depth combination' infoPrintCR.
^ nil.
].
photometric := #rgb.
samplesPerPixel := 4.
bitsPerSample := Array with:depth with:depth with:depth with:depth.
] ifFalse:[
('PNGReader: invalid colorType: ' , colorType printString , '.') infoPrintCR.
^ nil
]]]]].
[self getChunk] whileTrue:[ ].
photometric == #palette ifTrue:[
redBytes isNil ifTrue:[
'PNGReader: missing palette chunk.' infoPrintCR.
^ nil
].
colorMap := Colormap
redVector:redBytes
greenVector:greenBytes
blueVector:blueBytes.
].
self halt.
"
PNGReader fromFile:'/home/cg/libpng-0.89c/pngtest.png'
"
"Modified: 21.6.1996 / 21:44:34 / cg"
! !
!PNGReader class methodsFor:'documentation'!
version
^ '$Header: /cvs/stx/stx/libview2/PNGReader.st,v 1.3 1997-06-30 20:56:07 cg Exp $'
! !
PNGReader initialize!