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:
parent
41cf08abcf
commit
d165204442
|
@ -109,9 +109,20 @@ const (
|
||||||
mNRGBA
|
mNRGBA
|
||||||
)
|
)
|
||||||
|
|
||||||
// Compression describes the type of compression used in Options.
|
// CompressionType describes the type of compression used in Options.
|
||||||
type CompressionType int
|
type CompressionType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Uncompressed CompressionType = iota
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
package tiff
|
package tiff
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/zlib"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"image"
|
"image"
|
||||||
"io"
|
"io"
|
||||||
|
@ -179,57 +181,82 @@ type Options struct {
|
||||||
// image is written.
|
// image is written.
|
||||||
func Encode(w io.Writer, m image.Image, opt *Options) error {
|
func Encode(w io.Writer, m image.Image, opt *Options) error {
|
||||||
predictor := false
|
predictor := false
|
||||||
compression := Uncompressed
|
compression := uint32(cNone)
|
||||||
if opt != nil {
|
if opt != nil {
|
||||||
predictor = opt.Predictor
|
predictor = opt.Predictor
|
||||||
compression = opt.Compression
|
compression = opt.Compression.specValue()
|
||||||
}
|
|
||||||
if compression != Uncompressed {
|
|
||||||
return UnsupportedError("compression type")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var extrasamples uint32
|
|
||||||
_, err := io.WriteString(w, leHeader)
|
_, err := io.WriteString(w, leHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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()
|
bounds := m.Bounds()
|
||||||
width, height := bounds.Dx(), bounds.Dy()
|
width, height := bounds.Dx(), bounds.Dy()
|
||||||
// imageLen is the length of the image data in bytes.
|
|
||||||
imageLen := width * height * 4
|
switch compression {
|
||||||
ifdOffset := imageLen + 8 // 8 bytes for TIFF header.
|
case cNone:
|
||||||
err = binary.Write(w, enc, uint32(ifdOffset))
|
dst = w
|
||||||
|
// Write IFD offset before outputting pixel data.
|
||||||
|
imageLen = width * height * 4
|
||||||
|
err = binary.Write(w, enc, uint32(imageLen+8))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case cDeflate:
|
||||||
|
dst = zlib.NewWriter(&buf)
|
||||||
|
}
|
||||||
|
|
||||||
var pr uint32 = prNone
|
var pr uint32 = prNone
|
||||||
extrasamples = 1 // Associated alpha (default).
|
var extrasamples uint32 = 1 // Associated alpha (default).
|
||||||
if predictor {
|
if predictor {
|
||||||
pr = prHorizontal
|
pr = prHorizontal
|
||||||
err = writeImgData(w, m, predictor)
|
err = writeImgData(dst, m, predictor)
|
||||||
} else {
|
} else {
|
||||||
switch img := m.(type) {
|
switch img := m.(type) {
|
||||||
case *image.NRGBA:
|
case *image.NRGBA:
|
||||||
extrasamples = 2 // Unassociated alpha.
|
extrasamples = 2 // Unassociated alpha.
|
||||||
off := img.PixOffset(img.Rect.Min.X, img.Rect.Min.Y)
|
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:
|
case *image.RGBA:
|
||||||
off := img.PixOffset(img.Rect.Min.X, img.Rect.Min.Y)
|
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:
|
default:
|
||||||
err = writeImgData(w, m, predictor)
|
err = writeImgData(dst, m, predictor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)}},
|
{tImageWidth, dtShort, []uint32{uint32(width)}},
|
||||||
{tImageLength, dtShort, []uint32{uint32(height)}},
|
{tImageLength, dtShort, []uint32{uint32(height)}},
|
||||||
{tBitsPerSample, dtShort, []uint32{8, 8, 8, 8}},
|
{tBitsPerSample, dtShort, []uint32{8, 8, 8, 8}},
|
||||||
{tCompression, dtShort, []uint32{cNone}},
|
{tCompression, dtShort, []uint32{compression}},
|
||||||
{tPhotometricInterpretation, dtShort, []uint32{pRGB}},
|
{tPhotometricInterpretation, dtShort, []uint32{pRGB}},
|
||||||
{tStripOffsets, dtLong, []uint32{8}},
|
{tStripOffsets, dtLong, []uint32{8}},
|
||||||
{tSamplesPerPixel, dtShort, []uint32{4}},
|
{tSamplesPerPixel, dtShort, []uint32{4}},
|
||||||
|
|
|
@ -19,6 +19,8 @@ var roundtripTests = []struct {
|
||||||
{"video-001.tiff", nil},
|
{"video-001.tiff", nil},
|
||||||
{"bw-packbits.tiff", nil},
|
{"bw-packbits.tiff", nil},
|
||||||
{"video-001.tiff", &Options{Predictor: true}},
|
{"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) {
|
func openImage(filename string) (image.Image, error) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user