diff options
Diffstat (limited to 'src/pkg/image/tiff/reader.go')
-rw-r--r-- | src/pkg/image/tiff/reader.go | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/src/pkg/image/tiff/reader.go b/src/pkg/image/tiff/reader.go new file mode 100644 index 000000000..26e52144d --- /dev/null +++ b/src/pkg/image/tiff/reader.go @@ -0,0 +1,419 @@ +// Copyright 2011 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 tiff implements a TIFF image decoder. +// +// The TIFF specification is at http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf +package tiff + +import ( + "compress/lzw" + "compress/zlib" + "encoding/binary" + "image" + "io" + "io/ioutil" + "os" +) + +// A FormatError reports that the input is not a valid TIFF image. +type FormatError string + +func (e FormatError) String() string { + return "tiff: invalid format: " + string(e) +} + +// An UnsupportedError reports that the input uses a valid but +// unimplemented feature. +type UnsupportedError string + +func (e UnsupportedError) String() string { + return "tiff: unsupported feature: " + string(e) +} + +// An InternalError reports that an internal error was encountered. +type InternalError string + +func (e InternalError) String() string { + return "tiff: internal error: " + string(e) +} + +type decoder struct { + r io.ReaderAt + byteOrder binary.ByteOrder + config image.Config + mode imageMode + features map[int][]uint + palette []image.Color + + buf []byte + off int // Current offset in buf. + v uint32 // Buffer value for reading with arbitrary bit depths. + nbits uint // Remaining number of bits in v. +} + +// firstVal returns the first uint of the features entry with the given tag, +// or 0 if the tag does not exist. +func (d *decoder) firstVal(tag int) uint { + f := d.features[tag] + if len(f) == 0 { + return 0 + } + return f[0] +} + +// ifdUint decodes the IFD entry in p, which must be of the Byte, Short +// or Long type, and returns the decoded uint values. +func (d *decoder) ifdUint(p []byte) (u []uint, err os.Error) { + var raw []byte + datatype := d.byteOrder.Uint16(p[2:4]) + count := d.byteOrder.Uint32(p[4:8]) + if datalen := lengths[datatype] * count; datalen > 4 { + // The IFD contains a pointer to the real value. + raw = make([]byte, datalen) + _, err = d.r.ReadAt(raw, int64(d.byteOrder.Uint32(p[8:12]))) + } else { + raw = p[8 : 8+datalen] + } + if err != nil { + return nil, err + } + + u = make([]uint, count) + switch datatype { + case dtByte: + for i := uint32(0); i < count; i++ { + u[i] = uint(raw[i]) + } + case dtShort: + for i := uint32(0); i < count; i++ { + u[i] = uint(d.byteOrder.Uint16(raw[2*i : 2*(i+1)])) + } + case dtLong: + for i := uint32(0); i < count; i++ { + u[i] = uint(d.byteOrder.Uint32(raw[4*i : 4*(i+1)])) + } + default: + return nil, UnsupportedError("data type") + } + return u, nil +} + +// parseIFD decides whether the the IFD entry in p is "interesting" and +// stows away the data in the decoder. +func (d *decoder) parseIFD(p []byte) os.Error { + tag := d.byteOrder.Uint16(p[0:2]) + switch tag { + case tBitsPerSample, + tExtraSamples, + tPhotometricInterpretation, + tCompression, + tPredictor, + tStripOffsets, + tStripByteCounts, + tRowsPerStrip, + tImageLength, + tImageWidth: + val, err := d.ifdUint(p) + if err != nil { + return err + } + d.features[int(tag)] = val + case tColorMap: + val, err := d.ifdUint(p) + if err != nil { + return err + } + numcolors := len(val) / 3 + if len(val)%3 != 0 || numcolors <= 0 || numcolors > 256 { + return FormatError("bad ColorMap length") + } + d.palette = make([]image.Color, numcolors) + for i := 0; i < numcolors; i++ { + d.palette[i] = image.RGBA64Color{ + uint16(val[i]), + uint16(val[i+numcolors]), + uint16(val[i+2*numcolors]), + 0xffff, + } + } + case tSampleFormat: + // Page 27 of the spec: If the SampleFormat is present and + // the value is not 1 [= unsigned integer data], a Baseline + // TIFF reader that cannot handle the SampleFormat value + // must terminate the import process gracefully. + val, err := d.ifdUint(p) + if err != nil { + return err + } + for _, v := range val { + if v != 1 { + return UnsupportedError("sample format") + } + } + } + return nil +} + +// readBits reads n bits from the internal buffer starting at the current offset. +func (d *decoder) readBits(n uint) uint32 { + for d.nbits < n { + d.v <<= 8 + d.v |= uint32(d.buf[d.off]) + d.off++ + d.nbits += 8 + } + d.nbits -= n + rv := d.v >> d.nbits + d.v &^= rv << d.nbits + return rv +} + +// flushBits discards the unread bits in the buffer used by readBits. +// It is used at the end of a line. +func (d *decoder) flushBits() { + d.v = 0 + d.nbits = 0 +} + +// decode decodes the raw data of an image. +// It reads from d.buf and writes the strip with ymin <= y < ymax into dst. +func (d *decoder) decode(dst image.Image, ymin, ymax int) os.Error { + spp := len(d.features[tBitsPerSample]) // samples per pixel + d.off = 0 + width := dst.Bounds().Dx() + + // Apply horizontal predictor if necessary. + // In this case, p contains the color difference to the preceding pixel. + // See page 64-65 of the spec. + if d.firstVal(tPredictor) == prHorizontal && d.firstVal(tBitsPerSample) == 8 { + for y := ymin; y < ymax; y++ { + d.off += spp + for x := 0; x < (width-1)*spp; x++ { + d.buf[d.off] += d.buf[d.off-spp] + d.off++ + } + } + d.off = 0 + } + + switch d.mode { + case mGray, mGrayInvert: + img := dst.(*image.Gray) + bpp := d.firstVal(tBitsPerSample) + max := uint32((1 << bpp) - 1) + for y := ymin; y < ymax; y++ { + for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { + v := uint8(d.readBits(bpp) * 0xff / max) + if d.mode == mGrayInvert { + v = 0xff - v + } + img.SetGray(x, y, image.GrayColor{v}) + } + d.flushBits() + } + case mPaletted: + img := dst.(*image.Paletted) + bpp := d.firstVal(tBitsPerSample) + for y := ymin; y < ymax; y++ { + for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { + img.SetColorIndex(x, y, uint8(d.readBits(bpp))) + } + d.flushBits() + } + case mRGB: + img := dst.(*image.RGBA) + for y := ymin; y < ymax; y++ { + for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { + img.SetRGBA(x, y, image.RGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], 0xff}) + d.off += spp + } + } + case mNRGBA: + img := dst.(*image.NRGBA) + for y := ymin; y < ymax; y++ { + for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { + img.SetNRGBA(x, y, image.NRGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], d.buf[d.off+3]}) + d.off += spp + } + } + case mRGBA: + img := dst.(*image.RGBA) + for y := ymin; y < ymax; y++ { + for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { + img.SetRGBA(x, y, image.RGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], d.buf[d.off+3]}) + d.off += spp + } + } + } + + return nil +} + +func newDecoder(r io.Reader) (*decoder, os.Error) { + d := &decoder{ + r: newReaderAt(r), + features: make(map[int][]uint), + } + + p := make([]byte, 8) + if _, err := d.r.ReadAt(p, 0); err != nil { + return nil, err + } + switch string(p[0:4]) { + case leHeader: + d.byteOrder = binary.LittleEndian + case beHeader: + d.byteOrder = binary.BigEndian + default: + return nil, FormatError("malformed header") + } + + ifdOffset := int64(d.byteOrder.Uint32(p[4:8])) + + // The first two bytes contain the number of entries (12 bytes each). + if _, err := d.r.ReadAt(p[0:2], ifdOffset); err != nil { + return nil, err + } + numItems := int(d.byteOrder.Uint16(p[0:2])) + + // All IFD entries are read in one chunk. + p = make([]byte, ifdLen*numItems) + if _, err := d.r.ReadAt(p, ifdOffset+2); err != nil { + return nil, err + } + + for i := 0; i < len(p); i += ifdLen { + if err := d.parseIFD(p[i : i+ifdLen]); err != nil { + return nil, err + } + } + + d.config.Width = int(d.firstVal(tImageWidth)) + d.config.Height = int(d.firstVal(tImageLength)) + + if _, ok := d.features[tBitsPerSample]; !ok { + return nil, FormatError("BitsPerSample tag missing") + } + + // Determine the image mode. + switch d.firstVal(tPhotometricInterpretation) { + case pRGB: + for _, b := range d.features[tBitsPerSample] { + if b != 8 { + return nil, UnsupportedError("non-8-bit RGB image") + } + } + d.config.ColorModel = image.RGBAColorModel + // RGB images normally have 3 samples per pixel. + // If there are more, ExtraSamples (p. 31-32 of the spec) + // gives their meaning (usually an alpha channel). + switch len(d.features[tBitsPerSample]) { + case 3: + d.mode = mRGB + case 4: + switch d.firstVal(tExtraSamples) { + case 1: + d.mode = mRGBA + case 2: + d.mode = mNRGBA + d.config.ColorModel = image.NRGBAColorModel + default: + // The extra sample is discarded. + d.mode = mRGB + } + default: + return nil, FormatError("wrong number of samples for RGB") + } + case pPaletted: + d.mode = mPaletted + d.config.ColorModel = image.PalettedColorModel(d.palette) + case pWhiteIsZero: + d.mode = mGrayInvert + d.config.ColorModel = image.GrayColorModel + case pBlackIsZero: + d.mode = mGray + d.config.ColorModel = image.GrayColorModel + default: + return nil, UnsupportedError("color model") + } + + return d, nil +} + +// DecodeConfig returns the color model and dimensions of a TIFF image without +// decoding the entire image. +func DecodeConfig(r io.Reader) (image.Config, os.Error) { + d, err := newDecoder(r) + if err != nil { + return image.Config{}, err + } + return d.config, nil +} + +// Decode reads a TIFF image from r and returns it as an image.Image. +// The type of Image returned depends on the contents of the TIFF. +func Decode(r io.Reader) (img image.Image, err os.Error) { + d, err := newDecoder(r) + if err != nil { + return + } + + // Check if we have the right number of strips, offsets and counts. + rps := int(d.firstVal(tRowsPerStrip)) + numStrips := (d.config.Height + rps - 1) / rps + if rps == 0 || len(d.features[tStripOffsets]) < numStrips || len(d.features[tStripByteCounts]) < numStrips { + return nil, FormatError("inconsistent header") + } + + switch d.mode { + case mGray, mGrayInvert: + img = image.NewGray(d.config.Width, d.config.Height) + case mPaletted: + img = image.NewPaletted(d.config.Width, d.config.Height, d.palette) + case mNRGBA: + img = image.NewNRGBA(d.config.Width, d.config.Height) + case mRGB, mRGBA: + img = image.NewRGBA(d.config.Width, d.config.Height) + } + + for i := 0; i < numStrips; i++ { + ymin := i * rps + // The last strip may be shorter. + if i == numStrips-1 && d.config.Height%rps != 0 { + rps = d.config.Height % rps + } + offset := int64(d.features[tStripOffsets][i]) + n := int64(d.features[tStripByteCounts][i]) + switch d.firstVal(tCompression) { + case cNone: + // TODO(bsiegert): Avoid copy if r is a tiff.buffer. + d.buf = make([]byte, n) + _, err = d.r.ReadAt(d.buf, offset) + case cLZW: + r := lzw.NewReader(io.NewSectionReader(d.r, offset, n), lzw.MSB, 8) + d.buf, err = ioutil.ReadAll(r) + r.Close() + case cDeflate, cDeflateOld: + r, err := zlib.NewReader(io.NewSectionReader(d.r, offset, n)) + if err != nil { + return nil, err + } + d.buf, err = ioutil.ReadAll(r) + r.Close() + default: + err = UnsupportedError("compression") + } + if err != nil { + return + } + err = d.decode(img, ymin, ymin+rps) + } + return +} + +func init() { + image.RegisterFormat("tiff", leHeader, Decode, DecodeConfig) + image.RegisterFormat("tiff", beHeader, Decode, DecodeConfig) +} |