1
2
3
4
5 package png
6
7 import (
8 "bufio"
9 "compress/zlib"
10 "hash/crc32"
11 "image"
12 "io"
13 "os"
14 "strconv"
15 )
16
17 type encoder struct {
18 w io.Writer
19 m image.Image
20 cb int
21 err os.Error
22 header [8]byte
23 footer [4]byte
24 tmp [3 * 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[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
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[0:13], "IHDR")
126 }
127
128 func (e *encoder) writePLTE(p image.PalettedColorModel) {
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 image.PalettedColorModel) {
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
158
159
160
161
162
163
164
165
166
167 func (e *encoder) Write(b []byte) (int, os.Error) {
168 e.writeChunk(b, "IDAT")
169 if e.err != nil {
170 return 0, e.err
171 }
172 return len(b), nil
173 }
174
175
176
177 func filter(cr *[nFilter][]byte, pr []byte, bpp int) int {
178
179
180
181
182 cdat0 := cr[0][1:]
183 cdat1 := cr[1][1:]
184 cdat2 := cr[2][1:]
185 cdat3 := cr[3][1:]
186 cdat4 := cr[4][1:]
187 pdat := pr[1:]
188 n := len(cdat0)
189
190
191 sum := 0
192 for i := 0; i < n; i++ {
193 cdat2[i] = cdat0[i] - pdat[i]
194 sum += abs8(cdat2[i])
195 }
196 best := sum
197 filter := ftUp
198
199
200 sum = 0
201 for i := 0; i < bpp; i++ {
202 cdat4[i] = cdat0[i] - paeth(0, pdat[i], 0)
203 sum += abs8(cdat4[i])
204 }
205 for i := bpp; i < n; i++ {
206 cdat4[i] = cdat0[i] - paeth(cdat0[i-bpp], pdat[i], pdat[i-bpp])
207 sum += abs8(cdat4[i])
208 if sum >= best {
209 break
210 }
211 }
212 if sum < best {
213 best = sum
214 filter = ftPaeth
215 }
216
217
218 sum = 0
219 for i := 0; i < n; i++ {
220 sum += abs8(cdat0[i])
221 if sum >= best {
222 break
223 }
224 }
225 if sum < best {
226 best = sum
227 filter = ftNone
228 }
229
230
231 sum = 0
232 for i := 0; i < bpp; i++ {
233 cdat1[i] = cdat0[i]
234 sum += abs8(cdat1[i])
235 }
236 for i := bpp; i < n; i++ {
237 cdat1[i] = cdat0[i] - cdat0[i-bpp]
238 sum += abs8(cdat1[i])
239 if sum >= best {
240 break
241 }
242 }
243 if sum < best {
244 best = sum
245 filter = ftSub
246 }
247
248
249 sum = 0
250 for i := 0; i < bpp; i++ {
251 cdat3[i] = cdat0[i] - pdat[i]/2
252 sum += abs8(cdat3[i])
253 }
254 for i := bpp; i < n; i++ {
255 cdat3[i] = cdat0[i] - uint8((int(cdat0[i-bpp])+int(pdat[i]))/2)
256 sum += abs8(cdat3[i])
257 if sum >= best {
258 break
259 }
260 }
261 if sum < best {
262 best = sum
263 filter = ftAverage
264 }
265
266 return filter
267 }
268
269 func writeImage(w io.Writer, m image.Image, cb int) os.Error {
270 zw, err := zlib.NewWriter(w)
271 if err != nil {
272 return err
273 }
274 defer zw.Close()
275
276 bpp := 0
277
278 switch cb {
279 case cbG8:
280 bpp = 1
281 case cbTC8:
282 bpp = 3
283 case cbP8:
284 bpp = 1
285 case cbTCA8:
286 bpp = 4
287 case cbTC16:
288 bpp = 6
289 case cbTCA16:
290 bpp = 8
291 case cbG16:
292 bpp = 2
293 }
294
295
296
297
298
299 b := m.Bounds()
300 var cr [nFilter][]uint8
301 for i := range cr {
302 cr[i] = make([]uint8, 1+bpp*b.Dx())
303 cr[i][0] = uint8(i)
304 }
305 pr := make([]uint8, 1+bpp*b.Dx())
306
307 for y := b.Min.Y; y < b.Max.Y; y++ {
308
309 i := 1
310 switch cb {
311 case cbG8:
312 for x := b.Min.X; x < b.Max.X; x++ {
313 c := image.GrayColorModel.Convert(m.At(x, y)).(image.GrayColor)
314 cr[0][i] = c.Y
315 i++
316 }
317 case cbTC8:
318
319 cr0 := cr[0]
320 if rgba, _ := m.(*image.RGBA); rgba != nil {
321 j0 := (y - b.Min.Y) * rgba.Stride
322 j1 := j0 + b.Dx()*4
323 for j := j0; j < j1; j += 4 {
324 cr0[i+0] = rgba.Pix[j+0]
325 cr0[i+1] = rgba.Pix[j+1]
326 cr0[i+2] = rgba.Pix[j+2]
327 i += 3
328 }
329 } else {
330 for x := b.Min.X; x < b.Max.X; x++ {
331 r, g, b, _ := m.At(x, y).RGBA()
332 cr0[i+0] = uint8(r >> 8)
333 cr0[i+1] = uint8(g >> 8)
334 cr0[i+2] = uint8(b >> 8)
335 i += 3
336 }
337 }
338 case cbP8:
339 paletted := m.(*image.Paletted)
340 offset := (y - b.Min.Y) * paletted.Stride
341 copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
342 case cbTCA8:
343
344 for x := b.Min.X; x < b.Max.X; x++ {
345 c := image.NRGBAColorModel.Convert(m.At(x, y)).(image.NRGBAColor)
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 := image.Gray16ColorModel.Convert(m.At(x, y)).(image.Gray16Color)
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
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
373 for x := b.Min.X; x < b.Max.X; x++ {
374 c := image.NRGBA64ColorModel.Convert(m.At(x, y)).(image.NRGBA64Color)
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
388 f := filter(&cr, pr, bpp)
389
390
391 _, err = zw.Write(cr[f])
392 if err != nil {
393 return err
394 }
395
396
397 pr, cr[0] = cr[0], pr
398 }
399 return nil
400 }
401
402
403 func (e *encoder) writeIDATs() {
404 if e.err != nil {
405 return
406 }
407 var bw *bufio.Writer
408 bw, e.err = bufio.NewWriterSize(e, 1<<15)
409 if e.err != nil {
410 return
411 }
412 e.err = writeImage(bw, e.m, e.cb)
413 if e.err != nil {
414 return
415 }
416 e.err = bw.Flush()
417 }
418
419 func (e *encoder) writeIEND() { e.writeChunk(e.tmp[0:0], "IEND") }
420
421
422
423 func Encode(w io.Writer, m image.Image) os.Error {
424
425
426
427 mw, mh := int64(m.Bounds().Dx()), int64(m.Bounds().Dy())
428 if mw <= 0 || mh <= 0 || mw >= 1<<32 || mh >= 1<<32 {
429 return FormatError("invalid image size: " + strconv.Itoa64(mw) + "x" + strconv.Itoa64(mw))
430 }
431
432 var e encoder
433 e.w = w
434 e.m = m
435 pal, _ := m.(*image.Paletted)
436 if pal != nil {
437 e.cb = cbP8
438 } else {
439 switch m.ColorModel() {
440 case image.GrayColorModel:
441 e.cb = cbG8
442 case image.Gray16ColorModel:
443 e.cb = cbG16
444 case image.RGBAColorModel, image.NRGBAColorModel, image.AlphaColorModel:
445 if opaque(m) {
446 e.cb = cbTC8
447 } else {
448 e.cb = cbTCA8
449 }
450 default:
451 if opaque(m) {
452 e.cb = cbTC16
453 } else {
454 e.cb = cbTCA16
455 }
456 }
457 }
458
459 _, e.err = io.WriteString(w, pngHeader)
460 e.writeIHDR()
461 if pal != nil {
462 e.writePLTE(pal.Palette)
463 e.maybeWritetRNS(pal.Palette)
464 }
465 e.writeIDATs()
466 e.writeIEND()
467 return e.err
468 }