Source file
src/image/png/writer.go
1
2
3
4
5 package png
6
7 import (
8 "bufio"
9 "compress/zlib"
10 "encoding/binary"
11 "hash/crc32"
12 "image"
13 "image/color"
14 "io"
15 "strconv"
16 )
17
18
19 type Encoder struct {
20 CompressionLevel CompressionLevel
21
22
23
24 BufferPool EncoderBufferPool
25 }
26
27
28
29
30 type EncoderBufferPool interface {
31 Get() *EncoderBuffer
32 Put(*EncoderBuffer)
33 }
34
35
36 type EncoderBuffer encoder
37
38 type encoder struct {
39 enc *Encoder
40 w io.Writer
41 m image.Image
42 cb int
43 err error
44 header [8]byte
45 footer [4]byte
46 tmp [4 * 256]byte
47 cr [nFilter][]uint8
48 pr []uint8
49 zw *zlib.Writer
50 zwLevel int
51 bw *bufio.Writer
52 }
53
54
55 type CompressionLevel int
56
57 const (
58 DefaultCompression CompressionLevel = 0
59 NoCompression CompressionLevel = -1
60 BestSpeed CompressionLevel = -2
61 BestCompression CompressionLevel = -3
62
63
64
65 )
66
67 type opaquer interface {
68 Opaque() bool
69 }
70
71
72 func opaque(m image.Image) bool {
73 if o, ok := m.(opaquer); ok {
74 return o.Opaque()
75 }
76 b := m.Bounds()
77 for y := b.Min.Y; y < b.Max.Y; y++ {
78 for x := b.Min.X; x < b.Max.X; x++ {
79 _, _, _, a := m.At(x, y).RGBA()
80 if a != 0xffff {
81 return false
82 }
83 }
84 }
85 return true
86 }
87
88
89 func abs8(d uint8) int {
90 if d < 128 {
91 return int(d)
92 }
93 return 256 - int(d)
94 }
95
96 func (e *encoder) writeChunk(b []byte, name string) {
97 if e.err != nil {
98 return
99 }
100 n := uint32(len(b))
101 if int(n) != len(b) {
102 e.err = UnsupportedError(name + " chunk is too large: " + strconv.Itoa(len(b)))
103 return
104 }
105 binary.BigEndian.PutUint32(e.header[:4], n)
106 e.header[4] = name[0]
107 e.header[5] = name[1]
108 e.header[6] = name[2]
109 e.header[7] = name[3]
110 crc := crc32.NewIEEE()
111 crc.Write(e.header[4:8])
112 crc.Write(b)
113 binary.BigEndian.PutUint32(e.footer[:4], crc.Sum32())
114
115 _, e.err = e.w.Write(e.header[:8])
116 if e.err != nil {
117 return
118 }
119 _, e.err = e.w.Write(b)
120 if e.err != nil {
121 return
122 }
123 _, e.err = e.w.Write(e.footer[:4])
124 }
125
126 func (e *encoder) writeIHDR() {
127 b := e.m.Bounds()
128 binary.BigEndian.PutUint32(e.tmp[0:4], uint32(b.Dx()))
129 binary.BigEndian.PutUint32(e.tmp[4:8], uint32(b.Dy()))
130
131 switch e.cb {
132 case cbG8:
133 e.tmp[8] = 8
134 e.tmp[9] = ctGrayscale
135 case cbTC8:
136 e.tmp[8] = 8
137 e.tmp[9] = ctTrueColor
138 case cbP8:
139 e.tmp[8] = 8
140 e.tmp[9] = ctPaletted
141 case cbP4:
142 e.tmp[8] = 4
143 e.tmp[9] = ctPaletted
144 case cbP2:
145 e.tmp[8] = 2
146 e.tmp[9] = ctPaletted
147 case cbP1:
148 e.tmp[8] = 1
149 e.tmp[9] = ctPaletted
150 case cbTCA8:
151 e.tmp[8] = 8
152 e.tmp[9] = ctTrueColorAlpha
153 case cbG16:
154 e.tmp[8] = 16
155 e.tmp[9] = ctGrayscale
156 case cbTC16:
157 e.tmp[8] = 16
158 e.tmp[9] = ctTrueColor
159 case cbTCA16:
160 e.tmp[8] = 16
161 e.tmp[9] = ctTrueColorAlpha
162 }
163 e.tmp[10] = 0
164 e.tmp[11] = 0
165 e.tmp[12] = 0
166 e.writeChunk(e.tmp[:13], "IHDR")
167 }
168
169 func (e *encoder) writePLTEAndTRNS(p color.Palette) {
170 if len(p) < 1 || len(p) > 256 {
171 e.err = FormatError("bad palette length: " + strconv.Itoa(len(p)))
172 return
173 }
174 last := -1
175 for i, c := range p {
176 c1 := color.NRGBAModel.Convert(c).(color.NRGBA)
177 e.tmp[3*i+0] = c1.R
178 e.tmp[3*i+1] = c1.G
179 e.tmp[3*i+2] = c1.B
180 if c1.A != 0xff {
181 last = i
182 }
183 e.tmp[3*256+i] = c1.A
184 }
185 e.writeChunk(e.tmp[:3*len(p)], "PLTE")
186 if last != -1 {
187 e.writeChunk(e.tmp[3*256:3*256+1+last], "tRNS")
188 }
189 }
190
191
192
193
194
195
196
197 func (e *encoder) Write(b []byte) (int, error) {
198 e.writeChunk(b, "IDAT")
199 if e.err != nil {
200 return 0, e.err
201 }
202 return len(b), nil
203 }
204
205
206
207 func filter(cr *[nFilter][]byte, pr []byte, bpp int) int {
208
209
210
211
212 cdat0 := cr[0][1:]
213 cdat1 := cr[1][1:]
214 cdat2 := cr[2][1:]
215 cdat3 := cr[3][1:]
216 cdat4 := cr[4][1:]
217 pdat := pr[1:]
218 n := len(cdat0)
219
220
221 sum := 0
222 for i := 0; i < n; i++ {
223 cdat2[i] = cdat0[i] - pdat[i]
224 sum += abs8(cdat2[i])
225 }
226 best := sum
227 filter := ftUp
228
229
230 sum = 0
231 for i := 0; i < bpp; i++ {
232 cdat4[i] = cdat0[i] - pdat[i]
233 sum += abs8(cdat4[i])
234 }
235 for i := bpp; i < n; i++ {
236 cdat4[i] = cdat0[i] - paeth(cdat0[i-bpp], pdat[i], pdat[i-bpp])
237 sum += abs8(cdat4[i])
238 if sum >= best {
239 break
240 }
241 }
242 if sum < best {
243 best = sum
244 filter = ftPaeth
245 }
246
247
248 sum = 0
249 for i := 0; i < n; i++ {
250 sum += abs8(cdat0[i])
251 if sum >= best {
252 break
253 }
254 }
255 if sum < best {
256 best = sum
257 filter = ftNone
258 }
259
260
261 sum = 0
262 for i := 0; i < bpp; i++ {
263 cdat1[i] = cdat0[i]
264 sum += abs8(cdat1[i])
265 }
266 for i := bpp; i < n; i++ {
267 cdat1[i] = cdat0[i] - cdat0[i-bpp]
268 sum += abs8(cdat1[i])
269 if sum >= best {
270 break
271 }
272 }
273 if sum < best {
274 best = sum
275 filter = ftSub
276 }
277
278
279 sum = 0
280 for i := 0; i < bpp; i++ {
281 cdat3[i] = cdat0[i] - pdat[i]/2
282 sum += abs8(cdat3[i])
283 }
284 for i := bpp; i < n; i++ {
285 cdat3[i] = cdat0[i] - uint8((int(cdat0[i-bpp])+int(pdat[i]))/2)
286 sum += abs8(cdat3[i])
287 if sum >= best {
288 break
289 }
290 }
291 if sum < best {
292 filter = ftAverage
293 }
294
295 return filter
296 }
297
298 func zeroMemory(v []uint8) {
299 for i := range v {
300 v[i] = 0
301 }
302 }
303
304 func (e *encoder) writeImage(w io.Writer, m image.Image, cb int, level int) error {
305 if e.zw == nil || e.zwLevel != level {
306 zw, err := zlib.NewWriterLevel(w, level)
307 if err != nil {
308 return err
309 }
310 e.zw = zw
311 e.zwLevel = level
312 } else {
313 e.zw.Reset(w)
314 }
315 defer e.zw.Close()
316
317 bitsPerPixel := 0
318
319 switch cb {
320 case cbG8:
321 bitsPerPixel = 8
322 case cbTC8:
323 bitsPerPixel = 24
324 case cbP8:
325 bitsPerPixel = 8
326 case cbP4:
327 bitsPerPixel = 4
328 case cbP2:
329 bitsPerPixel = 2
330 case cbP1:
331 bitsPerPixel = 1
332 case cbTCA8:
333 bitsPerPixel = 32
334 case cbTC16:
335 bitsPerPixel = 48
336 case cbTCA16:
337 bitsPerPixel = 64
338 case cbG16:
339 bitsPerPixel = 16
340 }
341
342
343
344
345
346
347 b := m.Bounds()
348 sz := 1 + (bitsPerPixel*b.Dx()+7)/8
349 for i := range e.cr {
350 if cap(e.cr[i]) < sz {
351 e.cr[i] = make([]uint8, sz)
352 } else {
353 e.cr[i] = e.cr[i][:sz]
354 }
355 e.cr[i][0] = uint8(i)
356 }
357 cr := e.cr
358 if cap(e.pr) < sz {
359 e.pr = make([]uint8, sz)
360 } else {
361 e.pr = e.pr[:sz]
362 zeroMemory(e.pr)
363 }
364 pr := e.pr
365
366 gray, _ := m.(*image.Gray)
367 rgba, _ := m.(*image.RGBA)
368 paletted, _ := m.(*image.Paletted)
369 nrgba, _ := m.(*image.NRGBA)
370
371 for y := b.Min.Y; y < b.Max.Y; y++ {
372
373 i := 1
374 switch cb {
375 case cbG8:
376 if gray != nil {
377 offset := (y - b.Min.Y) * gray.Stride
378 copy(cr[0][1:], gray.Pix[offset:offset+b.Dx()])
379 } else {
380 for x := b.Min.X; x < b.Max.X; x++ {
381 c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
382 cr[0][i] = c.Y
383 i++
384 }
385 }
386 case cbTC8:
387
388 cr0 := cr[0]
389 stride, pix := 0, []byte(nil)
390 if rgba != nil {
391 stride, pix = rgba.Stride, rgba.Pix
392 } else if nrgba != nil {
393 stride, pix = nrgba.Stride, nrgba.Pix
394 }
395 if stride != 0 {
396 j0 := (y - b.Min.Y) * stride
397 j1 := j0 + b.Dx()*4
398 for j := j0; j < j1; j += 4 {
399 cr0[i+0] = pix[j+0]
400 cr0[i+1] = pix[j+1]
401 cr0[i+2] = pix[j+2]
402 i += 3
403 }
404 } else {
405 for x := b.Min.X; x < b.Max.X; x++ {
406 r, g, b, _ := m.At(x, y).RGBA()
407 cr0[i+0] = uint8(r >> 8)
408 cr0[i+1] = uint8(g >> 8)
409 cr0[i+2] = uint8(b >> 8)
410 i += 3
411 }
412 }
413 case cbP8:
414 if paletted != nil {
415 offset := (y - b.Min.Y) * paletted.Stride
416 copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
417 } else {
418 pi := m.(image.PalettedImage)
419 for x := b.Min.X; x < b.Max.X; x++ {
420 cr[0][i] = pi.ColorIndexAt(x, y)
421 i += 1
422 }
423 }
424
425 case cbP4, cbP2, cbP1:
426 pi := m.(image.PalettedImage)
427
428 var a uint8
429 var c int
430 pixelsPerByte := 8 / bitsPerPixel
431 for x := b.Min.X; x < b.Max.X; x++ {
432 a = a<<uint(bitsPerPixel) | pi.ColorIndexAt(x, y)
433 c++
434 if c == pixelsPerByte {
435 cr[0][i] = a
436 i += 1
437 a = 0
438 c = 0
439 }
440 }
441 if c != 0 {
442 for c != pixelsPerByte {
443 a = a << uint(bitsPerPixel)
444 c++
445 }
446 cr[0][i] = a
447 }
448
449 case cbTCA8:
450 if nrgba != nil {
451 offset := (y - b.Min.Y) * nrgba.Stride
452 copy(cr[0][1:], nrgba.Pix[offset:offset+b.Dx()*4])
453 } else {
454
455 for x := b.Min.X; x < b.Max.X; x++ {
456 c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA)
457 cr[0][i+0] = c.R
458 cr[0][i+1] = c.G
459 cr[0][i+2] = c.B
460 cr[0][i+3] = c.A
461 i += 4
462 }
463 }
464 case cbG16:
465 for x := b.Min.X; x < b.Max.X; x++ {
466 c := color.Gray16Model.Convert(m.At(x, y)).(color.Gray16)
467 cr[0][i+0] = uint8(c.Y >> 8)
468 cr[0][i+1] = uint8(c.Y)
469 i += 2
470 }
471 case cbTC16:
472
473 for x := b.Min.X; x < b.Max.X; x++ {
474 r, g, b, _ := m.At(x, y).RGBA()
475 cr[0][i+0] = uint8(r >> 8)
476 cr[0][i+1] = uint8(r)
477 cr[0][i+2] = uint8(g >> 8)
478 cr[0][i+3] = uint8(g)
479 cr[0][i+4] = uint8(b >> 8)
480 cr[0][i+5] = uint8(b)
481 i += 6
482 }
483 case cbTCA16:
484
485 for x := b.Min.X; x < b.Max.X; x++ {
486 c := color.NRGBA64Model.Convert(m.At(x, y)).(color.NRGBA64)
487 cr[0][i+0] = uint8(c.R >> 8)
488 cr[0][i+1] = uint8(c.R)
489 cr[0][i+2] = uint8(c.G >> 8)
490 cr[0][i+3] = uint8(c.G)
491 cr[0][i+4] = uint8(c.B >> 8)
492 cr[0][i+5] = uint8(c.B)
493 cr[0][i+6] = uint8(c.A >> 8)
494 cr[0][i+7] = uint8(c.A)
495 i += 8
496 }
497 }
498
499
500
501
502
503 f := ftNone
504 if level != zlib.NoCompression && cb != cbP8 && cb != cbP4 && cb != cbP2 && cb != cbP1 {
505
506
507 bpp := bitsPerPixel / 8
508 f = filter(&cr, pr, bpp)
509 }
510
511
512 if _, err := e.zw.Write(cr[f]); err != nil {
513 return err
514 }
515
516
517 pr, cr[0] = cr[0], pr
518 }
519 return nil
520 }
521
522
523 func (e *encoder) writeIDATs() {
524 if e.err != nil {
525 return
526 }
527 if e.bw == nil {
528 e.bw = bufio.NewWriterSize(e, 1<<15)
529 } else {
530 e.bw.Reset(e)
531 }
532 e.err = e.writeImage(e.bw, e.m, e.cb, levelToZlib(e.enc.CompressionLevel))
533 if e.err != nil {
534 return
535 }
536 e.err = e.bw.Flush()
537 }
538
539
540
541 func levelToZlib(l CompressionLevel) int {
542 switch l {
543 case DefaultCompression:
544 return zlib.DefaultCompression
545 case NoCompression:
546 return zlib.NoCompression
547 case BestSpeed:
548 return zlib.BestSpeed
549 case BestCompression:
550 return zlib.BestCompression
551 default:
552 return zlib.DefaultCompression
553 }
554 }
555
556 func (e *encoder) writeIEND() { e.writeChunk(nil, "IEND") }
557
558
559
560 func Encode(w io.Writer, m image.Image) error {
561 var e Encoder
562 return e.Encode(w, m)
563 }
564
565
566 func (enc *Encoder) Encode(w io.Writer, m image.Image) error {
567
568
569
570 mw, mh := int64(m.Bounds().Dx()), int64(m.Bounds().Dy())
571 if mw <= 0 || mh <= 0 || mw >= 1<<32 || mh >= 1<<32 {
572 return FormatError("invalid image size: " + strconv.FormatInt(mw, 10) + "x" + strconv.FormatInt(mh, 10))
573 }
574
575 var e *encoder
576 if enc.BufferPool != nil {
577 buffer := enc.BufferPool.Get()
578 e = (*encoder)(buffer)
579
580 }
581 if e == nil {
582 e = &encoder{}
583 }
584 if enc.BufferPool != nil {
585 defer enc.BufferPool.Put((*EncoderBuffer)(e))
586 }
587
588 e.enc = enc
589 e.w = w
590 e.m = m
591
592 var pal color.Palette
593
594 if _, ok := m.(image.PalettedImage); ok {
595 pal, _ = m.ColorModel().(color.Palette)
596 }
597 if pal != nil {
598 if len(pal) <= 2 {
599 e.cb = cbP1
600 } else if len(pal) <= 4 {
601 e.cb = cbP2
602 } else if len(pal) <= 16 {
603 e.cb = cbP4
604 } else {
605 e.cb = cbP8
606 }
607 } else {
608 switch m.ColorModel() {
609 case color.GrayModel:
610 e.cb = cbG8
611 case color.Gray16Model:
612 e.cb = cbG16
613 case color.RGBAModel, color.NRGBAModel, color.AlphaModel:
614 if opaque(m) {
615 e.cb = cbTC8
616 } else {
617 e.cb = cbTCA8
618 }
619 default:
620 if opaque(m) {
621 e.cb = cbTC16
622 } else {
623 e.cb = cbTCA16
624 }
625 }
626 }
627
628 _, e.err = io.WriteString(w, pngHeader)
629 e.writeIHDR()
630 if pal != nil {
631 e.writePLTEAndTRNS(pal)
632 }
633 e.writeIDATs()
634 e.writeIEND()
635 return e.err
636 }
637
View as plain text