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}