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 (e *encoder) writeImage(w io.Writer, m image.Image, cb int, level int) error {
299 if e.zw == nil || e.zwLevel != level {
300 zw, err := zlib.NewWriterLevel(w, level)
301 if err != nil {
302 return err
303 }
304 e.zw = zw
305 e.zwLevel = level
306 } else {
307 e.zw.Reset(w)
308 }
309 defer e.zw.Close()
310
311 bitsPerPixel := 0
312
313 switch cb {
314 case cbG8:
315 bitsPerPixel = 8
316 case cbTC8:
317 bitsPerPixel = 24
318 case cbP8:
319 bitsPerPixel = 8
320 case cbP4:
321 bitsPerPixel = 4
322 case cbP2:
323 bitsPerPixel = 2
324 case cbP1:
325 bitsPerPixel = 1
326 case cbTCA8:
327 bitsPerPixel = 32
328 case cbTC16:
329 bitsPerPixel = 48
330 case cbTCA16:
331 bitsPerPixel = 64
332 case cbG16:
333 bitsPerPixel = 16
334 }
335
336
337
338
339
340
341 b := m.Bounds()
342 sz := 1 + (bitsPerPixel*b.Dx()+7)/8
343 for i := range e.cr {
344 if cap(e.cr[i]) < sz {
345 e.cr[i] = make([]uint8, sz)
346 } else {
347 e.cr[i] = e.cr[i][:sz]
348 }
349 e.cr[i][0] = uint8(i)
350 }
351 cr := e.cr
352 if cap(e.pr) < sz {
353 e.pr = make([]uint8, sz)
354 } else {
355 e.pr = e.pr[:sz]
356 clear(e.pr)
357 }
358 pr := e.pr
359
360 gray, _ := m.(*image.Gray)
361 rgba, _ := m.(*image.RGBA)
362 paletted, _ := m.(*image.Paletted)
363 nrgba, _ := m.(*image.NRGBA)
364
365 for y := b.Min.Y; y < b.Max.Y; y++ {
366
367 i := 1
368 switch cb {
369 case cbG8:
370 if gray != nil {
371 offset := (y - b.Min.Y) * gray.Stride
372 copy(cr[0][1:], gray.Pix[offset:offset+b.Dx()])
373 } else {
374 for x := b.Min.X; x < b.Max.X; x++ {
375 c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
376 cr[0][i] = c.Y
377 i++
378 }
379 }
380 case cbTC8:
381
382 cr0 := cr[0]
383 stride, pix := 0, []byte(nil)
384 if rgba != nil {
385 stride, pix = rgba.Stride, rgba.Pix
386 } else if nrgba != nil {
387 stride, pix = nrgba.Stride, nrgba.Pix
388 }
389 if stride != 0 {
390 j0 := (y - b.Min.Y) * stride
391 j1 := j0 + b.Dx()*4
392 for j := j0; j < j1; j += 4 {
393 cr0[i+0] = pix[j+0]
394 cr0[i+1] = pix[j+1]
395 cr0[i+2] = pix[j+2]
396 i += 3
397 }
398 } else {
399 for x := b.Min.X; x < b.Max.X; x++ {
400 r, g, b, _ := m.At(x, y).RGBA()
401 cr0[i+0] = uint8(r >> 8)
402 cr0[i+1] = uint8(g >> 8)
403 cr0[i+2] = uint8(b >> 8)
404 i += 3
405 }
406 }
407 case cbP8:
408 if paletted != nil {
409 offset := (y - b.Min.Y) * paletted.Stride
410 copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
411 } else {
412 pi := m.(image.PalettedImage)
413 for x := b.Min.X; x < b.Max.X; x++ {
414 cr[0][i] = pi.ColorIndexAt(x, y)
415 i += 1
416 }
417 }
418
419 case cbP4, cbP2, cbP1:
420 pi := m.(image.PalettedImage)
421
422 var a uint8
423 var c int
424 pixelsPerByte := 8 / bitsPerPixel
425 for x := b.Min.X; x < b.Max.X; x++ {
426 a = a<<uint(bitsPerPixel) | pi.ColorIndexAt(x, y)
427 c++
428 if c == pixelsPerByte {
429 cr[0][i] = a
430 i += 1
431 a = 0
432 c = 0
433 }
434 }
435 if c != 0 {
436 for c != pixelsPerByte {
437 a = a << uint(bitsPerPixel)
438 c++
439 }
440 cr[0][i] = a
441 }
442
443 case cbTCA8:
444 if nrgba != nil {
445 offset := (y - b.Min.Y) * nrgba.Stride
446 copy(cr[0][1:], nrgba.Pix[offset:offset+b.Dx()*4])
447 } else if rgba != nil {
448 dst := cr[0][1:]
449 src := rgba.Pix[rgba.PixOffset(b.Min.X, y):rgba.PixOffset(b.Max.X, y)]
450 for ; len(src) >= 4; dst, src = dst[4:], src[4:] {
451 d := (*[4]byte)(dst)
452 s := (*[4]byte)(src)
453 if s[3] == 0x00 {
454 d[0] = 0
455 d[1] = 0
456 d[2] = 0
457 d[3] = 0
458 } else if s[3] == 0xff {
459 copy(d[:], s[:])
460 } else {
461
462
463
464
465
466
467
468
469 const m = 0x101 * 0xffff
470 a := uint32(s[3]) * 0x101
471 d[0] = uint8((uint32(s[0]) * m / a) >> 8)
472 d[1] = uint8((uint32(s[1]) * m / a) >> 8)
473 d[2] = uint8((uint32(s[2]) * m / a) >> 8)
474 d[3] = s[3]
475 }
476 }
477 } else {
478
479 for x := b.Min.X; x < b.Max.X; x++ {
480 c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA)
481 cr[0][i+0] = c.R
482 cr[0][i+1] = c.G
483 cr[0][i+2] = c.B
484 cr[0][i+3] = c.A
485 i += 4
486 }
487 }
488 case cbG16:
489 for x := b.Min.X; x < b.Max.X; x++ {
490 c := color.Gray16Model.Convert(m.At(x, y)).(color.Gray16)
491 cr[0][i+0] = uint8(c.Y >> 8)
492 cr[0][i+1] = uint8(c.Y)
493 i += 2
494 }
495 case cbTC16:
496
497 for x := b.Min.X; x < b.Max.X; x++ {
498 r, g, b, _ := m.At(x, y).RGBA()
499 cr[0][i+0] = uint8(r >> 8)
500 cr[0][i+1] = uint8(r)
501 cr[0][i+2] = uint8(g >> 8)
502 cr[0][i+3] = uint8(g)
503 cr[0][i+4] = uint8(b >> 8)
504 cr[0][i+5] = uint8(b)
505 i += 6
506 }
507 case cbTCA16:
508
509 for x := b.Min.X; x < b.Max.X; x++ {
510 c := color.NRGBA64Model.Convert(m.At(x, y)).(color.NRGBA64)
511 cr[0][i+0] = uint8(c.R >> 8)
512 cr[0][i+1] = uint8(c.R)
513 cr[0][i+2] = uint8(c.G >> 8)
514 cr[0][i+3] = uint8(c.G)
515 cr[0][i+4] = uint8(c.B >> 8)
516 cr[0][i+5] = uint8(c.B)
517 cr[0][i+6] = uint8(c.A >> 8)
518 cr[0][i+7] = uint8(c.A)
519 i += 8
520 }
521 }
522
523
524
525
526
527 f := ftNone
528 if level != zlib.NoCompression && cb != cbP8 && cb != cbP4 && cb != cbP2 && cb != cbP1 {
529
530
531 bpp := bitsPerPixel / 8
532 f = filter(&cr, pr, bpp)
533 }
534
535
536 if _, err := e.zw.Write(cr[f]); err != nil {
537 return err
538 }
539
540
541 pr, cr[0] = cr[0], pr
542 }
543 return nil
544 }
545
546
547 func (e *encoder) writeIDATs() {
548 if e.err != nil {
549 return
550 }
551 if e.bw == nil {
552 e.bw = bufio.NewWriterSize(e, 1<<15)
553 } else {
554 e.bw.Reset(e)
555 }
556 e.err = e.writeImage(e.bw, e.m, e.cb, levelToZlib(e.enc.CompressionLevel))
557 if e.err != nil {
558 return
559 }
560 e.err = e.bw.Flush()
561 }
562
563
564
565 func levelToZlib(l CompressionLevel) int {
566 switch l {
567 case DefaultCompression:
568 return zlib.DefaultCompression
569 case NoCompression:
570 return zlib.NoCompression
571 case BestSpeed:
572 return zlib.BestSpeed
573 case BestCompression:
574 return zlib.BestCompression
575 default:
576 return zlib.DefaultCompression
577 }
578 }
579
580 func (e *encoder) writeIEND() { e.writeChunk(nil, "IEND") }
581
582
583
584 func Encode(w io.Writer, m image.Image) error {
585 var e Encoder
586 return e.Encode(w, m)
587 }
588
589
590 func (enc *Encoder) Encode(w io.Writer, m image.Image) error {
591
592
593
594 mw, mh := int64(m.Bounds().Dx()), int64(m.Bounds().Dy())
595 if mw <= 0 || mh <= 0 || mw >= 1<<32 || mh >= 1<<32 {
596 return FormatError("invalid image size: " + strconv.FormatInt(mw, 10) + "x" + strconv.FormatInt(mh, 10))
597 }
598
599 var e *encoder
600 if enc.BufferPool != nil {
601 buffer := enc.BufferPool.Get()
602 e = (*encoder)(buffer)
603
604 }
605 if e == nil {
606 e = &encoder{}
607 }
608 if enc.BufferPool != nil {
609 defer enc.BufferPool.Put((*EncoderBuffer)(e))
610 }
611
612 e.enc = enc
613 e.w = w
614 e.m = m
615
616 var pal color.Palette
617
618 if _, ok := m.(image.PalettedImage); ok {
619 pal, _ = m.ColorModel().(color.Palette)
620 }
621 if pal != nil {
622 if len(pal) <= 2 {
623 e.cb = cbP1
624 } else if len(pal) <= 4 {
625 e.cb = cbP2
626 } else if len(pal) <= 16 {
627 e.cb = cbP4
628 } else {
629 e.cb = cbP8
630 }
631 } else {
632 switch m.ColorModel() {
633 case color.GrayModel:
634 e.cb = cbG8
635 case color.Gray16Model:
636 e.cb = cbG16
637 case color.RGBAModel, color.NRGBAModel, color.AlphaModel:
638 if opaque(m) {
639 e.cb = cbTC8
640 } else {
641 e.cb = cbTCA8
642 }
643 default:
644 if opaque(m) {
645 e.cb = cbTC16
646 } else {
647 e.cb = cbTCA16
648 }
649 }
650 }
651
652 _, e.err = io.WriteString(w, pngHeader)
653 e.writeIHDR()
654 if pal != nil {
655 e.writePLTEAndTRNS(pal)
656 }
657 e.writeIDATs()
658 e.writeIEND()
659 return e.err
660 }
661
View as plain text