writer.mx raw

   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 []byte) {
  97  	if e.err != nil {
  98  		return
  99  	}
 100  	n := uint32(len(b))
 101  	if int(n) != len(b) {
 102  		e.err = UnsupportedError(string(append(append(append([]byte(nil), 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(append([]byte("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] = []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 = []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(append(append(append([]byte("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 = w.Write([]byte(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