// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package png implements a PNG image decoder and encoder. // // The PNG specification is at https://www.w3.org/TR/PNG/.
package png import ( ) // Color type, as per the PNG spec. const ( ctGrayscale = 0 ctTrueColor = 2 ctPaletted = 3 ctGrayscaleAlpha = 4 ctTrueColorAlpha = 6 ) // A cb is a combination of color type and bit depth. const ( cbInvalid = iota cbG1 cbG2 cbG4 cbG8 cbGA8 cbTC8 cbP1 cbP2 cbP4 cbP8 cbTCA8 cbG16 cbGA16 cbTC16 cbTCA16 ) func cbPaletted( int) bool { return cbP1 <= && <= cbP8 } // Filter type, as per the PNG spec. const ( ftNone = 0 ftSub = 1 ftUp = 2 ftAverage = 3 ftPaeth = 4 nFilter = 5 ) // Interlace type. const ( itNone = 0 itAdam7 = 1 ) // interlaceScan defines the placement and size of a pass for Adam7 interlacing. type interlaceScan struct { xFactor, yFactor, xOffset, yOffset int } // interlacing defines Adam7 interlacing, with 7 passes of reduced images. // See https://www.w3.org/TR/PNG/#8Interlace var interlacing = []interlaceScan{ {8, 8, 0, 0}, {8, 8, 4, 0}, {4, 8, 0, 4}, {4, 4, 2, 0}, {2, 4, 0, 2}, {2, 2, 1, 0}, {1, 2, 0, 1}, } // Decoding stage. // The PNG specification says that the IHDR, PLTE (if present), tRNS (if // present), IDAT and IEND chunks must appear in that order. There may be // multiple IDAT chunks, and IDAT chunks must be sequential (i.e. they may not // have any other chunks between them). // https://www.w3.org/TR/PNG/#5ChunkOrdering const ( dsStart = iota dsSeenIHDR dsSeenPLTE dsSeentRNS dsSeenIDAT dsSeenIEND ) const pngHeader = "\x89PNG\r\n\x1a\n" type decoder struct { r io.Reader img image.Image crc hash.Hash32 width, height int depth int palette color.Palette cb int stage int idatLength uint32 tmp [3 * 256]byte interlace int // useTransparent and transparent are used for grayscale and truecolor // transparency, as opposed to palette transparency. useTransparent bool transparent [6]byte } // A FormatError reports that the input is not a valid PNG. type FormatError string func ( FormatError) () string { return "png: invalid format: " + string() } var chunkOrderError = FormatError("chunk out of order") // An UnsupportedError reports that the input uses a valid but unimplemented PNG feature. type UnsupportedError string func ( UnsupportedError) () string { return "png: unsupported feature: " + string() } func min(, int) int { if < { return } return } func ( *decoder) ( uint32) error { if != 13 { return FormatError("bad IHDR length") } if , := io.ReadFull(.r, .tmp[:13]); != nil { return } .crc.Write(.tmp[:13]) if .tmp[10] != 0 { return UnsupportedError("compression method") } if .tmp[11] != 0 { return UnsupportedError("filter method") } if .tmp[12] != itNone && .tmp[12] != itAdam7 { return FormatError("invalid interlace method") } .interlace = int(.tmp[12]) := int32(binary.BigEndian.Uint32(.tmp[0:4])) := int32(binary.BigEndian.Uint32(.tmp[4:8])) if <= 0 || <= 0 { return FormatError("non-positive dimension") } := int64() * int64() := int() if != int64() { return UnsupportedError("dimension overflow") } // There can be up to 8 bytes per pixel, for 16 bits per channel RGBA. if != (*8)/8 { return UnsupportedError("dimension overflow") } .cb = cbInvalid .depth = int(.tmp[8]) switch .depth { case 1: switch .tmp[9] { case ctGrayscale: .cb = cbG1 case ctPaletted: .cb = cbP1 } case 2: switch .tmp[9] { case ctGrayscale: .cb = cbG2 case ctPaletted: .cb = cbP2 } case 4: switch .tmp[9] { case ctGrayscale: .cb = cbG4 case ctPaletted: .cb = cbP4 } case 8: switch .tmp[9] { case ctGrayscale: .cb = cbG8 case ctTrueColor: .cb = cbTC8 case ctPaletted: .cb = cbP8 case ctGrayscaleAlpha: .cb = cbGA8 case ctTrueColorAlpha: .cb = cbTCA8 } case 16: switch .tmp[9] { case ctGrayscale: .cb = cbG16 case ctTrueColor: .cb = cbTC16 case ctGrayscaleAlpha: .cb = cbGA16 case ctTrueColorAlpha: .cb = cbTCA16 } } if .cb == cbInvalid { return UnsupportedError(fmt.Sprintf("bit depth %d, color type %d", .tmp[8], .tmp[9])) } .width, .height = int(), int() return .verifyChecksum() } func ( *decoder) ( uint32) error { := int( / 3) // The number of palette entries. if %3 != 0 || <= 0 || > 256 || > 1<<uint(.depth) { return FormatError("bad PLTE length") } , := io.ReadFull(.r, .tmp[:3*]) if != nil { return } .crc.Write(.tmp[:]) switch .cb { case cbP1, cbP2, cbP4, cbP8: .palette = make(color.Palette, 256) for := 0; < ; ++ { .palette[] = color.RGBA{.tmp[3*+0], .tmp[3*+1], .tmp[3*+2], 0xff} } for := ; < 256; ++ { // Initialize the rest of the palette to opaque black. The spec (section // 11.2.3) says that "any out-of-range pixel value found in the image data // is an error", but some real-world PNG files have out-of-range pixel // values. We fall back to opaque black, the same as libpng 1.5.13; // ImageMagick 6.5.7 returns an error. .palette[] = color.RGBA{0x00, 0x00, 0x00, 0xff} } .palette = .palette[:] case cbTC8, cbTCA8, cbTC16, cbTCA16: // As per the PNG spec, a PLTE chunk is optional (and for practical purposes, // ignorable) for the ctTrueColor and ctTrueColorAlpha color types (section 4.1.2). default: return FormatError("PLTE, color type mismatch") } return .verifyChecksum() } func ( *decoder) ( uint32) error { switch .cb { case cbG1, cbG2, cbG4, cbG8, cbG16: if != 2 { return FormatError("bad tRNS length") } , := io.ReadFull(.r, .tmp[:]) if != nil { return } .crc.Write(.tmp[:]) copy(.transparent[:], .tmp[:]) switch .cb { case cbG1: .transparent[1] *= 0xff case cbG2: .transparent[1] *= 0x55 case cbG4: .transparent[1] *= 0x11 } .useTransparent = true case cbTC8, cbTC16: if != 6 { return FormatError("bad tRNS length") } , := io.ReadFull(.r, .tmp[:]) if != nil { return } .crc.Write(.tmp[:]) copy(.transparent[:], .tmp[:]) .useTransparent = true case cbP1, cbP2, cbP4, cbP8: if > 256 { return FormatError("bad tRNS length") } , := io.ReadFull(.r, .tmp[:]) if != nil { return } .crc.Write(.tmp[:]) if len(.palette) < { .palette = .palette[:] } for := 0; < ; ++ { := .palette[].(color.RGBA) .palette[] = color.NRGBA{.R, .G, .B, .tmp[]} } default: return FormatError("tRNS, color type mismatch") } return .verifyChecksum() } // Read presents one or more IDAT chunks as one continuous stream (minus the // intermediate chunk headers and footers). If the PNG data looked like: // ... len0 IDAT xxx crc0 len1 IDAT yy crc1 len2 IEND crc2 // then this reader presents xxxyy. For well-formed PNG data, the decoder state // immediately before the first Read call is that d.r is positioned between the // first IDAT and xxx, and the decoder state immediately after the last Read // call is that d.r is positioned between yy and crc1. func ( *decoder) ( []byte) (int, error) { if len() == 0 { return 0, nil } for .idatLength == 0 { // We have exhausted an IDAT chunk. Verify the checksum of that chunk. if := .verifyChecksum(); != nil { return 0, } // Read the length and chunk type of the next chunk, and check that // it is an IDAT chunk. if , := io.ReadFull(.r, .tmp[:8]); != nil { return 0, } .idatLength = binary.BigEndian.Uint32(.tmp[:4]) if string(.tmp[4:8]) != "IDAT" { return 0, FormatError("not enough pixel data") } .crc.Reset() .crc.Write(.tmp[4:8]) } if int(.idatLength) < 0 { return 0, UnsupportedError("IDAT chunk length overflow") } , := .r.Read([:min(len(), int(.idatLength))]) .crc.Write([:]) .idatLength -= uint32() return , } // decode decodes the IDAT data into an image. func ( *decoder) () (image.Image, error) { , := zlib.NewReader() if != nil { return nil, } defer .Close() var image.Image if .interlace == itNone { , = .readImagePass(, 0, false) if != nil { return nil, } } else if .interlace == itAdam7 { // Allocate a blank image of the full size. , = .readImagePass(nil, 0, true) if != nil { return nil, } for := 0; < 7; ++ { , := .readImagePass(, , false) if != nil { return nil, } if != nil { .mergePassInto(, , ) } } } // Check for EOF, to verify the zlib checksum. := 0 for := 0; == 0 && == nil; ++ { if == 100 { return nil, io.ErrNoProgress } , = .Read(.tmp[:1]) } if != nil && != io.EOF { return nil, FormatError(.Error()) } if != 0 || .idatLength != 0 { return nil, FormatError("too much pixel data") } return , nil } // readImagePass reads a single image pass, sized according to the pass number. func ( *decoder) ( io.Reader, int, bool) (image.Image, error) { := 0 := 0 var ( *image.Gray *image.RGBA *image.Paletted *image.NRGBA *image.Gray16 *image.RGBA64 *image.NRGBA64 image.Image ) , := .width, .height if .interlace == itAdam7 && ! { := interlacing[] // Add the multiplication factor and subtract one, effectively rounding up. = ( - .xOffset + .xFactor - 1) / .xFactor = ( - .yOffset + .yFactor - 1) / .yFactor // A PNG image can't have zero width or height, but for an interlaced // image, an individual pass might have zero width or height. If so, we // shouldn't even read a per-row filter type byte, so return early. if == 0 || == 0 { return nil, nil } } switch .cb { case cbG1, cbG2, cbG4, cbG8: = .depth if .useTransparent { = image.NewNRGBA(image.Rect(0, 0, , )) = } else { = image.NewGray(image.Rect(0, 0, , )) = } case cbGA8: = 16 = image.NewNRGBA(image.Rect(0, 0, , )) = case cbTC8: = 24 if .useTransparent { = image.NewNRGBA(image.Rect(0, 0, , )) = } else { = image.NewRGBA(image.Rect(0, 0, , )) = } case cbP1, cbP2, cbP4, cbP8: = .depth = image.NewPaletted(image.Rect(0, 0, , ), .palette) = case cbTCA8: = 32 = image.NewNRGBA(image.Rect(0, 0, , )) = case cbG16: = 16 if .useTransparent { = image.NewNRGBA64(image.Rect(0, 0, , )) = } else { = image.NewGray16(image.Rect(0, 0, , )) = } case cbGA16: = 32 = image.NewNRGBA64(image.Rect(0, 0, , )) = case cbTC16: = 48 if .useTransparent { = image.NewNRGBA64(image.Rect(0, 0, , )) = } else { = image.NewRGBA64(image.Rect(0, 0, , )) = } case cbTCA16: = 64 = image.NewNRGBA64(image.Rect(0, 0, , )) = } if { return , nil } := ( + 7) / 8 // The +1 is for the per-row filter type, which is at cr[0]. := 1 + (int64()*int64()+7)/8 if != int64(int()) { return nil, UnsupportedError("dimension overflow") } // cr and pr are the bytes for the current and previous row. := make([]uint8, ) := make([]uint8, ) for := 0; < ; ++ { // Read the decompressed bytes. , := io.ReadFull(, ) if != nil { if == io.EOF || == io.ErrUnexpectedEOF { return nil, FormatError("not enough pixel data") } return nil, } // Apply the filter. := [1:] := [1:] switch [0] { case ftNone: // No-op. case ftSub: for := ; < len(); ++ { [] += [-] } case ftUp: for , := range { [] += } case ftAverage: // The first column has no column to the left of it, so it is a // special case. We know that the first column exists because we // check above that width != 0, and so len(cdat) != 0. for := 0; < ; ++ { [] += [] / 2 } for := ; < len(); ++ { [] += uint8((int([-]) + int([])) / 2) } case ftPaeth: filterPaeth(, , ) default: return nil, FormatError("bad filter type") } // Convert from bytes to colors. switch .cb { case cbG1: if .useTransparent { := .transparent[1] for := 0; < ; += 8 { := [/8] for := 0; < 8 && + < ; ++ { := ( >> 7) * 0xff := uint8(0xff) if == { = 0x00 } .SetNRGBA(+, , color.NRGBA{, , , }) <<= 1 } } } else { for := 0; < ; += 8 { := [/8] for := 0; < 8 && + < ; ++ { .SetGray(+, , color.Gray{( >> 7) * 0xff}) <<= 1 } } } case cbG2: if .useTransparent { := .transparent[1] for := 0; < ; += 4 { := [/4] for := 0; < 4 && + < ; ++ { := ( >> 6) * 0x55 := uint8(0xff) if == { = 0x00 } .SetNRGBA(+, , color.NRGBA{, , , }) <<= 2 } } } else { for := 0; < ; += 4 { := [/4] for := 0; < 4 && + < ; ++ { .SetGray(+, , color.Gray{( >> 6) * 0x55}) <<= 2 } } } case cbG4: if .useTransparent { := .transparent[1] for := 0; < ; += 2 { := [/2] for := 0; < 2 && + < ; ++ { := ( >> 4) * 0x11 := uint8(0xff) if == { = 0x00 } .SetNRGBA(+, , color.NRGBA{, , , }) <<= 4 } } } else { for := 0; < ; += 2 { := [/2] for := 0; < 2 && + < ; ++ { .SetGray(+, , color.Gray{( >> 4) * 0x11}) <<= 4 } } } case cbG8: if .useTransparent { := .transparent[1] for := 0; < ; ++ { := [] := uint8(0xff) if == { = 0x00 } .SetNRGBA(, , color.NRGBA{, , , }) } } else { copy(.Pix[:], ) += .Stride } case cbGA8: for := 0; < ; ++ { := [2*+0] .SetNRGBA(, , color.NRGBA{, , , [2*+1]}) } case cbTC8: if .useTransparent { , , := .Pix, , 0 , , := .transparent[1], .transparent[3], .transparent[5] for := 0; < ; ++ { := [+0] := [+1] := [+2] := uint8(0xff) if == && == && == { = 0x00 } [+0] = [+1] = [+2] = [+3] = += 4 += 3 } += .Stride } else { , , := .Pix, , 0 for := 0; < ; ++ { [+0] = [+0] [+1] = [+1] [+2] = [+2] [+3] = 0xff += 4 += 3 } += .Stride } case cbP1: for := 0; < ; += 8 { := [/8] for := 0; < 8 && + < ; ++ { := >> 7 if len(.Palette) <= int() { .Palette = .Palette[:int()+1] } .SetColorIndex(+, , ) <<= 1 } } case cbP2: for := 0; < ; += 4 { := [/4] for := 0; < 4 && + < ; ++ { := >> 6 if len(.Palette) <= int() { .Palette = .Palette[:int()+1] } .SetColorIndex(+, , ) <<= 2 } } case cbP4: for := 0; < ; += 2 { := [/2] for := 0; < 2 && + < ; ++ { := >> 4 if len(.Palette) <= int() { .Palette = .Palette[:int()+1] } .SetColorIndex(+, , ) <<= 4 } } case cbP8: if len(.Palette) != 256 { for := 0; < ; ++ { if len(.Palette) <= int([]) { .Palette = .Palette[:int([])+1] } } } copy(.Pix[:], ) += .Stride case cbTCA8: copy(.Pix[:], ) += .Stride case cbG16: if .useTransparent { := uint16(.transparent[0])<<8 | uint16(.transparent[1]) for := 0; < ; ++ { := uint16([2*+0])<<8 | uint16([2*+1]) := uint16(0xffff) if == { = 0x0000 } .SetNRGBA64(, , color.NRGBA64{, , , }) } } else { for := 0; < ; ++ { := uint16([2*+0])<<8 | uint16([2*+1]) .SetGray16(, , color.Gray16{}) } } case cbGA16: for := 0; < ; ++ { := uint16([4*+0])<<8 | uint16([4*+1]) := uint16([4*+2])<<8 | uint16([4*+3]) .SetNRGBA64(, , color.NRGBA64{, , , }) } case cbTC16: if .useTransparent { := uint16(.transparent[0])<<8 | uint16(.transparent[1]) := uint16(.transparent[2])<<8 | uint16(.transparent[3]) := uint16(.transparent[4])<<8 | uint16(.transparent[5]) for := 0; < ; ++ { := uint16([6*+0])<<8 | uint16([6*+1]) := uint16([6*+2])<<8 | uint16([6*+3]) := uint16([6*+4])<<8 | uint16([6*+5]) := uint16(0xffff) if == && == && == { = 0x0000 } .SetNRGBA64(, , color.NRGBA64{, , , }) } } else { for := 0; < ; ++ { := uint16([6*+0])<<8 | uint16([6*+1]) := uint16([6*+2])<<8 | uint16([6*+3]) := uint16([6*+4])<<8 | uint16([6*+5]) .SetRGBA64(, , color.RGBA64{, , , 0xffff}) } } case cbTCA16: for := 0; < ; ++ { := uint16([8*+0])<<8 | uint16([8*+1]) := uint16([8*+2])<<8 | uint16([8*+3]) := uint16([8*+4])<<8 | uint16([8*+5]) := uint16([8*+6])<<8 | uint16([8*+7]) .SetNRGBA64(, , color.NRGBA64{, , , }) } } // The current row for y is the previous row for y+1. , = , } return , nil } // mergePassInto merges a single pass into a full sized image. func ( *decoder) ( image.Image, image.Image, int) { := interlacing[] var ( []uint8 []uint8 int image.Rectangle int ) switch target := .(type) { case *image.Alpha: = .(*image.Alpha).Pix , , = .Pix, .Stride, .Rect = 1 case *image.Alpha16: = .(*image.Alpha16).Pix , , = .Pix, .Stride, .Rect = 2 case *image.Gray: = .(*image.Gray).Pix , , = .Pix, .Stride, .Rect = 1 case *image.Gray16: = .(*image.Gray16).Pix , , = .Pix, .Stride, .Rect = 2 case *image.NRGBA: = .(*image.NRGBA).Pix , , = .Pix, .Stride, .Rect = 4 case *image.NRGBA64: = .(*image.NRGBA64).Pix , , = .Pix, .Stride, .Rect = 8 case *image.Paletted: = .(*image.Paletted).Pix , , = .Pix, .Stride, .Rect = 1 case *image.RGBA: = .(*image.RGBA).Pix , , = .Pix, .Stride, .Rect = 4 case *image.RGBA64: = .(*image.RGBA64).Pix , , = .Pix, .Stride, .Rect = 8 } , := 0, .Bounds() for := .Min.Y; < .Max.Y; ++ { := (*.yFactor+.yOffset-.Min.Y)* + (.xOffset-.Min.X)* for := .Min.X; < .Max.X; ++ { := + *.xFactor* copy([:], [:+]) += } } } func ( *decoder) ( uint32) ( error) { .idatLength = .img, = .decode() if != nil { return } return .verifyChecksum() } func ( *decoder) ( uint32) error { if != 0 { return FormatError("bad IEND length") } return .verifyChecksum() } func ( *decoder) () error { // Read the length and chunk type. if , := io.ReadFull(.r, .tmp[:8]); != nil { return } := binary.BigEndian.Uint32(.tmp[:4]) .crc.Reset() .crc.Write(.tmp[4:8]) // Read the chunk data. switch string(.tmp[4:8]) { case "IHDR": if .stage != dsStart { return chunkOrderError } .stage = dsSeenIHDR return .parseIHDR() case "PLTE": if .stage != dsSeenIHDR { return chunkOrderError } .stage = dsSeenPLTE return .parsePLTE() case "tRNS": if cbPaletted(.cb) { if .stage != dsSeenPLTE { return chunkOrderError } } else if .stage != dsSeenIHDR { return chunkOrderError } .stage = dsSeentRNS return .parsetRNS() case "IDAT": if .stage < dsSeenIHDR || .stage > dsSeenIDAT || (.stage == dsSeenIHDR && cbPaletted(.cb)) { return chunkOrderError } else if .stage == dsSeenIDAT { // Ignore trailing zero-length or garbage IDAT chunks. // // This does not affect valid PNG images that contain multiple IDAT // chunks, since the first call to parseIDAT below will consume all // consecutive IDAT chunks required for decoding the image. break } .stage = dsSeenIDAT return .parseIDAT() case "IEND": if .stage != dsSeenIDAT { return chunkOrderError } .stage = dsSeenIEND return .parseIEND() } if > 0x7fffffff { return FormatError(fmt.Sprintf("Bad chunk length: %d", )) } // Ignore this chunk (of a known length). var [4096]byte for > 0 { , := io.ReadFull(.r, [:min(len(), int())]) if != nil { return } .crc.Write([:]) -= uint32() } return .verifyChecksum() } func ( *decoder) () error { if , := io.ReadFull(.r, .tmp[:4]); != nil { return } if binary.BigEndian.Uint32(.tmp[:4]) != .crc.Sum32() { return FormatError("invalid checksum") } return nil } func ( *decoder) () error { , := io.ReadFull(.r, .tmp[:len(pngHeader)]) if != nil { return } if string(.tmp[:len(pngHeader)]) != pngHeader { return FormatError("not a PNG file") } return nil } // Decode reads a PNG image from r and returns it as an image.Image. // The type of Image returned depends on the PNG contents. func ( io.Reader) (image.Image, error) { := &decoder{ r: , crc: crc32.NewIEEE(), } if := .checkHeader(); != nil { if == io.EOF { = io.ErrUnexpectedEOF } return nil, } for .stage != dsSeenIEND { if := .parseChunk(); != nil { if == io.EOF { = io.ErrUnexpectedEOF } return nil, } } return .img, nil } // DecodeConfig returns the color model and dimensions of a PNG image without // decoding the entire image. func ( io.Reader) (image.Config, error) { := &decoder{ r: , crc: crc32.NewIEEE(), } if := .checkHeader(); != nil { if == io.EOF { = io.ErrUnexpectedEOF } return image.Config{}, } for { if := .parseChunk(); != nil { if == io.EOF { = io.ErrUnexpectedEOF } return image.Config{}, } := cbPaletted(.cb) if .stage == dsSeenIHDR && ! { break } if .stage == dsSeenPLTE && { break } } var color.Model switch .cb { case cbG1, cbG2, cbG4, cbG8: = color.GrayModel case cbGA8: = color.NRGBAModel case cbTC8: = color.RGBAModel case cbP1, cbP2, cbP4, cbP8: = .palette case cbTCA8: = color.NRGBAModel case cbG16: = color.Gray16Model case cbGA16: = color.NRGBA64Model case cbTC16: = color.RGBA64Model case cbTCA16: = color.NRGBA64Model } return image.Config{ ColorModel: , Width: .width, Height: .height, }, nil } func init() { image.RegisterFormat("png", pngHeader, Decode, DecodeConfig) }