github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/image/png/writer.go (about)

     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.
     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  // Encoder configures encoding PNG images.
    18  type Encoder struct {
    19  	CompressionLevel CompressionLevel
    20  }
    21  
    22  type encoder struct {
    23  	enc    *Encoder
    24  	w      io.Writer
    25  	m      image.Image
    26  	cb     int
    27  	err    error
    28  	header [8]byte
    29  	footer [4]byte
    30  	tmp    [4 * 256]byte
    31  }
    32  
    33  type CompressionLevel int
    34  
    35  const (
    36  	DefaultCompression CompressionLevel = 0
    37  	NoCompression      CompressionLevel = -1
    38  	BestSpeed          CompressionLevel = -2
    39  	BestCompression    CompressionLevel = -3
    40  
    41  	// Positive CompressionLevel values are reserved to mean a numeric zlib
    42  	// compression level, although that is not implemented yet.
    43  )
    44  
    45  // Big-endian.
    46  func writeUint32(b []uint8, u uint32) {
    47  	b[0] = uint8(u >> 24)
    48  	b[1] = uint8(u >> 16)
    49  	b[2] = uint8(u >> 8)
    50  	b[3] = uint8(u >> 0)
    51  }
    52  
    53  type opaquer interface {
    54  	Opaque() bool
    55  }
    56  
    57  // Returns whether or not the image is fully opaque.
    58  func opaque(m image.Image) bool {
    59  	if o, ok := m.(opaquer); ok {
    60  		return o.Opaque()
    61  	}
    62  	b := m.Bounds()
    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()
    66  			if a != 0xffff {
    67  				return false
    68  			}
    69  		}
    70  	}
    71  	return true
    72  }
    73  
    74  // The absolute value of a byte interpreted as a signed int8.
    75  func abs8(d uint8) int {
    76  	if d < 128 {
    77  		return int(d)
    78  	}
    79  	return 256 - int(d)
    80  }
    81  
    82  func (e *encoder) writeChunk(b []byte, name string) {
    83  	if e.err != nil {
    84  		return
    85  	}
    86  	n := uint32(len(b))
    87  	if int(n) != len(b) {
    88  		e.err = UnsupportedError(name + " chunk is too large: " + strconv.Itoa(len(b)))
    89  		return
    90  	}
    91  	writeUint32(e.header[:4], n)
    92  	e.header[4] = name[0]
    93  	e.header[5] = name[1]
    94  	e.header[6] = name[2]
    95  	e.header[7] = name[3]
    96  	crc := crc32.NewIEEE()
    97  	crc.Write(e.header[4:8])
    98  	crc.Write(b)
    99  	writeUint32(e.footer[:4], crc.Sum32())
   100  
   101  	_, e.err = e.w.Write(e.header[:8])
   102  	if e.err != nil {
   103  		return
   104  	}
   105  	_, e.err = e.w.Write(b)
   106  	if e.err != nil {
   107  		return
   108  	}
   109  	_, e.err = e.w.Write(e.footer[:4])
   110  }
   111  
   112  func (e *encoder) writeIHDR() {
   113  	b := e.m.Bounds()
   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.
   117  	switch e.cb {
   118  	case cbG8:
   119  		e.tmp[8] = 8
   120  		e.tmp[9] = ctGrayscale
   121  	case cbTC8:
   122  		e.tmp[8] = 8
   123  		e.tmp[9] = ctTrueColor
   124  	case cbP8:
   125  		e.tmp[8] = 8
   126  		e.tmp[9] = ctPaletted
   127  	case cbTCA8:
   128  		e.tmp[8] = 8
   129  		e.tmp[9] = ctTrueColorAlpha
   130  	case cbG16:
   131  		e.tmp[8] = 16
   132  		e.tmp[9] = ctGrayscale
   133  	case cbTC16:
   134  		e.tmp[8] = 16
   135  		e.tmp[9] = ctTrueColor
   136  	case cbTCA16:
   137  		e.tmp[8] = 16
   138  		e.tmp[9] = ctTrueColorAlpha
   139  	}
   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")
   144  }
   145  
   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)))
   149  		return
   150  	}
   151  	last := -1
   152  	for i, c := range p {
   153  		c1 := color.NRGBAModel.Convert(c).(color.NRGBA)
   154  		e.tmp[3*i+0] = c1.R
   155  		e.tmp[3*i+1] = c1.G
   156  		e.tmp[3*i+2] = c1.B
   157  		if c1.A != 0xff {
   158  			last = i
   159  		}
   160  		e.tmp[3*256+i] = c1.A
   161  	}
   162  	e.writeChunk(e.tmp[:3*len(p)], "PLTE")
   163  	if last != -1 {
   164  		e.writeChunk(e.tmp[3*256:3*256+1+last], "tRNS")
   165  	}
   166  }
   167  
   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.
   171  //
   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")
   176  	if e.err != nil {
   177  		return 0, e.err
   178  	}
   179  	return len(b), nil
   180  }
   181  
   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).
   189  	cdat0 := cr[0][1:]
   190  	cdat1 := cr[1][1:]
   191  	cdat2 := cr[2][1:]
   192  	cdat3 := cr[3][1:]
   193  	cdat4 := cr[4][1:]
   194  	pdat := pr[1:]
   195  	n := len(cdat0)
   196  
   197  	// The up filter.
   198  	sum := 0
   199  	for i := 0; i < n; i++ {
   200  		cdat2[i] = cdat0[i] - pdat[i]
   201  		sum += abs8(cdat2[i])
   202  	}
   203  	best := sum
   204  	filter := ftUp
   205  
   206  	// The Paeth filter.
   207  	sum = 0
   208  	for i := 0; i < bpp; i++ {
   209  		cdat4[i] = cdat0[i] - pdat[i]
   210  		sum += abs8(cdat4[i])
   211  	}
   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])
   215  		if sum >= best {
   216  			break
   217  		}
   218  	}
   219  	if sum < best {
   220  		best = sum
   221  		filter = ftPaeth
   222  	}
   223  
   224  	// The none filter.
   225  	sum = 0
   226  	for i := 0; i < n; i++ {
   227  		sum += abs8(cdat0[i])
   228  		if sum >= best {
   229  			break
   230  		}
   231  	}
   232  	if sum < best {
   233  		best = sum
   234  		filter = ftNone
   235  	}
   236  
   237  	// The sub filter.
   238  	sum = 0
   239  	for i := 0; i < bpp; i++ {
   240  		cdat1[i] = cdat0[i]
   241  		sum += abs8(cdat1[i])
   242  	}
   243  	for i := bpp; i < n; i++ {
   244  		cdat1[i] = cdat0[i] - cdat0[i-bpp]
   245  		sum += abs8(cdat1[i])
   246  		if sum >= best {
   247  			break
   248  		}
   249  	}
   250  	if sum < best {
   251  		best = sum
   252  		filter = ftSub
   253  	}
   254  
   255  	// The average filter.
   256  	sum = 0
   257  	for i := 0; i < bpp; i++ {
   258  		cdat3[i] = cdat0[i] - pdat[i]/2
   259  		sum += abs8(cdat3[i])
   260  	}
   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])
   264  		if sum >= best {
   265  			break
   266  		}
   267  	}
   268  	if sum < best {
   269  		best = sum
   270  		filter = ftAverage
   271  	}
   272  
   273  	return filter
   274  }
   275  
   276  func writeImage(w io.Writer, m image.Image, cb int, level int) error {
   277  	zw, err := zlib.NewWriterLevel(w, level)
   278  	if err != nil {
   279  		return err
   280  	}
   281  	defer zw.Close()
   282  
   283  	bpp := 0 // Bytes per pixel.
   284  
   285  	switch cb {
   286  	case cbG8:
   287  		bpp = 1
   288  	case cbTC8:
   289  		bpp = 3
   290  	case cbP8:
   291  		bpp = 1
   292  	case cbTCA8:
   293  		bpp = 4
   294  	case cbTC16:
   295  		bpp = 6
   296  	case cbTCA16:
   297  		bpp = 8
   298  	case cbG16:
   299  		bpp = 2
   300  	}
   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].
   306  	b := m.Bounds()
   307  	var cr [nFilter][]uint8
   308  	for i := range cr {
   309  		cr[i] = make([]uint8, 1+bpp*b.Dx())
   310  		cr[i][0] = uint8(i)
   311  	}
   312  	pr := make([]uint8, 1+bpp*b.Dx())
   313  
   314  	gray, _ := m.(*image.Gray)
   315  	rgba, _ := m.(*image.RGBA)
   316  	paletted, _ := m.(*image.Paletted)
   317  	nrgba, _ := m.(*image.NRGBA)
   318  
   319  	for y := b.Min.Y; y < b.Max.Y; y++ {
   320  		// Convert from colors to bytes.
   321  		i := 1
   322  		switch cb {
   323  		case cbG8:
   324  			if gray != nil {
   325  				offset := (y - b.Min.Y) * gray.Stride
   326  				copy(cr[0][1:], gray.Pix[offset:offset+b.Dx()])
   327  			} else {
   328  				for x := b.Min.X; x < b.Max.X; x++ {
   329  					c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
   330  					cr[0][i] = c.Y
   331  					i++
   332  				}
   333  			}
   334  		case cbTC8:
   335  			// We have previously verified that the alpha value is fully opaque.
   336  			cr0 := cr[0]
   337  			stride, pix := 0, []byte(nil)
   338  			if rgba != nil {
   339  				stride, pix = rgba.Stride, rgba.Pix
   340  			} else if nrgba != nil {
   341  				stride, pix = nrgba.Stride, nrgba.Pix
   342  			}
   343  			if stride != 0 {
   344  				j0 := (y - b.Min.Y) * stride
   345  				j1 := j0 + b.Dx()*4
   346  				for j := j0; j < j1; j += 4 {
   347  					cr0[i+0] = pix[j+0]
   348  					cr0[i+1] = pix[j+1]
   349  					cr0[i+2] = pix[j+2]
   350  					i += 3
   351  				}
   352  			} else {
   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)
   358  					i += 3
   359  				}
   360  			}
   361  		case cbP8:
   362  			if paletted != nil {
   363  				offset := (y - b.Min.Y) * paletted.Stride
   364  				copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
   365  			} else {
   366  				pi := m.(image.PalettedImage)
   367  				for x := b.Min.X; x < b.Max.X; x++ {
   368  					cr[0][i] = pi.ColorIndexAt(x, y)
   369  					i += 1
   370  				}
   371  			}
   372  		case cbTCA8:
   373  			if nrgba != nil {
   374  				offset := (y - b.Min.Y) * nrgba.Stride
   375  				copy(cr[0][1:], nrgba.Pix[offset:offset+b.Dx()*4])
   376  			} else {
   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)
   380  					cr[0][i+0] = c.R
   381  					cr[0][i+1] = c.G
   382  					cr[0][i+2] = c.B
   383  					cr[0][i+3] = c.A
   384  					i += 4
   385  				}
   386  			}
   387  		case cbG16:
   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)
   392  				i += 2
   393  			}
   394  		case cbTC16:
   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)
   404  				i += 6
   405  			}
   406  		case cbTCA16:
   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)
   418  				i += 8
   419  			}
   420  		}
   421  
   422  		// Apply the filter.
   423  		f := ftNone
   424  		if level != zlib.NoCompression {
   425  			f = filter(&cr, pr, bpp)
   426  		}
   427  
   428  		// Write the compressed bytes.
   429  		if _, err := zw.Write(cr[f]); err != nil {
   430  			return err
   431  		}
   432  
   433  		// The current row for y is the previous row for y+1.
   434  		pr, cr[0] = cr[0], pr
   435  	}
   436  	return nil
   437  }
   438  
   439  // Write the actual image data to one or more IDAT chunks.
   440  func (e *encoder) writeIDATs() {
   441  	if e.err != nil {
   442  		return
   443  	}
   444  	var bw *bufio.Writer
   445  	bw = bufio.NewWriterSize(e, 1<<15)
   446  	e.err = writeImage(bw, e.m, e.cb, levelToZlib(e.enc.CompressionLevel))
   447  	if e.err != nil {
   448  		return
   449  	}
   450  	e.err = bw.Flush()
   451  }
   452  
   453  // This function is required because we want the zero value of
   454  // Encoder.CompressionLevel to map to zlib.DefaultCompression.
   455  func levelToZlib(l CompressionLevel) int {
   456  	switch l {
   457  	case DefaultCompression:
   458  		return zlib.DefaultCompression
   459  	case NoCompression:
   460  		return zlib.NoCompression
   461  	case BestSpeed:
   462  		return zlib.BestSpeed
   463  	case BestCompression:
   464  		return zlib.BestCompression
   465  	default:
   466  		return zlib.DefaultCompression
   467  	}
   468  }
   469  
   470  func (e *encoder) writeIEND() { e.writeChunk(nil, "IEND") }
   471  
   472  // Encode writes the Image m to w in PNG format. Any Image may be
   473  // encoded, but images that are not image.NRGBA might be encoded lossily.
   474  func Encode(w io.Writer, m image.Image) error {
   475  	var e Encoder
   476  	return e.Encode(w, m)
   477  }
   478  
   479  // Encode writes the Image m to w in PNG format.
   480  func (enc *Encoder) Encode(w io.Writer, m image.Image) error {
   481  	// Obviously, negative widths and heights are invalid. Furthermore, the PNG
   482  	// spec section 11.2.2 says that zero is invalid. Excessively large images are
   483  	// also rejected.
   484  	mw, mh := int64(m.Bounds().Dx()), int64(m.Bounds().Dy())
   485  	if mw <= 0 || mh <= 0 || mw >= 1<<32 || mh >= 1<<32 {
   486  		return FormatError("invalid image size: " + strconv.FormatInt(mw, 10) + "x" + strconv.FormatInt(mh, 10))
   487  	}
   488  
   489  	var e encoder
   490  	e.enc = enc
   491  	e.w = w
   492  	e.m = m
   493  
   494  	var pal color.Palette
   495  	// cbP8 encoding needs PalettedImage's ColorIndexAt method.
   496  	if _, ok := m.(image.PalettedImage); ok {
   497  		pal, _ = m.ColorModel().(color.Palette)
   498  	}
   499  	if pal != nil {
   500  		e.cb = cbP8
   501  	} else {
   502  		switch m.ColorModel() {
   503  		case color.GrayModel:
   504  			e.cb = cbG8
   505  		case color.Gray16Model:
   506  			e.cb = cbG16
   507  		case color.RGBAModel, color.NRGBAModel, color.AlphaModel:
   508  			if opaque(m) {
   509  				e.cb = cbTC8
   510  			} else {
   511  				e.cb = cbTCA8
   512  			}
   513  		default:
   514  			if opaque(m) {
   515  				e.cb = cbTC16
   516  			} else {
   517  				e.cb = cbTCA16
   518  			}
   519  		}
   520  	}
   521  
   522  	_, e.err = io.WriteString(w, pngHeader)
   523  	e.writeIHDR()
   524  	if pal != nil {
   525  		e.writePLTEAndTRNS(pal)
   526  	}
   527  	e.writeIDATs()
   528  	e.writeIEND()
   529  	return e.err
   530  }