]> Cypherpunks repositories - gostls13.git/commitdiff
image/gif: don't encode local color tables if they're the same as the
authorNigel Tao <nigeltao@golang.org>
Wed, 29 Apr 2015 06:40:24 +0000 (16:40 +1000)
committerNigel Tao <nigeltao@golang.org>
Mon, 4 May 2015 06:38:54 +0000 (06:38 +0000)
global color table.

Change-Id: Ia38f75708ed5e5b430680a1eecafb4fc8047269c
Reviewed-on: https://go-review.googlesource.com/9467
Reviewed-by: Rob Pike <r@golang.org>
src/image/gif/writer.go
src/image/gif/writer_test.go

index 322b353fcb2a307ae8bda0287c20fefd36fd7c07..e14d0346028d00c882dab01690347373609fc951 100644 (file)
@@ -6,6 +6,7 @@ package gif
 
 import (
        "bufio"
+       "bytes"
        "compress/lzw"
        "errors"
        "image"
@@ -53,8 +54,12 @@ type encoder struct {
        err error
        // g is a reference to the data that is being encoded.
        g GIF
-       // buf is a scratch buffer. It must be at least 768 so we can write the color map.
-       buf [1024]byte
+       // globalCT is the size in bytes of the global color table.
+       globalCT int
+       // buf is a scratch buffer. It must be at least 256 for the blockWriter.
+       buf              [256]byte
+       globalColorTable [3 * 256]byte
+       localColorTable  [3 * 256]byte
 }
 
 // blockWriter writes the block structure of GIF image data, which
@@ -127,7 +132,8 @@ func (e *encoder) writeHeader() {
                e.buf[1] = e.g.BackgroundIndex
                e.buf[2] = 0x00 // Pixel Aspect Ratio.
                e.write(e.buf[:3])
-               e.writeColorTable(p, paddedSize)
+               e.globalCT = encodeColorTable(e.globalColorTable[:], p, paddedSize)
+               e.write(e.globalColorTable[:e.globalCT])
        } else {
                // All frames have a local color table, so a global color table
                // is not needed.
@@ -155,25 +161,22 @@ func (e *encoder) writeHeader() {
        }
 }
 
-func (e *encoder) writeColorTable(p color.Palette, size int) {
-       if e.err != nil {
-               return
-       }
-
-       for i := 0; i < log2Lookup[size]; i++ {
+func encodeColorTable(dst []byte, p color.Palette, size int) int {
+       n := log2Lookup[size]
+       for i := 0; i < n; i++ {
                if i < len(p) {
                        r, g, b, _ := p[i].RGBA()
-                       e.buf[3*i+0] = uint8(r >> 8)
-                       e.buf[3*i+1] = uint8(g >> 8)
-                       e.buf[3*i+2] = uint8(b >> 8)
+                       dst[3*i+0] = uint8(r >> 8)
+                       dst[3*i+1] = uint8(g >> 8)
+                       dst[3*i+2] = uint8(b >> 8)
                } else {
                        // Pad with black.
-                       e.buf[3*i+0] = 0x00
-                       e.buf[3*i+1] = 0x00
-                       e.buf[3*i+2] = 0x00
+                       dst[3*i+0] = 0x00
+                       dst[3*i+1] = 0x00
+                       dst[3*i+2] = 0x00
                }
        }
-       e.write(e.buf[:3*log2Lookup[size]])
+       return 3 * n
 }
 
 func (e *encoder) writeImageBlock(pm *image.Paletted, delay int, disposal byte) {
@@ -232,11 +235,15 @@ func (e *encoder) writeImageBlock(pm *image.Paletted, delay int, disposal byte)
        e.write(e.buf[:9])
 
        paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n).
-       // Interlacing is not supported.
-       e.writeByte(0x80 | uint8(paddedSize))
-
-       // Local Color Table.
-       e.writeColorTable(pm.Palette, paddedSize)
+       ct := encodeColorTable(e.localColorTable[:], pm.Palette, paddedSize)
+       if ct != e.globalCT || !bytes.Equal(e.globalColorTable[:ct], e.localColorTable[:ct]) {
+               // Use a local color table.
+               e.writeByte(fColorTable | uint8(paddedSize))
+               e.write(e.localColorTable[:ct])
+       } else {
+               // Use the global color table.
+               e.writeByte(0)
+       }
 
        litWidth := paddedSize + 1
        if litWidth < 2 {
index d661015b17ca9b44b211a04f922c353569aec1e1..d33976e415a502517be7b0f3dbd94a59d7d92802 100644 (file)
@@ -357,7 +357,60 @@ func TestEncodeImplicitConfigSize(t *testing.T) {
        }
 }
 
-// TODO: add test for when a frame has the same color map (palette) as the global one.
+func TestEncodePalettes(t *testing.T) {
+       const w, h = 5, 5
+       pals := []color.Palette{{
+               color.RGBA{0x00, 0x00, 0x00, 0xff},
+               color.RGBA{0x01, 0x00, 0x00, 0xff},
+               color.RGBA{0x02, 0x00, 0x00, 0xff},
+       }, {
+               color.RGBA{0x00, 0x00, 0x00, 0xff},
+               color.RGBA{0x00, 0x01, 0x00, 0xff},
+       }, {
+               color.RGBA{0x00, 0x00, 0x03, 0xff},
+               color.RGBA{0x00, 0x00, 0x02, 0xff},
+               color.RGBA{0x00, 0x00, 0x01, 0xff},
+               color.RGBA{0x00, 0x00, 0x00, 0xff},
+       }, {
+               color.RGBA{0x10, 0x07, 0xf0, 0xff},
+               color.RGBA{0x20, 0x07, 0xf0, 0xff},
+               color.RGBA{0x30, 0x07, 0xf0, 0xff},
+               color.RGBA{0x40, 0x07, 0xf0, 0xff},
+               color.RGBA{0x50, 0x07, 0xf0, 0xff},
+       }}
+       g0 := &GIF{
+               Image: []*image.Paletted{
+                       image.NewPaletted(image.Rect(0, 0, w, h), pals[0]),
+                       image.NewPaletted(image.Rect(0, 0, w, h), pals[1]),
+                       image.NewPaletted(image.Rect(0, 0, w, h), pals[2]),
+                       image.NewPaletted(image.Rect(0, 0, w, h), pals[3]),
+               },
+               Delay:    make([]int, len(pals)),
+               Disposal: make([]byte, len(pals)),
+               Config: image.Config{
+                       ColorModel: pals[2],
+                       Width:      w,
+                       Height:     h,
+               },
+       }
+
+       var buf bytes.Buffer
+       if err := EncodeAll(&buf, g0); err != nil {
+               t.Fatalf("EncodeAll: %v", err)
+       }
+       g1, err := DecodeAll(&buf)
+       if err != nil {
+               t.Fatalf("DecodeAll: %v", err)
+       }
+       if len(g0.Image) != len(g1.Image) {
+               t.Fatalf("image lengths differ: %d and %d", len(g0.Image), len(g1.Image))
+       }
+       for i, m := range g1.Image {
+               if got, want := m.Palette, pals[i]; !palettesEqual(got, want) {
+                       t.Errorf("frame %d:\ngot  %v\nwant %v", i, got, want)
+               }
+       }
+}
 
 func BenchmarkEncode(b *testing.B) {
        b.StopTimer()