Source file src/pkg/image/png/writer.go
1
2
3
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 [4 * 256]byte
25 }
26
27
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
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
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[: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[:4], crc.Sum32())
82
83 _, e.err = e.w.Write(e.header[: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[: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
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
123 e.tmp[11] = 0
124 e.tmp[12] = 0
125 e.writeChunk(e.tmp[:13], "IHDR")
126 }
127
128 func (e *encoder) writePLTEAndTRNS(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 last := -1
134 for i, c := range p {
135 c1 := color.NRGBAModel.Convert(c).(color.NRGBA)
136 e.tmp[3*i+0] = c1.R
137 e.tmp[3*i+1] = c1.G
138 e.tmp[3*i+2] = c1.B
139 if c1.A != 0xff {
140 last = i
141 }
142 e.tmp[3*256+i] = c1.A
143 }
144 e.writeChunk(e.tmp[:3*len(p)], "PLTE")
145 if last != -1 {
146 e.writeChunk(e.tmp[3*256:3*256+1+last], "tRNS")
147 }
148 }
149
150
151
152
153
154
155
156 func (e *encoder) Write(b []byte) (int, error) {
157 e.writeChunk(b, "IDAT")
158 if e.err != nil {
159 return 0, e.err
160 }
161 return len(b), nil
162 }
163
164
165
166 func filter(cr *[nFilter][]byte, pr []byte, bpp int) int {
167
168
169
170
171 cdat0 := cr[0][1:]
172 cdat1 := cr[1][1:]
173 cdat2 := cr[2][1:]
174 cdat3 := cr[3][1:]
175 cdat4 := cr[4][1:]
176 pdat := pr[1:]
177 n := len(cdat0)
178
179
180 sum := 0
181 for i := 0; i < n; i++ {
182 cdat2[i] = cdat0[i] - pdat[i]
183 sum += abs8(cdat2[i])
184 }
185 best := sum
186 filter := ftUp
187
188
189 sum = 0
190 for i := 0; i < bpp; i++ {
191 cdat4[i] = cdat0[i] - paeth(0, pdat[i], 0)
192 sum += abs8(cdat4[i])
193 }
194 for i := bpp; i < n; i++ {
195 cdat4[i] = cdat0[i] - paeth(cdat0[i-bpp], pdat[i], pdat[i-bpp])
196 sum += abs8(cdat4[i])
197 if sum >= best {
198 break
199 }
200 }
201 if sum < best {
202 best = sum
203 filter = ftPaeth
204 }
205
206
207 sum = 0
208 for i := 0; i < n; i++ {
209 sum += abs8(cdat0[i])
210 if sum >= best {
211 break
212 }
213 }
214 if sum < best {
215 best = sum
216 filter = ftNone
217 }
218
219
220 sum = 0
221 for i := 0; i < bpp; i++ {
222 cdat1[i] = cdat0[i]
223 sum += abs8(cdat1[i])
224 }
225 for i := bpp; i < n; i++ {
226 cdat1[i] = cdat0[i] - cdat0[i-bpp]
227 sum += abs8(cdat1[i])
228 if sum >= best {
229 break
230 }
231 }
232 if sum < best {
233 best = sum
234 filter = ftSub
235 }
236
237
238 sum = 0
239 for i := 0; i < bpp; i++ {
240 cdat3[i] = cdat0[i] - pdat[i]/2
241 sum += abs8(cdat3[i])
242 }
243 for i := bpp; i < n; i++ {
244 cdat3[i] = cdat0[i] - uint8((int(cdat0[i-bpp])+int(pdat[i]))/2)
245 sum += abs8(cdat3[i])
246 if sum >= best {
247 break
248 }
249 }
250 if sum < best {
251 best = sum
252 filter = ftAverage
253 }
254
255 return filter
256 }
257
258 func writeImage(w io.Writer, m image.Image, cb int) error {
259 zw := zlib.NewWriter(w)
260 defer zw.Close()
261
262 bpp := 0
263
264 switch cb {
265 case cbG8:
266 bpp = 1
267 case cbTC8:
268 bpp = 3
269 case cbP8:
270 bpp = 1
271 case cbTCA8:
272 bpp = 4
273 case cbTC16:
274 bpp = 6
275 case cbTCA16:
276 bpp = 8
277 case cbG16:
278 bpp = 2
279 }
280
281
282
283
284
285 b := m.Bounds()
286 var cr [nFilter][]uint8
287 for i := range cr {
288 cr[i] = make([]uint8, 1+bpp*b.Dx())
289 cr[i][0] = uint8(i)
290 }
291 pr := make([]uint8, 1+bpp*b.Dx())
292
293 gray, _ := m.(*image.Gray)
294 rgba, _ := m.(*image.RGBA)
295 paletted, _ := m.(*image.Paletted)
296 nrgba, _ := m.(*image.NRGBA)
297
298 for y := b.Min.Y; y < b.Max.Y; y++ {
299
300 i := 1
301 switch cb {
302 case cbG8:
303 if gray != nil {
304 offset := (y - b.Min.Y) * gray.Stride
305 copy(cr[0][1:], gray.Pix[offset:offset+b.Dx()])
306 } else {
307 for x := b.Min.X; x < b.Max.X; x++ {
308 c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
309 cr[0][i] = c.Y
310 i++
311 }
312 }
313 case cbTC8:
314
315 cr0 := cr[0]
316 stride, pix := 0, []byte(nil)
317 if rgba != nil {
318 stride, pix = rgba.Stride, rgba.Pix
319 } else if nrgba != nil {
320 stride, pix = nrgba.Stride, nrgba.Pix
321 }
322 if stride != 0 {
323 j0 := (y - b.Min.Y) * stride
324 j1 := j0 + b.Dx()*4
325 for j := j0; j < j1; j += 4 {
326 cr0[i+0] = pix[j+0]
327 cr0[i+1] = pix[j+1]
328 cr0[i+2] = pix[j+2]
329 i += 3
330 }
331 } else {
332 for x := b.Min.X; x < b.Max.X; x++ {
333 r, g, b, _ := m.At(x, y).RGBA()
334 cr0[i+0] = uint8(r >> 8)
335 cr0[i+1] = uint8(g >> 8)
336 cr0[i+2] = uint8(b >> 8)
337 i += 3
338 }
339 }
340 case cbP8:
341 if paletted != nil {
342 offset := (y - b.Min.Y) * paletted.Stride
343 copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
344 } else {
345 pi := m.(image.PalettedImage)
346 for x := b.Min.X; x < b.Max.X; x++ {
347 cr[0][i] = pi.ColorIndexAt(x, y)
348 i += 1
349 }
350 }
351 case cbTCA8:
352 if nrgba != nil {
353 offset := (y - b.Min.Y) * nrgba.Stride
354 copy(cr[0][1:], nrgba.Pix[offset:offset+b.Dx()*4])
355 } else {
356
357 for x := b.Min.X; x < b.Max.X; x++ {
358 c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA)
359 cr[0][i+0] = c.R
360 cr[0][i+1] = c.G
361 cr[0][i+2] = c.B
362 cr[0][i+3] = c.A
363 i += 4
364 }
365 }
366 case cbG16:
367 for x := b.Min.X; x < b.Max.X; x++ {
368 c := color.Gray16Model.Convert(m.At(x, y)).(color.Gray16)
369 cr[0][i+0] = uint8(c.Y >> 8)
370 cr[0][i+1] = uint8(c.Y)
371 i += 2
372 }
373 case cbTC16:
374
375 for x := b.Min.X; x < b.Max.X; x++ {
376 r, g, b, _ := m.At(x, y).RGBA()
377 cr[0][i+0] = uint8(r >> 8)
378 cr[0][i+1] = uint8(r)
379 cr[0][i+2] = uint8(g >> 8)
380 cr[0][i+3] = uint8(g)
381 cr[0][i+4] = uint8(b >> 8)
382 cr[0][i+5] = uint8(b)
383 i += 6
384 }
385 case cbTCA16:
386
387 for x := b.Min.X; x < b.Max.X; x++ {
388 c := color.NRGBA64Model.Convert(m.At(x, y)).(color.NRGBA64)
389 cr[0][i+0] = uint8(c.R >> 8)
390 cr[0][i+1] = uint8(c.R)
391 cr[0][i+2] = uint8(c.G >> 8)
392 cr[0][i+3] = uint8(c.G)
393 cr[0][i+4] = uint8(c.B >> 8)
394 cr[0][i+5] = uint8(c.B)
395 cr[0][i+6] = uint8(c.A >> 8)
396 cr[0][i+7] = uint8(c.A)
397 i += 8
398 }
399 }
400
401
402 f := filter(&cr, pr, bpp)
403
404
405 if _, err := zw.Write(cr[f]); err != nil {
406 return err
407 }
408
409
410 pr, cr[0] = cr[0], pr
411 }
412 return nil
413 }
414
415
416 func (e *encoder) writeIDATs() {
417 if e.err != nil {
418 return
419 }
420 var bw *bufio.Writer
421 bw = bufio.NewWriterSize(e, 1<<15)
422 e.err = writeImage(bw, e.m, e.cb)
423 if e.err != nil {
424 return
425 }
426 e.err = bw.Flush()
427 }
428
429 func (e *encoder) writeIEND() { e.writeChunk(nil, "IEND") }
430
431
432
433 func Encode(w io.Writer, m image.Image) error {
434
435
436
437 mw, mh := int64(m.Bounds().Dx()), int64(m.Bounds().Dy())
438 if mw <= 0 || mh <= 0 || mw >= 1<<32 || mh >= 1<<32 {
439 return FormatError("invalid image size: " + strconv.FormatInt(mw, 10) + "x" + strconv.FormatInt(mh, 10))
440 }
441
442 var e encoder
443 e.w = w
444 e.m = m
445
446 var pal color.Palette
447
448 if _, ok := m.(image.PalettedImage); ok {
449 pal, _ = m.ColorModel().(color.Palette)
450 }
451 if pal != nil {
452 e.cb = cbP8
453 } else {
454 switch m.ColorModel() {
455 case color.GrayModel:
456 e.cb = cbG8
457 case color.Gray16Model:
458 e.cb = cbG16
459 case color.RGBAModel, color.NRGBAModel, color.AlphaModel:
460 if opaque(m) {
461 e.cb = cbTC8
462 } else {
463 e.cb = cbTCA8
464 }
465 default:
466 if opaque(m) {
467 e.cb = cbTC16
468 } else {
469 e.cb = cbTCA16
470 }
471 }
472 }
473
474 _, e.err = io.WriteString(w, pngHeader)
475 e.writeIHDR()
476 if pal != nil {
477 e.writePLTEAndTRNS(pal)
478 }
479 e.writeIDATs()
480 e.writeIEND()
481 return e.err
482 }
View as plain text