1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
28 func writeUint32(b []uint8, u uint32) {
35 type opaquer interface {
39 // Returns whether or not the image is fully opaque.
40 func opaque(m image.Image) bool {
41 if o, ok := m.(opaquer); ok {
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()
56 // The absolute value of a byte interpreted as a signed int8.
57 func abs8(d uint8) int {
64 func (e *encoder) writeChunk(b []byte, name string) {
70 e.err = UnsupportedError(name + " chunk is too large: " + strconv.Itoa(len(b)))
73 writeUint32(e.header[:4], n)
78 crc := crc32.NewIEEE()
79 crc.Write(e.header[4:8])
81 writeUint32(e.footer[:4], crc.Sum32())
83 _, e.err = e.w.Write(e.header[:8])
87 _, e.err = e.w.Write(b)
91 _, e.err = e.w.Write(e.footer[:4])
94 func (e *encoder) writeIHDR() {
96 writeUint32(e.tmp[0:4], uint32(b.Dx()))
97 writeUint32(e.tmp[4:8], uint32(b.Dy()))
98 // Set bit depth and color type.
102 e.tmp[9] = ctGrayscale
105 e.tmp[9] = ctTrueColor
108 e.tmp[9] = ctPaletted
111 e.tmp[9] = ctTrueColorAlpha
114 e.tmp[9] = ctGrayscale
117 e.tmp[9] = ctTrueColor
120 e.tmp[9] = ctTrueColorAlpha
122 e.tmp[10] = 0 // default compression method
123 e.tmp[11] = 0 // default filter method
124 e.tmp[12] = 0 // non-interlaced
125 e.writeChunk(e.tmp[:13], "IHDR")
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)))
134 for i, c := range p {
135 c1 := color.NRGBAModel.Convert(c).(color.NRGBA)
142 e.tmp[3*256+i] = c1.A
144 e.writeChunk(e.tmp[:3*len(p)], "PLTE")
146 e.writeChunk(e.tmp[3*256:3*256+1+last], "tRNS")
150 // An encoder is an io.Writer that satisfies writes by writing PNG IDAT chunks,
151 // including an 8-byte header and 4-byte CRC checksum per Write call. Such calls
152 // should be relatively infrequent, since writeIDATs uses a bufio.Writer.
154 // This method should only be called from writeIDATs (via writeImage).
155 // No other code should treat an encoder as an io.Writer.
156 func (e *encoder) Write(b []byte) (int, error) {
157 e.writeChunk(b, "IDAT")
164 // Chooses the filter to use for encoding the current row, and applies it.
165 // The return value is the index of the filter and also of the row in cr that has had it applied.
166 func filter(cr *[nFilter][]byte, pr []byte, bpp int) int {
167 // We try all five filter types, and pick the one that minimizes the sum of absolute differences.
168 // This is the same heuristic that libpng uses, although the filters are attempted in order of
169 // estimated most likely to be minimal (ftUp, ftPaeth, ftNone, ftSub, ftAverage), rather than
170 // in their enumeration order (ftNone, ftSub, ftUp, ftAverage, ftPaeth).
181 for i := 0; i < n; i++ {
182 cdat2[i] = cdat0[i] - pdat[i]
183 sum += abs8(cdat2[i])
190 for i := 0; i < bpp; i++ {
191 cdat4[i] = cdat0[i] - paeth(0, pdat[i], 0)
192 sum += abs8(cdat4[i])
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])
208 for i := 0; i < n; i++ {
209 sum += abs8(cdat0[i])
221 for i := 0; i < bpp; i++ {
223 sum += abs8(cdat1[i])
225 for i := bpp; i < n; i++ {
226 cdat1[i] = cdat0[i] - cdat0[i-bpp]
227 sum += abs8(cdat1[i])
237 // The average filter.
239 for i := 0; i < bpp; i++ {
240 cdat3[i] = cdat0[i] - pdat[i]/2
241 sum += abs8(cdat3[i])
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])
258 func writeImage(w io.Writer, m image.Image, cb int) error {
259 zw := zlib.NewWriter(w)
262 bpp := 0 // Bytes per pixel.
280 // cr[*] and pr are the bytes for the current and previous row.
281 // cr[0] is unfiltered (or equivalently, filtered with the ftNone filter).
282 // cr[ft], for non-zero filter types ft, are buffers for transforming cr[0] under the
283 // other PNG filter types. These buffers are allocated once and re-used for each row.
284 // The +1 is for the per-row filter type, which is at cr[*][0].
286 var cr [nFilter][]uint8
288 cr[i] = make([]uint8, 1+bpp*b.Dx())
291 pr := make([]uint8, 1+bpp*b.Dx())
293 gray, _ := m.(*image.Gray)
294 rgba, _ := m.(*image.RGBA)
295 paletted, _ := m.(*image.Paletted)
296 nrgba, _ := m.(*image.NRGBA)
298 for y := b.Min.Y; y < b.Max.Y; y++ {
299 // Convert from colors to bytes.
304 offset := (y - b.Min.Y) * gray.Stride
305 copy(cr[0][1:], gray.Pix[offset:offset+b.Dx()])
307 for x := b.Min.X; x < b.Max.X; x++ {
308 c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
314 // We have previously verified that the alpha value is fully opaque.
316 stride, pix := 0, []byte(nil)
318 stride, pix = rgba.Stride, rgba.Pix
319 } else if nrgba != nil {
320 stride, pix = nrgba.Stride, nrgba.Pix
323 j0 := (y - b.Min.Y) * stride
325 for j := j0; j < j1; j += 4 {
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)
342 offset := (y - b.Min.Y) * paletted.Stride
343 copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
345 pi := m.(image.PalettedImage)
346 for x := b.Min.X; x < b.Max.X; x++ {
347 cr[0][i] = pi.ColorIndexAt(x, y)
353 offset := (y - b.Min.Y) * nrgba.Stride
354 copy(cr[0][1:], nrgba.Pix[offset:offset+b.Dx()*4])
356 // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
357 for x := b.Min.X; x < b.Max.X; x++ {
358 c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA)
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)
374 // We have previously verified that the alpha value is fully opaque.
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)
386 // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
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)
402 f := filter(&cr, pr, bpp)
404 // Write the compressed bytes.
405 if _, err := zw.Write(cr[f]); err != nil {
409 // The current row for y is the previous row for y+1.
410 pr, cr[0] = cr[0], pr
415 // Write the actual image data to one or more IDAT chunks.
416 func (e *encoder) writeIDATs() {
421 bw = bufio.NewWriterSize(e, 1<<15)
422 e.err = writeImage(bw, e.m, e.cb)
429 func (e *encoder) writeIEND() { e.writeChunk(nil, "IEND") }
431 // Encode writes the Image m to w in PNG format. Any Image may be encoded, but
432 // images that are not image.NRGBA might be encoded lossily.
433 func Encode(w io.Writer, m image.Image) error {
434 // Obviously, negative widths and heights are invalid. Furthermore, the PNG
435 // spec section 11.2.2 says that zero is invalid. Excessively large images are
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(mw, 10))
446 var pal color.Palette
447 // cbP8 encoding needs PalettedImage's ColorIndexAt method.
448 if _, ok := m.(image.PalettedImage); ok {
449 pal, _ = m.ColorModel().(color.Palette)
454 switch m.ColorModel() {
455 case color.GrayModel:
457 case color.Gray16Model:
459 case color.RGBAModel, color.NRGBAModel, color.AlphaModel:
474 _, e.err = io.WriteString(w, pngHeader)
477 e.writePLTEAndTRNS(pal)