// 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. // The png package implements a PNG image decoder and encoder. // // The PNG specification is at http://www.libpng.org/pub/png/spec/1.2/PNG-Contents.html package png import ( "compress/zlib"; "hash"; "hash/crc32"; "image"; "io"; "os"; ) // Color type, as per the PNG spec. const ( ctGrayscale = 0; ctTrueColor = 2; ctPaletted = 3; ctGrayscaleAlpha = 4; ctTrueColorAlpha = 6; ) // Filter type, as per the PNG spec. const ( ftNone = 0; ftSub = 1; ftUp = 2; ftAverage = 3; ftPaeth = 4; nFilter = 5; ) // Decoding stage. // The PNG specification says that the IHDR, PLTE (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). const ( dsStart = iota; dsSeenIHDR; dsSeenPLTE; dsSeenIDAT; dsSeenIEND; ) const pngHeader = "\x89PNG\r\n\x1a\n" type decoder struct { width, height int; image image.Image; colorType uint8; stage int; idatWriter io.WriteCloser; idatDone chan os.Error; tmp [3*256]byte; } // A FormatError reports that the input is not a valid PNG. type FormatError string func (e FormatError) String() string { return "invalid PNG format: " + string(e); } var chunkOrderError = FormatError("chunk out of order") // An IDATDecodingError wraps an inner error (such as a ZLIB decoding error) encountered while processing an IDAT chunk. type IDATDecodingError struct { Err os.Error; } func (e IDATDecodingError) String() string { return "IDAT decoding error: " + e.Err.String(); } // An UnsupportedError reports that the input uses a valid but unimplemented PNG feature. type UnsupportedError string func (e UnsupportedError) String() string { return "unsupported PNG feature: " + string(e); } // Big-endian. func parseUint32(b []uint8) uint32 { return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]); } func abs(x int) int { if x < 0 { return -x; } return x; } func min(a, b int) int { if a < b { return a; } return b; } func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Error { if length != 13 { return FormatError("bad IHDR length"); } _, err := io.ReadFull(r, d.tmp[0:13]); if err != nil { return err; } crc.Write(d.tmp[0:13]); if d.tmp[8] != 8 { return UnsupportedError("bit depth"); } if d.tmp[10] != 0 || d.tmp[11] != 0 || d.tmp[12] != 0 { return UnsupportedError("compression, filter or interlace method"); } w := int32(parseUint32(d.tmp[0:4])); h := int32(parseUint32(d.tmp[4:8])); if w < 0 || h < 0 { return FormatError("negative dimension"); } nPixels := int64(w)*int64(h); if nPixels != int64(int(nPixels)) { return UnsupportedError("dimension overflow"); } d.colorType = d.tmp[9]; switch d.colorType { case ctTrueColor: d.image = image.NewRGBA(int(w), int(h)); case ctPaletted: d.image = image.NewPaletted(int(w), int(h), nil); case ctTrueColorAlpha: d.image = image.NewNRGBA(int(w), int(h)); default: return UnsupportedError("color type"); } d.width, d.height = int(w), int(h); return nil; } func (d *decoder) parsePLTE(r io.Reader, crc hash.Hash32, length uint32) os.Error { np := int(length/3); // The number of palette entries. if length%3 != 0 || np <= 0 || np > 256 { return FormatError("bad PLTE length"); } n, err := io.ReadFull(r, d.tmp[0 : 3*np]); if err != nil { return err; } crc.Write(d.tmp[0:n]); switch d.colorType { case ctPaletted: palette := make([]image.Color, np); for i := 0; i < np; i++ { palette[i] = image.RGBAColor{d.tmp[3*i + 0], d.tmp[3*i + 1], d.tmp[3*i + 2], 0xff}; } d.image.(*image.Paletted).Palette = image.PalettedColorModel(palette); case ctTrueColor, ctTrueColorAlpha: // 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). return nil; default: return FormatError("PLTE, color type mismatch"); } return nil; } // The Paeth filter function, as per the PNG specification. func paeth(a, b, c uint8) uint8 { p := int(a)+int(b)-int(c); pa := abs(p-int(a)); pb := abs(p-int(b)); pc := abs(p-int(c)); if pa <= pb && pa <= pc { return a; } else if pb <= pc { return b; } return c; } func (d *decoder) idatReader(idat io.Reader) os.Error { r, err := zlib.NewInflater(idat); if err != nil { return err; } defer r.Close(); bpp := 0; // Bytes per pixel. maxPalette := uint8(0); var ( rgba *image.RGBA; nrgba *image.NRGBA; paletted *image.Paletted; ) switch d.colorType { case ctTrueColor: bpp = 3; rgba = d.image.(*image.RGBA); case ctPaletted: bpp = 1; paletted = d.image.(*image.Paletted); maxPalette = uint8(len(paletted.Palette)-1); case ctTrueColorAlpha: bpp = 4; nrgba = d.image.(*image.NRGBA); } // cr and pr are the bytes for the current and previous row. // The +1 is for the per-row filter type, which is at cr[0]. cr := make([]uint8, 1 + bpp * d.width); pr := make([]uint8, 1 + bpp * d.width); for y := 0; y < d.height; y++ { // Read the decompressed bytes. _, err := io.ReadFull(r, cr); if err != nil { return err; } // Apply the filter. cdat := cr[1:len(cr)]; pdat := pr[1:len(pr)]; switch cr[0] { case ftNone: // No-op. case ftSub: for i := bpp; i < len(cdat); i++ { cdat[i] += cdat[i-bpp]; } case ftUp: for i := 0; i < len(cdat); i++ { cdat[i] += pdat[i]; } case ftAverage: for i := 0; i < bpp; i++ { cdat[i] += pdat[i]/2; } for i := bpp; i < len(cdat); i++ { cdat[i] += uint8((int(cdat[i-bpp])+int(pdat[i]))/2); } case ftPaeth: for i := 0; i < bpp; i++ { cdat[i] += paeth(0, pdat[i], 0); } for i := bpp; i < len(cdat); i++ { cdat[i] += paeth(cdat[i-bpp], pdat[i], pdat[i-bpp]); } default: return FormatError("bad filter type"); } // Convert from bytes to colors. switch d.colorType { case ctTrueColor: for x := 0; x < d.width; x++ { rgba.Set(x, y, image.RGBAColor{cdat[3*x + 0], cdat[3*x + 1], cdat[3*x + 2], 0xff}); } case ctPaletted: for x := 0; x < d.width; x++ { if cdat[x] > maxPalette { return FormatError("palette index out of range"); } paletted.SetColorIndex(x, y, cdat[x]); } case ctTrueColorAlpha: for x := 0; x < d.width; x++ { nrgba.Set(x, y, image.NRGBAColor{cdat[4*x + 0], cdat[4*x + 1], cdat[4*x + 2], cdat[4*x + 3]}); } } // The current row for y is the previous row for y+1. pr, cr = cr, pr; } return nil; } func (d *decoder) parseIDAT(r io.Reader, crc hash.Hash32, length uint32) os.Error { // There may be more than one IDAT chunk, but their contents must be // treated as if it was one continuous stream (to the zlib decoder). // We bring up an io.Pipe and write the IDAT chunks into the pipe as // we see them, and decode the stream in a separate go-routine, which // signals its completion (successful or not) via a channel. if d.idatWriter == nil { pr, pw := io.Pipe(); d.idatWriter = pw; d.idatDone = make(chan os.Error); go func() { err := d.idatReader(pr); if err == os.EOF { err = FormatError("too little IDAT"); } pr.CloseWithError(FormatError("too much IDAT")); d.idatDone <- err; }(); } var buf [4096]byte; for length > 0 { n, err1 := r.Read(buf[0 : min(len(buf), int(length))]); // We delay checking err1. It is possible to get n bytes and an error, // but if the n bytes themselves contain a FormatError, for example, we // want to report that error, and not the one that made the Read stop. n, err2 := d.idatWriter.Write(buf[0:n]); if err2 != nil { return err2; } if err1 != nil { return err1; } crc.Write(buf[0:n]); length -= uint32(n); } return nil; } func (d *decoder) parseIEND(r io.Reader, crc hash.Hash32, length uint32) os.Error { if length != 0 { return FormatError("bad IEND length"); } return nil; } func (d *decoder) parseChunk(r io.Reader) os.Error { // Read the length. n, err := io.ReadFull(r, d.tmp[0:4]); if err == os.EOF { return io.ErrUnexpectedEOF; } if err != nil { return err; } length := parseUint32(d.tmp[0:4]); // Read the chunk type. n, err = io.ReadFull(r, d.tmp[0:4]); if err == os.EOF { return io.ErrUnexpectedEOF; } if err != nil { return err; } crc := crc32.NewIEEE(); crc.Write(d.tmp[0:4]); // Read the chunk data. switch string(d.tmp[0:4]) { case "IHDR": if d.stage != dsStart { return chunkOrderError; } d.stage = dsSeenIHDR; err = d.parseIHDR(r, crc, length); case "PLTE": if d.stage != dsSeenIHDR { return chunkOrderError; } d.stage = dsSeenPLTE; err = d.parsePLTE(r, crc, length); case "IDAT": if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.colorType == ctPaletted && d.stage == dsSeenIHDR) { return chunkOrderError; } d.stage = dsSeenIDAT; err = d.parseIDAT(r, crc, length); case "IEND": if d.stage != dsSeenIDAT { return chunkOrderError; } d.stage = dsSeenIEND; err = d.parseIEND(r, crc, length); default: // Ignore this chunk (of a known length). var ignored [4096]byte; for length > 0 { n, err = io.ReadFull(r, ignored[0 : min(len(ignored), int(length))]); if err != nil { return err; } crc.Write(ignored[0:n]); length -= uint32(n); } } if err != nil { return err; } // Read the checksum. n, err = io.ReadFull(r, d.tmp[0:4]); if err == os.EOF { return io.ErrUnexpectedEOF; } if err != nil { return err; } if parseUint32(d.tmp[0:4]) != crc.Sum32() { return FormatError("invalid checksum"); } return nil; } func (d *decoder) checkHeader(r io.Reader) os.Error { _, err := io.ReadFull(r, d.tmp[0:8]); if err != nil { return err; } if string(d.tmp[0:8]) != pngHeader { return FormatError("not a PNG file"); } return nil; } // Decode reads a PNG formatted image from r and returns it as an image.Image. // The type of Image returned depends on the PNG contents. func Decode(r io.Reader) (image.Image, os.Error) { var d decoder; err := d.checkHeader(r); if err != nil { return nil, err; } for d.stage = dsStart; d.stage != dsSeenIEND; { err = d.parseChunk(r); if err != nil { break; } } if d.idatWriter != nil { d.idatWriter.Close(); err1 := <-d.idatDone; if err == nil { err = err1; } } if err != nil { return nil, err; } return d.image, nil; }