| // 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 |
| } |
| |
| // 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 |
| } |
| |
| // decode decodes the raw data of an image with 8 bits in each sample. |
| // It reads from p and writes the strip with ymin <= y < ymax into dst. |
| func (d *decoder) decode(dst image.Image, p []byte, ymin, ymax int) os.Error { |
| spp := len(d.features[tBitsPerSample]) // samples per pixel |
| off := 0 |
| width := dst.Bounds().Dx() |
| |
| if len(p) < spp*(ymax-ymin)*width { |
| return FormatError("short data strip") |
| } |
| |
| // 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 { |
| for y := ymin; y < ymax; y++ { |
| off += spp |
| for x := 0; x < (width-1)*spp; x++ { |
| p[off] += p[off-spp] |
| off++ |
| } |
| } |
| off = 0 |
| } |
| |
| switch d.mode { |
| case mGray: |
| img := dst.(*image.Gray) |
| for y := ymin; y < ymax; y++ { |
| for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { |
| img.Set(x, y, image.GrayColor{p[off]}) |
| off += spp |
| } |
| } |
| case mGrayInvert: |
| img := dst.(*image.Gray) |
| for y := ymin; y < ymax; y++ { |
| for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { |
| img.Set(x, y, image.GrayColor{0xff - p[off]}) |
| off += spp |
| } |
| } |
| case mPaletted: |
| img := dst.(*image.Paletted) |
| for y := ymin; y < ymax; y++ { |
| for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { |
| img.SetColorIndex(x, y, p[off]) |
| off += spp |
| } |
| } |
| 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.Set(x, y, image.RGBAColor{p[off], p[off+1], p[off+2], 0xff}) |
| 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.Set(x, y, image.NRGBAColor{p[off], p[off+1], p[off+2], p[off+3]}) |
| 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.Set(x, y, image.RGBAColor{p[off], p[off+1], p[off+2], p[off+3]}) |
| 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)) |
| |
| // Determine the image mode. |
| switch d.firstVal(tPhotometricInterpretation) { |
| case pRGB: |
| 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") |
| } |
| |
| if _, ok := d.features[tBitsPerSample]; !ok { |
| return nil, FormatError("BitsPerSample tag missing") |
| } |
| for _, b := range d.features[tBitsPerSample] { |
| if b != 8 { |
| return nil, UnsupportedError("not an 8-bit image") |
| } |
| } |
| |
| 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) |
| } |
| |
| var p []byte |
| 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. |
| p = make([]byte, 0, n) |
| _, err = d.r.ReadAt(p, offset) |
| case cLZW: |
| r := lzw.NewReader(io.NewSectionReader(d.r, offset, n), lzw.MSB, 8) |
| p, 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 |
| } |
| p, err = ioutil.ReadAll(r) |
| r.Close() |
| default: |
| err = UnsupportedError("compression") |
| } |
| if err != nil { |
| return |
| } |
| err = d.decode(img, p, ymin, ymin+rps) |
| } |
| return |
| } |
| |
| func init() { |
| image.RegisterFormat("tiff", leHeader, Decode, DecodeConfig) |
| image.RegisterFormat("tiff", beHeader, Decode, DecodeConfig) |
| } |