Home | History | Annotate | Download | only in 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) Write(data []byte) (int, error) {
     74 	if b.e.err != nil {
     75 		return 0, b.e.err
     76 	}
     77 	if len(data) == 0 {
     78 		return 0, nil
     79 	}
     80 	total := 0
     81 	for total < len(data) {
     82 		n := copy(b.e.buf[1:256], data[total:])
     83 		total += n
     84 		b.e.buf[0] = uint8(n)
     85 
     86 		n, b.e.err = b.e.w.Write(b.e.buf[:n+1])
     87 		if b.e.err != nil {
     88 			return 0, b.e.err
     89 		}
     90 	}
     91 	return total, b.e.err
     92 }
     93 
     94 func (e *encoder) flush() {
     95 	if e.err != nil {
     96 		return
     97 	}
     98 	e.err = e.w.Flush()
     99 }
    100 
    101 func (e *encoder) write(p []byte) {
    102 	if e.err != nil {
    103 		return
    104 	}
    105 	_, e.err = e.w.Write(p)
    106 }
    107 
    108 func (e *encoder) writeByte(b byte) {
    109 	if e.err != nil {
    110 		return
    111 	}
    112 	e.err = e.w.WriteByte(b)
    113 }
    114 
    115 func (e *encoder) writeHeader() {
    116 	if e.err != nil {
    117 		return
    118 	}
    119 	_, e.err = io.WriteString(e.w, "GIF89a")
    120 	if e.err != nil {
    121 		return
    122 	}
    123 
    124 	// Logical screen width and height.
    125 	writeUint16(e.buf[0:2], uint16(e.g.Config.Width))
    126 	writeUint16(e.buf[2:4], uint16(e.g.Config.Height))
    127 	e.write(e.buf[:4])
    128 
    129 	if p, ok := e.g.Config.ColorModel.(color.Palette); ok && len(p) > 0 {
    130 		paddedSize := log2(len(p)) // Size of Global Color Table: 2^(1+n).
    131 		e.buf[0] = fColorTable | uint8(paddedSize)
    132 		e.buf[1] = e.g.BackgroundIndex
    133 		e.buf[2] = 0x00 // Pixel Aspect Ratio.
    134 		e.write(e.buf[:3])
    135 		e.globalCT = encodeColorTable(e.globalColorTable[:], p, paddedSize)
    136 		e.write(e.globalColorTable[:e.globalCT])
    137 	} else {
    138 		// All frames have a local color table, so a global color table
    139 		// is not needed.
    140 		e.buf[0] = 0x00
    141 		e.buf[1] = 0x00 // Background Color Index.
    142 		e.buf[2] = 0x00 // Pixel Aspect Ratio.
    143 		e.write(e.buf[:3])
    144 	}
    145 
    146 	// Add animation info if necessary.
    147 	if len(e.g.Image) > 1 {
    148 		e.buf[0] = 0x21 // Extension Introducer.
    149 		e.buf[1] = 0xff // Application Label.
    150 		e.buf[2] = 0x0b // Block Size.
    151 		e.write(e.buf[:3])
    152 		_, e.err = io.WriteString(e.w, "NETSCAPE2.0") // Application Identifier.
    153 		if e.err != nil {
    154 			return
    155 		}
    156 		e.buf[0] = 0x03 // Block Size.
    157 		e.buf[1] = 0x01 // Sub-block Index.
    158 		writeUint16(e.buf[2:4], uint16(e.g.LoopCount))
    159 		e.buf[4] = 0x00 // Block Terminator.
    160 		e.write(e.buf[:5])
    161 	}
    162 }
    163 
    164 func encodeColorTable(dst []byte, p color.Palette, size int) int {
    165 	n := log2Lookup[size]
    166 	for i := 0; i < n; i++ {
    167 		if i < len(p) {
    168 			r, g, b, _ := p[i].RGBA()
    169 			dst[3*i+0] = uint8(r >> 8)
    170 			dst[3*i+1] = uint8(g >> 8)
    171 			dst[3*i+2] = uint8(b >> 8)
    172 		} else {
    173 			// Pad with black.
    174 			dst[3*i+0] = 0x00
    175 			dst[3*i+1] = 0x00
    176 			dst[3*i+2] = 0x00
    177 		}
    178 	}
    179 	return 3 * n
    180 }
    181 
    182 func (e *encoder) writeImageBlock(pm *image.Paletted, delay int, disposal byte) {
    183 	if e.err != nil {
    184 		return
    185 	}
    186 
    187 	if len(pm.Palette) == 0 {
    188 		e.err = errors.New("gif: cannot encode image block with empty palette")
    189 		return
    190 	}
    191 
    192 	b := pm.Bounds()
    193 	if b.Min.X < 0 || b.Max.X >= 1<<16 || b.Min.Y < 0 || b.Max.Y >= 1<<16 {
    194 		e.err = errors.New("gif: image block is too large to encode")
    195 		return
    196 	}
    197 	if !b.In(image.Rectangle{Max: image.Point{e.g.Config.Width, e.g.Config.Height}}) {
    198 		e.err = errors.New("gif: image block is out of bounds")
    199 		return
    200 	}
    201 
    202 	transparentIndex := -1
    203 	for i, c := range pm.Palette {
    204 		if _, _, _, a := c.RGBA(); a == 0 {
    205 			transparentIndex = i
    206 			break
    207 		}
    208 	}
    209 
    210 	if delay > 0 || disposal != 0 || transparentIndex != -1 {
    211 		e.buf[0] = sExtension  // Extension Introducer.
    212 		e.buf[1] = gcLabel     // Graphic Control Label.
    213 		e.buf[2] = gcBlockSize // Block Size.
    214 		if transparentIndex != -1 {
    215 			e.buf[3] = 0x01 | disposal<<2
    216 		} else {
    217 			e.buf[3] = 0x00 | disposal<<2
    218 		}
    219 		writeUint16(e.buf[4:6], uint16(delay)) // Delay Time (1/100ths of a second)
    220 
    221 		// Transparent color index.
    222 		if transparentIndex != -1 {
    223 			e.buf[6] = uint8(transparentIndex)
    224 		} else {
    225 			e.buf[6] = 0x00
    226 		}
    227 		e.buf[7] = 0x00 // Block Terminator.
    228 		e.write(e.buf[:8])
    229 	}
    230 	e.buf[0] = sImageDescriptor
    231 	writeUint16(e.buf[1:3], uint16(b.Min.X))
    232 	writeUint16(e.buf[3:5], uint16(b.Min.Y))
    233 	writeUint16(e.buf[5:7], uint16(b.Dx()))
    234 	writeUint16(e.buf[7:9], uint16(b.Dy()))
    235 	e.write(e.buf[:9])
    236 
    237 	paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n).
    238 	ct := encodeColorTable(e.localColorTable[:], pm.Palette, paddedSize)
    239 	if ct != e.globalCT || !bytes.Equal(e.globalColorTable[:ct], e.localColorTable[:ct]) {
    240 		// Use a local color table.
    241 		e.writeByte(fColorTable | uint8(paddedSize))
    242 		e.write(e.localColorTable[:ct])
    243 	} else {
    244 		// Use the global color table.
    245 		e.writeByte(0)
    246 	}
    247 
    248 	litWidth := paddedSize + 1
    249 	if litWidth < 2 {
    250 		litWidth = 2
    251 	}
    252 	e.writeByte(uint8(litWidth)) // LZW Minimum Code Size.
    253 
    254 	lzww := lzw.NewWriter(blockWriter{e: e}, lzw.LSB, litWidth)
    255 	if dx := b.Dx(); dx == pm.Stride {
    256 		_, e.err = lzww.Write(pm.Pix)
    257 		if e.err != nil {
    258 			lzww.Close()
    259 			return
    260 		}
    261 	} else {
    262 		for i, y := 0, b.Min.Y; y < b.Max.Y; i, y = i+pm.Stride, y+1 {
    263 			_, e.err = lzww.Write(pm.Pix[i : i+dx])
    264 			if e.err != nil {
    265 				lzww.Close()
    266 				return
    267 			}
    268 		}
    269 	}
    270 	lzww.Close()
    271 	e.writeByte(0x00) // Block Terminator.
    272 }
    273 
    274 // Options are the encoding parameters.
    275 type Options struct {
    276 	// NumColors is the maximum number of colors used in the image.
    277 	// It ranges from 1 to 256.
    278 	NumColors int
    279 
    280 	// Quantizer is used to produce a palette with size NumColors.
    281 	// palette.Plan9 is used in place of a nil Quantizer.
    282 	Quantizer draw.Quantizer
    283 
    284 	// Drawer is used to convert the source image to the desired palette.
    285 	// draw.FloydSteinberg is used in place of a nil Drawer.
    286 	Drawer draw.Drawer
    287 }
    288 
    289 // EncodeAll writes the images in g to w in GIF format with the
    290 // given loop count and delay between frames.
    291 func EncodeAll(w io.Writer, g *GIF) error {
    292 	if len(g.Image) == 0 {
    293 		return errors.New("gif: must provide at least one image")
    294 	}
    295 
    296 	if len(g.Image) != len(g.Delay) {
    297 		return errors.New("gif: mismatched image and delay lengths")
    298 	}
    299 	if g.LoopCount < 0 {
    300 		g.LoopCount = 0
    301 	}
    302 
    303 	e := encoder{g: *g}
    304 	// The GIF.Disposal, GIF.Config and GIF.BackgroundIndex fields were added
    305 	// in Go 1.5. Valid Go 1.4 code, such as when the Disposal field is omitted
    306 	// in a GIF struct literal, should still produce valid GIFs.
    307 	if e.g.Disposal != nil && len(e.g.Image) != len(e.g.Disposal) {
    308 		return errors.New("gif: mismatched image and disposal lengths")
    309 	}
    310 	if e.g.Config == (image.Config{}) {
    311 		p := g.Image[0].Bounds().Max
    312 		e.g.Config.Width = p.X
    313 		e.g.Config.Height = p.Y
    314 	} else if e.g.Config.ColorModel != nil {
    315 		if _, ok := e.g.Config.ColorModel.(color.Palette); !ok {
    316 			return errors.New("gif: GIF color model must be a color.Palette")
    317 		}
    318 	}
    319 
    320 	if ww, ok := w.(writer); ok {
    321 		e.w = ww
    322 	} else {
    323 		e.w = bufio.NewWriter(w)
    324 	}
    325 
    326 	e.writeHeader()
    327 	for i, pm := range g.Image {
    328 		disposal := uint8(0)
    329 		if g.Disposal != nil {
    330 			disposal = g.Disposal[i]
    331 		}
    332 		e.writeImageBlock(pm, g.Delay[i], disposal)
    333 	}
    334 	e.writeByte(sTrailer)
    335 	e.flush()
    336 	return e.err
    337 }
    338 
    339 // Encode writes the Image m to w in GIF format.
    340 func Encode(w io.Writer, m image.Image, o *Options) error {
    341 	// Check for bounds and size restrictions.
    342 	b := m.Bounds()
    343 	if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 {
    344 		return errors.New("gif: image is too large to encode")
    345 	}
    346 
    347 	opts := Options{}
    348 	if o != nil {
    349 		opts = *o
    350 	}
    351 	if opts.NumColors < 1 || 256 < opts.NumColors {
    352 		opts.NumColors = 256
    353 	}
    354 	if opts.Drawer == nil {
    355 		opts.Drawer = draw.FloydSteinberg
    356 	}
    357 
    358 	pm, ok := m.(*image.Paletted)
    359 	if !ok || len(pm.Palette) > opts.NumColors {
    360 		// TODO: Pick a better sub-sample of the Plan 9 palette.
    361 		pm = image.NewPaletted(b, palette.Plan9[:opts.NumColors])
    362 		if opts.Quantizer != nil {
    363 			pm.Palette = opts.Quantizer.Quantize(make(color.Palette, 0, opts.NumColors), m)
    364 		}
    365 		opts.Drawer.Draw(pm, b, m, image.ZP)
    366 	}
    367 
    368 	// When calling Encode instead of EncodeAll, the single-frame image is
    369 	// translated such that its top-left corner is (0, 0), so that the single
    370 	// frame completely fills the overall GIF's bounds.
    371 	if pm.Rect.Min != (image.Point{}) {
    372 		dup := *pm
    373 		dup.Rect = dup.Rect.Sub(dup.Rect.Min)
    374 		pm = &dup
    375 	}
    376 
    377 	return EncodeAll(w, &GIF{
    378 		Image: []*image.Paletted{pm},
    379 		Delay: []int{0},
    380 		Config: image.Config{
    381 			ColorModel: pm.Palette,
    382 			Width:      b.Dx(),
    383 			Height:     b.Dy(),
    384 		},
    385 	})
    386 }
    387