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.
17 // Encoder configures encoding PNG images.
19 CompressionLevel CompressionLevel
33 type CompressionLevel
int
36 DefaultCompression CompressionLevel
= 0
37 NoCompression CompressionLevel
= -1
38 BestSpeed CompressionLevel
= -2
39 BestCompression CompressionLevel
= -3
41 // Positive CompressionLevel values are reserved to mean a numeric zlib
42 // compression level, although that is not implemented yet.
46 func writeUint32(b
[]uint8, u
uint32) {
53 type opaquer
interface {
57 // Returns whether or not the image is fully opaque.
58 func opaque(m image
.Image
) bool {
59 if o
, ok
:= m
.(opaquer
); ok
{
63 for y
:= b
.Min
.Y
; y
< b
.Max
.Y
; y
++ {
64 for x
:= b
.Min
.X
; x
< b
.Max
.X
; x
++ {
65 _
, _
, _
, a
:= m
.At(x
, y
).RGBA()
74 // The absolute value of a byte interpreted as a signed int8.
75 func abs8(d
uint8) int {
82 func (e
*encoder
) writeChunk(b
[]byte, name
string) {
88 e
.err
= UnsupportedError(name
+ " chunk is too large: " + strconv
.Itoa(len(b
)))
91 writeUint32(e
.header
[:4], n
)
96 crc
:= crc32
.NewIEEE()
97 crc
.Write(e
.header
[4:8])
99 writeUint32(e
.footer
[:4], crc
.Sum32())
101 _
, e
.err
= e
.w
.Write(e
.header
[:8])
105 _
, e
.err
= e
.w
.Write(b
)
109 _
, e
.err
= e
.w
.Write(e
.footer
[:4])
112 func (e
*encoder
) writeIHDR() {
114 writeUint32(e
.tmp
[0:4], uint32(b
.Dx()))
115 writeUint32(e
.tmp
[4:8], uint32(b
.Dy()))
116 // Set bit depth and color type.
120 e
.tmp
[9] = ctGrayscale
123 e
.tmp
[9] = ctTrueColor
126 e
.tmp
[9] = ctPaletted
129 e
.tmp
[9] = ctTrueColorAlpha
132 e
.tmp
[9] = ctGrayscale
135 e
.tmp
[9] = ctTrueColor
138 e
.tmp
[9] = ctTrueColorAlpha
140 e
.tmp
[10] = 0 // default compression method
141 e
.tmp
[11] = 0 // default filter method
142 e
.tmp
[12] = 0 // non-interlaced
143 e
.writeChunk(e
.tmp
[:13], "IHDR")
146 func (e
*encoder
) writePLTEAndTRNS(p color
.Palette
) {
147 if len(p
) < 1 ||
len(p
) > 256 {
148 e
.err
= FormatError("bad palette length: " + strconv
.Itoa(len(p
)))
152 for i
, c
:= range p
{
153 c1
:= color
.NRGBAModel
.Convert(c
).(color
.NRGBA
)
160 e
.tmp
[3*256+i
] = c1
.A
162 e
.writeChunk(e
.tmp
[:3*len(p
)], "PLTE")
164 e
.writeChunk(e
.tmp
[3*256:3*256+1+last
], "tRNS")
168 // An encoder is an io.Writer that satisfies writes by writing PNG IDAT chunks,
169 // including an 8-byte header and 4-byte CRC checksum per Write call. Such calls
170 // should be relatively infrequent, since writeIDATs uses a bufio.Writer.
172 // This method should only be called from writeIDATs (via writeImage).
173 // No other code should treat an encoder as an io.Writer.
174 func (e
*encoder
) Write(b
[]byte) (int, error
) {
175 e
.writeChunk(b
, "IDAT")
182 // Chooses the filter to use for encoding the current row, and applies it.
183 // The return value is the index of the filter and also of the row in cr that has had it applied.
184 func filter(cr
*[nFilter
][]byte, pr
[]byte, bpp
int) int {
185 // We try all five filter types, and pick the one that minimizes the sum of absolute differences.
186 // This is the same heuristic that libpng uses, although the filters are attempted in order of
187 // estimated most likely to be minimal (ftUp, ftPaeth, ftNone, ftSub, ftAverage), rather than
188 // in their enumeration order (ftNone, ftSub, ftUp, ftAverage, ftPaeth).
199 for i
:= 0; i
< n
; i
++ {
200 cdat2
[i
] = cdat0
[i
] - pdat
[i
]
201 sum
+= abs8(cdat2
[i
])
208 for i
:= 0; i
< bpp
; i
++ {
209 cdat4
[i
] = cdat0
[i
] - pdat
[i
]
210 sum
+= abs8(cdat4
[i
])
212 for i
:= bpp
; i
< n
; i
++ {
213 cdat4
[i
] = cdat0
[i
] - paeth(cdat0
[i
-bpp
], pdat
[i
], pdat
[i
-bpp
])
214 sum
+= abs8(cdat4
[i
])
226 for i
:= 0; i
< n
; i
++ {
227 sum
+= abs8(cdat0
[i
])
239 for i
:= 0; i
< bpp
; i
++ {
241 sum
+= abs8(cdat1
[i
])
243 for i
:= bpp
; i
< n
; i
++ {
244 cdat1
[i
] = cdat0
[i
] - cdat0
[i
-bpp
]
245 sum
+= abs8(cdat1
[i
])
255 // The average filter.
257 for i
:= 0; i
< bpp
; i
++ {
258 cdat3
[i
] = cdat0
[i
] - pdat
[i
]/2
259 sum
+= abs8(cdat3
[i
])
261 for i
:= bpp
; i
< n
; i
++ {
262 cdat3
[i
] = cdat0
[i
] - uint8((int(cdat0
[i
-bpp
])+int(pdat
[i
]))/2)
263 sum
+= abs8(cdat3
[i
])
276 func writeImage(w io
.Writer
, m image
.Image
, cb
int, level
int) error
{
277 zw
, err
:= zlib
.NewWriterLevel(w
, level
)
283 bpp
:= 0 // Bytes per pixel.
301 // cr[*] and pr are the bytes for the current and previous row.
302 // cr[0] is unfiltered (or equivalently, filtered with the ftNone filter).
303 // cr[ft], for non-zero filter types ft, are buffers for transforming cr[0] under the
304 // other PNG filter types. These buffers are allocated once and re-used for each row.
305 // The +1 is for the per-row filter type, which is at cr[*][0].
307 var cr
[nFilter
][]uint8
309 cr
[i
] = make([]uint8, 1+bpp
*b
.Dx())
312 pr
:= make([]uint8, 1+bpp
*b
.Dx())
314 gray
, _
:= m
.(*image
.Gray
)
315 rgba
, _
:= m
.(*image
.RGBA
)
316 paletted
, _
:= m
.(*image
.Paletted
)
317 nrgba
, _
:= m
.(*image
.NRGBA
)
319 for y
:= b
.Min
.Y
; y
< b
.Max
.Y
; y
++ {
320 // Convert from colors to bytes.
325 offset
:= (y
- b
.Min
.Y
) * gray
.Stride
326 copy(cr
[0][1:], gray
.Pix
[offset
:offset
+b
.Dx()])
328 for x
:= b
.Min
.X
; x
< b
.Max
.X
; x
++ {
329 c
:= color
.GrayModel
.Convert(m
.At(x
, y
)).(color
.Gray
)
335 // We have previously verified that the alpha value is fully opaque.
337 stride
, pix
:= 0, []byte(nil)
339 stride
, pix
= rgba
.Stride
, rgba
.Pix
340 } else if nrgba
!= nil {
341 stride
, pix
= nrgba
.Stride
, nrgba
.Pix
344 j0
:= (y
- b
.Min
.Y
) * stride
346 for j
:= j0
; j
< j1
; j
+= 4 {
353 for x
:= b
.Min
.X
; x
< b
.Max
.X
; x
++ {
354 r
, g
, b
, _
:= m
.At(x
, y
).RGBA()
355 cr0
[i
+0] = uint8(r
>> 8)
356 cr0
[i
+1] = uint8(g
>> 8)
357 cr0
[i
+2] = uint8(b
>> 8)
363 offset
:= (y
- b
.Min
.Y
) * paletted
.Stride
364 copy(cr
[0][1:], paletted
.Pix
[offset
:offset
+b
.Dx()])
366 pi
:= m
.(image
.PalettedImage
)
367 for x
:= b
.Min
.X
; x
< b
.Max
.X
; x
++ {
368 cr
[0][i
] = pi
.ColorIndexAt(x
, y
)
374 offset
:= (y
- b
.Min
.Y
) * nrgba
.Stride
375 copy(cr
[0][1:], nrgba
.Pix
[offset
:offset
+b
.Dx()*4])
377 // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
378 for x
:= b
.Min
.X
; x
< b
.Max
.X
; x
++ {
379 c
:= color
.NRGBAModel
.Convert(m
.At(x
, y
)).(color
.NRGBA
)
388 for x
:= b
.Min
.X
; x
< b
.Max
.X
; x
++ {
389 c
:= color
.Gray16Model
.Convert(m
.At(x
, y
)).(color
.Gray16
)
390 cr
[0][i
+0] = uint8(c
.Y
>> 8)
391 cr
[0][i
+1] = uint8(c
.Y
)
395 // We have previously verified that the alpha value is fully opaque.
396 for x
:= b
.Min
.X
; x
< b
.Max
.X
; x
++ {
397 r
, g
, b
, _
:= m
.At(x
, y
).RGBA()
398 cr
[0][i
+0] = uint8(r
>> 8)
399 cr
[0][i
+1] = uint8(r
)
400 cr
[0][i
+2] = uint8(g
>> 8)
401 cr
[0][i
+3] = uint8(g
)
402 cr
[0][i
+4] = uint8(b
>> 8)
403 cr
[0][i
+5] = uint8(b
)
407 // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
408 for x
:= b
.Min
.X
; x
< b
.Max
.X
; x
++ {
409 c
:= color
.NRGBA64Model
.Convert(m
.At(x
, y
)).(color
.NRGBA64
)
410 cr
[0][i
+0] = uint8(c
.R
>> 8)
411 cr
[0][i
+1] = uint8(c
.R
)
412 cr
[0][i
+2] = uint8(c
.G
>> 8)
413 cr
[0][i
+3] = uint8(c
.G
)
414 cr
[0][i
+4] = uint8(c
.B
>> 8)
415 cr
[0][i
+5] = uint8(c
.B
)
416 cr
[0][i
+6] = uint8(c
.A
>> 8)
417 cr
[0][i
+7] = uint8(c
.A
)
423 // Skip filter for NoCompression and paletted images (cbP8) as
424 // "filters are rarely useful on palette images" and will result
425 // in larger files (see http://www.libpng.org/pub/png/book/chapter09.html).
427 if level
!= zlib
.NoCompression
&& cb
!= cbP8
{
428 f
= filter(&cr
, pr
, bpp
)
431 // Write the compressed bytes.
432 if _
, err
:= zw
.Write(cr
[f
]); err
!= nil {
436 // The current row for y is the previous row for y+1.
437 pr
, cr
[0] = cr
[0], pr
442 // Write the actual image data to one or more IDAT chunks.
443 func (e
*encoder
) writeIDATs() {
448 bw
= bufio
.NewWriterSize(e
, 1<<15)
449 e
.err
= writeImage(bw
, e
.m
, e
.cb
, levelToZlib(e
.enc
.CompressionLevel
))
456 // This function is required because we want the zero value of
457 // Encoder.CompressionLevel to map to zlib.DefaultCompression.
458 func levelToZlib(l CompressionLevel
) int {
460 case DefaultCompression
:
461 return zlib
.DefaultCompression
463 return zlib
.NoCompression
465 return zlib
.BestSpeed
466 case BestCompression
:
467 return zlib
.BestCompression
469 return zlib
.DefaultCompression
473 func (e
*encoder
) writeIEND() { e
.writeChunk(nil, "IEND") }
475 // Encode writes the Image m to w in PNG format. Any Image may be
476 // encoded, but images that are not image.NRGBA might be encoded lossily.
477 func Encode(w io
.Writer
, m image
.Image
) error
{
479 return e
.Encode(w
, m
)
482 // Encode writes the Image m to w in PNG format.
483 func (enc
*Encoder
) Encode(w io
.Writer
, m image
.Image
) error
{
484 // Obviously, negative widths and heights are invalid. Furthermore, the PNG
485 // spec section 11.2.2 says that zero is invalid. Excessively large images are
487 mw
, mh
:= int64(m
.Bounds().Dx()), int64(m
.Bounds().Dy())
488 if mw
<= 0 || mh
<= 0 || mw
>= 1<<32 || mh
>= 1<<32 {
489 return FormatError("invalid image size: " + strconv
.FormatInt(mw
, 10) + "x" + strconv
.FormatInt(mh
, 10))
497 var pal color
.Palette
498 // cbP8 encoding needs PalettedImage's ColorIndexAt method.
499 if _
, ok
:= m
.(image
.PalettedImage
); ok
{
500 pal
, _
= m
.ColorModel().(color
.Palette
)
505 switch m
.ColorModel() {
506 case color
.GrayModel
:
508 case color
.Gray16Model
:
510 case color
.RGBAModel
, color
.NRGBAModel
, color
.AlphaModel
:
525 _
, e
.err
= io
.WriteString(w
, pngHeader
)
528 e
.writePLTEAndTRNS(pal
)