github.com/spotify/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/pkg/image/gif/writer.go (about)

     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.
     4  
     5  package gif
     6  
     7  import (
     8  	"bufio"
     9  	"compress/lzw"
    10  	"errors"
    11  	"image"
    12  	"image/color"
    13  	"image/color/palette"
    14  	"image/draw"
    15  	"io"
    16  )
    17  
    18  // Graphic control extension fields.
    19  const (
    20  	gcLabel     = 0xF9
    21  	gcBlockSize = 0x04
    22  )
    23  
    24  var log2Lookup = [8]int{2, 4, 8, 16, 32, 64, 128, 256}
    25  
    26  func log2(x int) int {
    27  	for i, v := range log2Lookup {
    28  		if x <= v {
    29  			return i
    30  		}
    31  	}
    32  	return -1
    33  }
    34  
    35  // Little-endian.
    36  func writeUint16(b []uint8, u uint16) {
    37  	b[0] = uint8(u)
    38  	b[1] = uint8(u >> 8)
    39  }
    40  
    41  // writer is a buffered writer.
    42  type writer interface {
    43  	Flush() error
    44  	io.Writer
    45  	io.ByteWriter
    46  }
    47  
    48  // encoder encodes an image to the GIF format.
    49  type encoder struct {
    50  	// w is the writer to write to. err is the first error encountered during
    51  	// writing. All attempted writes after the first error become no-ops.
    52  	w   writer
    53  	err error
    54  	// g is a reference to the data that is being encoded.
    55  	g *GIF
    56  	// buf is a scratch buffer. It must be at least 768 so we can write the color map.
    57  	buf [1024]byte
    58  }
    59  
    60  // blockWriter writes the block structure of GIF image data, which
    61  // comprises (n, (n bytes)) blocks, with 1 <= n <= 255. It is the
    62  // writer given to the LZW encoder, which is thus immune to the
    63  // blocking.
    64  type blockWriter struct {
    65  	e *encoder
    66  }
    67  
    68  func (b blockWriter) Write(data []byte) (int, error) {
    69  	if b.e.err != nil {
    70  		return 0, b.e.err
    71  	}
    72  	if len(data) == 0 {
    73  		return 0, nil
    74  	}
    75  	total := 0
    76  	for total < len(data) {
    77  		n := copy(b.e.buf[1:256], data[total:])
    78  		total += n
    79  		b.e.buf[0] = uint8(n)
    80  
    81  		n, b.e.err = b.e.w.Write(b.e.buf[:n+1])
    82  		if b.e.err != nil {
    83  			return 0, b.e.err
    84  		}
    85  	}
    86  	return total, b.e.err
    87  }
    88  
    89  func (e *encoder) flush() {
    90  	if e.err != nil {
    91  		return
    92  	}
    93  	e.err = e.w.Flush()
    94  }
    95  
    96  func (e *encoder) write(p []byte) {
    97  	if e.err != nil {
    98  		return
    99  	}
   100  	_, e.err = e.w.Write(p)
   101  }
   102  
   103  func (e *encoder) writeByte(b byte) {
   104  	if e.err != nil {
   105  		return
   106  	}
   107  	e.err = e.w.WriteByte(b)
   108  }
   109  
   110  func (e *encoder) writeHeader() {
   111  	if e.err != nil {
   112  		return
   113  	}
   114  	_, e.err = io.WriteString(e.w, "GIF89a")
   115  	if e.err != nil {
   116  		return
   117  	}
   118  
   119  	pm := e.g.Image[0]
   120  	// Logical screen width and height.
   121  	writeUint16(e.buf[0:2], uint16(pm.Bounds().Dx()))
   122  	writeUint16(e.buf[2:4], uint16(pm.Bounds().Dy()))
   123  	e.write(e.buf[:4])
   124  
   125  	// All frames have a local color table, so a global color table
   126  	// is not needed.
   127  	e.buf[0] = 0x00
   128  	e.buf[1] = 0x00 // Background Color Index.
   129  	e.buf[2] = 0x00 // Pixel Aspect Ratio.
   130  	e.write(e.buf[:3])
   131  
   132  	// Add animation info if necessary.
   133  	if len(e.g.Image) > 1 {
   134  		e.buf[0] = 0x21 // Extension Introducer.
   135  		e.buf[1] = 0xff // Application Label.
   136  		e.buf[2] = 0x0b // Block Size.
   137  		e.write(e.buf[:3])
   138  		_, e.err = io.WriteString(e.w, "NETSCAPE2.0") // Application Identifier.
   139  		if e.err != nil {
   140  			return
   141  		}
   142  		e.buf[0] = 0x03 // Block Size.
   143  		e.buf[1] = 0x01 // Sub-block Index.
   144  		writeUint16(e.buf[2:4], uint16(e.g.LoopCount))
   145  		e.buf[4] = 0x00 // Block Terminator.
   146  		e.write(e.buf[:5])
   147  	}
   148  }
   149  
   150  func (e *encoder) writeColorTable(p color.Palette, size int) {
   151  	if e.err != nil {
   152  		return
   153  	}
   154  
   155  	for i := 0; i < log2Lookup[size]; i++ {
   156  		if i < len(p) {
   157  			r, g, b, _ := p[i].RGBA()
   158  			e.buf[3*i+0] = uint8(r >> 8)
   159  			e.buf[3*i+1] = uint8(g >> 8)
   160  			e.buf[3*i+2] = uint8(b >> 8)
   161  		} else {
   162  			// Pad with black.
   163  			e.buf[3*i+0] = 0x00
   164  			e.buf[3*i+1] = 0x00
   165  			e.buf[3*i+2] = 0x00
   166  		}
   167  	}
   168  	e.write(e.buf[:3*log2Lookup[size]])
   169  }
   170  
   171  func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) {
   172  	if e.err != nil {
   173  		return
   174  	}
   175  
   176  	if len(pm.Palette) == 0 {
   177  		e.err = errors.New("gif: cannot encode image block with empty palette")
   178  		return
   179  	}
   180  
   181  	b := pm.Bounds()
   182  	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 {
   183  		e.err = errors.New("gif: image block is too large to encode")
   184  		return
   185  	}
   186  
   187  	transparentIndex := -1
   188  	for i, c := range pm.Palette {
   189  		if _, _, _, a := c.RGBA(); a == 0 {
   190  			transparentIndex = i
   191  			break
   192  		}
   193  	}
   194  
   195  	if delay > 0 || transparentIndex != -1 {
   196  		e.buf[0] = sExtension  // Extension Introducer.
   197  		e.buf[1] = gcLabel     // Graphic Control Label.
   198  		e.buf[2] = gcBlockSize // Block Size.
   199  		if transparentIndex != -1 {
   200  			e.buf[3] = 0x01
   201  		} else {
   202  			e.buf[3] = 0x00
   203  		}
   204  		writeUint16(e.buf[4:6], uint16(delay)) // Delay Time (1/100ths of a second)
   205  
   206  		// Transparent color index.
   207  		if transparentIndex != -1 {
   208  			e.buf[6] = uint8(transparentIndex)
   209  		} else {
   210  			e.buf[6] = 0x00
   211  		}
   212  		e.buf[7] = 0x00 // Block Terminator.
   213  		e.write(e.buf[:8])
   214  	}
   215  	e.buf[0] = sImageDescriptor
   216  	writeUint16(e.buf[1:3], uint16(b.Min.X))
   217  	writeUint16(e.buf[3:5], uint16(b.Min.Y))
   218  	writeUint16(e.buf[5:7], uint16(b.Dx()))
   219  	writeUint16(e.buf[7:9], uint16(b.Dy()))
   220  	e.write(e.buf[:9])
   221  
   222  	paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n).
   223  	// Interlacing is not supported.
   224  	e.writeByte(0x80 | uint8(paddedSize))
   225  
   226  	// Local Color Table.
   227  	e.writeColorTable(pm.Palette, paddedSize)
   228  
   229  	litWidth := paddedSize + 1
   230  	if litWidth < 2 {
   231  		litWidth = 2
   232  	}
   233  	e.writeByte(uint8(litWidth)) // LZW Minimum Code Size.
   234  
   235  	lzww := lzw.NewWriter(blockWriter{e: e}, lzw.LSB, litWidth)
   236  	_, e.err = lzww.Write(pm.Pix)
   237  	if e.err != nil {
   238  		lzww.Close()
   239  		return
   240  	}
   241  	lzww.Close()
   242  	e.writeByte(0x00) // Block Terminator.
   243  }
   244  
   245  // Options are the encoding parameters.
   246  type Options struct {
   247  	// NumColors is the maximum number of colors used in the image.
   248  	// It ranges from 1 to 256.
   249  	NumColors int
   250  
   251  	// Quantizer is used to produce a palette with size NumColors.
   252  	// palette.Plan9 is used in place of a nil Quantizer.
   253  	Quantizer draw.Quantizer
   254  
   255  	// Drawer is used to convert the source image to the desired palette.
   256  	// draw.FloydSteinberg is used in place of a nil Drawer.
   257  	Drawer draw.Drawer
   258  }
   259  
   260  // EncodeAll writes the images in g to w in GIF format with the
   261  // given loop count and delay between frames.
   262  func EncodeAll(w io.Writer, g *GIF) error {
   263  	if len(g.Image) == 0 {
   264  		return errors.New("gif: must provide at least one image")
   265  	}
   266  
   267  	if len(g.Image) != len(g.Delay) {
   268  		return errors.New("gif: mismatched image and delay lengths")
   269  	}
   270  	if g.LoopCount < 0 {
   271  		g.LoopCount = 0
   272  	}
   273  
   274  	e := encoder{g: g}
   275  	if ww, ok := w.(writer); ok {
   276  		e.w = ww
   277  	} else {
   278  		e.w = bufio.NewWriter(w)
   279  	}
   280  
   281  	e.writeHeader()
   282  	for i, pm := range g.Image {
   283  		e.writeImageBlock(pm, g.Delay[i])
   284  	}
   285  	e.writeByte(sTrailer)
   286  	e.flush()
   287  	return e.err
   288  }
   289  
   290  // Encode writes the Image m to w in GIF format.
   291  func Encode(w io.Writer, m image.Image, o *Options) error {
   292  	// Check for bounds and size restrictions.
   293  	b := m.Bounds()
   294  	if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 {
   295  		return errors.New("gif: image is too large to encode")
   296  	}
   297  
   298  	opts := Options{}
   299  	if o != nil {
   300  		opts = *o
   301  	}
   302  	if opts.NumColors < 1 || 256 < opts.NumColors {
   303  		opts.NumColors = 256
   304  	}
   305  	if opts.Drawer == nil {
   306  		opts.Drawer = draw.FloydSteinberg
   307  	}
   308  
   309  	pm, ok := m.(*image.Paletted)
   310  	if !ok || len(pm.Palette) > opts.NumColors {
   311  		// TODO: Pick a better sub-sample of the Plan 9 palette.
   312  		pm = image.NewPaletted(b, palette.Plan9[:opts.NumColors])
   313  		if opts.Quantizer != nil {
   314  			pm.Palette = opts.Quantizer.Quantize(make(color.Palette, 0, opts.NumColors), m)
   315  		}
   316  		opts.Drawer.Draw(pm, b, m, image.ZP)
   317  	}
   318  
   319  	return EncodeAll(w, &GIF{
   320  		Image: []*image.Paletted{pm},
   321  		Delay: []int{0},
   322  	})
   323  }