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.
19 // Graphic control extension fields.
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
{
37 func writeUint16(b
[]uint8, u
uint16) {
42 // writer is a buffered writer.
43 type writer
interface {
49 // encoder encodes an image to the GIF format.
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.
55 // g is a reference to the data that is being encoded.
57 // globalCT is the size in bytes of the global color table.
59 // buf is a scratch buffer. It must be at least 256 for the blockWriter.
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
69 type blockWriter
struct {
73 func (b blockWriter
) Write(data
[]byte) (int, error
) {
81 for total
< len(data
) {
82 n
:= copy(b
.e
.buf
[1:256], data
[total
:])
86 _
, b
.e
.err
= b
.e
.w
.Write(b
.e
.buf
[:n
+1])
94 func (e
*encoder
) flush() {
101 func (e
*encoder
) write(p
[]byte) {
105 _
, e
.err
= e
.w
.Write(p
)
108 func (e
*encoder
) writeByte(b
byte) {
112 e
.err
= e
.w
.WriteByte(b
)
115 func (e
*encoder
) writeHeader() {
119 _
, e
.err
= io
.WriteString(e
.w
, "GIF89a")
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
))
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.
136 e
.globalCT
, err
= encodeColorTable(e
.globalColorTable
[:], p
, paddedSize
)
137 if err
!= nil && e
.err
== nil {
141 e
.write(e
.globalColorTable
[:e
.globalCT
])
143 // All frames have a local color table, so a global color table
146 e
.buf
[1] = 0x00 // Background Color Index.
147 e
.buf
[2] = 0x00 // Pixel Aspect Ratio.
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.
157 _
, err
:= io
.WriteString(e
.w
, "NETSCAPE2.0") // Application Identifier.
158 if err
!= nil && e
.err
== nil {
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.
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
++ {
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)
195 func (e
*encoder
) writeImageBlock(pm
*image
.Paletted
, delay
int, disposal
byte) {
200 if len(pm
.Palette
) == 0 {
201 e
.err
= errors
.New("gif: cannot encode image block with empty palette")
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")
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")
215 transparentIndex
:= -1
216 for i
, c
:= range pm
.Palette
{
218 e
.err
= errors
.New("gif: cannot encode color table with nil entries")
221 if _
, _
, _
, a
:= c
.RGBA(); a
== 0 {
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
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
)
244 e
.buf
[7] = 0x00 // Block Terminator.
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()))
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 {
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
])
265 // Use the global color table.
269 litWidth
:= paddedSize
+ 1
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()])
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
])
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.
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.
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")
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
{
344 e
.w
= bufio
.NewWriter(w
)
348 for i
, pm
:= range g
.Image
{
350 if g
.Disposal
!= nil {
351 disposal
= g
.Disposal
[i
]
353 e
.writeImageBlock(pm
, g
.Delay
[i
], disposal
)
355 e
.writeByte(sTrailer
)
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.
364 if b
.Dx() >= 1<<16 || b
.Dy() >= 1<<16 {
365 return errors
.New("gif: image is too large to encode")
372 if opts
.NumColors
< 1 ||
256 < opts
.NumColors
{
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
{}) {
394 dup
.Rect
= dup
.Rect
.Sub(dup
.Rect
.Min
)
398 return EncodeAll(w
, &GIF
{
399 Image
: []*image
.Paletted
{pm
},
401 Config
: image
.Config
{
402 ColorModel
: pm
.Palette
,