OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [image/] [png/] [reader.go] - Rev 747

Compare with Previous | Blame | View Log

// 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 http://www.w3.org/TR/PNG/.
package png

import (
        "compress/zlib"
        "encoding/binary"
        "fmt"
        "hash"
        "hash/crc32"
        "image"
        "image/color"
        "io"
)

// 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
)

// 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).
// http://www.w3.org/TR/PNG/#5ChunkOrdering
const (
        dsStart = iota
        dsSeenIHDR
        dsSeenPLTE
        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
}

// A FormatError reports that the input is not a valid PNG.
type FormatError string

func (e FormatError) Error() string { return "png: invalid format: " + string(e) }

var chunkOrderError = FormatError("chunk out of order")

// An UnsupportedError reports that the input uses a valid but unimplemented PNG feature.
type UnsupportedError string

func (e UnsupportedError) Error() string { return "png: unsupported feature: " + string(e) }

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(length uint32) error {
        if length != 13 {
                return FormatError("bad IHDR length")
        }
        if _, err := io.ReadFull(d.r, d.tmp[:13]); err != nil {
                return err
        }
        d.crc.Write(d.tmp[:13])
        if d.tmp[10] != 0 || d.tmp[11] != 0 || d.tmp[12] != 0 {
                return UnsupportedError("compression, filter or interlace method")
        }
        w := int32(binary.BigEndian.Uint32(d.tmp[0:4]))
        h := int32(binary.BigEndian.Uint32(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.cb = cbInvalid
        d.depth = int(d.tmp[8])
        switch d.depth {
        case 1:
                switch d.tmp[9] {
                case ctGrayscale:
                        d.cb = cbG1
                case ctPaletted:
                        d.cb = cbP1
                }
        case 2:
                switch d.tmp[9] {
                case ctGrayscale:
                        d.cb = cbG2
                case ctPaletted:
                        d.cb = cbP2
                }
        case 4:
                switch d.tmp[9] {
                case ctGrayscale:
                        d.cb = cbG4
                case ctPaletted:
                        d.cb = cbP4
                }
        case 8:
                switch d.tmp[9] {
                case ctGrayscale:
                        d.cb = cbG8
                case ctTrueColor:
                        d.cb = cbTC8
                case ctPaletted:
                        d.cb = cbP8
                case ctGrayscaleAlpha:
                        d.cb = cbGA8
                case ctTrueColorAlpha:
                        d.cb = cbTCA8
                }
        case 16:
                switch d.tmp[9] {
                case ctGrayscale:
                        d.cb = cbG16
                case ctTrueColor:
                        d.cb = cbTC16
                case ctGrayscaleAlpha:
                        d.cb = cbGA16
                case ctTrueColorAlpha:
                        d.cb = cbTCA16
                }
        }
        if d.cb == cbInvalid {
                return UnsupportedError(fmt.Sprintf("bit depth %d, color type %d", d.tmp[8], d.tmp[9]))
        }
        d.width, d.height = int(w), int(h)
        return d.verifyChecksum()
}

func (d *decoder) parsePLTE(length uint32) error {
        np := int(length / 3) // The number of palette entries.
        if length%3 != 0 || np <= 0 || np > 256 || np > 1<<uint(d.depth) {
                return FormatError("bad PLTE length")
        }
        n, err := io.ReadFull(d.r, d.tmp[:3*np])
        if err != nil {
                return err
        }
        d.crc.Write(d.tmp[:n])
        switch d.cb {
        case cbP1, cbP2, cbP4, cbP8:
                d.palette = color.Palette(make([]color.Color, np))
                for i := 0; i < np; i++ {
                        d.palette[i] = color.RGBA{d.tmp[3*i+0], d.tmp[3*i+1], d.tmp[3*i+2], 0xff}
                }
        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 d.verifyChecksum()
}

func (d *decoder) parsetRNS(length uint32) error {
        if length > 256 {
                return FormatError("bad tRNS length")
        }
        n, err := io.ReadFull(d.r, d.tmp[:length])
        if err != nil {
                return err
        }
        d.crc.Write(d.tmp[:n])
        switch d.cb {
        case cbG8, cbG16:
                return UnsupportedError("grayscale transparency")
        case cbTC8, cbTC16:
                return UnsupportedError("truecolor transparency")
        case cbP1, cbP2, cbP4, cbP8:
                if n > len(d.palette) {
                        return FormatError("bad tRNS length")
                }
                for i := 0; i < n; i++ {
                        rgba := d.palette[i].(color.RGBA)
                        d.palette[i] = color.RGBA{rgba.R, rgba.G, rgba.B, d.tmp[i]}
                }
        case cbGA8, cbGA16, cbTCA8, cbTCA16:
                return FormatError("tRNS, color type mismatch")
        }
        return d.verifyChecksum()
}

// 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
}

// 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 (d *decoder) Read(p []byte) (int, error) {
        if len(p) == 0 {
                return 0, nil
        }
        for d.idatLength == 0 {
                // We have exhausted an IDAT chunk. Verify the checksum of that chunk.
                if err := d.verifyChecksum(); err != nil {
                        return 0, err
                }
                // Read the length and chunk type of the next chunk, and check that
                // it is an IDAT chunk.
                if _, err := io.ReadFull(d.r, d.tmp[:8]); err != nil {
                        return 0, err
                }
                d.idatLength = binary.BigEndian.Uint32(d.tmp[:4])
                if string(d.tmp[4:8]) != "IDAT" {
                        return 0, FormatError("not enough pixel data")
                }
                d.crc.Reset()
                d.crc.Write(d.tmp[4:8])
        }
        if int(d.idatLength) < 0 {
                return 0, UnsupportedError("IDAT chunk length overflow")
        }
        n, err := d.r.Read(p[:min(len(p), int(d.idatLength))])
        d.crc.Write(p[:n])
        d.idatLength -= uint32(n)
        return n, err
}

// decode decodes the IDAT data into an image.
func (d *decoder) decode() (image.Image, error) {
        r, err := zlib.NewReader(d)
        if err != nil {
                return nil, err
        }
        defer r.Close()
        bitsPerPixel := 0
        maxPalette := uint8(0)
        var (
                gray     *image.Gray
                rgba     *image.RGBA
                paletted *image.Paletted
                nrgba    *image.NRGBA
                gray16   *image.Gray16
                rgba64   *image.RGBA64
                nrgba64  *image.NRGBA64
                img      image.Image
        )
        switch d.cb {
        case cbG1, cbG2, cbG4, cbG8:
                bitsPerPixel = d.depth
                gray = image.NewGray(image.Rect(0, 0, d.width, d.height))
                img = gray
        case cbGA8:
                bitsPerPixel = 16
                nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height))
                img = nrgba
        case cbTC8:
                bitsPerPixel = 24
                rgba = image.NewRGBA(image.Rect(0, 0, d.width, d.height))
                img = rgba
        case cbP1, cbP2, cbP4, cbP8:
                bitsPerPixel = d.depth
                paletted = image.NewPaletted(image.Rect(0, 0, d.width, d.height), d.palette)
                img = paletted
                maxPalette = uint8(len(d.palette) - 1)
        case cbTCA8:
                bitsPerPixel = 32
                nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height))
                img = nrgba
        case cbG16:
                bitsPerPixel = 16
                gray16 = image.NewGray16(image.Rect(0, 0, d.width, d.height))
                img = gray16
        case cbGA16:
                bitsPerPixel = 32
                nrgba64 = image.NewNRGBA64(image.Rect(0, 0, d.width, d.height))
                img = nrgba64
        case cbTC16:
                bitsPerPixel = 48
                rgba64 = image.NewRGBA64(image.Rect(0, 0, d.width, d.height))
                img = rgba64
        case cbTCA16:
                bitsPerPixel = 64
                nrgba64 = image.NewNRGBA64(image.Rect(0, 0, d.width, d.height))
                img = nrgba64
        }
        bytesPerPixel := (bitsPerPixel + 7) / 8

        // 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+(bitsPerPixel*d.width+7)/8)
        pr := make([]uint8, 1+(bitsPerPixel*d.width+7)/8)

        for y := 0; y < d.height; y++ {
                // Read the decompressed bytes.
                _, err := io.ReadFull(r, cr)
                if err != nil {
                        return nil, err
                }

                // Apply the filter.
                cdat := cr[1:]
                pdat := pr[1:]
                switch cr[0] {
                case ftNone:
                        // No-op.
                case ftSub:
                        for i := bytesPerPixel; i < len(cdat); i++ {
                                cdat[i] += cdat[i-bytesPerPixel]
                        }
                case ftUp:
                        for i := 0; i < len(cdat); i++ {
                                cdat[i] += pdat[i]
                        }
                case ftAverage:
                        for i := 0; i < bytesPerPixel; i++ {
                                cdat[i] += pdat[i] / 2
                        }
                        for i := bytesPerPixel; i < len(cdat); i++ {
                                cdat[i] += uint8((int(cdat[i-bytesPerPixel]) + int(pdat[i])) / 2)
                        }
                case ftPaeth:
                        for i := 0; i < bytesPerPixel; i++ {
                                cdat[i] += paeth(0, pdat[i], 0)
                        }
                        for i := bytesPerPixel; i < len(cdat); i++ {
                                cdat[i] += paeth(cdat[i-bytesPerPixel], pdat[i], pdat[i-bytesPerPixel])
                        }
                default:
                        return nil, FormatError("bad filter type")
                }

                // Convert from bytes to colors.
                switch d.cb {
                case cbG1:
                        for x := 0; x < d.width; x += 8 {
                                b := cdat[x/8]
                                for x2 := 0; x2 < 8 && x+x2 < d.width; x2++ {
                                        gray.SetGray(x+x2, y, color.Gray{(b >> 7) * 0xff})
                                        b <<= 1
                                }
                        }
                case cbG2:
                        for x := 0; x < d.width; x += 4 {
                                b := cdat[x/4]
                                for x2 := 0; x2 < 4 && x+x2 < d.width; x2++ {
                                        gray.SetGray(x+x2, y, color.Gray{(b >> 6) * 0x55})
                                        b <<= 2
                                }
                        }
                case cbG4:
                        for x := 0; x < d.width; x += 2 {
                                b := cdat[x/2]
                                for x2 := 0; x2 < 2 && x+x2 < d.width; x2++ {
                                        gray.SetGray(x+x2, y, color.Gray{(b >> 4) * 0x11})
                                        b <<= 4
                                }
                        }
                case cbG8:
                        for x := 0; x < d.width; x++ {
                                gray.SetGray(x, y, color.Gray{cdat[x]})
                        }
                case cbGA8:
                        for x := 0; x < d.width; x++ {
                                ycol := cdat[2*x+0]
                                nrgba.SetNRGBA(x, y, color.NRGBA{ycol, ycol, ycol, cdat[2*x+1]})
                        }
                case cbTC8:
                        for x := 0; x < d.width; x++ {
                                rgba.SetRGBA(x, y, color.RGBA{cdat[3*x+0], cdat[3*x+1], cdat[3*x+2], 0xff})
                        }
                case cbP1:
                        for x := 0; x < d.width; x += 8 {
                                b := cdat[x/8]
                                for x2 := 0; x2 < 8 && x+x2 < d.width; x2++ {
                                        idx := b >> 7
                                        if idx > maxPalette {
                                                return nil, FormatError("palette index out of range")
                                        }
                                        paletted.SetColorIndex(x+x2, y, idx)
                                        b <<= 1
                                }
                        }
                case cbP2:
                        for x := 0; x < d.width; x += 4 {
                                b := cdat[x/4]
                                for x2 := 0; x2 < 4 && x+x2 < d.width; x2++ {
                                        idx := b >> 6
                                        if idx > maxPalette {
                                                return nil, FormatError("palette index out of range")
                                        }
                                        paletted.SetColorIndex(x+x2, y, idx)
                                        b <<= 2
                                }
                        }
                case cbP4:
                        for x := 0; x < d.width; x += 2 {
                                b := cdat[x/2]
                                for x2 := 0; x2 < 2 && x+x2 < d.width; x2++ {
                                        idx := b >> 4
                                        if idx > maxPalette {
                                                return nil, FormatError("palette index out of range")
                                        }
                                        paletted.SetColorIndex(x+x2, y, idx)
                                        b <<= 4
                                }
                        }
                case cbP8:
                        for x := 0; x < d.width; x++ {
                                if cdat[x] > maxPalette {
                                        return nil, FormatError("palette index out of range")
                                }
                                paletted.SetColorIndex(x, y, cdat[x])
                        }
                case cbTCA8:
                        for x := 0; x < d.width; x++ {
                                nrgba.SetNRGBA(x, y, color.NRGBA{cdat[4*x+0], cdat[4*x+1], cdat[4*x+2], cdat[4*x+3]})
                        }
                case cbG16:
                        for x := 0; x < d.width; x++ {
                                ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1])
                                gray16.SetGray16(x, y, color.Gray16{ycol})
                        }
                case cbGA16:
                        for x := 0; x < d.width; x++ {
                                ycol := uint16(cdat[4*x+0])<<8 | uint16(cdat[4*x+1])
                                acol := uint16(cdat[4*x+2])<<8 | uint16(cdat[4*x+3])
                                nrgba64.SetNRGBA64(x, y, color.NRGBA64{ycol, ycol, ycol, acol})
                        }
                case cbTC16:
                        for x := 0; x < d.width; x++ {
                                rcol := uint16(cdat[6*x+0])<<8 | uint16(cdat[6*x+1])
                                gcol := uint16(cdat[6*x+2])<<8 | uint16(cdat[6*x+3])
                                bcol := uint16(cdat[6*x+4])<<8 | uint16(cdat[6*x+5])
                                rgba64.SetRGBA64(x, y, color.RGBA64{rcol, gcol, bcol, 0xffff})
                        }
                case cbTCA16:
                        for x := 0; x < d.width; x++ {
                                rcol := uint16(cdat[8*x+0])<<8 | uint16(cdat[8*x+1])
                                gcol := uint16(cdat[8*x+2])<<8 | uint16(cdat[8*x+3])
                                bcol := uint16(cdat[8*x+4])<<8 | uint16(cdat[8*x+5])
                                acol := uint16(cdat[8*x+6])<<8 | uint16(cdat[8*x+7])
                                nrgba64.SetNRGBA64(x, y, color.NRGBA64{rcol, gcol, bcol, acol})
                        }
                }

                // The current row for y is the previous row for y+1.
                pr, cr = cr, pr
        }

        // Check for EOF, to verify the zlib checksum.
        n, err := r.Read(pr[:1])
        if err != io.EOF {
                return nil, FormatError(err.Error())
        }
        if n != 0 || d.idatLength != 0 {
                return nil, FormatError("too much pixel data")
        }

        return img, nil
}

func (d *decoder) parseIDAT(length uint32) (err error) {
        d.idatLength = length
        d.img, err = d.decode()
        if err != nil {
                return err
        }
        return d.verifyChecksum()
}

func (d *decoder) parseIEND(length uint32) error {
        if length != 0 {
                return FormatError("bad IEND length")
        }
        return d.verifyChecksum()
}

func (d *decoder) parseChunk() error {
        // Read the length and chunk type.
        n, err := io.ReadFull(d.r, d.tmp[:8])
        if err != nil {
                return err
        }
        length := binary.BigEndian.Uint32(d.tmp[:4])
        d.crc.Reset()
        d.crc.Write(d.tmp[4:8])

        // Read the chunk data.
        switch string(d.tmp[4:8]) {
        case "IHDR":
                if d.stage != dsStart {
                        return chunkOrderError
                }
                d.stage = dsSeenIHDR
                return d.parseIHDR(length)
        case "PLTE":
                if d.stage != dsSeenIHDR {
                        return chunkOrderError
                }
                d.stage = dsSeenPLTE
                return d.parsePLTE(length)
        case "tRNS":
                if d.stage != dsSeenPLTE {
                        return chunkOrderError
                }
                return d.parsetRNS(length)
        case "IDAT":
                if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.cb == cbP8 && d.stage == dsSeenIHDR) {
                        return chunkOrderError
                }
                d.stage = dsSeenIDAT
                return d.parseIDAT(length)
        case "IEND":
                if d.stage != dsSeenIDAT {
                        return chunkOrderError
                }
                d.stage = dsSeenIEND
                return d.parseIEND(length)
        }
        // Ignore this chunk (of a known length).
        var ignored [4096]byte
        for length > 0 {
                n, err = io.ReadFull(d.r, ignored[:min(len(ignored), int(length))])
                if err != nil {
                        return err
                }
                d.crc.Write(ignored[:n])
                length -= uint32(n)
        }
        return d.verifyChecksum()
}

func (d *decoder) verifyChecksum() error {
        if _, err := io.ReadFull(d.r, d.tmp[:4]); err != nil {
                return err
        }
        if binary.BigEndian.Uint32(d.tmp[:4]) != d.crc.Sum32() {
                return FormatError("invalid checksum")
        }
        return nil
}

func (d *decoder) checkHeader() error {
        _, err := io.ReadFull(d.r, d.tmp[:len(pngHeader)])
        if err != nil {
                return err
        }
        if string(d.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 Decode(r io.Reader) (image.Image, error) {
        d := &decoder{
                r:   r,
                crc: crc32.NewIEEE(),
        }
        if err := d.checkHeader(); err != nil {
                if err == io.EOF {
                        err = io.ErrUnexpectedEOF
                }
                return nil, err
        }
        for d.stage != dsSeenIEND {
                if err := d.parseChunk(); err != nil {
                        if err == io.EOF {
                                err = io.ErrUnexpectedEOF
                        }
                        return nil, err
                }
        }
        return d.img, nil
}

// DecodeConfig returns the color model and dimensions of a PNG image without
// decoding the entire image.
func DecodeConfig(r io.Reader) (image.Config, error) {
        d := &decoder{
                r:   r,
                crc: crc32.NewIEEE(),
        }
        if err := d.checkHeader(); err != nil {
                if err == io.EOF {
                        err = io.ErrUnexpectedEOF
                }
                return image.Config{}, err
        }
        for {
                if err := d.parseChunk(); err != nil {
                        if err == io.EOF {
                                err = io.ErrUnexpectedEOF
                        }
                        return image.Config{}, err
                }
                if d.stage == dsSeenIHDR && d.cb != cbP8 {
                        break
                }
                if d.stage == dsSeenPLTE && d.cb == cbP8 {
                        break
                }
        }
        var cm color.Model
        switch d.cb {
        case cbG1, cbG2, cbG4, cbG8:
                cm = color.GrayModel
        case cbGA8:
                cm = color.NRGBAModel
        case cbTC8:
                cm = color.RGBAModel
        case cbP1, cbP2, cbP4, cbP8:
                cm = d.palette
        case cbTCA8:
                cm = color.NRGBAModel
        case cbG16:
                cm = color.Gray16Model
        case cbGA16:
                cm = color.NRGBA64Model
        case cbTC16:
                cm = color.RGBA64Model
        case cbTCA16:
                cm = color.NRGBA64Model
        }
        return image.Config{
                ColorModel: cm,
                Width:      d.width,
                Height:     d.height,
        }, nil
}

func init() {
        image.RegisterFormat("png", pngHeader, Decode, DecodeConfig)
}

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.