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