mirror of
https://github.com/golang/go
synced 2024-11-05 17:36:15 -07:00
image/gif: don't encode local color tables if they're the same as the
global color table. Change-Id: Ia38f75708ed5e5b430680a1eecafb4fc8047269c Reviewed-on: https://go-review.googlesource.com/9467 Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
parent
10f6d30315
commit
4ddd751c92
@ -6,6 +6,7 @@ package gif
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"compress/lzw"
|
"compress/lzw"
|
||||||
"errors"
|
"errors"
|
||||||
"image"
|
"image"
|
||||||
@ -53,8 +54,12 @@ type encoder struct {
|
|||||||
err error
|
err error
|
||||||
// g is a reference to the data that is being encoded.
|
// g is a reference to the data that is being encoded.
|
||||||
g GIF
|
g GIF
|
||||||
// buf is a scratch buffer. It must be at least 768 so we can write the color map.
|
// globalCT is the size in bytes of the global color table.
|
||||||
buf [1024]byte
|
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
|
// 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[1] = e.g.BackgroundIndex
|
||||||
e.buf[2] = 0x00 // Pixel Aspect Ratio.
|
e.buf[2] = 0x00 // Pixel Aspect Ratio.
|
||||||
e.write(e.buf[:3])
|
e.write(e.buf[:3])
|
||||||
e.writeColorTable(p, paddedSize)
|
e.globalCT = encodeColorTable(e.globalColorTable[:], p, paddedSize)
|
||||||
|
e.write(e.globalColorTable[:e.globalCT])
|
||||||
} else {
|
} else {
|
||||||
// All frames have a local color table, so a global color table
|
// All frames have a local color table, so a global color table
|
||||||
// is not needed.
|
// is not needed.
|
||||||
@ -155,25 +161,22 @@ func (e *encoder) writeHeader() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) writeColorTable(p color.Palette, size int) {
|
func encodeColorTable(dst []byte, p color.Palette, size int) int {
|
||||||
if e.err != nil {
|
n := log2Lookup[size]
|
||||||
return
|
for i := 0; i < n; i++ {
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < log2Lookup[size]; i++ {
|
|
||||||
if i < len(p) {
|
if i < len(p) {
|
||||||
r, g, b, _ := p[i].RGBA()
|
r, g, b, _ := p[i].RGBA()
|
||||||
e.buf[3*i+0] = uint8(r >> 8)
|
dst[3*i+0] = uint8(r >> 8)
|
||||||
e.buf[3*i+1] = uint8(g >> 8)
|
dst[3*i+1] = uint8(g >> 8)
|
||||||
e.buf[3*i+2] = uint8(b >> 8)
|
dst[3*i+2] = uint8(b >> 8)
|
||||||
} else {
|
} else {
|
||||||
// Pad with black.
|
// Pad with black.
|
||||||
e.buf[3*i+0] = 0x00
|
dst[3*i+0] = 0x00
|
||||||
e.buf[3*i+1] = 0x00
|
dst[3*i+1] = 0x00
|
||||||
e.buf[3*i+2] = 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) {
|
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])
|
e.write(e.buf[:9])
|
||||||
|
|
||||||
paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n).
|
paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n).
|
||||||
// Interlacing is not supported.
|
ct := encodeColorTable(e.localColorTable[:], pm.Palette, paddedSize)
|
||||||
e.writeByte(0x80 | uint8(paddedSize))
|
if ct != e.globalCT || !bytes.Equal(e.globalColorTable[:ct], e.localColorTable[:ct]) {
|
||||||
|
// Use a local color table.
|
||||||
// Local Color Table.
|
e.writeByte(fColorTable | uint8(paddedSize))
|
||||||
e.writeColorTable(pm.Palette, paddedSize)
|
e.write(e.localColorTable[:ct])
|
||||||
|
} else {
|
||||||
|
// Use the global color table.
|
||||||
|
e.writeByte(0)
|
||||||
|
}
|
||||||
|
|
||||||
litWidth := paddedSize + 1
|
litWidth := paddedSize + 1
|
||||||
if litWidth < 2 {
|
if litWidth < 2 {
|
||||||
|
@ -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) {
|
func BenchmarkEncode(b *testing.B) {
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
|
Loading…
Reference in New Issue
Block a user