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

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

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

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