LEFT | RIGHT |
1 // Copyright 2013 The Go Authors. All rights reserved. | 1 // Copyright 2013 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package gif | 5 package gif |
6 | 6 |
7 import ( | 7 import ( |
8 "bufio" | 8 "bufio" |
9 "compress/lzw" | 9 "compress/lzw" |
10 "errors" | 10 "errors" |
11 "image" | 11 "image" |
12 "image/color" | 12 "image/color" |
13 "image/draw" | 13 "image/draw" |
14 "io" | 14 "io" |
15 ) | 15 ) |
16 | 16 |
17 // Graphic control extension fields. | 17 // Graphic control extension fields. |
18 const ( | 18 const ( |
19 gcLabel = 0xF9 | 19 gcLabel = 0xF9 |
20 gcBlockSize = 0x04 | 20 gcBlockSize = 0x04 |
21 ) | 21 ) |
22 | 22 |
23 var log2Lookup = [8]int{2, 4, 8, 16, 32, 64, 128, 256} | 23 var log2Lookup = [8]int{2, 4, 8, 16, 32, 64, 128, 256} |
24 | 24 |
25 func log2Int256(x int) int { | 25 func log2(x int) int { |
26 for i, v := range log2Lookup { | 26 for i, v := range log2Lookup { |
27 if x <= v { | 27 if x <= v { |
28 return i | 28 return i |
29 } | 29 } |
30 } | 30 } |
31 return -1 | 31 return -1 |
32 } | 32 } |
33 | 33 |
34 // Little-endian. | 34 // Little-endian. |
35 func writeUint16(b []uint8, u uint16) { | 35 func writeUint16(b []uint8, u uint16) { |
(...skipping 16 matching lines...) Expand all Loading... |
52 err error | 52 err error |
53 // g is a reference to the data that is being encoded. | 53 // g is a reference to the data that is being encoded. |
54 g *GIF | 54 g *GIF |
55 // bitsPerPixel is the number of bits required to represent each color | 55 // bitsPerPixel is the number of bits required to represent each color |
56 // in the image. | 56 // in the image. |
57 bitsPerPixel int | 57 bitsPerPixel int |
58 // buf is a scratch buffer. It must be at least 768 so we can write the
color map. | 58 // buf is a scratch buffer. It must be at least 768 so we can write the
color map. |
59 buf [1024]byte | 59 buf [1024]byte |
60 } | 60 } |
61 | 61 |
62 // newEncoder returns a new encoder with the given writer. | |
63 func newEncoder(w io.Writer) *encoder { | |
64 var e encoder | |
65 if ww, ok := w.(writer); ok { | |
66 e.w = ww | |
67 } else { | |
68 e.w = bufio.NewWriter(w) | |
69 } | |
70 return &e | |
71 } | |
72 | |
73 // blockWriter writes the block structure of GIF image data, which | 62 // blockWriter writes the block structure of GIF image data, which |
74 // comprises (n, (n bytes)) blocks, with 1 <= n <= 255. It is the | 63 // comprises (n, (n bytes)) blocks, with 1 <= n <= 255. It is the |
75 // writer given to the LZW encoder, which is thus immune to the | 64 // writer given to the LZW encoder, which is thus immune to the |
76 // blocking. | 65 // blocking. |
77 type blockWriter struct { | 66 type blockWriter struct { |
78 » w writer | 67 » e *encoder |
79 » err error | 68 } |
80 » tmp [256]byte | 69 |
81 } | 70 func (b blockWriter) Write(data []byte) (int, error) { |
82 | 71 » if b.e.err != nil { |
83 func (b *blockWriter) Write(data []byte) (int, error) { | 72 » » return 0, b.e.err |
84 » if b.err != nil { | |
85 » » return 0, b.err | |
86 } | 73 } |
87 if len(data) == 0 { | 74 if len(data) == 0 { |
88 return 0, nil | 75 return 0, nil |
89 } | 76 } |
90 total := 0 | 77 total := 0 |
91 for total < len(data) { | 78 for total < len(data) { |
92 » » n := copy(b.tmp[1:256], data[total:]) | 79 » » n := copy(b.e.buf[1:256], data[total:]) |
93 total += n | 80 total += n |
94 » » b.tmp[0] = uint8(n) | 81 » » b.e.buf[0] = uint8(n) |
95 | 82 |
96 » » n, b.err = b.w.Write(b.tmp[:n+1]) | 83 » » n, b.e.err = b.e.w.Write(b.e.buf[:n+1]) |
97 » » if b.err != nil { | 84 » » if b.e.err != nil { |
98 » » » return 0, b.err | 85 » » » return 0, b.e.err |
99 » » } | 86 » » } |
100 » } | 87 » } |
101 » return total, b.err | 88 » return total, b.e.err |
102 } | 89 } |
103 | 90 |
104 func (e *encoder) flush() { | 91 func (e *encoder) flush() { |
105 if e.err != nil { | 92 if e.err != nil { |
106 return | 93 return |
107 } | 94 } |
108 e.err = e.w.Flush() | 95 e.err = e.w.Flush() |
109 } | 96 } |
110 | 97 |
111 func (e *encoder) write(p []byte) { | 98 func (e *encoder) write(p []byte) { |
112 if e.err != nil { | 99 if e.err != nil { |
113 return | 100 return |
114 } | 101 } |
115 _, e.err = e.w.Write(p) | 102 _, e.err = e.w.Write(p) |
116 } | 103 } |
117 | 104 |
118 func (e *encoder) writeByte(b byte) { | 105 func (e *encoder) writeByte(b byte) { |
119 if e.err != nil { | 106 if e.err != nil { |
120 return | 107 return |
121 } | 108 } |
122 e.err = e.w.WriteByte(b) | 109 e.err = e.w.WriteByte(b) |
123 } | 110 } |
124 | 111 |
125 func (e *encoder) writeHeader() { | 112 func (e *encoder) writeHeader() { |
126 if e.err != nil { | 113 if e.err != nil { |
127 return | 114 return |
128 } | 115 } |
129 // TODO: GIF87a could be valid depending on the features that | |
130 // the image uses. | |
131 _, e.err = io.WriteString(e.w, "GIF89a") | 116 _, e.err = io.WriteString(e.w, "GIF89a") |
132 if e.err != nil { | 117 if e.err != nil { |
133 return | 118 return |
134 } | 119 } |
135 | 120 |
136 // TODO: This bases the global color table on the first image | 121 // TODO: This bases the global color table on the first image |
137 // only. | 122 // only. |
138 pm := e.g.Image[0] | 123 pm := e.g.Image[0] |
139 // Logical screen width and height. | 124 // Logical screen width and height. |
140 » writeUint16(e.buf[:2], uint16(pm.Bounds().Dx())) | 125 » writeUint16(e.buf[0:2], uint16(pm.Bounds().Dx())) |
141 writeUint16(e.buf[2:4], uint16(pm.Bounds().Dy())) | 126 writeUint16(e.buf[2:4], uint16(pm.Bounds().Dy())) |
142 e.write(e.buf[:4]) | 127 e.write(e.buf[:4]) |
143 | 128 |
144 » e.bitsPerPixel = log2Int256(len(pm.Palette)) + 1 | 129 » e.bitsPerPixel = log2(len(pm.Palette)) + 1 |
145 e.buf[0] = 0x80 | ((uint8(e.bitsPerPixel) - 1) << 4) | (uint8(e.bitsPerP
ixel) - 1) | 130 e.buf[0] = 0x80 | ((uint8(e.bitsPerPixel) - 1) << 4) | (uint8(e.bitsPerP
ixel) - 1) |
146 e.buf[1] = 0x00 // Background Color Index. | 131 e.buf[1] = 0x00 // Background Color Index. |
147 e.buf[2] = 0x00 // Pixel Aspect Ratio. | 132 e.buf[2] = 0x00 // Pixel Aspect Ratio. |
148 e.write(e.buf[:3]) | 133 e.write(e.buf[:3]) |
149 | 134 |
150 // Global Color Table. | 135 // Global Color Table. |
151 e.writeColorTable(pm.Palette, e.bitsPerPixel-1) | 136 e.writeColorTable(pm.Palette, e.bitsPerPixel-1) |
152 | 137 |
153 // Add animation info if necessary. | 138 // Add animation info if necessary. |
154 if len(e.g.Image) > 1 { | 139 if len(e.g.Image) > 1 { |
(...skipping 14 matching lines...) Expand all Loading... |
169 } | 154 } |
170 | 155 |
171 func (e *encoder) writeColorTable(p color.Palette, size int) { | 156 func (e *encoder) writeColorTable(p color.Palette, size int) { |
172 if e.err != nil { | 157 if e.err != nil { |
173 return | 158 return |
174 } | 159 } |
175 | 160 |
176 for i := 0; i < log2Lookup[size]; i++ { | 161 for i := 0; i < log2Lookup[size]; i++ { |
177 if i < len(p) { | 162 if i < len(p) { |
178 r, g, b, _ := p[i].RGBA() | 163 r, g, b, _ := p[i].RGBA() |
179 » » » e.buf[3*i] = uint8(r >> 8) | 164 » » » e.buf[3*i+0] = uint8(r >> 8) |
180 e.buf[3*i+1] = uint8(g >> 8) | 165 e.buf[3*i+1] = uint8(g >> 8) |
181 e.buf[3*i+2] = uint8(b >> 8) | 166 e.buf[3*i+2] = uint8(b >> 8) |
182 } else { | 167 } else { |
183 // Pad with black. | 168 // Pad with black. |
184 » » » e.buf[3*i] = 0x00 | 169 » » » e.buf[3*i+0] = 0x00 |
185 e.buf[3*i+1] = 0x00 | 170 e.buf[3*i+1] = 0x00 |
186 e.buf[3*i+2] = 0x00 | 171 e.buf[3*i+2] = 0x00 |
187 } | 172 } |
188 } | 173 } |
189 e.write(e.buf[:3*log2Lookup[size]]) | 174 e.write(e.buf[:3*log2Lookup[size]]) |
190 } | 175 } |
191 | 176 |
192 func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) { | 177 func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) { |
193 if e.err != nil { | 178 if e.err != nil { |
194 return | 179 return |
195 } | 180 } |
196 | 181 |
197 if len(pm.Palette) == 0 { | 182 if len(pm.Palette) == 0 { |
198 e.err = errors.New("gif: cannot encode image block with empty pa
lette") | 183 e.err = errors.New("gif: cannot encode image block with empty pa
lette") |
199 return | 184 return |
200 } | 185 } |
201 | 186 |
202 b := pm.Bounds() | 187 b := pm.Bounds() |
203 » if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 || b.Min.X >= 1<<16 || b.Min.Y >=
1<<16 { | 188 » 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 { |
204 e.err = errors.New("gif: image block is too large to encode") | 189 e.err = errors.New("gif: image block is too large to encode") |
205 return | 190 return |
206 } | 191 } |
207 | 192 |
208 transparentIndex := -1 | 193 transparentIndex := -1 |
209 for i, c := range pm.Palette { | 194 for i, c := range pm.Palette { |
210 if _, _, _, a := c.RGBA(); a == 0 { | 195 if _, _, _, a := c.RGBA(); a == 0 { |
211 transparentIndex = i | 196 transparentIndex = i |
212 break | 197 break |
213 } | 198 } |
(...skipping 19 matching lines...) Expand all Loading... |
233 e.buf[7] = 0x00 // Block Terminator. | 218 e.buf[7] = 0x00 // Block Terminator. |
234 e.write(e.buf[:8]) | 219 e.write(e.buf[:8]) |
235 } | 220 } |
236 e.buf[0] = sImageDescriptor | 221 e.buf[0] = sImageDescriptor |
237 writeUint16(e.buf[1:3], uint16(b.Min.X)) | 222 writeUint16(e.buf[1:3], uint16(b.Min.X)) |
238 writeUint16(e.buf[3:5], uint16(b.Min.Y)) | 223 writeUint16(e.buf[3:5], uint16(b.Min.Y)) |
239 writeUint16(e.buf[5:7], uint16(b.Dx())) | 224 writeUint16(e.buf[5:7], uint16(b.Dx())) |
240 writeUint16(e.buf[7:9], uint16(b.Dy())) | 225 writeUint16(e.buf[7:9], uint16(b.Dy())) |
241 e.write(e.buf[:9]) | 226 e.write(e.buf[:9]) |
242 | 227 |
243 » paddedSize := log2Int256(len(pm.Palette)) // Size of Local Color Table:
2^(1+n). | 228 » paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n
). |
244 // Interlacing is not supported. | 229 // Interlacing is not supported. |
245 e.writeByte(0x80 | uint8(paddedSize)) | 230 e.writeByte(0x80 | uint8(paddedSize)) |
246 | 231 |
247 // Local Color Table. | 232 // Local Color Table. |
248 e.writeColorTable(pm.Palette, paddedSize) | 233 e.writeColorTable(pm.Palette, paddedSize) |
249 | 234 |
250 litWidth := e.bitsPerPixel | 235 litWidth := e.bitsPerPixel |
251 if litWidth < 2 { | 236 if litWidth < 2 { |
252 litWidth = 2 | 237 litWidth = 2 |
253 } | 238 } |
254 e.writeByte(uint8(litWidth)) // LZW Minimum Code Size. | 239 e.writeByte(uint8(litWidth)) // LZW Minimum Code Size. |
255 | 240 |
256 » bw := &blockWriter{w: e.w} | 241 » lzww := lzw.NewWriter(blockWriter{e: e}, lzw.LSB, litWidth) |
257 » lzww := lzw.NewWriter(bw, lzw.LSB, litWidth) | |
258 _, e.err = lzww.Write(pm.Pix) | 242 _, e.err = lzww.Write(pm.Pix) |
259 if e.err != nil { | 243 if e.err != nil { |
260 lzww.Close() | 244 lzww.Close() |
261 return | 245 return |
262 } | 246 } |
263 lzww.Close() | 247 lzww.Close() |
264 e.writeByte(0x00) // Block Terminator. | 248 e.writeByte(0x00) // Block Terminator. |
265 } | 249 } |
266 | |
267 // DefaultNumColors is the default NumColors encoding parameter. | |
268 const DefaultNumColors = 256 | |
269 | 250 |
270 // Options are the encoding parameters. | 251 // Options are the encoding parameters. |
271 type Options struct { | 252 type Options struct { |
272 // NumColors is the maximum number of colors used in the image. | 253 // NumColors is the maximum number of colors used in the image. |
273 // It ranges from 1 to 256. | 254 // It ranges from 1 to 256. |
274 NumColors int | 255 NumColors int |
275 | 256 |
276 // Quantizer is used to produce a palette with size NumColors. | 257 // Quantizer is used to produce a palette with size NumColors. |
277 // color.Plan9Palette is used in place of a nil Quantizer. | 258 // color.Plan9Palette is used in place of a nil Quantizer. |
278 Quantizer draw.Quantizer | 259 Quantizer draw.Quantizer |
(...skipping 10 matching lines...) Expand all Loading... |
289 return errors.New("gif: must provide at least one image") | 270 return errors.New("gif: must provide at least one image") |
290 } | 271 } |
291 | 272 |
292 if len(g.Image) != len(g.Delay) { | 273 if len(g.Image) != len(g.Delay) { |
293 return errors.New("gif: mismatched image and delay lengths") | 274 return errors.New("gif: mismatched image and delay lengths") |
294 } | 275 } |
295 if g.LoopCount < 0 { | 276 if g.LoopCount < 0 { |
296 g.LoopCount = 0 | 277 g.LoopCount = 0 |
297 } | 278 } |
298 | 279 |
299 » e := newEncoder(w) | 280 » e := encoder{g: g} |
300 » e.g = g | 281 » if ww, ok := w.(writer); ok { |
| 282 » » e.w = ww |
| 283 » } else { |
| 284 » » e.w = bufio.NewWriter(w) |
| 285 » } |
| 286 |
301 e.writeHeader() | 287 e.writeHeader() |
302 for i, pm := range g.Image { | 288 for i, pm := range g.Image { |
303 e.writeImageBlock(pm, g.Delay[i]) | 289 e.writeImageBlock(pm, g.Delay[i]) |
304 } | 290 } |
305 e.writeByte(sTrailer) | 291 e.writeByte(sTrailer) |
306 e.flush() | 292 e.flush() |
307 return e.err | 293 return e.err |
308 } | 294 } |
309 | 295 |
310 // Encode writes the Image m to w in GIF format. | 296 // Encode writes the Image m to w in GIF format. |
311 func Encode(w io.Writer, m image.Image, o *Options) error { | 297 func Encode(w io.Writer, m image.Image, o *Options) error { |
312 // Check for bounds and size restrictions. | 298 // Check for bounds and size restrictions. |
313 b := m.Bounds() | 299 b := m.Bounds() |
314 if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 { | 300 if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 { |
315 return errors.New("gif: image is too large to encode") | 301 return errors.New("gif: image is too large to encode") |
316 } | 302 } |
317 | 303 |
318 » if o == nil { | 304 » opts := Options{} |
319 » » o = &Options{} | 305 » if o != nil { |
320 » } | 306 » » opts = *o |
321 » if o.NumColors < 1 || o.NumColors > DefaultNumColors { | 307 » } |
322 » » o.NumColors = DefaultNumColors | 308 » if opts.NumColors < 1 || 256 < opts.NumColors { |
323 » } | 309 » » opts.NumColors = 256 |
324 » if o.Drawer == nil { | 310 » } |
325 » » o.Drawer = draw.FloydSteinberg | 311 » if opts.Drawer == nil { |
| 312 » » opts.Drawer = draw.FloydSteinberg |
326 } | 313 } |
327 | 314 |
328 pm, ok := m.(*image.Paletted) | 315 pm, ok := m.(*image.Paletted) |
329 » if !ok || len(pm.Palette) > o.NumColors { | 316 » if !ok || len(pm.Palette) > opts.NumColors { |
330 » » pm = image.NewPaletted(b, color.Plan9Palette) | 317 » » // TODO: Pick a better sub-sample of the Plan 9 palette. |
331 » » if o.Quantizer != nil { | 318 » » pm = image.NewPaletted(b, color.Plan9Palette[:opts.NumColors]) |
332 » » » pm.Palette = o.Quantizer.Quantize(make(color.Palette, 0,
o.NumColors), m) | 319 » » if opts.Quantizer != nil { |
333 » » } | 320 » » » pm.Palette = opts.Quantizer.Quantize(make(color.Palette,
0, opts.NumColors), m) |
334 » » o.Drawer.Draw(pm, b, m, image.ZP) | 321 » » } |
| 322 » » opts.Drawer.Draw(pm, b, m, image.ZP) |
335 } | 323 } |
336 | 324 |
337 return EncodeAll(w, &GIF{ | 325 return EncodeAll(w, &GIF{ |
338 Image: []*image.Paletted{pm}, | 326 Image: []*image.Paletted{pm}, |
339 Delay: []int{0}, | 327 Delay: []int{0}, |
340 }) | 328 }) |
341 } | 329 } |
LEFT | RIGHT |