...
Run Format

Source file src/image/gif/writer.go

Documentation: image/gif

     1  // Copyright 2013 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 gif
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"compress/lzw"
    11  	"errors"
    12  	"image"
    13  	"image/color"
    14  	"image/color/palette"
    15  	"image/draw"
    16  	"io"
    17  )
    18  
    19  // Graphic control extension fields.
    20  const (
    21  	gcLabel     = 0xF9
    22  	gcBlockSize = 0x04
    23  )
    24  
    25  var log2Lookup = [8]int{2, 4, 8, 16, 32, 64, 128, 256}
    26  
    27  func log2(x int) int {
    28  	for i, v := range log2Lookup {
    29  		if x <= v {
    30  			return i
    31  		}
    32  	}
    33  	return -1
    34  }
    35  
    36  // Little-endian.
    37  func writeUint16(b []uint8, u uint16) {
    38  	b[0] = uint8(u)
    39  	b[1] = uint8(u >> 8)
    40  }
    41  
    42  // writer is a buffered writer.
    43  type writer interface {
    44  	Flush() error
    45  	io.Writer
    46  	io.ByteWriter
    47  }
    48  
    49  // encoder encodes an image to the GIF format.
    50  type encoder struct {
    51  	// w is the writer to write to. err is the first error encountered during
    52  	// writing. All attempted writes after the first error become no-ops.
    53  	w   writer
    54  	err error
    55  	// g is a reference to the data that is being encoded.
    56  	g GIF
    57  	// globalCT is the size in bytes of the global color table.
    58  	globalCT int
    59  	// buf is a scratch buffer. It must be at least 256 for the blockWriter.
    60  	buf              [256]byte
    61  	globalColorTable [3 * 256]byte
    62  	localColorTable  [3 * 256]byte
    63  }
    64  
    65  // blockWriter writes the block structure of GIF image data, which
    66  // comprises (n, (n bytes)) blocks, with 1 <= n <= 255. It is the
    67  // writer given to the LZW encoder, which is thus immune to the
    68  // blocking.
    69  type blockWriter struct {
    70  	e *encoder
    71  }
    72  
    73  func (b blockWriter) setup() {
    74  	b.e.buf[0] = 0
    75  }
    76  
    77  func (b blockWriter) Flush() error {
    78  	return b.e.err
    79  }
    80  
    81  func (b blockWriter) WriteByte(c byte) error {
    82  	if b.e.err != nil {
    83  		return b.e.err
    84  	}
    85  
    86  	// Append c to buffered sub-block.
    87  	b.e.buf[0]++
    88  	b.e.buf[b.e.buf[0]] = c
    89  	if b.e.buf[0] < 255 {
    90  		return nil
    91  	}
    92  
    93  	// Flush block
    94  	b.e.write(b.e.buf[:256])
    95  	b.e.buf[0] = 0
    96  	return b.e.err
    97  }
    98  
    99  // blockWriter must be an io.Writer for lzw.NewWriter, but this is never
   100  // actually called.
   101  func (b blockWriter) Write(data []byte) (int, error) {
   102  	for i, c := range data {
   103  		if err := b.WriteByte(c); err != nil {
   104  			return i, err
   105  		}
   106  	}
   107  	return len(data), nil
   108  }
   109  
   110  func (b blockWriter) close() {
   111  	// Write the block terminator (0x00), either by itself, or along with a
   112  	// pending sub-block.
   113  	if b.e.buf[0] == 0 {
   114  		b.e.writeByte(0)
   115  	} else {
   116  		n := uint(b.e.buf[0])
   117  		b.e.buf[n+1] = 0
   118  		b.e.write(b.e.buf[:n+2])
   119  	}
   120  	b.e.flush()
   121  }
   122  
   123  func (e *encoder) flush() {
   124  	if e.err != nil {
   125  		return
   126  	}
   127  	e.err = e.w.Flush()
   128  }
   129  
   130  func (e *encoder) write(p []byte) {
   131  	if e.err != nil {
   132  		return
   133  	}
   134  	_, e.err = e.w.Write(p)
   135  }
   136  
   137  func (e *encoder) writeByte(b byte) {
   138  	if e.err != nil {
   139  		return
   140  	}
   141  	e.err = e.w.WriteByte(b)
   142  }
   143  
   144  func (e *encoder) writeHeader() {
   145  	if e.err != nil {
   146  		return
   147  	}
   148  	_, e.err = io.WriteString(e.w, "GIF89a")
   149  	if e.err != nil {
   150  		return
   151  	}
   152  
   153  	// Logical screen width and height.
   154  	writeUint16(e.buf[0:2], uint16(e.g.Config.Width))
   155  	writeUint16(e.buf[2:4], uint16(e.g.Config.Height))
   156  	e.write(e.buf[:4])
   157  
   158  	if p, ok := e.g.Config.ColorModel.(color.Palette); ok && len(p) > 0 {
   159  		paddedSize := log2(len(p)) // Size of Global Color Table: 2^(1+n).
   160  		e.buf[0] = fColorTable | uint8(paddedSize)
   161  		e.buf[1] = e.g.BackgroundIndex
   162  		e.buf[2] = 0x00 // Pixel Aspect Ratio.
   163  		e.write(e.buf[:3])
   164  		var err error
   165  		e.globalCT, err = encodeColorTable(e.globalColorTable[:], p, paddedSize)
   166  		if err != nil && e.err == nil {
   167  			e.err = err
   168  			return
   169  		}
   170  		e.write(e.globalColorTable[:e.globalCT])
   171  	} else {
   172  		// All frames have a local color table, so a global color table
   173  		// is not needed.
   174  		e.buf[0] = 0x00
   175  		e.buf[1] = 0x00 // Background Color Index.
   176  		e.buf[2] = 0x00 // Pixel Aspect Ratio.
   177  		e.write(e.buf[:3])
   178  	}
   179  
   180  	// Add animation info if necessary.
   181  	if len(e.g.Image) > 1 && e.g.LoopCount >= 0 {
   182  		e.buf[0] = 0x21 // Extension Introducer.
   183  		e.buf[1] = 0xff // Application Label.
   184  		e.buf[2] = 0x0b // Block Size.
   185  		e.write(e.buf[:3])
   186  		_, err := io.WriteString(e.w, "NETSCAPE2.0") // Application Identifier.
   187  		if err != nil && e.err == nil {
   188  			e.err = err
   189  			return
   190  		}
   191  		e.buf[0] = 0x03 // Block Size.
   192  		e.buf[1] = 0x01 // Sub-block Index.
   193  		writeUint16(e.buf[2:4], uint16(e.g.LoopCount))
   194  		e.buf[4] = 0x00 // Block Terminator.
   195  		e.write(e.buf[:5])
   196  	}
   197  }
   198  
   199  func encodeColorTable(dst []byte, p color.Palette, size int) (int, error) {
   200  	if uint(size) >= uint(len(log2Lookup)) {
   201  		return 0, errors.New("gif: cannot encode color table with more than 256 entries")
   202  	}
   203  	for i, c := range p {
   204  		if c == nil {
   205  			return 0, errors.New("gif: cannot encode color table with nil entries")
   206  		}
   207  		var r, g, b uint8
   208  		// It is most likely that the palette is full of color.RGBAs, so they
   209  		// get a fast path.
   210  		if rgba, ok := c.(color.RGBA); ok {
   211  			r, g, b = rgba.R, rgba.G, rgba.B
   212  		} else {
   213  			rr, gg, bb, _ := c.RGBA()
   214  			r, g, b = uint8(rr>>8), uint8(gg>>8), uint8(bb>>8)
   215  		}
   216  		dst[3*i+0] = r
   217  		dst[3*i+1] = g
   218  		dst[3*i+2] = b
   219  	}
   220  	n := log2Lookup[size]
   221  	if n > len(p) {
   222  		// Pad with black.
   223  		fill := dst[3*len(p) : 3*n]
   224  		for i := range fill {
   225  			fill[i] = 0
   226  		}
   227  	}
   228  	return 3 * n, nil
   229  }
   230  
   231  func (e *encoder) colorTablesMatch(localLen, transparentIndex int) bool {
   232  	localSize := 3 * localLen
   233  	if transparentIndex >= 0 {
   234  		trOff := 3 * transparentIndex
   235  		return bytes.Equal(e.globalColorTable[:trOff], e.localColorTable[:trOff]) &&
   236  			bytes.Equal(e.globalColorTable[trOff+3:localSize], e.localColorTable[trOff+3:localSize])
   237  	}
   238  	return bytes.Equal(e.globalColorTable[:localSize], e.localColorTable[:localSize])
   239  }
   240  
   241  func (e *encoder) writeImageBlock(pm *image.Paletted, delay int, disposal byte) {
   242  	if e.err != nil {
   243  		return
   244  	}
   245  
   246  	if len(pm.Palette) == 0 {
   247  		e.err = errors.New("gif: cannot encode image block with empty palette")
   248  		return
   249  	}
   250  
   251  	b := pm.Bounds()
   252  	if b.Min.X < 0 || b.Max.X >= 1<<16 || b.Min.Y < 0 || b.Max.Y >= 1<<16 {
   253  		e.err = errors.New("gif: image block is too large to encode")
   254  		return
   255  	}
   256  	if !b.In(image.Rectangle{Max: image.Point{e.g.Config.Width, e.g.Config.Height}}) {
   257  		e.err = errors.New("gif: image block is out of bounds")
   258  		return
   259  	}
   260  
   261  	transparentIndex := -1
   262  	for i, c := range pm.Palette {
   263  		if c == nil {
   264  			e.err = errors.New("gif: cannot encode color table with nil entries")
   265  			return
   266  		}
   267  		if _, _, _, a := c.RGBA(); a == 0 {
   268  			transparentIndex = i
   269  			break
   270  		}
   271  	}
   272  
   273  	if delay > 0 || disposal != 0 || transparentIndex != -1 {
   274  		e.buf[0] = sExtension  // Extension Introducer.
   275  		e.buf[1] = gcLabel     // Graphic Control Label.
   276  		e.buf[2] = gcBlockSize // Block Size.
   277  		if transparentIndex != -1 {
   278  			e.buf[3] = 0x01 | disposal<<2
   279  		} else {
   280  			e.buf[3] = 0x00 | disposal<<2
   281  		}
   282  		writeUint16(e.buf[4:6], uint16(delay)) // Delay Time (1/100ths of a second)
   283  
   284  		// Transparent color index.
   285  		if transparentIndex != -1 {
   286  			e.buf[6] = uint8(transparentIndex)
   287  		} else {
   288  			e.buf[6] = 0x00
   289  		}
   290  		e.buf[7] = 0x00 // Block Terminator.
   291  		e.write(e.buf[:8])
   292  	}
   293  	e.buf[0] = sImageDescriptor
   294  	writeUint16(e.buf[1:3], uint16(b.Min.X))
   295  	writeUint16(e.buf[3:5], uint16(b.Min.Y))
   296  	writeUint16(e.buf[5:7], uint16(b.Dx()))
   297  	writeUint16(e.buf[7:9], uint16(b.Dy()))
   298  	e.write(e.buf[:9])
   299  
   300  	// To determine whether or not this frame's palette is the same as the
   301  	// global palette, we can check a couple things. First, do they actually
   302  	// point to the same []color.Color? If so, they are equal so long as the
   303  	// frame's palette is not longer than the global palette...
   304  	paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n).
   305  	if gp, ok := e.g.Config.ColorModel.(color.Palette); ok && len(pm.Palette) <= len(gp) && &gp[0] == &pm.Palette[0] {
   306  		e.writeByte(0) // Use the global color table.
   307  	} else {
   308  		ct, err := encodeColorTable(e.localColorTable[:], pm.Palette, paddedSize)
   309  		if err != nil {
   310  			if e.err == nil {
   311  				e.err = err
   312  			}
   313  			return
   314  		}
   315  		// This frame's palette is not the very same slice as the global
   316  		// palette, but it might be a copy, possibly with one value turned into
   317  		// transparency by DecodeAll.
   318  		if ct <= e.globalCT && e.colorTablesMatch(len(pm.Palette), transparentIndex) {
   319  			e.writeByte(0) // Use the global color table.
   320  		} else {
   321  			// Use a local color table.
   322  			e.writeByte(fColorTable | uint8(paddedSize))
   323  			e.write(e.localColorTable[:ct])
   324  		}
   325  	}
   326  
   327  	litWidth := paddedSize + 1
   328  	if litWidth < 2 {
   329  		litWidth = 2
   330  	}
   331  	e.writeByte(uint8(litWidth)) // LZW Minimum Code Size.
   332  
   333  	bw := blockWriter{e: e}
   334  	bw.setup()
   335  	lzww := lzw.NewWriter(bw, lzw.LSB, litWidth)
   336  	if dx := b.Dx(); dx == pm.Stride {
   337  		_, e.err = lzww.Write(pm.Pix[:dx*b.Dy()])
   338  		if e.err != nil {
   339  			lzww.Close()
   340  			return
   341  		}
   342  	} else {
   343  		for i, y := 0, b.Min.Y; y < b.Max.Y; i, y = i+pm.Stride, y+1 {
   344  			_, e.err = lzww.Write(pm.Pix[i : i+dx])
   345  			if e.err != nil {
   346  				lzww.Close()
   347  				return
   348  			}
   349  		}
   350  	}
   351  	lzww.Close() // flush to bw
   352  	bw.close()   // flush to e.w
   353  }
   354  
   355  // Options are the encoding parameters.
   356  type Options struct {
   357  	// NumColors is the maximum number of colors used in the image.
   358  	// It ranges from 1 to 256.
   359  	NumColors int
   360  
   361  	// Quantizer is used to produce a palette with size NumColors.
   362  	// palette.Plan9 is used in place of a nil Quantizer.
   363  	Quantizer draw.Quantizer
   364  
   365  	// Drawer is used to convert the source image to the desired palette.
   366  	// draw.FloydSteinberg is used in place of a nil Drawer.
   367  	Drawer draw.Drawer
   368  }
   369  
   370  // EncodeAll writes the images in g to w in GIF format with the
   371  // given loop count and delay between frames.
   372  func EncodeAll(w io.Writer, g *GIF) error {
   373  	if len(g.Image) == 0 {
   374  		return errors.New("gif: must provide at least one image")
   375  	}
   376  
   377  	if len(g.Image) != len(g.Delay) {
   378  		return errors.New("gif: mismatched image and delay lengths")
   379  	}
   380  
   381  	e := encoder{g: *g}
   382  	// The GIF.Disposal, GIF.Config and GIF.BackgroundIndex fields were added
   383  	// in Go 1.5. Valid Go 1.4 code, such as when the Disposal field is omitted
   384  	// in a GIF struct literal, should still produce valid GIFs.
   385  	if e.g.Disposal != nil && len(e.g.Image) != len(e.g.Disposal) {
   386  		return errors.New("gif: mismatched image and disposal lengths")
   387  	}
   388  	if e.g.Config == (image.Config{}) {
   389  		p := g.Image[0].Bounds().Max
   390  		e.g.Config.Width = p.X
   391  		e.g.Config.Height = p.Y
   392  	} else if e.g.Config.ColorModel != nil {
   393  		if _, ok := e.g.Config.ColorModel.(color.Palette); !ok {
   394  			return errors.New("gif: GIF color model must be a color.Palette")
   395  		}
   396  	}
   397  
   398  	if ww, ok := w.(writer); ok {
   399  		e.w = ww
   400  	} else {
   401  		e.w = bufio.NewWriter(w)
   402  	}
   403  
   404  	e.writeHeader()
   405  	for i, pm := range g.Image {
   406  		disposal := uint8(0)
   407  		if g.Disposal != nil {
   408  			disposal = g.Disposal[i]
   409  		}
   410  		e.writeImageBlock(pm, g.Delay[i], disposal)
   411  	}
   412  	e.writeByte(sTrailer)
   413  	e.flush()
   414  	return e.err
   415  }
   416  
   417  // Encode writes the Image m to w in GIF format.
   418  func Encode(w io.Writer, m image.Image, o *Options) error {
   419  	// Check for bounds and size restrictions.
   420  	b := m.Bounds()
   421  	if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 {
   422  		return errors.New("gif: image is too large to encode")
   423  	}
   424  
   425  	opts := Options{}
   426  	if o != nil {
   427  		opts = *o
   428  	}
   429  	if opts.NumColors < 1 || 256 < opts.NumColors {
   430  		opts.NumColors = 256
   431  	}
   432  	if opts.Drawer == nil {
   433  		opts.Drawer = draw.FloydSteinberg
   434  	}
   435  
   436  	pm, ok := m.(*image.Paletted)
   437  	if !ok || len(pm.Palette) > opts.NumColors {
   438  		// TODO: Pick a better sub-sample of the Plan 9 palette.
   439  		pm = image.NewPaletted(b, palette.Plan9[:opts.NumColors])
   440  		if opts.Quantizer != nil {
   441  			pm.Palette = opts.Quantizer.Quantize(make(color.Palette, 0, opts.NumColors), m)
   442  		}
   443  		opts.Drawer.Draw(pm, b, m, image.ZP)
   444  	}
   445  
   446  	// When calling Encode instead of EncodeAll, the single-frame image is
   447  	// translated such that its top-left corner is (0, 0), so that the single
   448  	// frame completely fills the overall GIF's bounds.
   449  	if pm.Rect.Min != (image.Point{}) {
   450  		dup := *pm
   451  		dup.Rect = dup.Rect.Sub(dup.Rect.Min)
   452  		pm = &dup
   453  	}
   454  
   455  	return EncodeAll(w, &GIF{
   456  		Image: []*image.Paletted{pm},
   457  		Delay: []int{0},
   458  		Config: image.Config{
   459  			ColorModel: pm.Palette,
   460  			Width:      b.Dx(),
   461  			Height:     b.Dy(),
   462  		},
   463  	})
   464  }
   465  

View as plain text