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