go.image/tiff: support for writing compressed images.

Only Deflate compression is supported for now.

R=nigeltao, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/6866051
This commit is contained in:
Benny Siegert 2012-12-12 13:24:12 +11:00 committed by Nigel Tao
parent 41cf08abcf
commit d165204442
3 changed files with 60 additions and 20 deletions

View File

@ -109,9 +109,20 @@ const (
mNRGBA
)
// Compression describes the type of compression used in Options.
// CompressionType describes the type of compression used in Options.
type CompressionType int
const (
Uncompressed CompressionType = iota
Deflate
)
// specValue returns the compression type constant from the TIFF spec that
// is equivalent to c.
func (c CompressionType) specValue() uint32 {
switch c {
case Deflate:
return cDeflate
}
return cNone
}

View File

@ -5,6 +5,8 @@
package tiff
import (
"bytes"
"compress/zlib"
"encoding/binary"
"image"
"io"
@ -179,57 +181,82 @@ type Options struct {
// image is written.
func Encode(w io.Writer, m image.Image, opt *Options) error {
predictor := false
compression := Uncompressed
compression := uint32(cNone)
if opt != nil {
predictor = opt.Predictor
compression = opt.Compression
}
if compression != Uncompressed {
return UnsupportedError("compression type")
compression = opt.Compression.specValue()
}
var extrasamples uint32
_, err := io.WriteString(w, leHeader)
if err != nil {
return err
}
// Compressed data is written into a buffer first, so that we
// know the compressed size.
var buf bytes.Buffer
// dst holds the destination for the pixel data of the image --
// either w or a writer to buf.
var dst io.Writer
// imageLen is the length of the pixel data in bytes.
// The offset of the IFD is imageLen + 8 header bytes.
var imageLen int
bounds := m.Bounds()
width, height := bounds.Dx(), bounds.Dy()
// imageLen is the length of the image data in bytes.
imageLen := width * height * 4
ifdOffset := imageLen + 8 // 8 bytes for TIFF header.
err = binary.Write(w, enc, uint32(ifdOffset))
if err != nil {
return err
switch compression {
case cNone:
dst = w
// Write IFD offset before outputting pixel data.
imageLen = width * height * 4
err = binary.Write(w, enc, uint32(imageLen+8))
if err != nil {
return err
}
case cDeflate:
dst = zlib.NewWriter(&buf)
}
var pr uint32 = prNone
extrasamples = 1 // Associated alpha (default).
var extrasamples uint32 = 1 // Associated alpha (default).
if predictor {
pr = prHorizontal
err = writeImgData(w, m, predictor)
err = writeImgData(dst, m, predictor)
} else {
switch img := m.(type) {
case *image.NRGBA:
extrasamples = 2 // Unassociated alpha.
off := img.PixOffset(img.Rect.Min.X, img.Rect.Min.Y)
err = writePix(w, img.Pix[off:], img.Rect.Dy(), 4*img.Rect.Dx(), img.Stride)
err = writePix(dst, img.Pix[off:], img.Rect.Dy(), 4*img.Rect.Dx(), img.Stride)
case *image.RGBA:
off := img.PixOffset(img.Rect.Min.X, img.Rect.Min.Y)
err = writePix(w, img.Pix[off:], img.Rect.Dy(), 4*img.Rect.Dx(), img.Stride)
err = writePix(dst, img.Pix[off:], img.Rect.Dy(), 4*img.Rect.Dx(), img.Stride)
default:
err = writeImgData(w, m, predictor)
err = writeImgData(dst, m, predictor)
}
}
if err != nil {
return err
}
return writeIFD(w, ifdOffset, []ifdEntry{
if compression != cNone {
if err = dst.(io.Closer).Close(); err != nil {
return err
}
imageLen = buf.Len()
if err = binary.Write(w, enc, uint32(imageLen+8)); err != nil {
return err
}
if _, err = buf.WriteTo(w); err != nil {
return err
}
}
return writeIFD(w, imageLen+8, []ifdEntry{
{tImageWidth, dtShort, []uint32{uint32(width)}},
{tImageLength, dtShort, []uint32{uint32(height)}},
{tBitsPerSample, dtShort, []uint32{8, 8, 8, 8}},
{tCompression, dtShort, []uint32{cNone}},
{tCompression, dtShort, []uint32{compression}},
{tPhotometricInterpretation, dtShort, []uint32{pRGB}},
{tStripOffsets, dtLong, []uint32{8}},
{tSamplesPerPixel, dtShort, []uint32{4}},

View File

@ -19,6 +19,8 @@ var roundtripTests = []struct {
{"video-001.tiff", nil},
{"bw-packbits.tiff", nil},
{"video-001.tiff", &Options{Predictor: true}},
{"video-001.tiff", &Options{Compression: Deflate}},
{"video-001.tiff", &Options{Predictor: true, Compression: Deflate}},
}
func openImage(filename string) (image.Image, error) {