Source file src/image/png/writer.go

     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  	"encoding/binary"
    11  	"hash/crc32"
    12  	"image"
    13  	"image/color"
    14  	"io"
    15  	"strconv"
    16  )
    17  
    18  // Encoder configures encoding PNG images.
    19  type Encoder struct {
    20  	CompressionLevel CompressionLevel
    21  
    22  	// BufferPool optionally specifies a buffer pool to get temporary
    23  	// EncoderBuffers when encoding an image.
    24  	BufferPool EncoderBufferPool
    25  }
    26  
    27  // EncoderBufferPool is an interface for getting and returning temporary
    28  // instances of the [EncoderBuffer] struct. This can be used to reuse buffers
    29  // when encoding multiple images.
    30  type EncoderBufferPool interface {
    31  	Get() *EncoderBuffer
    32  	Put(*EncoderBuffer)
    33  }
    34  
    35  // EncoderBuffer holds the buffers used for encoding PNG images.
    36  type EncoderBuffer encoder
    37  
    38  type encoder struct {
    39  	enc     *Encoder
    40  	w       io.Writer
    41  	m       image.Image
    42  	cb      int
    43  	err     error
    44  	header  [8]byte
    45  	footer  [4]byte
    46  	tmp     [4 * 256]byte
    47  	cr      [nFilter][]uint8
    48  	pr      []uint8
    49  	zw      *zlib.Writer
    50  	zwLevel int
    51  	bw      *bufio.Writer
    52  }
    53  
    54  // CompressionLevel indicates the compression level.
    55  type CompressionLevel int
    56  
    57  const (
    58  	DefaultCompression CompressionLevel = 0
    59  	NoCompression      CompressionLevel = -1
    60  	BestSpeed          CompressionLevel = -2
    61  	BestCompression    CompressionLevel = -3
    62  
    63  	// Positive CompressionLevel values are reserved to mean a numeric zlib
    64  	// compression level, although that is not implemented yet.
    65  )
    66  
    67  type opaquer interface {
    68  	Opaque() bool
    69  }
    70  
    71  // Returns whether or not the image is fully opaque.
    72  func opaque(m image.Image) bool {
    73  	if o, ok := m.(opaquer); ok {
    74  		return o.Opaque()
    75  	}
    76  	b := m.Bounds()
    77  	for y := b.Min.Y; y < b.Max.Y; y++ {
    78  		for x := b.Min.X; x < b.Max.X; x++ {
    79  			_, _, _, a := m.At(x, y).RGBA()
    80  			if a != 0xffff {
    81  				return false
    82  			}
    83  		}
    84  	}
    85  	return true
    86  }
    87  
    88  // The absolute value of a byte interpreted as a signed int8.
    89  func abs8(d uint8) int {
    90  	if d < 128 {
    91  		return int(d)
    92  	}
    93  	return 256 - int(d)
    94  }
    95  
    96  func (e *encoder) writeChunk(b []byte, name string) {
    97  	if e.err != nil {
    98  		return
    99  	}
   100  	n := uint32(len(b))
   101  	if int(n) != len(b) {
   102  		e.err = UnsupportedError(name + " chunk is too large: " + strconv.Itoa(len(b)))
   103  		return
   104  	}
   105  	binary.BigEndian.PutUint32(e.header[:4], n)
   106  	e.header[4] = name[0]
   107  	e.header[5] = name[1]
   108  	e.header[6] = name[2]
   109  	e.header[7] = name[3]
   110  	crc := crc32.NewIEEE()
   111  	crc.Write(e.header[4:8])
   112  	crc.Write(b)
   113  	binary.BigEndian.PutUint32(e.footer[:4], crc.Sum32())
   114  
   115  	_, e.err = e.w.Write(e.header[:8])
   116  	if e.err != nil {
   117  		return
   118  	}
   119  	_, e.err = e.w.Write(b)
   120  	if e.err != nil {
   121  		return
   122  	}
   123  	_, e.err = e.w.Write(e.footer[:4])
   124  }
   125  
   126  func (e *encoder) writeIHDR() {
   127  	b := e.m.Bounds()
   128  	binary.BigEndian.PutUint32(e.tmp[0:4], uint32(b.Dx()))
   129  	binary.BigEndian.PutUint32(e.tmp[4:8], uint32(b.Dy()))
   130  	// Set bit depth and color type.
   131  	switch e.cb {
   132  	case cbG8:
   133  		e.tmp[8] = 8
   134  		e.tmp[9] = ctGrayscale
   135  	case cbTC8:
   136  		e.tmp[8] = 8
   137  		e.tmp[9] = ctTrueColor
   138  	case cbP8:
   139  		e.tmp[8] = 8
   140  		e.tmp[9] = ctPaletted
   141  	case cbP4:
   142  		e.tmp[8] = 4
   143  		e.tmp[9] = ctPaletted
   144  	case cbP2:
   145  		e.tmp[8] = 2
   146  		e.tmp[9] = ctPaletted
   147  	case cbP1:
   148  		e.tmp[8] = 1
   149  		e.tmp[9] = ctPaletted
   150  	case cbTCA8:
   151  		e.tmp[8] = 8
   152  		e.tmp[9] = ctTrueColorAlpha
   153  	case cbG16:
   154  		e.tmp[8] = 16
   155  		e.tmp[9] = ctGrayscale
   156  	case cbTC16:
   157  		e.tmp[8] = 16
   158  		e.tmp[9] = ctTrueColor
   159  	case cbTCA16:
   160  		e.tmp[8] = 16
   161  		e.tmp[9] = ctTrueColorAlpha
   162  	}
   163  	e.tmp[10] = 0 // default compression method
   164  	e.tmp[11] = 0 // default filter method
   165  	e.tmp[12] = 0 // non-interlaced
   166  	e.writeChunk(e.tmp[:13], "IHDR")
   167  }
   168  
   169  func (e *encoder) writePLTEAndTRNS(p color.Palette) {
   170  	if len(p) < 1 || len(p) > 256 {
   171  		e.err = FormatError("bad palette length: " + strconv.Itoa(len(p)))
   172  		return
   173  	}
   174  	last := -1
   175  	for i, c := range p {
   176  		c1 := color.NRGBAModel.Convert(c).(color.NRGBA)
   177  		e.tmp[3*i+0] = c1.R
   178  		e.tmp[3*i+1] = c1.G
   179  		e.tmp[3*i+2] = c1.B
   180  		if c1.A != 0xff {
   181  			last = i
   182  		}
   183  		e.tmp[3*256+i] = c1.A
   184  	}
   185  	e.writeChunk(e.tmp[:3*len(p)], "PLTE")
   186  	if last != -1 {
   187  		e.writeChunk(e.tmp[3*256:3*256+1+last], "tRNS")
   188  	}
   189  }
   190  
   191  // An encoder is an io.Writer that satisfies writes by writing PNG IDAT chunks,
   192  // including an 8-byte header and 4-byte CRC checksum per Write call. Such calls
   193  // should be relatively infrequent, since writeIDATs uses a [bufio.Writer].
   194  //
   195  // This method should only be called from writeIDATs (via writeImage).
   196  // No other code should treat an encoder as an io.Writer.
   197  func (e *encoder) Write(b []byte) (int, error) {
   198  	e.writeChunk(b, "IDAT")
   199  	if e.err != nil {
   200  		return 0, e.err
   201  	}
   202  	return len(b), nil
   203  }
   204  
   205  // Chooses the filter to use for encoding the current row, and applies it.
   206  // The return value is the index of the filter and also of the row in cr that has had it applied.
   207  func filter(cr *[nFilter][]byte, pr []byte, bpp int) int {
   208  	// We try all five filter types, and pick the one that minimizes the sum of absolute differences.
   209  	// This is the same heuristic that libpng uses, although the filters are attempted in order of
   210  	// estimated most likely to be minimal (ftUp, ftPaeth, ftNone, ftSub, ftAverage), rather than
   211  	// in their enumeration order (ftNone, ftSub, ftUp, ftAverage, ftPaeth).
   212  	cdat0 := cr[0][1:]
   213  	cdat1 := cr[1][1:]
   214  	cdat2 := cr[2][1:]
   215  	cdat3 := cr[3][1:]
   216  	cdat4 := cr[4][1:]
   217  	pdat := pr[1:]
   218  	n := len(cdat0)
   219  
   220  	// The up filter.
   221  	sum := 0
   222  	for i := 0; i < n; i++ {
   223  		cdat2[i] = cdat0[i] - pdat[i]
   224  		sum += abs8(cdat2[i])
   225  	}
   226  	best := sum
   227  	filter := ftUp
   228  
   229  	// The Paeth filter.
   230  	sum = 0
   231  	for i := 0; i < bpp; i++ {
   232  		cdat4[i] = cdat0[i] - pdat[i]
   233  		sum += abs8(cdat4[i])
   234  	}
   235  	for i := bpp; i < n; i++ {
   236  		cdat4[i] = cdat0[i] - paeth(cdat0[i-bpp], pdat[i], pdat[i-bpp])
   237  		sum += abs8(cdat4[i])
   238  		if sum >= best {
   239  			break
   240  		}
   241  	}
   242  	if sum < best {
   243  		best = sum
   244  		filter = ftPaeth
   245  	}
   246  
   247  	// The none filter.
   248  	sum = 0
   249  	for i := 0; i < n; i++ {
   250  		sum += abs8(cdat0[i])
   251  		if sum >= best {
   252  			break
   253  		}
   254  	}
   255  	if sum < best {
   256  		best = sum
   257  		filter = ftNone
   258  	}
   259  
   260  	// The sub filter.
   261  	sum = 0
   262  	for i := 0; i < bpp; i++ {
   263  		cdat1[i] = cdat0[i]
   264  		sum += abs8(cdat1[i])
   265  	}
   266  	for i := bpp; i < n; i++ {
   267  		cdat1[i] = cdat0[i] - cdat0[i-bpp]
   268  		sum += abs8(cdat1[i])
   269  		if sum >= best {
   270  			break
   271  		}
   272  	}
   273  	if sum < best {
   274  		best = sum
   275  		filter = ftSub
   276  	}
   277  
   278  	// The average filter.
   279  	sum = 0
   280  	for i := 0; i < bpp; i++ {
   281  		cdat3[i] = cdat0[i] - pdat[i]/2
   282  		sum += abs8(cdat3[i])
   283  	}
   284  	for i := bpp; i < n; i++ {
   285  		cdat3[i] = cdat0[i] - uint8((int(cdat0[i-bpp])+int(pdat[i]))/2)
   286  		sum += abs8(cdat3[i])
   287  		if sum >= best {
   288  			break
   289  		}
   290  	}
   291  	if sum < best {
   292  		filter = ftAverage
   293  	}
   294  
   295  	return filter
   296  }
   297  
   298  func (e *encoder) writeImage(w io.Writer, m image.Image, cb int, level int) error {
   299  	if e.zw == nil || e.zwLevel != level {
   300  		zw, err := zlib.NewWriterLevel(w, level)
   301  		if err != nil {
   302  			return err
   303  		}
   304  		e.zw = zw
   305  		e.zwLevel = level
   306  	} else {
   307  		e.zw.Reset(w)
   308  	}
   309  	defer e.zw.Close()
   310  
   311  	bitsPerPixel := 0
   312  
   313  	switch cb {
   314  	case cbG8:
   315  		bitsPerPixel = 8
   316  	case cbTC8:
   317  		bitsPerPixel = 24
   318  	case cbP8:
   319  		bitsPerPixel = 8
   320  	case cbP4:
   321  		bitsPerPixel = 4
   322  	case cbP2:
   323  		bitsPerPixel = 2
   324  	case cbP1:
   325  		bitsPerPixel = 1
   326  	case cbTCA8:
   327  		bitsPerPixel = 32
   328  	case cbTC16:
   329  		bitsPerPixel = 48
   330  	case cbTCA16:
   331  		bitsPerPixel = 64
   332  	case cbG16:
   333  		bitsPerPixel = 16
   334  	}
   335  
   336  	// cr[*] and pr are the bytes for the current and previous row.
   337  	// cr[0] is unfiltered (or equivalently, filtered with the ftNone filter).
   338  	// cr[ft], for non-zero filter types ft, are buffers for transforming cr[0] under the
   339  	// other PNG filter types. These buffers are allocated once and re-used for each row.
   340  	// The +1 is for the per-row filter type, which is at cr[*][0].
   341  	b := m.Bounds()
   342  	sz := 1 + (bitsPerPixel*b.Dx()+7)/8
   343  	for i := range e.cr {
   344  		if cap(e.cr[i]) < sz {
   345  			e.cr[i] = make([]uint8, sz)
   346  		} else {
   347  			e.cr[i] = e.cr[i][:sz]
   348  		}
   349  		e.cr[i][0] = uint8(i)
   350  	}
   351  	cr := e.cr
   352  	if cap(e.pr) < sz {
   353  		e.pr = make([]uint8, sz)
   354  	} else {
   355  		e.pr = e.pr[:sz]
   356  		clear(e.pr)
   357  	}
   358  	pr := e.pr
   359  
   360  	gray, _ := m.(*image.Gray)
   361  	rgba, _ := m.(*image.RGBA)
   362  	paletted, _ := m.(*image.Paletted)
   363  	nrgba, _ := m.(*image.NRGBA)
   364  
   365  	for y := b.Min.Y; y < b.Max.Y; y++ {
   366  		// Convert from colors to bytes.
   367  		i := 1
   368  		switch cb {
   369  		case cbG8:
   370  			if gray != nil {
   371  				offset := (y - b.Min.Y) * gray.Stride
   372  				copy(cr[0][1:], gray.Pix[offset:offset+b.Dx()])
   373  			} else {
   374  				for x := b.Min.X; x < b.Max.X; x++ {
   375  					c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
   376  					cr[0][i] = c.Y
   377  					i++
   378  				}
   379  			}
   380  		case cbTC8:
   381  			// We have previously verified that the alpha value is fully opaque.
   382  			cr0 := cr[0]
   383  			stride, pix := 0, []byte(nil)
   384  			if rgba != nil {
   385  				stride, pix = rgba.Stride, rgba.Pix
   386  			} else if nrgba != nil {
   387  				stride, pix = nrgba.Stride, nrgba.Pix
   388  			}
   389  			if stride != 0 {
   390  				j0 := (y - b.Min.Y) * stride
   391  				j1 := j0 + b.Dx()*4
   392  				for j := j0; j < j1; j += 4 {
   393  					cr0[i+0] = pix[j+0]
   394  					cr0[i+1] = pix[j+1]
   395  					cr0[i+2] = pix[j+2]
   396  					i += 3
   397  				}
   398  			} else {
   399  				for x := b.Min.X; x < b.Max.X; x++ {
   400  					r, g, b, _ := m.At(x, y).RGBA()
   401  					cr0[i+0] = uint8(r >> 8)
   402  					cr0[i+1] = uint8(g >> 8)
   403  					cr0[i+2] = uint8(b >> 8)
   404  					i += 3
   405  				}
   406  			}
   407  		case cbP8:
   408  			if paletted != nil {
   409  				offset := (y - b.Min.Y) * paletted.Stride
   410  				copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
   411  			} else {
   412  				pi := m.(image.PalettedImage)
   413  				for x := b.Min.X; x < b.Max.X; x++ {
   414  					cr[0][i] = pi.ColorIndexAt(x, y)
   415  					i += 1
   416  				}
   417  			}
   418  
   419  		case cbP4, cbP2, cbP1:
   420  			pi := m.(image.PalettedImage)
   421  
   422  			var a uint8
   423  			var c int
   424  			pixelsPerByte := 8 / bitsPerPixel
   425  			for x := b.Min.X; x < b.Max.X; x++ {
   426  				a = a<<uint(bitsPerPixel) | pi.ColorIndexAt(x, y)
   427  				c++
   428  				if c == pixelsPerByte {
   429  					cr[0][i] = a
   430  					i += 1
   431  					a = 0
   432  					c = 0
   433  				}
   434  			}
   435  			if c != 0 {
   436  				for c != pixelsPerByte {
   437  					a = a << uint(bitsPerPixel)
   438  					c++
   439  				}
   440  				cr[0][i] = a
   441  			}
   442  
   443  		case cbTCA8:
   444  			if nrgba != nil {
   445  				offset := (y - b.Min.Y) * nrgba.Stride
   446  				copy(cr[0][1:], nrgba.Pix[offset:offset+b.Dx()*4])
   447  			} else if rgba != nil {
   448  				dst := cr[0][1:]
   449  				src := rgba.Pix[rgba.PixOffset(b.Min.X, y):rgba.PixOffset(b.Max.X, y)]
   450  				for ; len(src) >= 4; dst, src = dst[4:], src[4:] {
   451  					d := (*[4]byte)(dst)
   452  					s := (*[4]byte)(src)
   453  					if s[3] == 0x00 {
   454  						d[0] = 0
   455  						d[1] = 0
   456  						d[2] = 0
   457  						d[3] = 0
   458  					} else if s[3] == 0xff {
   459  						copy(d[:], s[:])
   460  					} else {
   461  						// This code does the same as color.NRGBAModel.Convert(
   462  						// rgba.At(x, y)).(color.NRGBA) but with no extra memory
   463  						// allocations or interface/function call overhead.
   464  						//
   465  						// The multiplier m combines 0x101 (which converts
   466  						// 8-bit color to 16-bit color) and 0xffff (which, when
   467  						// combined with the division-by-a, converts from
   468  						// alpha-premultiplied to non-alpha-premultiplied).
   469  						const m = 0x101 * 0xffff
   470  						a := uint32(s[3]) * 0x101
   471  						d[0] = uint8((uint32(s[0]) * m / a) >> 8)
   472  						d[1] = uint8((uint32(s[1]) * m / a) >> 8)
   473  						d[2] = uint8((uint32(s[2]) * m / a) >> 8)
   474  						d[3] = s[3]
   475  					}
   476  				}
   477  			} else {
   478  				// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
   479  				for x := b.Min.X; x < b.Max.X; x++ {
   480  					c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA)
   481  					cr[0][i+0] = c.R
   482  					cr[0][i+1] = c.G
   483  					cr[0][i+2] = c.B
   484  					cr[0][i+3] = c.A
   485  					i += 4
   486  				}
   487  			}
   488  		case cbG16:
   489  			for x := b.Min.X; x < b.Max.X; x++ {
   490  				c := color.Gray16Model.Convert(m.At(x, y)).(color.Gray16)
   491  				cr[0][i+0] = uint8(c.Y >> 8)
   492  				cr[0][i+1] = uint8(c.Y)
   493  				i += 2
   494  			}
   495  		case cbTC16:
   496  			// We have previously verified that the alpha value is fully opaque.
   497  			for x := b.Min.X; x < b.Max.X; x++ {
   498  				r, g, b, _ := m.At(x, y).RGBA()
   499  				cr[0][i+0] = uint8(r >> 8)
   500  				cr[0][i+1] = uint8(r)
   501  				cr[0][i+2] = uint8(g >> 8)
   502  				cr[0][i+3] = uint8(g)
   503  				cr[0][i+4] = uint8(b >> 8)
   504  				cr[0][i+5] = uint8(b)
   505  				i += 6
   506  			}
   507  		case cbTCA16:
   508  			// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
   509  			for x := b.Min.X; x < b.Max.X; x++ {
   510  				c := color.NRGBA64Model.Convert(m.At(x, y)).(color.NRGBA64)
   511  				cr[0][i+0] = uint8(c.R >> 8)
   512  				cr[0][i+1] = uint8(c.R)
   513  				cr[0][i+2] = uint8(c.G >> 8)
   514  				cr[0][i+3] = uint8(c.G)
   515  				cr[0][i+4] = uint8(c.B >> 8)
   516  				cr[0][i+5] = uint8(c.B)
   517  				cr[0][i+6] = uint8(c.A >> 8)
   518  				cr[0][i+7] = uint8(c.A)
   519  				i += 8
   520  			}
   521  		}
   522  
   523  		// Apply the filter.
   524  		// Skip filter for NoCompression and paletted images (cbP8) as
   525  		// "filters are rarely useful on palette images" and will result
   526  		// in larger files (see http://www.libpng.org/pub/png/book/chapter09.html).
   527  		f := ftNone
   528  		if level != zlib.NoCompression && cb != cbP8 && cb != cbP4 && cb != cbP2 && cb != cbP1 {
   529  			// Since we skip paletted images we don't have to worry about
   530  			// bitsPerPixel not being a multiple of 8
   531  			bpp := bitsPerPixel / 8
   532  			f = filter(&cr, pr, bpp)
   533  		}
   534  
   535  		// Write the compressed bytes.
   536  		if _, err := e.zw.Write(cr[f]); err != nil {
   537  			return err
   538  		}
   539  
   540  		// The current row for y is the previous row for y+1.
   541  		pr, cr[0] = cr[0], pr
   542  	}
   543  	return nil
   544  }
   545  
   546  // Write the actual image data to one or more IDAT chunks.
   547  func (e *encoder) writeIDATs() {
   548  	if e.err != nil {
   549  		return
   550  	}
   551  	if e.bw == nil {
   552  		e.bw = bufio.NewWriterSize(e, 1<<15)
   553  	} else {
   554  		e.bw.Reset(e)
   555  	}
   556  	e.err = e.writeImage(e.bw, e.m, e.cb, levelToZlib(e.enc.CompressionLevel))
   557  	if e.err != nil {
   558  		return
   559  	}
   560  	e.err = e.bw.Flush()
   561  }
   562  
   563  // This function is required because we want the zero value of
   564  // Encoder.CompressionLevel to map to zlib.DefaultCompression.
   565  func levelToZlib(l CompressionLevel) int {
   566  	switch l {
   567  	case DefaultCompression:
   568  		return zlib.DefaultCompression
   569  	case NoCompression:
   570  		return zlib.NoCompression
   571  	case BestSpeed:
   572  		return zlib.BestSpeed
   573  	case BestCompression:
   574  		return zlib.BestCompression
   575  	default:
   576  		return zlib.DefaultCompression
   577  	}
   578  }
   579  
   580  func (e *encoder) writeIEND() { e.writeChunk(nil, "IEND") }
   581  
   582  // Encode writes the Image m to w in PNG format. Any Image may be
   583  // encoded, but images that are not [image.NRGBA] might be encoded lossily.
   584  func Encode(w io.Writer, m image.Image) error {
   585  	var e Encoder
   586  	return e.Encode(w, m)
   587  }
   588  
   589  // Encode writes the Image m to w in PNG format.
   590  func (enc *Encoder) Encode(w io.Writer, m image.Image) error {
   591  	// Obviously, negative widths and heights are invalid. Furthermore, the PNG
   592  	// spec section 11.2.2 says that zero is invalid. Excessively large images are
   593  	// also rejected.
   594  	mw, mh := int64(m.Bounds().Dx()), int64(m.Bounds().Dy())
   595  	if mw <= 0 || mh <= 0 || mw >= 1<<32 || mh >= 1<<32 {
   596  		return FormatError("invalid image size: " + strconv.FormatInt(mw, 10) + "x" + strconv.FormatInt(mh, 10))
   597  	}
   598  
   599  	var e *encoder
   600  	if enc.BufferPool != nil {
   601  		buffer := enc.BufferPool.Get()
   602  		e = (*encoder)(buffer)
   603  
   604  	}
   605  	if e == nil {
   606  		e = &encoder{}
   607  	}
   608  	if enc.BufferPool != nil {
   609  		defer enc.BufferPool.Put((*EncoderBuffer)(e))
   610  	}
   611  
   612  	e.enc = enc
   613  	e.w = w
   614  	e.m = m
   615  
   616  	var pal color.Palette
   617  	// cbP8 encoding needs PalettedImage's ColorIndexAt method.
   618  	if _, ok := m.(image.PalettedImage); ok {
   619  		pal, _ = m.ColorModel().(color.Palette)
   620  	}
   621  	if pal != nil {
   622  		if len(pal) <= 2 {
   623  			e.cb = cbP1
   624  		} else if len(pal) <= 4 {
   625  			e.cb = cbP2
   626  		} else if len(pal) <= 16 {
   627  			e.cb = cbP4
   628  		} else {
   629  			e.cb = cbP8
   630  		}
   631  	} else {
   632  		switch m.ColorModel() {
   633  		case color.GrayModel:
   634  			e.cb = cbG8
   635  		case color.Gray16Model:
   636  			e.cb = cbG16
   637  		case color.RGBAModel, color.NRGBAModel, color.AlphaModel:
   638  			if opaque(m) {
   639  				e.cb = cbTC8
   640  			} else {
   641  				e.cb = cbTCA8
   642  			}
   643  		default:
   644  			if opaque(m) {
   645  				e.cb = cbTC16
   646  			} else {
   647  				e.cb = cbTCA16
   648  			}
   649  		}
   650  	}
   651  
   652  	_, e.err = io.WriteString(w, pngHeader)
   653  	e.writeIHDR()
   654  	if pal != nil {
   655  		e.writePLTEAndTRNS(pal)
   656  	}
   657  	e.writeIDATs()
   658  	e.writeIEND()
   659  	return e.err
   660  }
   661  

View as plain text