writer.go

  1// Copyright 2012 The Go Authors. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5package tiff
  6
  7import (
  8	"bytes"
  9	"compress/zlib"
 10	"encoding/binary"
 11	"image"
 12	"io"
 13	"sort"
 14)
 15
 16// The TIFF format allows to choose the order of the different elements freely.
 17// The basic structure of a TIFF file written by this package is:
 18//
 19//   1. Header (8 bytes).
 20//   2. Image data.
 21//   3. Image File Directory (IFD).
 22//   4. "Pointer area" for larger entries in the IFD.
 23
 24// We only write little-endian TIFF files.
 25var enc = binary.LittleEndian
 26
 27// An ifdEntry is a single entry in an Image File Directory.
 28// A value of type dtRational is composed of two 32-bit values,
 29// thus data contains two uints (numerator and denominator) for a single number.
 30type ifdEntry struct {
 31	tag      int
 32	datatype int
 33	data     []uint32
 34}
 35
 36func (e ifdEntry) putData(p []byte) {
 37	for _, d := range e.data {
 38		switch e.datatype {
 39		case dtByte, dtASCII:
 40			p[0] = byte(d)
 41			p = p[1:]
 42		case dtShort:
 43			enc.PutUint16(p, uint16(d))
 44			p = p[2:]
 45		case dtLong, dtRational:
 46			enc.PutUint32(p, uint32(d))
 47			p = p[4:]
 48		}
 49	}
 50}
 51
 52type byTag []ifdEntry
 53
 54func (d byTag) Len() int           { return len(d) }
 55func (d byTag) Less(i, j int) bool { return d[i].tag < d[j].tag }
 56func (d byTag) Swap(i, j int)      { d[i], d[j] = d[j], d[i] }
 57
 58func encodeGray(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
 59	if !predictor {
 60		return writePix(w, pix, dy, dx, stride)
 61	}
 62	buf := make([]byte, dx)
 63	for y := 0; y < dy; y++ {
 64		min := y*stride + 0
 65		max := y*stride + dx
 66		off := 0
 67		var v0 uint8
 68		for i := min; i < max; i++ {
 69			v1 := pix[i]
 70			buf[off] = v1 - v0
 71			v0 = v1
 72			off++
 73		}
 74		if _, err := w.Write(buf); err != nil {
 75			return err
 76		}
 77	}
 78	return nil
 79}
 80
 81func encodeGray16(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
 82	buf := make([]byte, dx*2)
 83	for y := 0; y < dy; y++ {
 84		min := y*stride + 0
 85		max := y*stride + dx*2
 86		off := 0
 87		var v0 uint16
 88		for i := min; i < max; i += 2 {
 89			// An image.Gray16's Pix is in big-endian order.
 90			v1 := uint16(pix[i])<<8 | uint16(pix[i+1])
 91			if predictor {
 92				v0, v1 = v1, v1-v0
 93			}
 94			// We only write little-endian TIFF files.
 95			buf[off+0] = byte(v1)
 96			buf[off+1] = byte(v1 >> 8)
 97			off += 2
 98		}
 99		if _, err := w.Write(buf); err != nil {
100			return err
101		}
102	}
103	return nil
104}
105
106func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
107	if !predictor {
108		return writePix(w, pix, dy, dx*4, stride)
109	}
110	buf := make([]byte, dx*4)
111	for y := 0; y < dy; y++ {
112		min := y*stride + 0
113		max := y*stride + dx*4
114		off := 0
115		var r0, g0, b0, a0 uint8
116		for i := min; i < max; i += 4 {
117			r1, g1, b1, a1 := pix[i+0], pix[i+1], pix[i+2], pix[i+3]
118			buf[off+0] = r1 - r0
119			buf[off+1] = g1 - g0
120			buf[off+2] = b1 - b0
121			buf[off+3] = a1 - a0
122			off += 4
123			r0, g0, b0, a0 = r1, g1, b1, a1
124		}
125		if _, err := w.Write(buf); err != nil {
126			return err
127		}
128	}
129	return nil
130}
131
132func encodeRGBA64(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
133	buf := make([]byte, dx*8)
134	for y := 0; y < dy; y++ {
135		min := y*stride + 0
136		max := y*stride + dx*8
137		off := 0
138		var r0, g0, b0, a0 uint16
139		for i := min; i < max; i += 8 {
140			// An image.RGBA64's Pix is in big-endian order.
141			r1 := uint16(pix[i+0])<<8 | uint16(pix[i+1])
142			g1 := uint16(pix[i+2])<<8 | uint16(pix[i+3])
143			b1 := uint16(pix[i+4])<<8 | uint16(pix[i+5])
144			a1 := uint16(pix[i+6])<<8 | uint16(pix[i+7])
145			if predictor {
146				r0, r1 = r1, r1-r0
147				g0, g1 = g1, g1-g0
148				b0, b1 = b1, b1-b0
149				a0, a1 = a1, a1-a0
150			}
151			// We only write little-endian TIFF files.
152			buf[off+0] = byte(r1)
153			buf[off+1] = byte(r1 >> 8)
154			buf[off+2] = byte(g1)
155			buf[off+3] = byte(g1 >> 8)
156			buf[off+4] = byte(b1)
157			buf[off+5] = byte(b1 >> 8)
158			buf[off+6] = byte(a1)
159			buf[off+7] = byte(a1 >> 8)
160			off += 8
161		}
162		if _, err := w.Write(buf); err != nil {
163			return err
164		}
165	}
166	return nil
167}
168
169func encode(w io.Writer, m image.Image, predictor bool) error {
170	bounds := m.Bounds()
171	buf := make([]byte, 4*bounds.Dx())
172	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
173		off := 0
174		if predictor {
175			var r0, g0, b0, a0 uint8
176			for x := bounds.Min.X; x < bounds.Max.X; x++ {
177				r, g, b, a := m.At(x, y).RGBA()
178				r1 := uint8(r >> 8)
179				g1 := uint8(g >> 8)
180				b1 := uint8(b >> 8)
181				a1 := uint8(a >> 8)
182				buf[off+0] = r1 - r0
183				buf[off+1] = g1 - g0
184				buf[off+2] = b1 - b0
185				buf[off+3] = a1 - a0
186				off += 4
187				r0, g0, b0, a0 = r1, g1, b1, a1
188			}
189		} else {
190			for x := bounds.Min.X; x < bounds.Max.X; x++ {
191				r, g, b, a := m.At(x, y).RGBA()
192				buf[off+0] = uint8(r >> 8)
193				buf[off+1] = uint8(g >> 8)
194				buf[off+2] = uint8(b >> 8)
195				buf[off+3] = uint8(a >> 8)
196				off += 4
197			}
198		}
199		if _, err := w.Write(buf); err != nil {
200			return err
201		}
202	}
203	return nil
204}
205
206// writePix writes the internal byte array of an image to w. It is less general
207// but much faster then encode. writePix is used when pix directly
208// corresponds to one of the TIFF image types.
209func writePix(w io.Writer, pix []byte, nrows, length, stride int) error {
210	if length == stride {
211		_, err := w.Write(pix[:nrows*length])
212		return err
213	}
214	for ; nrows > 0; nrows-- {
215		if _, err := w.Write(pix[:length]); err != nil {
216			return err
217		}
218		pix = pix[stride:]
219	}
220	return nil
221}
222
223func writeIFD(w io.Writer, ifdOffset int, d []ifdEntry) error {
224	var buf [ifdLen]byte
225	// Make space for "pointer area" containing IFD entry data
226	// longer than 4 bytes.
227	parea := make([]byte, 1024)
228	pstart := ifdOffset + ifdLen*len(d) + 6
229	var o int // Current offset in parea.
230
231	// The IFD has to be written with the tags in ascending order.
232	sort.Sort(byTag(d))
233
234	// Write the number of entries in this IFD.
235	if err := binary.Write(w, enc, uint16(len(d))); err != nil {
236		return err
237	}
238	for _, ent := range d {
239		enc.PutUint16(buf[0:2], uint16(ent.tag))
240		enc.PutUint16(buf[2:4], uint16(ent.datatype))
241		count := uint32(len(ent.data))
242		if ent.datatype == dtRational {
243			count /= 2
244		}
245		enc.PutUint32(buf[4:8], count)
246		datalen := int(count * lengths[ent.datatype])
247		if datalen <= 4 {
248			ent.putData(buf[8:12])
249		} else {
250			if (o + datalen) > len(parea) {
251				newlen := len(parea) + 1024
252				for (o + datalen) > newlen {
253					newlen += 1024
254				}
255				newarea := make([]byte, newlen)
256				copy(newarea, parea)
257				parea = newarea
258			}
259			ent.putData(parea[o : o+datalen])
260			enc.PutUint32(buf[8:12], uint32(pstart+o))
261			o += datalen
262		}
263		if _, err := w.Write(buf[:]); err != nil {
264			return err
265		}
266	}
267	// The IFD ends with the offset of the next IFD in the file,
268	// or zero if it is the last one (page 14).
269	if err := binary.Write(w, enc, uint32(0)); err != nil {
270		return err
271	}
272	_, err := w.Write(parea[:o])
273	return err
274}
275
276// Options are the encoding parameters.
277type Options struct {
278	// Compression is the type of compression used.
279	Compression CompressionType
280	// Predictor determines whether a differencing predictor is used;
281	// if true, instead of each pixel's color, the color difference to the
282	// preceding one is saved.  This improves the compression for certain
283	// types of images and compressors. For example, it works well for
284	// photos with Deflate compression.
285	Predictor bool
286}
287
288// Encode writes the image m to w. opt determines the options used for
289// encoding, such as the compression type. If opt is nil, an uncompressed
290// image is written.
291func Encode(w io.Writer, m image.Image, opt *Options) error {
292	d := m.Bounds().Size()
293
294	compression := uint32(cNone)
295	predictor := false
296	if opt != nil {
297		compression = opt.Compression.specValue()
298		// The predictor field is only used with LZW. See page 64 of the spec.
299		predictor = opt.Predictor && compression == cLZW
300	}
301
302	_, err := io.WriteString(w, leHeader)
303	if err != nil {
304		return err
305	}
306
307	// Compressed data is written into a buffer first, so that we
308	// know the compressed size.
309	var buf bytes.Buffer
310	// dst holds the destination for the pixel data of the image --
311	// either w or a writer to buf.
312	var dst io.Writer
313	// imageLen is the length of the pixel data in bytes.
314	// The offset of the IFD is imageLen + 8 header bytes.
315	var imageLen int
316
317	switch compression {
318	case cNone:
319		dst = w
320		// Write IFD offset before outputting pixel data.
321		switch m.(type) {
322		case *image.Paletted:
323			imageLen = d.X * d.Y * 1
324		case *image.Gray:
325			imageLen = d.X * d.Y * 1
326		case *image.Gray16:
327			imageLen = d.X * d.Y * 2
328		case *image.RGBA64:
329			imageLen = d.X * d.Y * 8
330		case *image.NRGBA64:
331			imageLen = d.X * d.Y * 8
332		default:
333			imageLen = d.X * d.Y * 4
334		}
335		err = binary.Write(w, enc, uint32(imageLen+8))
336		if err != nil {
337			return err
338		}
339	case cDeflate:
340		dst = zlib.NewWriter(&buf)
341	}
342
343	pr := uint32(prNone)
344	photometricInterpretation := uint32(pRGB)
345	samplesPerPixel := uint32(4)
346	bitsPerSample := []uint32{8, 8, 8, 8}
347	extraSamples := uint32(0)
348	colorMap := []uint32{}
349
350	if predictor {
351		pr = prHorizontal
352	}
353	switch m := m.(type) {
354	case *image.Paletted:
355		photometricInterpretation = pPaletted
356		samplesPerPixel = 1
357		bitsPerSample = []uint32{8}
358		colorMap = make([]uint32, 256*3)
359		for i := 0; i < 256 && i < len(m.Palette); i++ {
360			r, g, b, _ := m.Palette[i].RGBA()
361			colorMap[i+0*256] = uint32(r)
362			colorMap[i+1*256] = uint32(g)
363			colorMap[i+2*256] = uint32(b)
364		}
365		err = encodeGray(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
366	case *image.Gray:
367		photometricInterpretation = pBlackIsZero
368		samplesPerPixel = 1
369		bitsPerSample = []uint32{8}
370		err = encodeGray(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
371	case *image.Gray16:
372		photometricInterpretation = pBlackIsZero
373		samplesPerPixel = 1
374		bitsPerSample = []uint32{16}
375		err = encodeGray16(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
376	case *image.NRGBA:
377		extraSamples = 2 // Unassociated alpha.
378		err = encodeRGBA(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
379	case *image.NRGBA64:
380		extraSamples = 2 // Unassociated alpha.
381		bitsPerSample = []uint32{16, 16, 16, 16}
382		err = encodeRGBA64(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
383	case *image.RGBA:
384		extraSamples = 1 // Associated alpha.
385		err = encodeRGBA(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
386	case *image.RGBA64:
387		extraSamples = 1 // Associated alpha.
388		bitsPerSample = []uint32{16, 16, 16, 16}
389		err = encodeRGBA64(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
390	default:
391		extraSamples = 1 // Associated alpha.
392		err = encode(dst, m, predictor)
393	}
394	if err != nil {
395		return err
396	}
397
398	if compression != cNone {
399		if err = dst.(io.Closer).Close(); err != nil {
400			return err
401		}
402		imageLen = buf.Len()
403		if err = binary.Write(w, enc, uint32(imageLen+8)); err != nil {
404			return err
405		}
406		if _, err = buf.WriteTo(w); err != nil {
407			return err
408		}
409	}
410
411	ifd := []ifdEntry{
412		{tImageWidth, dtShort, []uint32{uint32(d.X)}},
413		{tImageLength, dtShort, []uint32{uint32(d.Y)}},
414		{tBitsPerSample, dtShort, bitsPerSample},
415		{tCompression, dtShort, []uint32{compression}},
416		{tPhotometricInterpretation, dtShort, []uint32{photometricInterpretation}},
417		{tStripOffsets, dtLong, []uint32{8}},
418		{tSamplesPerPixel, dtShort, []uint32{samplesPerPixel}},
419		{tRowsPerStrip, dtShort, []uint32{uint32(d.Y)}},
420		{tStripByteCounts, dtLong, []uint32{uint32(imageLen)}},
421		// There is currently no support for storing the image
422		// resolution, so give a bogus value of 72x72 dpi.
423		{tXResolution, dtRational, []uint32{72, 1}},
424		{tYResolution, dtRational, []uint32{72, 1}},
425		{tResolutionUnit, dtShort, []uint32{resPerInch}},
426	}
427	if pr != prNone {
428		ifd = append(ifd, ifdEntry{tPredictor, dtShort, []uint32{pr}})
429	}
430	if len(colorMap) != 0 {
431		ifd = append(ifd, ifdEntry{tColorMap, dtShort, colorMap})
432	}
433	if extraSamples > 0 {
434		ifd = append(ifd, ifdEntry{tExtraSamples, dtShort, []uint32{extraSamples}})
435	}
436
437	return writeIFD(w, imageLen+8, ifd)
438}