1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package png implements a PNG image decoder and encoder.
7 // The PNG specification is at http://www.w3.org/TR/PNG/.
21 // Color type, as per the PNG spec.
30 // A cb is a combination of color type and bit depth.
50 func cbPaletted(cb
int) bool {
51 return cbP1
<= cb
&& cb
<= cbP8
54 // Filter type, as per the PNG spec.
70 // interlaceScan defines the placement and size of a pass for Adam7 interlacing.
71 type interlaceScan
struct {
72 xFactor
, yFactor
, xOffset
, yOffset
int
75 // interlacing defines Adam7 interlacing, with 7 passes of reduced images.
76 // See http://www.w3.org/TR/PNG/#8Interlace
77 var interlacing
= []interlaceScan
{
88 // The PNG specification says that the IHDR, PLTE (if present), tRNS (if
89 // present), IDAT and IEND chunks must appear in that order. There may be
90 // multiple IDAT chunks, and IDAT chunks must be sequential (i.e. they may not
91 // have any other chunks between them).
92 // http://www.w3.org/TR/PNG/#5ChunkOrdering
102 const pngHeader
= "\x89PNG\r\n\x1a\n"
104 type decoder
struct {
110 palette color
.Palette
117 // useTransparent and transparent are used for grayscale and truecolor
118 // transparency, as opposed to palette transparency.
123 // A FormatError reports that the input is not a valid PNG.
124 type FormatError
string
126 func (e FormatError
) Error() string { return "png: invalid format: " + string(e
) }
128 var chunkOrderError
= FormatError("chunk out of order")
130 // An UnsupportedError reports that the input uses a valid but unimplemented PNG feature.
131 type UnsupportedError
string
133 func (e UnsupportedError
) Error() string { return "png: unsupported feature: " + string(e
) }
135 func min(a
, b
int) int {
142 func (d
*decoder
) parseIHDR(length
uint32) error
{
144 return FormatError("bad IHDR length")
146 if _
, err
:= io
.ReadFull(d
.r
, d
.tmp
[:13]); err
!= nil {
149 d
.crc
.Write(d
.tmp
[:13])
151 return UnsupportedError("compression method")
154 return UnsupportedError("filter method")
156 if d
.tmp
[12] != itNone
&& d
.tmp
[12] != itAdam7
{
157 return FormatError("invalid interlace method")
159 d
.interlace
= int(d
.tmp
[12])
160 w
:= int32(binary
.BigEndian
.Uint32(d
.tmp
[0:4]))
161 h
:= int32(binary
.BigEndian
.Uint32(d
.tmp
[4:8]))
162 if w
<= 0 || h
<= 0 {
163 return FormatError("non-positive dimension")
165 nPixels
:= int64(w
) * int64(h
)
166 if nPixels
!= int64(int(nPixels
)) {
167 return UnsupportedError("dimension overflow")
170 d
.depth
= int(d
.tmp
[8])
201 case ctGrayscaleAlpha
:
203 case ctTrueColorAlpha
:
212 case ctGrayscaleAlpha
:
214 case ctTrueColorAlpha
:
218 if d
.cb
== cbInvalid
{
219 return UnsupportedError(fmt
.Sprintf("bit depth %d, color type %d", d
.tmp
[8], d
.tmp
[9]))
221 d
.width
, d
.height
= int(w
), int(h
)
222 return d
.verifyChecksum()
225 func (d
*decoder
) parsePLTE(length
uint32) error
{
226 np
:= int(length
/ 3) // The number of palette entries.
227 if length%3
!= 0 || np
<= 0 || np
> 256 || np
> 1<<uint(d
.depth
) {
228 return FormatError("bad PLTE length")
230 n
, err
:= io
.ReadFull(d
.r
, d
.tmp
[:3*np
])
234 d
.crc
.Write(d
.tmp
[:n
])
236 case cbP1
, cbP2
, cbP4
, cbP8
:
237 d
.palette
= make(color
.Palette
, 256)
238 for i
:= 0; i
< np
; i
++ {
239 d
.palette
[i
] = color
.RGBA
{d
.tmp
[3*i
+0], d
.tmp
[3*i
+1], d
.tmp
[3*i
+2], 0xff}
241 for i
:= np
; i
< 256; i
++ {
242 // Initialize the rest of the palette to opaque black. The spec (section
243 // 11.2.3) says that "any out-of-range pixel value found in the image data
244 // is an error", but some real-world PNG files have out-of-range pixel
245 // values. We fall back to opaque black, the same as libpng 1.5.13;
246 // ImageMagick 6.5.7 returns an error.
247 d
.palette
[i
] = color
.RGBA
{0x00, 0x00, 0x00, 0xff}
249 d
.palette
= d
.palette
[:np
]
250 case cbTC8
, cbTCA8
, cbTC16
, cbTCA16
:
251 // As per the PNG spec, a PLTE chunk is optional (and for practical purposes,
252 // ignorable) for the ctTrueColor and ctTrueColorAlpha color types (section 4.1.2).
254 return FormatError("PLTE, color type mismatch")
256 return d
.verifyChecksum()
259 func (d
*decoder
) parsetRNS(length
uint32) error
{
261 case cbG1
, cbG2
, cbG4
, cbG8
, cbG16
:
263 return FormatError("bad tRNS length")
265 n
, err
:= io
.ReadFull(d
.r
, d
.tmp
[:length
])
269 d
.crc
.Write(d
.tmp
[:n
])
271 copy(d
.transparent
[:], d
.tmp
[:length
])
274 d
.transparent
[1] *= 0xff
276 d
.transparent
[1] *= 0x55
278 d
.transparent
[1] *= 0x11
280 d
.useTransparent
= true
284 return FormatError("bad tRNS length")
286 n
, err
:= io
.ReadFull(d
.r
, d
.tmp
[:length
])
290 d
.crc
.Write(d
.tmp
[:n
])
292 copy(d
.transparent
[:], d
.tmp
[:length
])
293 d
.useTransparent
= true
295 case cbP1
, cbP2
, cbP4
, cbP8
:
297 return FormatError("bad tRNS length")
299 n
, err
:= io
.ReadFull(d
.r
, d
.tmp
[:length
])
303 d
.crc
.Write(d
.tmp
[:n
])
305 if len(d
.palette
) < n
{
306 d
.palette
= d
.palette
[:n
]
308 for i
:= 0; i
< n
; i
++ {
309 rgba
:= d
.palette
[i
].(color
.RGBA
)
310 d
.palette
[i
] = color
.NRGBA
{rgba
.R
, rgba
.G
, rgba
.B
, d
.tmp
[i
]}
314 return FormatError("tRNS, color type mismatch")
316 return d
.verifyChecksum()
319 // Read presents one or more IDAT chunks as one continuous stream (minus the
320 // intermediate chunk headers and footers). If the PNG data looked like:
321 // ... len0 IDAT xxx crc0 len1 IDAT yy crc1 len2 IEND crc2
322 // then this reader presents xxxyy. For well-formed PNG data, the decoder state
323 // immediately before the first Read call is that d.r is positioned between the
324 // first IDAT and xxx, and the decoder state immediately after the last Read
325 // call is that d.r is positioned between yy and crc1.
326 func (d
*decoder
) Read(p
[]byte) (int, error
) {
330 for d
.idatLength
== 0 {
331 // We have exhausted an IDAT chunk. Verify the checksum of that chunk.
332 if err
:= d
.verifyChecksum(); err
!= nil {
335 // Read the length and chunk type of the next chunk, and check that
336 // it is an IDAT chunk.
337 if _
, err
:= io
.ReadFull(d
.r
, d
.tmp
[:8]); err
!= nil {
340 d
.idatLength
= binary
.BigEndian
.Uint32(d
.tmp
[:4])
341 if string(d
.tmp
[4:8]) != "IDAT" {
342 return 0, FormatError("not enough pixel data")
345 d
.crc
.Write(d
.tmp
[4:8])
347 if int(d
.idatLength
) < 0 {
348 return 0, UnsupportedError("IDAT chunk length overflow")
350 n
, err
:= d
.r
.Read(p
[:min(len(p
), int(d
.idatLength
))])
352 d
.idatLength
-= uint32(n
)
356 // decode decodes the IDAT data into an image.
357 func (d
*decoder
) decode() (image
.Image
, error
) {
358 r
, err
:= zlib
.NewReader(d
)
364 if d
.interlace
== itNone
{
365 img
, err
= d
.readImagePass(r
, 0, false)
369 } else if d
.interlace
== itAdam7
{
370 // Allocate a blank image of the full size.
371 img
, err
= d
.readImagePass(nil, 0, true)
375 for pass
:= 0; pass
< 7; pass
++ {
376 imagePass
, err
:= d
.readImagePass(r
, pass
, false)
380 if imagePass
!= nil {
381 d
.mergePassInto(img
, imagePass
, pass
)
386 // Check for EOF, to verify the zlib checksum.
388 for i
:= 0; n
== 0 && err
== nil; i
++ {
390 return nil, io
.ErrNoProgress
392 n
, err
= r
.Read(d
.tmp
[:1])
394 if err
!= nil && err
!= io
.EOF
{
395 return nil, FormatError(err
.Error())
397 if n
!= 0 || d
.idatLength
!= 0 {
398 return nil, FormatError("too much pixel data")
404 // readImagePass reads a single image pass, sized according to the pass number.
405 func (d
*decoder
) readImagePass(r io
.Reader
, pass
int, allocateOnly
bool) (image
.Image
, error
) {
411 paletted
*image
.Paletted
415 nrgba64
*image
.NRGBA64
418 width
, height
:= d
.width
, d
.height
419 if d
.interlace
== itAdam7
&& !allocateOnly
{
420 p
:= interlacing
[pass
]
421 // Add the multiplication factor and subtract one, effectively rounding up.
422 width
= (width
- p
.xOffset
+ p
.xFactor
- 1) / p
.xFactor
423 height
= (height
- p
.yOffset
+ p
.yFactor
- 1) / p
.yFactor
424 // A PNG image can't have zero width or height, but for an interlaced
425 // image, an individual pass might have zero width or height. If so, we
426 // shouldn't even read a per-row filter type byte, so return early.
427 if width
== 0 || height
== 0 {
432 case cbG1
, cbG2
, cbG4
, cbG8
:
433 bitsPerPixel
= d
.depth
434 if d
.useTransparent
{
435 nrgba
= image
.NewNRGBA(image
.Rect(0, 0, width
, height
))
438 gray
= image
.NewGray(image
.Rect(0, 0, width
, height
))
443 nrgba
= image
.NewNRGBA(image
.Rect(0, 0, width
, height
))
447 if d
.useTransparent
{
448 nrgba
= image
.NewNRGBA(image
.Rect(0, 0, width
, height
))
451 rgba
= image
.NewRGBA(image
.Rect(0, 0, width
, height
))
454 case cbP1
, cbP2
, cbP4
, cbP8
:
455 bitsPerPixel
= d
.depth
456 paletted
= image
.NewPaletted(image
.Rect(0, 0, width
, height
), d
.palette
)
460 nrgba
= image
.NewNRGBA(image
.Rect(0, 0, width
, height
))
464 if d
.useTransparent
{
465 nrgba64
= image
.NewNRGBA64(image
.Rect(0, 0, width
, height
))
468 gray16
= image
.NewGray16(image
.Rect(0, 0, width
, height
))
473 nrgba64
= image
.NewNRGBA64(image
.Rect(0, 0, width
, height
))
477 if d
.useTransparent
{
478 nrgba64
= image
.NewNRGBA64(image
.Rect(0, 0, width
, height
))
481 rgba64
= image
.NewRGBA64(image
.Rect(0, 0, width
, height
))
486 nrgba64
= image
.NewNRGBA64(image
.Rect(0, 0, width
, height
))
492 bytesPerPixel
:= (bitsPerPixel
+ 7) / 8
494 // The +1 is for the per-row filter type, which is at cr[0].
495 rowSize
:= 1 + (bitsPerPixel
*width
+7)/8
496 // cr and pr are the bytes for the current and previous row.
497 cr
:= make([]uint8, rowSize
)
498 pr
:= make([]uint8, rowSize
)
500 for y
:= 0; y
< height
; y
++ {
501 // Read the decompressed bytes.
502 _
, err
:= io
.ReadFull(r
, cr
)
504 if err
== io
.EOF || err
== io
.ErrUnexpectedEOF
{
505 return nil, FormatError("not enough pixel data")
517 for i
:= bytesPerPixel
; i
< len(cdat
); i
++ {
518 cdat
[i
] += cdat
[i
-bytesPerPixel
]
521 for i
, p
:= range pdat
{
525 // The first column has no column to the left of it, so it is a
526 // special case. We know that the first column exists because we
527 // check above that width != 0, and so len(cdat) != 0.
528 for i
:= 0; i
< bytesPerPixel
; i
++ {
529 cdat
[i
] += pdat
[i
] / 2
531 for i
:= bytesPerPixel
; i
< len(cdat
); i
++ {
532 cdat
[i
] += uint8((int(cdat
[i
-bytesPerPixel
]) + int(pdat
[i
])) / 2)
535 filterPaeth(cdat
, pdat
, bytesPerPixel
)
537 return nil, FormatError("bad filter type")
540 // Convert from bytes to colors.
543 if d
.useTransparent
{
544 ty
:= d
.transparent
[1]
545 for x
:= 0; x
< width
; x
+= 8 {
547 for x2
:= 0; x2
< 8 && x
+x2
< width
; x2
++ {
548 ycol
:= (b
>> 7) * 0xff
553 nrgba
.SetNRGBA(x
+x2
, y
, color
.NRGBA
{ycol
, ycol
, ycol
, acol
})
558 for x
:= 0; x
< width
; x
+= 8 {
560 for x2
:= 0; x2
< 8 && x
+x2
< width
; x2
++ {
561 gray
.SetGray(x
+x2
, y
, color
.Gray
{(b
>> 7) * 0xff})
567 if d
.useTransparent
{
568 ty
:= d
.transparent
[1]
569 for x
:= 0; x
< width
; x
+= 4 {
571 for x2
:= 0; x2
< 4 && x
+x2
< width
; x2
++ {
572 ycol
:= (b
>> 6) * 0x55
577 nrgba
.SetNRGBA(x
+x2
, y
, color
.NRGBA
{ycol
, ycol
, ycol
, acol
})
582 for x
:= 0; x
< width
; x
+= 4 {
584 for x2
:= 0; x2
< 4 && x
+x2
< width
; x2
++ {
585 gray
.SetGray(x
+x2
, y
, color
.Gray
{(b
>> 6) * 0x55})
591 if d
.useTransparent
{
592 ty
:= d
.transparent
[1]
593 for x
:= 0; x
< width
; x
+= 2 {
595 for x2
:= 0; x2
< 2 && x
+x2
< width
; x2
++ {
596 ycol
:= (b
>> 4) * 0x11
601 nrgba
.SetNRGBA(x
+x2
, y
, color
.NRGBA
{ycol
, ycol
, ycol
, acol
})
606 for x
:= 0; x
< width
; x
+= 2 {
608 for x2
:= 0; x2
< 2 && x
+x2
< width
; x2
++ {
609 gray
.SetGray(x
+x2
, y
, color
.Gray
{(b
>> 4) * 0x11})
615 if d
.useTransparent
{
616 ty
:= d
.transparent
[1]
617 for x
:= 0; x
< width
; x
++ {
623 nrgba
.SetNRGBA(x
, y
, color
.NRGBA
{ycol
, ycol
, ycol
, acol
})
626 copy(gray
.Pix
[pixOffset
:], cdat
)
627 pixOffset
+= gray
.Stride
630 for x
:= 0; x
< width
; x
++ {
632 nrgba
.SetNRGBA(x
, y
, color
.NRGBA
{ycol
, ycol
, ycol
, cdat
[2*x
+1]})
635 if d
.useTransparent
{
636 pix
, i
, j
:= nrgba
.Pix
, pixOffset
, 0
637 tr
, tg
, tb
:= d
.transparent
[1], d
.transparent
[3], d
.transparent
[5]
638 for x
:= 0; x
< width
; x
++ {
643 if r
== tr
&& g
== tg
&& b
== tb
{
653 pixOffset
+= nrgba
.Stride
655 pix
, i
, j
:= rgba
.Pix
, pixOffset
, 0
656 for x
:= 0; x
< width
; x
++ {
664 pixOffset
+= rgba
.Stride
667 for x
:= 0; x
< width
; x
+= 8 {
669 for x2
:= 0; x2
< 8 && x
+x2
< width
; x2
++ {
671 if len(paletted
.Palette
) <= int(idx
) {
672 paletted
.Palette
= paletted
.Palette
[:int(idx
)+1]
674 paletted
.SetColorIndex(x
+x2
, y
, idx
)
679 for x
:= 0; x
< width
; x
+= 4 {
681 for x2
:= 0; x2
< 4 && x
+x2
< width
; x2
++ {
683 if len(paletted
.Palette
) <= int(idx
) {
684 paletted
.Palette
= paletted
.Palette
[:int(idx
)+1]
686 paletted
.SetColorIndex(x
+x2
, y
, idx
)
691 for x
:= 0; x
< width
; x
+= 2 {
693 for x2
:= 0; x2
< 2 && x
+x2
< width
; x2
++ {
695 if len(paletted
.Palette
) <= int(idx
) {
696 paletted
.Palette
= paletted
.Palette
[:int(idx
)+1]
698 paletted
.SetColorIndex(x
+x2
, y
, idx
)
703 if len(paletted
.Palette
) != 255 {
704 for x
:= 0; x
< width
; x
++ {
705 if len(paletted
.Palette
) <= int(cdat
[x
]) {
706 paletted
.Palette
= paletted
.Palette
[:int(cdat
[x
])+1]
710 copy(paletted
.Pix
[pixOffset
:], cdat
)
711 pixOffset
+= paletted
.Stride
713 copy(nrgba
.Pix
[pixOffset
:], cdat
)
714 pixOffset
+= nrgba
.Stride
716 if d
.useTransparent
{
717 ty
:= uint16(d
.transparent
[0])<<8 |
uint16(d
.transparent
[1])
718 for x
:= 0; x
< width
; x
++ {
719 ycol
:= uint16(cdat
[2*x
+0])<<8 |
uint16(cdat
[2*x
+1])
720 acol
:= uint16(0xffff)
724 nrgba64
.SetNRGBA64(x
, y
, color
.NRGBA64
{ycol
, ycol
, ycol
, acol
})
727 for x
:= 0; x
< width
; x
++ {
728 ycol
:= uint16(cdat
[2*x
+0])<<8 |
uint16(cdat
[2*x
+1])
729 gray16
.SetGray16(x
, y
, color
.Gray16
{ycol
})
733 for x
:= 0; x
< width
; x
++ {
734 ycol
:= uint16(cdat
[4*x
+0])<<8 |
uint16(cdat
[4*x
+1])
735 acol
:= uint16(cdat
[4*x
+2])<<8 |
uint16(cdat
[4*x
+3])
736 nrgba64
.SetNRGBA64(x
, y
, color
.NRGBA64
{ycol
, ycol
, ycol
, acol
})
739 if d
.useTransparent
{
740 tr
:= uint16(d
.transparent
[0])<<8 |
uint16(d
.transparent
[1])
741 tg
:= uint16(d
.transparent
[2])<<8 |
uint16(d
.transparent
[3])
742 tb
:= uint16(d
.transparent
[4])<<8 |
uint16(d
.transparent
[5])
743 for x
:= 0; x
< width
; x
++ {
744 rcol
:= uint16(cdat
[6*x
+0])<<8 |
uint16(cdat
[6*x
+1])
745 gcol
:= uint16(cdat
[6*x
+2])<<8 |
uint16(cdat
[6*x
+3])
746 bcol
:= uint16(cdat
[6*x
+4])<<8 |
uint16(cdat
[6*x
+5])
747 acol
:= uint16(0xffff)
748 if rcol
== tr
&& gcol
== tg
&& bcol
== tb
{
751 nrgba64
.SetNRGBA64(x
, y
, color
.NRGBA64
{rcol
, gcol
, bcol
, acol
})
754 for x
:= 0; x
< width
; x
++ {
755 rcol
:= uint16(cdat
[6*x
+0])<<8 |
uint16(cdat
[6*x
+1])
756 gcol
:= uint16(cdat
[6*x
+2])<<8 |
uint16(cdat
[6*x
+3])
757 bcol
:= uint16(cdat
[6*x
+4])<<8 |
uint16(cdat
[6*x
+5])
758 rgba64
.SetRGBA64(x
, y
, color
.RGBA64
{rcol
, gcol
, bcol
, 0xffff})
762 for x
:= 0; x
< width
; x
++ {
763 rcol
:= uint16(cdat
[8*x
+0])<<8 |
uint16(cdat
[8*x
+1])
764 gcol
:= uint16(cdat
[8*x
+2])<<8 |
uint16(cdat
[8*x
+3])
765 bcol
:= uint16(cdat
[8*x
+4])<<8 |
uint16(cdat
[8*x
+5])
766 acol
:= uint16(cdat
[8*x
+6])<<8 |
uint16(cdat
[8*x
+7])
767 nrgba64
.SetNRGBA64(x
, y
, color
.NRGBA64
{rcol
, gcol
, bcol
, acol
})
771 // The current row for y is the previous row for y+1.
778 // mergePassInto merges a single pass into a full sized image.
779 func (d
*decoder
) mergePassInto(dst image
.Image
, src image
.Image
, pass
int) {
780 p
:= interlacing
[pass
]
788 switch target
:= dst
.(type) {
790 srcPix
= src
.(*image
.Alpha
).Pix
791 dstPix
, stride
, rect
= target
.Pix
, target
.Stride
, target
.Rect
794 srcPix
= src
.(*image
.Alpha16
).Pix
795 dstPix
, stride
, rect
= target
.Pix
, target
.Stride
, target
.Rect
798 srcPix
= src
.(*image
.Gray
).Pix
799 dstPix
, stride
, rect
= target
.Pix
, target
.Stride
, target
.Rect
802 srcPix
= src
.(*image
.Gray16
).Pix
803 dstPix
, stride
, rect
= target
.Pix
, target
.Stride
, target
.Rect
806 srcPix
= src
.(*image
.NRGBA
).Pix
807 dstPix
, stride
, rect
= target
.Pix
, target
.Stride
, target
.Rect
810 srcPix
= src
.(*image
.NRGBA64
).Pix
811 dstPix
, stride
, rect
= target
.Pix
, target
.Stride
, target
.Rect
813 case *image
.Paletted
:
814 srcPix
= src
.(*image
.Paletted
).Pix
815 dstPix
, stride
, rect
= target
.Pix
, target
.Stride
, target
.Rect
818 srcPix
= src
.(*image
.RGBA
).Pix
819 dstPix
, stride
, rect
= target
.Pix
, target
.Stride
, target
.Rect
822 srcPix
= src
.(*image
.RGBA64
).Pix
823 dstPix
, stride
, rect
= target
.Pix
, target
.Stride
, target
.Rect
826 s
, bounds
:= 0, src
.Bounds()
827 for y
:= bounds
.Min
.Y
; y
< bounds
.Max
.Y
; y
++ {
828 dBase
:= (y
*p
.yFactor
+p
.yOffset
-rect
.Min
.Y
)*stride
+ (p
.xOffset
-rect
.Min
.X
)*bytesPerPixel
829 for x
:= bounds
.Min
.X
; x
< bounds
.Max
.X
; x
++ {
830 d
:= dBase
+ x
*p
.xFactor
*bytesPerPixel
831 copy(dstPix
[d
:], srcPix
[s
:s
+bytesPerPixel
])
837 func (d
*decoder
) parseIDAT(length
uint32) (err error
) {
838 d
.idatLength
= length
839 d
.img
, err
= d
.decode()
843 return d
.verifyChecksum()
846 func (d
*decoder
) parseIEND(length
uint32) error
{
848 return FormatError("bad IEND length")
850 return d
.verifyChecksum()
853 func (d
*decoder
) parseChunk() error
{
854 // Read the length and chunk type.
855 n
, err
:= io
.ReadFull(d
.r
, d
.tmp
[:8])
859 length
:= binary
.BigEndian
.Uint32(d
.tmp
[:4])
861 d
.crc
.Write(d
.tmp
[4:8])
863 // Read the chunk data.
864 switch string(d
.tmp
[4:8]) {
866 if d
.stage
!= dsStart
{
867 return chunkOrderError
870 return d
.parseIHDR(length
)
872 if d
.stage
!= dsSeenIHDR
{
873 return chunkOrderError
876 return d
.parsePLTE(length
)
878 if cbPaletted(d
.cb
) {
879 if d
.stage
!= dsSeenPLTE
{
880 return chunkOrderError
882 } else if d
.stage
!= dsSeenIHDR
{
883 return chunkOrderError
886 return d
.parsetRNS(length
)
888 if d
.stage
< dsSeenIHDR || d
.stage
> dsSeenIDAT ||
(d
.stage
== dsSeenIHDR
&& cbPaletted(d
.cb
)) {
889 return chunkOrderError
890 } else if d
.stage
== dsSeenIDAT
{
891 // Ignore trailing zero-length or garbage IDAT chunks.
893 // This does not affect valid PNG images that contain multiple IDAT
894 // chunks, since the first call to parseIDAT below will consume all
895 // consecutive IDAT chunks required for decoding the image.
899 return d
.parseIDAT(length
)
901 if d
.stage
!= dsSeenIDAT
{
902 return chunkOrderError
905 return d
.parseIEND(length
)
907 if length
> 0x7fffffff {
908 return FormatError(fmt
.Sprintf("Bad chunk length: %d", length
))
910 // Ignore this chunk (of a known length).
911 var ignored
[4096]byte
913 n
, err
= io
.ReadFull(d
.r
, ignored
[:min(len(ignored
), int(length
))])
917 d
.crc
.Write(ignored
[:n
])
920 return d
.verifyChecksum()
923 func (d
*decoder
) verifyChecksum() error
{
924 if _
, err
:= io
.ReadFull(d
.r
, d
.tmp
[:4]); err
!= nil {
927 if binary
.BigEndian
.Uint32(d
.tmp
[:4]) != d
.crc
.Sum32() {
928 return FormatError("invalid checksum")
933 func (d
*decoder
) checkHeader() error
{
934 _
, err
:= io
.ReadFull(d
.r
, d
.tmp
[:len(pngHeader
)])
938 if string(d
.tmp
[:len(pngHeader
)]) != pngHeader
{
939 return FormatError("not a PNG file")
944 // Decode reads a PNG image from r and returns it as an image.Image.
945 // The type of Image returned depends on the PNG contents.
946 func Decode(r io
.Reader
) (image
.Image
, error
) {
949 crc
: crc32
.NewIEEE(),
951 if err
:= d
.checkHeader(); err
!= nil {
953 err
= io
.ErrUnexpectedEOF
957 for d
.stage
!= dsSeenIEND
{
958 if err
:= d
.parseChunk(); err
!= nil {
960 err
= io
.ErrUnexpectedEOF
968 // DecodeConfig returns the color model and dimensions of a PNG image without
969 // decoding the entire image.
970 func DecodeConfig(r io
.Reader
) (image
.Config
, error
) {
973 crc
: crc32
.NewIEEE(),
975 if err
:= d
.checkHeader(); err
!= nil {
977 err
= io
.ErrUnexpectedEOF
979 return image
.Config
{}, err
982 if err
:= d
.parseChunk(); err
!= nil {
984 err
= io
.ErrUnexpectedEOF
986 return image
.Config
{}, err
988 paletted
:= cbPaletted(d
.cb
)
989 if d
.stage
== dsSeenIHDR
&& !paletted
{
992 if d
.stage
== dsSeenPLTE
&& paletted
{
998 case cbG1
, cbG2
, cbG4
, cbG8
:
1001 cm
= color
.NRGBAModel
1003 cm
= color
.RGBAModel
1004 case cbP1
, cbP2
, cbP4
, cbP8
:
1007 cm
= color
.NRGBAModel
1009 cm
= color
.Gray16Model
1011 cm
= color
.NRGBA64Model
1013 cm
= color
.RGBA64Model
1015 cm
= color
.NRGBA64Model
1017 return image
.Config
{
1025 image
.RegisterFormat("png", pngHeader
, Decode
, DecodeConfig
)