* tree-ssa-reassoc.c (reassociate_bb): Clarify code slighly.
[official-gcc.git] / libgo / go / image / gif / writer.go
blob493c7549eb2ab8c93ff86b541f5a9e8312b9ecd3
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.
5 package gif
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"
19 // Graphic control extension fields.
20 const (
21 gcLabel = 0xF9
22 gcBlockSize = 0x04
25 var log2Lookup = [8]int{2, 4, 8, 16, 32, 64, 128, 256}
27 func log2(x int) int {
28 for i, v := range log2Lookup {
29 if x <= v {
30 return i
33 return -1
36 // Little-endian.
37 func writeUint16(b []uint8, u uint16) {
38 b[0] = uint8(u)
39 b[1] = uint8(u >> 8)
42 // writer is a buffered writer.
43 type writer interface {
44 Flush() error
45 io.Writer
46 io.ByteWriter
49 // encoder encodes an image to the GIF format.
50 type encoder struct {
51 // w is the writer to write to. err is the first error encountered during
52 // writing. All attempted writes after the first error become no-ops.
53 w writer
54 err error
55 // g is a reference to the data that is being encoded.
56 g GIF
57 // globalCT is the size in bytes of the global color table.
58 globalCT int
59 // buf is a scratch buffer. It must be at least 256 for the blockWriter.
60 buf [256]byte
61 globalColorTable [3 * 256]byte
62 localColorTable [3 * 256]byte
65 // blockWriter writes the block structure of GIF image data, which
66 // comprises (n, (n bytes)) blocks, with 1 <= n <= 255. It is the
67 // writer given to the LZW encoder, which is thus immune to the
68 // blocking.
69 type blockWriter struct {
70 e *encoder
73 func (b blockWriter) Write(data []byte) (int, error) {
74 if b.e.err != nil {
75 return 0, b.e.err
77 if len(data) == 0 {
78 return 0, nil
80 total := 0
81 for total < len(data) {
82 n := copy(b.e.buf[1:256], data[total:])
83 total += n
84 b.e.buf[0] = uint8(n)
86 _, b.e.err = b.e.w.Write(b.e.buf[:n+1])
87 if b.e.err != nil {
88 return 0, b.e.err
91 return total, b.e.err
94 func (e *encoder) flush() {
95 if e.err != nil {
96 return
98 e.err = e.w.Flush()
101 func (e *encoder) write(p []byte) {
102 if e.err != nil {
103 return
105 _, e.err = e.w.Write(p)
108 func (e *encoder) writeByte(b byte) {
109 if e.err != nil {
110 return
112 e.err = e.w.WriteByte(b)
115 func (e *encoder) writeHeader() {
116 if e.err != nil {
117 return
119 _, e.err = io.WriteString(e.w, "GIF89a")
120 if e.err != nil {
121 return
124 // Logical screen width and height.
125 writeUint16(e.buf[0:2], uint16(e.g.Config.Width))
126 writeUint16(e.buf[2:4], uint16(e.g.Config.Height))
127 e.write(e.buf[:4])
129 if p, ok := e.g.Config.ColorModel.(color.Palette); ok && len(p) > 0 {
130 paddedSize := log2(len(p)) // Size of Global Color Table: 2^(1+n).
131 e.buf[0] = fColorTable | uint8(paddedSize)
132 e.buf[1] = e.g.BackgroundIndex
133 e.buf[2] = 0x00 // Pixel Aspect Ratio.
134 e.write(e.buf[:3])
135 var err error
136 e.globalCT, err = encodeColorTable(e.globalColorTable[:], p, paddedSize)
137 if err != nil && e.err == nil {
138 e.err = err
139 return
141 e.write(e.globalColorTable[:e.globalCT])
142 } else {
143 // All frames have a local color table, so a global color table
144 // is not needed.
145 e.buf[0] = 0x00
146 e.buf[1] = 0x00 // Background Color Index.
147 e.buf[2] = 0x00 // Pixel Aspect Ratio.
148 e.write(e.buf[:3])
151 // Add animation info if necessary.
152 if len(e.g.Image) > 1 {
153 e.buf[0] = 0x21 // Extension Introducer.
154 e.buf[1] = 0xff // Application Label.
155 e.buf[2] = 0x0b // Block Size.
156 e.write(e.buf[:3])
157 _, err := io.WriteString(e.w, "NETSCAPE2.0") // Application Identifier.
158 if err != nil && e.err == nil {
159 e.err = err
160 return
162 e.buf[0] = 0x03 // Block Size.
163 e.buf[1] = 0x01 // Sub-block Index.
164 writeUint16(e.buf[2:4], uint16(e.g.LoopCount))
165 e.buf[4] = 0x00 // Block Terminator.
166 e.write(e.buf[:5])
170 func encodeColorTable(dst []byte, p color.Palette, size int) (int, error) {
171 if uint(size) >= uint(len(log2Lookup)) {
172 return 0, errors.New("gif: cannot encode color table with more than 256 entries")
174 n := log2Lookup[size]
175 for i := 0; i < n; i++ {
176 if i < len(p) {
177 c := p[i]
178 if c == nil {
179 return 0, errors.New("gif: cannot encode color table with nil entries")
181 r, g, b, _ := c.RGBA()
182 dst[3*i+0] = uint8(r >> 8)
183 dst[3*i+1] = uint8(g >> 8)
184 dst[3*i+2] = uint8(b >> 8)
185 } else {
186 // Pad with black.
187 dst[3*i+0] = 0x00
188 dst[3*i+1] = 0x00
189 dst[3*i+2] = 0x00
192 return 3 * n, nil
195 func (e *encoder) writeImageBlock(pm *image.Paletted, delay int, disposal byte) {
196 if e.err != nil {
197 return
200 if len(pm.Palette) == 0 {
201 e.err = errors.New("gif: cannot encode image block with empty palette")
202 return
205 b := pm.Bounds()
206 if b.Min.X < 0 || b.Max.X >= 1<<16 || b.Min.Y < 0 || b.Max.Y >= 1<<16 {
207 e.err = errors.New("gif: image block is too large to encode")
208 return
210 if !b.In(image.Rectangle{Max: image.Point{e.g.Config.Width, e.g.Config.Height}}) {
211 e.err = errors.New("gif: image block is out of bounds")
212 return
215 transparentIndex := -1
216 for i, c := range pm.Palette {
217 if c == nil {
218 e.err = errors.New("gif: cannot encode color table with nil entries")
219 return
221 if _, _, _, a := c.RGBA(); a == 0 {
222 transparentIndex = i
223 break
227 if delay > 0 || disposal != 0 || transparentIndex != -1 {
228 e.buf[0] = sExtension // Extension Introducer.
229 e.buf[1] = gcLabel // Graphic Control Label.
230 e.buf[2] = gcBlockSize // Block Size.
231 if transparentIndex != -1 {
232 e.buf[3] = 0x01 | disposal<<2
233 } else {
234 e.buf[3] = 0x00 | disposal<<2
236 writeUint16(e.buf[4:6], uint16(delay)) // Delay Time (1/100ths of a second)
238 // Transparent color index.
239 if transparentIndex != -1 {
240 e.buf[6] = uint8(transparentIndex)
241 } else {
242 e.buf[6] = 0x00
244 e.buf[7] = 0x00 // Block Terminator.
245 e.write(e.buf[:8])
247 e.buf[0] = sImageDescriptor
248 writeUint16(e.buf[1:3], uint16(b.Min.X))
249 writeUint16(e.buf[3:5], uint16(b.Min.Y))
250 writeUint16(e.buf[5:7], uint16(b.Dx()))
251 writeUint16(e.buf[7:9], uint16(b.Dy()))
252 e.write(e.buf[:9])
254 paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n).
255 if ct, err := encodeColorTable(e.localColorTable[:], pm.Palette, paddedSize); err != nil {
256 if e.err == nil {
257 e.err = err
259 return
260 } else if ct != e.globalCT || !bytes.Equal(e.globalColorTable[:ct], e.localColorTable[:ct]) {
261 // Use a local color table.
262 e.writeByte(fColorTable | uint8(paddedSize))
263 e.write(e.localColorTable[:ct])
264 } else {
265 // Use the global color table.
266 e.writeByte(0)
269 litWidth := paddedSize + 1
270 if litWidth < 2 {
271 litWidth = 2
273 e.writeByte(uint8(litWidth)) // LZW Minimum Code Size.
275 lzww := lzw.NewWriter(blockWriter{e: e}, lzw.LSB, litWidth)
276 if dx := b.Dx(); dx == pm.Stride {
277 _, e.err = lzww.Write(pm.Pix[:dx*b.Dy()])
278 if e.err != nil {
279 lzww.Close()
280 return
282 } else {
283 for i, y := 0, b.Min.Y; y < b.Max.Y; i, y = i+pm.Stride, y+1 {
284 _, e.err = lzww.Write(pm.Pix[i : i+dx])
285 if e.err != nil {
286 lzww.Close()
287 return
291 lzww.Close()
292 e.writeByte(0x00) // Block Terminator.
295 // Options are the encoding parameters.
296 type Options struct {
297 // NumColors is the maximum number of colors used in the image.
298 // It ranges from 1 to 256.
299 NumColors int
301 // Quantizer is used to produce a palette with size NumColors.
302 // palette.Plan9 is used in place of a nil Quantizer.
303 Quantizer draw.Quantizer
305 // Drawer is used to convert the source image to the desired palette.
306 // draw.FloydSteinberg is used in place of a nil Drawer.
307 Drawer draw.Drawer
310 // EncodeAll writes the images in g to w in GIF format with the
311 // given loop count and delay between frames.
312 func EncodeAll(w io.Writer, g *GIF) error {
313 if len(g.Image) == 0 {
314 return errors.New("gif: must provide at least one image")
317 if len(g.Image) != len(g.Delay) {
318 return errors.New("gif: mismatched image and delay lengths")
320 if g.LoopCount < 0 {
321 g.LoopCount = 0
324 e := encoder{g: *g}
325 // The GIF.Disposal, GIF.Config and GIF.BackgroundIndex fields were added
326 // in Go 1.5. Valid Go 1.4 code, such as when the Disposal field is omitted
327 // in a GIF struct literal, should still produce valid GIFs.
328 if e.g.Disposal != nil && len(e.g.Image) != len(e.g.Disposal) {
329 return errors.New("gif: mismatched image and disposal lengths")
331 if e.g.Config == (image.Config{}) {
332 p := g.Image[0].Bounds().Max
333 e.g.Config.Width = p.X
334 e.g.Config.Height = p.Y
335 } else if e.g.Config.ColorModel != nil {
336 if _, ok := e.g.Config.ColorModel.(color.Palette); !ok {
337 return errors.New("gif: GIF color model must be a color.Palette")
341 if ww, ok := w.(writer); ok {
342 e.w = ww
343 } else {
344 e.w = bufio.NewWriter(w)
347 e.writeHeader()
348 for i, pm := range g.Image {
349 disposal := uint8(0)
350 if g.Disposal != nil {
351 disposal = g.Disposal[i]
353 e.writeImageBlock(pm, g.Delay[i], disposal)
355 e.writeByte(sTrailer)
356 e.flush()
357 return e.err
360 // Encode writes the Image m to w in GIF format.
361 func Encode(w io.Writer, m image.Image, o *Options) error {
362 // Check for bounds and size restrictions.
363 b := m.Bounds()
364 if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 {
365 return errors.New("gif: image is too large to encode")
368 opts := Options{}
369 if o != nil {
370 opts = *o
372 if opts.NumColors < 1 || 256 < opts.NumColors {
373 opts.NumColors = 256
375 if opts.Drawer == nil {
376 opts.Drawer = draw.FloydSteinberg
379 pm, ok := m.(*image.Paletted)
380 if !ok || len(pm.Palette) > opts.NumColors {
381 // TODO: Pick a better sub-sample of the Plan 9 palette.
382 pm = image.NewPaletted(b, palette.Plan9[:opts.NumColors])
383 if opts.Quantizer != nil {
384 pm.Palette = opts.Quantizer.Quantize(make(color.Palette, 0, opts.NumColors), m)
386 opts.Drawer.Draw(pm, b, m, image.ZP)
389 // When calling Encode instead of EncodeAll, the single-frame image is
390 // translated such that its top-left corner is (0, 0), so that the single
391 // frame completely fills the overall GIF's bounds.
392 if pm.Rect.Min != (image.Point{}) {
393 dup := *pm
394 dup.Rect = dup.Rect.Sub(dup.Rect.Min)
395 pm = &dup
398 return EncodeAll(w, &GIF{
399 Image: []*image.Paletted{pm},
400 Delay: []int{0},
401 Config: image.Config{
402 ColorModel: pm.Palette,
403 Width: b.Dx(),
404 Height: b.Dy(),