Source file
src/image/gif/writer.go
1
2
3
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
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
37 func writeUint16(b []uint8, u uint16) {
38 b[0] = uint8(u)
39 b[1] = uint8(u >> 8)
40 }
41
42
43 type writer interface {
44 Flush() error
45 io.Writer
46 io.ByteWriter
47 }
48
49
50 type encoder struct {
51
52
53 w writer
54 err error
55
56 g GIF
57
58 globalCT int
59
60 buf [256]byte
61 globalColorTable [3 * 256]byte
62 localColorTable [3 * 256]byte
63 }
64
65
66
67
68
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
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
94 b.e.write(b.e.buf[:256])
95 b.e.buf[0] = 0
96 return b.e.err
97 }
98
99
100
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
112
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
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))
160 e.buf[0] = fColorTable | uint8(paddedSize)
161 e.buf[1] = e.g.BackgroundIndex
162 e.buf[2] = 0x00
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
173
174 e.buf[0] = 0x00
175 e.buf[1] = 0x00
176 e.buf[2] = 0x00
177 e.write(e.buf[:3])
178 }
179
180
181 if len(e.g.Image) > 1 && e.g.LoopCount >= 0 {
182 e.buf[0] = 0x21
183 e.buf[1] = 0xff
184 e.buf[2] = 0x0b
185 e.write(e.buf[:3])
186 _, err := io.WriteString(e.w, "NETSCAPE2.0")
187 if err != nil && e.err == nil {
188 e.err = err
189 return
190 }
191 e.buf[0] = 0x03
192 e.buf[1] = 0x01
193 writeUint16(e.buf[2:4], uint16(e.g.LoopCount))
194 e.buf[4] = 0x00
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
209
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
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
275 e.buf[1] = gcLabel
276 e.buf[2] = gcBlockSize
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))
283
284
285 if transparentIndex != -1 {
286 e.buf[6] = uint8(transparentIndex)
287 } else {
288 e.buf[6] = 0x00
289 }
290 e.buf[7] = 0x00
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
301
302
303
304 paddedSize := log2(len(pm.Palette))
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)
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
316
317
318 if ct <= e.globalCT && e.colorTablesMatch(len(pm.Palette), transparentIndex) {
319 e.writeByte(0)
320 } else {
321
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))
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()
352 bw.close()
353 }
354
355
356 type Options struct {
357
358
359 NumColors int
360
361
362
363 Quantizer draw.Quantizer
364
365
366
367 Drawer draw.Drawer
368 }
369
370
371
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
383
384
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
418 func Encode(w io.Writer, m image.Image, o *Options) error {
419
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
449
450
451
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
460
461
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