...

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, _ := m.(*image.Paletted)
	 437  	if pm == nil {
	 438  		if cp, ok := m.ColorModel().(color.Palette); ok {
	 439  			pm = image.NewPaletted(b, cp)
	 440  			for y := b.Min.Y; y < b.Max.Y; y++ {
	 441  				for x := b.Min.X; x < b.Max.X; x++ {
	 442  					pm.Set(x, y, cp.Convert(m.At(x, y)))
	 443  				}
	 444  			}
	 445  		}
	 446  	}
	 447  	if pm == nil || len(pm.Palette) > opts.NumColors {
	 448  		// Set pm to be a palettedized copy of m, including its bounds, which
	 449  		// might not start at (0, 0).
	 450  		//
	 451  		// TODO: Pick a better sub-sample of the Plan 9 palette.
	 452  		pm = image.NewPaletted(b, palette.Plan9[:opts.NumColors])
	 453  		if opts.Quantizer != nil {
	 454  			pm.Palette = opts.Quantizer.Quantize(make(color.Palette, 0, opts.NumColors), m)
	 455  		}
	 456  		opts.Drawer.Draw(pm, b, m, b.Min)
	 457  	}
	 458  
	 459  	// When calling Encode instead of EncodeAll, the single-frame image is
	 460  	// translated such that its top-left corner is (0, 0), so that the single
	 461  	// frame completely fills the overall GIF's bounds.
	 462  	if pm.Rect.Min != (image.Point{}) {
	 463  		dup := *pm
	 464  		dup.Rect = dup.Rect.Sub(dup.Rect.Min)
	 465  		pm = &dup
	 466  	}
	 467  
	 468  	return EncodeAll(w, &GIF{
	 469  		Image: []*image.Paletted{pm},
	 470  		Delay: []int{0},
	 471  		Config: image.Config{
	 472  			ColorModel: pm.Palette,
	 473  			Width:			b.Dx(),
	 474  			Height:		 b.Dy(),
	 475  		},
	 476  	})
	 477  }
	 478  

View as plain text