Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(703)

Delta Between Two Patch Sets: src/pkg/image/gif/writer.go

Issue 10896043: image/gif: add writer implementation (Closed)
Left Patch Set: diff -r c1301713bae7 https://code.google.com/p/go Created 10 years, 8 months ago
Right Patch Set: diff -r 26f441a1f78b https://code.google.com/p/go/ Created 10 years, 8 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « src/pkg/image/gif/reader.go ('k') | src/pkg/image/gif/writer_test.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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
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
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
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
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 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b