Source file src/pkg/image/png/writer.go
1 // Copyright 2009 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
5 package png
6
7 import (
8 "bufio"
9 "compress/zlib"
10 "hash/crc32"
11 "image"
12 "image/color"
13 "io"
14 "strconv"
15 )
16
17 type encoder struct {
18 w io.Writer
19 m image.Image
20 cb int
21 err error
22 header [8]byte
23 footer [4]byte
24 tmp [3 * 256]byte
25 }
26
27 // Big-endian.
28 func writeUint32(b []uint8, u uint32) {
29 b[0] = uint8(u >> 24)
30 b[1] = uint8(u >> 16)
31 b[2] = uint8(u >> 8)
32 b[3] = uint8(u >> 0)
33 }
34
35 type opaquer interface {
36 Opaque() bool
37 }
38
39 // Returns whether or not the image is fully opaque.
40 func opaque(m image.Image) bool {
41 if o, ok := m.(opaquer); ok {
42 return o.Opaque()
43 }
44 b := m.Bounds()
45 for y := b.Min.Y; y < b.Max.Y; y++ {
46 for x := b.Min.X; x < b.Max.X; x++ {
47 _, _, _, a := m.At(x, y).RGBA()
48 if a != 0xffff {
49 return false
50 }
51 }
52 }
53 return true
54 }
55
56 // The absolute value of a byte interpreted as a signed int8.
57 func abs8(d uint8) int {
58 if d < 128 {
59 return int(d)
60 }
61 return 256 - int(d)
62 }
63
64 func (e *encoder) writeChunk(b []byte, name string) {
65 if e.err != nil {
66 return
67 }
68 n := uint32(len(b))
69 if int(n) != len(b) {
70 e.err = UnsupportedError(name + " chunk is too large: " + strconv.Itoa(len(b)))
71 return
72 }
73 writeUint32(e.header[0:4], n)
74 e.header[4] = name[0]
75 e.header[5] = name[1]
76 e.header[6] = name[2]
77 e.header[7] = name[3]
78 crc := crc32.NewIEEE()
79 crc.Write(e.header[4:8])
80 crc.Write(b)
81 writeUint32(e.footer[0:4], crc.Sum32())
82
83 _, e.err = e.w.Write(e.header[0:8])
84 if e.err != nil {
85 return
86 }
87 _, e.err = e.w.Write(b)
88 if e.err != nil {
89 return
90 }
91 _, e.err = e.w.Write(e.footer[0:4])
92 }
93
94 func (e *encoder) writeIHDR() {
95 b := e.m.Bounds()
96 writeUint32(e.tmp[0:4], uint32(b.Dx()))
97 writeUint32(e.tmp[4:8], uint32(b.Dy()))
98 // Set bit depth and color type.
99 switch e.cb {
100 case cbG8:
101 e.tmp[8] = 8
102 e.tmp[9] = ctGrayscale
103 case cbTC8:
104 e.tmp[8] = 8
105 e.tmp[9] = ctTrueColor
106 case cbP8:
107 e.tmp[8] = 8
108 e.tmp[9] = ctPaletted
109 case cbTCA8:
110 e.tmp[8] = 8
111 e.tmp[9] = ctTrueColorAlpha
112 case cbG16:
113 e.tmp[8] = 16
114 e.tmp[9] = ctGrayscale
115 case cbTC16:
116 e.tmp[8] = 16
117 e.tmp[9] = ctTrueColor
118 case cbTCA16:
119 e.tmp[8] = 16
120 e.tmp[9] = ctTrueColorAlpha
121 }
122 e.tmp[10] = 0 // default compression method
123 e.tmp[11] = 0 // default filter method
124 e.tmp[12] = 0 // non-interlaced
125 e.writeChunk(e.tmp[0:13], "IHDR")
126 }
127
128 func (e *encoder) writePLTE(p color.Palette) {
129 if len(p) < 1 || len(p) > 256 {
130 e.err = FormatError("bad palette length: " + strconv.Itoa(len(p)))
131 return
132 }
133 for i, c := range p {
134 r, g, b, _ := c.RGBA()
135 e.tmp[3*i+0] = uint8(r >> 8)
136 e.tmp[3*i+1] = uint8(g >> 8)
137 e.tmp[3*i+2] = uint8(b >> 8)
138 }
139 e.writeChunk(e.tmp[0:3*len(p)], "PLTE")
140 }
141
142 func (e *encoder) maybeWritetRNS(p color.Palette) {
143 last := -1
144 for i, c := range p {
145 _, _, _, a := c.RGBA()
146 if a != 0xffff {
147 last = i
148 }
149 e.tmp[i] = uint8(a >> 8)
150 }
151 if last == -1 {
152 return
153 }
154 e.writeChunk(e.tmp[:last+1], "tRNS")
155 }
156
157 // An encoder is an io.Writer that satisfies writes by writing PNG IDAT chunks,
158 // including an 8-byte header and 4-byte CRC checksum per Write call. Such calls
159 // should be relatively infrequent, since writeIDATs uses a bufio.Writer.
160 //
161 // This method should only be called from writeIDATs (via writeImage).
162 // No other code should treat an encoder as an io.Writer.
163 func (e *encoder) Write(b []byte) (int, error) {
164 e.writeChunk(b, "IDAT")
165 if e.err != nil {
166 return 0, e.err
167 }
168 return len(b), nil
169 }
170
171 // Chooses the filter to use for encoding the current row, and applies it.
172 // The return value is the index of the filter and also of the row in cr that has had it applied.
173 func filter(cr *[nFilter][]byte, pr []byte, bpp int) int {
174 // We try all five filter types, and pick the one that minimizes the sum of absolute differences.
175 // This is the same heuristic that libpng uses, although the filters are attempted in order of
176 // estimated most likely to be minimal (ftUp, ftPaeth, ftNone, ftSub, ftAverage), rather than
177 // in their enumeration order (ftNone, ftSub, ftUp, ftAverage, ftPaeth).
178 cdat0 := cr[0][1:]
179 cdat1 := cr[1][1:]
180 cdat2 := cr[2][1:]
181 cdat3 := cr[3][1:]
182 cdat4 := cr[4][1:]
183 pdat := pr[1:]
184 n := len(cdat0)
185
186 // The up filter.
187 sum := 0
188 for i := 0; i < n; i++ {
189 cdat2[i] = cdat0[i] - pdat[i]
190 sum += abs8(cdat2[i])
191 }
192 best := sum
193 filter := ftUp
194
195 // The Paeth filter.
196 sum = 0
197 for i := 0; i < bpp; i++ {
198 cdat4[i] = cdat0[i] - paeth(0, pdat[i], 0)
199 sum += abs8(cdat4[i])
200 }
201 for i := bpp; i < n; i++ {
202 cdat4[i] = cdat0[i] - paeth(cdat0[i-bpp], pdat[i], pdat[i-bpp])
203 sum += abs8(cdat4[i])
204 if sum >= best {
205 break
206 }
207 }
208 if sum < best {
209 best = sum
210 filter = ftPaeth
211 }
212
213 // The none filter.
214 sum = 0
215 for i := 0; i < n; i++ {
216 sum += abs8(cdat0[i])
217 if sum >= best {
218 break
219 }
220 }
221 if sum < best {
222 best = sum
223 filter = ftNone
224 }
225
226 // The sub filter.
227 sum = 0
228 for i := 0; i < bpp; i++ {
229 cdat1[i] = cdat0[i]
230 sum += abs8(cdat1[i])
231 }
232 for i := bpp; i < n; i++ {
233 cdat1[i] = cdat0[i] - cdat0[i-bpp]
234 sum += abs8(cdat1[i])
235 if sum >= best {
236 break
237 }
238 }
239 if sum < best {
240 best = sum
241 filter = ftSub
242 }
243
244 // The average filter.
245 sum = 0
246 for i := 0; i < bpp; i++ {
247 cdat3[i] = cdat0[i] - pdat[i]/2
248 sum += abs8(cdat3[i])
249 }
250 for i := bpp; i < n; i++ {
251 cdat3[i] = cdat0[i] - uint8((int(cdat0[i-bpp])+int(pdat[i]))/2)
252 sum += abs8(cdat3[i])
253 if sum >= best {
254 break
255 }
256 }
257 if sum < best {
258 best = sum
259 filter = ftAverage
260 }
261
262 return filter
263 }
264
265 func writeImage(w io.Writer, m image.Image, cb int) error {
266 zw := zlib.NewWriter(w)
267 defer zw.Close()
268
269 bpp := 0 // Bytes per pixel.
270
271 switch cb {
272 case cbG8:
273 bpp = 1
274 case cbTC8:
275 bpp = 3
276 case cbP8:
277 bpp = 1
278 case cbTCA8:
279 bpp = 4
280 case cbTC16:
281 bpp = 6
282 case cbTCA16:
283 bpp = 8
284 case cbG16:
285 bpp = 2
286 }
287 // cr[*] and pr are the bytes for the current and previous row.
288 // cr[0] is unfiltered (or equivalently, filtered with the ftNone filter).
289 // cr[ft], for non-zero filter types ft, are buffers for transforming cr[0] under the
290 // other PNG filter types. These buffers are allocated once and re-used for each row.
291 // The +1 is for the per-row filter type, which is at cr[*][0].
292 b := m.Bounds()
293 var cr [nFilter][]uint8
294 for i := range cr {
295 cr[i] = make([]uint8, 1+bpp*b.Dx())
296 cr[i][0] = uint8(i)
297 }
298 pr := make([]uint8, 1+bpp*b.Dx())
299
300 for y := b.Min.Y; y < b.Max.Y; y++ {
301 // Convert from colors to bytes.
302 i := 1
303 switch cb {
304 case cbG8:
305 for x := b.Min.X; x < b.Max.X; x++ {
306 c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
307 cr[0][i] = c.Y
308 i++
309 }
310 case cbTC8:
311 // We have previously verified that the alpha value is fully opaque.
312 cr0 := cr[0]
313 if rgba, _ := m.(*image.RGBA); rgba != nil {
314 j0 := (y - b.Min.Y) * rgba.Stride
315 j1 := j0 + b.Dx()*4
316 for j := j0; j < j1; j += 4 {
317 cr0[i+0] = rgba.Pix[j+0]
318 cr0[i+1] = rgba.Pix[j+1]
319 cr0[i+2] = rgba.Pix[j+2]
320 i += 3
321 }
322 } else {
323 for x := b.Min.X; x < b.Max.X; x++ {
324 r, g, b, _ := m.At(x, y).RGBA()
325 cr0[i+0] = uint8(r >> 8)
326 cr0[i+1] = uint8(g >> 8)
327 cr0[i+2] = uint8(b >> 8)
328 i += 3
329 }
330 }
331 case cbP8:
332 if p, _ := m.(*image.Paletted); p != nil {
333 offset := (y - b.Min.Y) * p.Stride
334 copy(cr[0][1:], p.Pix[offset:offset+b.Dx()])
335 } else {
336 pi := m.(image.PalettedImage)
337 for x := b.Min.X; x < b.Max.X; x++ {
338 cr[0][i] = pi.ColorIndexAt(x, y)
339 i += 1
340 }
341 }
342 case cbTCA8:
343 // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
344 for x := b.Min.X; x < b.Max.X; x++ {
345 c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA)
346 cr[0][i+0] = c.R
347 cr[0][i+1] = c.G
348 cr[0][i+2] = c.B
349 cr[0][i+3] = c.A
350 i += 4
351 }
352 case cbG16:
353 for x := b.Min.X; x < b.Max.X; x++ {
354 c := color.Gray16Model.Convert(m.At(x, y)).(color.Gray16)
355 cr[0][i+0] = uint8(c.Y >> 8)
356 cr[0][i+1] = uint8(c.Y)
357 i += 2
358 }
359 case cbTC16:
360 // We have previously verified that the alpha value is fully opaque.
361 for x := b.Min.X; x < b.Max.X; x++ {
362 r, g, b, _ := m.At(x, y).RGBA()
363 cr[0][i+0] = uint8(r >> 8)
364 cr[0][i+1] = uint8(r)
365 cr[0][i+2] = uint8(g >> 8)
366 cr[0][i+3] = uint8(g)
367 cr[0][i+4] = uint8(b >> 8)
368 cr[0][i+5] = uint8(b)
369 i += 6
370 }
371 case cbTCA16:
372 // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
373 for x := b.Min.X; x < b.Max.X; x++ {
374 c := color.NRGBA64Model.Convert(m.At(x, y)).(color.NRGBA64)
375 cr[0][i+0] = uint8(c.R >> 8)
376 cr[0][i+1] = uint8(c.R)
377 cr[0][i+2] = uint8(c.G >> 8)
378 cr[0][i+3] = uint8(c.G)
379 cr[0][i+4] = uint8(c.B >> 8)
380 cr[0][i+5] = uint8(c.B)
381 cr[0][i+6] = uint8(c.A >> 8)
382 cr[0][i+7] = uint8(c.A)
383 i += 8
384 }
385 }
386
387 // Apply the filter.
388 f := filter(&cr, pr, bpp)
389
390 // Write the compressed bytes.
391 if _, err := zw.Write(cr[f]); err != nil {
392 return err
393 }
394
395 // The current row for y is the previous row for y+1.
396 pr, cr[0] = cr[0], pr
397 }
398 return nil
399 }
400
401 // Write the actual image data to one or more IDAT chunks.
402 func (e *encoder) writeIDATs() {
403 if e.err != nil {
404 return
405 }
406 var bw *bufio.Writer
407 bw = bufio.NewWriterSize(e, 1<<15)
408 e.err = writeImage(bw, e.m, e.cb)
409 if e.err != nil {
410 return
411 }
412 e.err = bw.Flush()
413 }
414
415 func (e *encoder) writeIEND() { e.writeChunk(e.tmp[0:0], "IEND") }
416
417 // Encode writes the Image m to w in PNG format. Any Image may be encoded, but
418 // images that are not image.NRGBA might be encoded lossily.
419 func Encode(w io.Writer, m image.Image) error {
420 // Obviously, negative widths and heights are invalid. Furthermore, the PNG
421 // spec section 11.2.2 says that zero is invalid. Excessively large images are
422 // also rejected.
423 mw, mh := int64(m.Bounds().Dx()), int64(m.Bounds().Dy())
424 if mw <= 0 || mh <= 0 || mw >= 1<<32 || mh >= 1<<32 {
425 return FormatError("invalid image size: " + strconv.FormatInt(mw, 10) + "x" + strconv.FormatInt(mw, 10))
426 }
427
428 var e encoder
429 e.w = w
430 e.m = m
431
432 var pal color.Palette
433 // cbP8 encoding needs PalettedImage's ColorIndexAt method.
434 if _, ok := m.(image.PalettedImage); ok {
435 pal, _ = m.ColorModel().(color.Palette)
436 }
437 if pal != nil {
438 e.cb = cbP8
439 } else {
440 switch m.ColorModel() {
441 case color.GrayModel:
442 e.cb = cbG8
443 case color.Gray16Model:
444 e.cb = cbG16
445 case color.RGBAModel, color.NRGBAModel, color.AlphaModel:
446 if opaque(m) {
447 e.cb = cbTC8
448 } else {
449 e.cb = cbTCA8
450 }
451 default:
452 if opaque(m) {
453 e.cb = cbTC16
454 } else {
455 e.cb = cbTCA16
456 }
457 }
458 }
459
460 _, e.err = io.WriteString(w, pngHeader)
461 e.writeIHDR()
462 if pal != nil {
463 e.writePLTE(pal)
464 e.maybeWritetRNS(pal)
465 }
466 e.writeIDATs()
467 e.writeIEND()
468 return e.err
469 }