github.com/shijuvar/go@v0.0.0-20141209052335-e8f13700b70c/src/image/gif/writer.go (about) 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 "compress/lzw" 10 "errors" 11 "image" 12 "image/color" 13 "image/color/palette" 14 "image/draw" 15 "io" 16 ) 17 18 // Graphic control extension fields. 19 const ( 20 gcLabel = 0xF9 21 gcBlockSize = 0x04 22 ) 23 24 var log2Lookup = [8]int{2, 4, 8, 16, 32, 64, 128, 256} 25 26 func log2(x int) int { 27 for i, v := range log2Lookup { 28 if x <= v { 29 return i 30 } 31 } 32 return -1 33 } 34 35 // Little-endian. 36 func writeUint16(b []uint8, u uint16) { 37 b[0] = uint8(u) 38 b[1] = uint8(u >> 8) 39 } 40 41 // writer is a buffered writer. 42 type writer interface { 43 Flush() error 44 io.Writer 45 io.ByteWriter 46 } 47 48 // encoder encodes an image to the GIF format. 49 type encoder struct { 50 // w is the writer to write to. err is the first error encountered during 51 // writing. All attempted writes after the first error become no-ops. 52 w writer 53 err error 54 // g is a reference to the data that is being encoded. 55 g *GIF 56 // buf is a scratch buffer. It must be at least 768 so we can write the color map. 57 buf [1024]byte 58 } 59 60 // blockWriter writes the block structure of GIF image data, which 61 // comprises (n, (n bytes)) blocks, with 1 <= n <= 255. It is the 62 // writer given to the LZW encoder, which is thus immune to the 63 // blocking. 64 type blockWriter struct { 65 e *encoder 66 } 67 68 func (b blockWriter) Write(data []byte) (int, error) { 69 if b.e.err != nil { 70 return 0, b.e.err 71 } 72 if len(data) == 0 { 73 return 0, nil 74 } 75 total := 0 76 for total < len(data) { 77 n := copy(b.e.buf[1:256], data[total:]) 78 total += n 79 b.e.buf[0] = uint8(n) 80 81 n, b.e.err = b.e.w.Write(b.e.buf[:n+1]) 82 if b.e.err != nil { 83 return 0, b.e.err 84 } 85 } 86 return total, b.e.err 87 } 88 89 func (e *encoder) flush() { 90 if e.err != nil { 91 return 92 } 93 e.err = e.w.Flush() 94 } 95 96 func (e *encoder) write(p []byte) { 97 if e.err != nil { 98 return 99 } 100 _, e.err = e.w.Write(p) 101 } 102 103 func (e *encoder) writeByte(b byte) { 104 if e.err != nil { 105 return 106 } 107 e.err = e.w.WriteByte(b) 108 } 109 110 func (e *encoder) writeHeader() { 111 if e.err != nil { 112 return 113 } 114 _, e.err = io.WriteString(e.w, "GIF89a") 115 if e.err != nil { 116 return 117 } 118 119 pm := e.g.Image[0] 120 // Logical screen width and height. 121 writeUint16(e.buf[0:2], uint16(pm.Bounds().Dx())) 122 writeUint16(e.buf[2:4], uint16(pm.Bounds().Dy())) 123 e.write(e.buf[:4]) 124 125 // All frames have a local color table, so a global color table 126 // is not needed. 127 e.buf[0] = 0x00 128 e.buf[1] = 0x00 // Background Color Index. 129 e.buf[2] = 0x00 // Pixel Aspect Ratio. 130 e.write(e.buf[:3]) 131 132 // Add animation info if necessary. 133 if len(e.g.Image) > 1 { 134 e.buf[0] = 0x21 // Extension Introducer. 135 e.buf[1] = 0xff // Application Label. 136 e.buf[2] = 0x0b // Block Size. 137 e.write(e.buf[:3]) 138 _, e.err = io.WriteString(e.w, "NETSCAPE2.0") // Application Identifier. 139 if e.err != nil { 140 return 141 } 142 e.buf[0] = 0x03 // Block Size. 143 e.buf[1] = 0x01 // Sub-block Index. 144 writeUint16(e.buf[2:4], uint16(e.g.LoopCount)) 145 e.buf[4] = 0x00 // Block Terminator. 146 e.write(e.buf[:5]) 147 } 148 } 149 150 func (e *encoder) writeColorTable(p color.Palette, size int) { 151 if e.err != nil { 152 return 153 } 154 155 for i := 0; i < log2Lookup[size]; i++ { 156 if i < len(p) { 157 r, g, b, _ := p[i].RGBA() 158 e.buf[3*i+0] = uint8(r >> 8) 159 e.buf[3*i+1] = uint8(g >> 8) 160 e.buf[3*i+2] = uint8(b >> 8) 161 } else { 162 // Pad with black. 163 e.buf[3*i+0] = 0x00 164 e.buf[3*i+1] = 0x00 165 e.buf[3*i+2] = 0x00 166 } 167 } 168 e.write(e.buf[:3*log2Lookup[size]]) 169 } 170 171 func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) { 172 if e.err != nil { 173 return 174 } 175 176 if len(pm.Palette) == 0 { 177 e.err = errors.New("gif: cannot encode image block with empty palette") 178 return 179 } 180 181 b := pm.Bounds() 182 if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 || b.Min.X < 0 || b.Min.X >= 1<<16 || b.Min.Y < 0 || b.Min.Y >= 1<<16 { 183 e.err = errors.New("gif: image block is too large to encode") 184 return 185 } 186 187 transparentIndex := -1 188 for i, c := range pm.Palette { 189 if _, _, _, a := c.RGBA(); a == 0 { 190 transparentIndex = i 191 break 192 } 193 } 194 195 if delay > 0 || transparentIndex != -1 { 196 e.buf[0] = sExtension // Extension Introducer. 197 e.buf[1] = gcLabel // Graphic Control Label. 198 e.buf[2] = gcBlockSize // Block Size. 199 if transparentIndex != -1 { 200 e.buf[3] = 0x01 201 } else { 202 e.buf[3] = 0x00 203 } 204 writeUint16(e.buf[4:6], uint16(delay)) // Delay Time (1/100ths of a second) 205 206 // Transparent color index. 207 if transparentIndex != -1 { 208 e.buf[6] = uint8(transparentIndex) 209 } else { 210 e.buf[6] = 0x00 211 } 212 e.buf[7] = 0x00 // Block Terminator. 213 e.write(e.buf[:8]) 214 } 215 e.buf[0] = sImageDescriptor 216 writeUint16(e.buf[1:3], uint16(b.Min.X)) 217 writeUint16(e.buf[3:5], uint16(b.Min.Y)) 218 writeUint16(e.buf[5:7], uint16(b.Dx())) 219 writeUint16(e.buf[7:9], uint16(b.Dy())) 220 e.write(e.buf[:9]) 221 222 paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n). 223 // Interlacing is not supported. 224 e.writeByte(0x80 | uint8(paddedSize)) 225 226 // Local Color Table. 227 e.writeColorTable(pm.Palette, paddedSize) 228 229 litWidth := paddedSize + 1 230 if litWidth < 2 { 231 litWidth = 2 232 } 233 e.writeByte(uint8(litWidth)) // LZW Minimum Code Size. 234 235 lzww := lzw.NewWriter(blockWriter{e: e}, lzw.LSB, litWidth) 236 if dx := b.Dx(); dx == pm.Stride { 237 _, e.err = lzww.Write(pm.Pix) 238 if e.err != nil { 239 lzww.Close() 240 return 241 } 242 } else { 243 for i, y := 0, b.Min.Y; y < b.Max.Y; i, y = i+pm.Stride, y+1 { 244 _, e.err = lzww.Write(pm.Pix[i : i+dx]) 245 if e.err != nil { 246 lzww.Close() 247 return 248 } 249 } 250 } 251 lzww.Close() 252 e.writeByte(0x00) // Block Terminator. 253 } 254 255 // Options are the encoding parameters. 256 type Options struct { 257 // NumColors is the maximum number of colors used in the image. 258 // It ranges from 1 to 256. 259 NumColors int 260 261 // Quantizer is used to produce a palette with size NumColors. 262 // palette.Plan9 is used in place of a nil Quantizer. 263 Quantizer draw.Quantizer 264 265 // Drawer is used to convert the source image to the desired palette. 266 // draw.FloydSteinberg is used in place of a nil Drawer. 267 Drawer draw.Drawer 268 } 269 270 // EncodeAll writes the images in g to w in GIF format with the 271 // given loop count and delay between frames. 272 func EncodeAll(w io.Writer, g *GIF) error { 273 if len(g.Image) == 0 { 274 return errors.New("gif: must provide at least one image") 275 } 276 277 if len(g.Image) != len(g.Delay) { 278 return errors.New("gif: mismatched image and delay lengths") 279 } 280 if g.LoopCount < 0 { 281 g.LoopCount = 0 282 } 283 284 e := encoder{g: g} 285 if ww, ok := w.(writer); ok { 286 e.w = ww 287 } else { 288 e.w = bufio.NewWriter(w) 289 } 290 291 e.writeHeader() 292 for i, pm := range g.Image { 293 e.writeImageBlock(pm, g.Delay[i]) 294 } 295 e.writeByte(sTrailer) 296 e.flush() 297 return e.err 298 } 299 300 // Encode writes the Image m to w in GIF format. 301 func Encode(w io.Writer, m image.Image, o *Options) error { 302 // Check for bounds and size restrictions. 303 b := m.Bounds() 304 if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 { 305 return errors.New("gif: image is too large to encode") 306 } 307 308 opts := Options{} 309 if o != nil { 310 opts = *o 311 } 312 if opts.NumColors < 1 || 256 < opts.NumColors { 313 opts.NumColors = 256 314 } 315 if opts.Drawer == nil { 316 opts.Drawer = draw.FloydSteinberg 317 } 318 319 pm, ok := m.(*image.Paletted) 320 if !ok || len(pm.Palette) > opts.NumColors { 321 // TODO: Pick a better sub-sample of the Plan 9 palette. 322 pm = image.NewPaletted(b, palette.Plan9[:opts.NumColors]) 323 if opts.Quantizer != nil { 324 pm.Palette = opts.Quantizer.Quantize(make(color.Palette, 0, opts.NumColors), m) 325 } 326 opts.Drawer.Draw(pm, b, m, image.ZP) 327 } 328 329 return EncodeAll(w, &GIF{ 330 Image: []*image.Paletted{pm}, 331 Delay: []int{0}, 332 }) 333 }