diff options
Diffstat (limited to 'src/image/gif/writer_test.go')
-rw-r--r-- | src/image/gif/writer_test.go | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/src/image/gif/writer_test.go b/src/image/gif/writer_test.go new file mode 100644 index 000000000..93306ffdb --- /dev/null +++ b/src/image/gif/writer_test.go @@ -0,0 +1,227 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gif + +import ( + "bytes" + "image" + "image/color" + _ "image/png" + "io/ioutil" + "math/rand" + "os" + "testing" +) + +func readImg(filename string) (image.Image, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + m, _, err := image.Decode(f) + return m, err +} + +func readGIF(filename string) (*GIF, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + return DecodeAll(f) +} + +func delta(u0, u1 uint32) int64 { + d := int64(u0) - int64(u1) + if d < 0 { + return -d + } + return d +} + +// averageDelta returns the average delta in RGB space. The two images must +// have the same bounds. +func averageDelta(m0, m1 image.Image) int64 { + b := m0.Bounds() + var sum, n int64 + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { + c0 := m0.At(x, y) + c1 := m1.At(x, y) + r0, g0, b0, _ := c0.RGBA() + r1, g1, b1, _ := c1.RGBA() + sum += delta(r0, r1) + sum += delta(g0, g1) + sum += delta(b0, b1) + n += 3 + } + } + return sum / n +} + +var testCase = []struct { + filename string + tolerance int64 +}{ + {"../testdata/video-001.png", 1 << 12}, + {"../testdata/video-001.gif", 0}, + {"../testdata/video-001.interlaced.gif", 0}, +} + +func TestWriter(t *testing.T) { + for _, tc := range testCase { + m0, err := readImg(tc.filename) + if err != nil { + t.Error(tc.filename, err) + continue + } + var buf bytes.Buffer + err = Encode(&buf, m0, nil) + if err != nil { + t.Error(tc.filename, err) + continue + } + m1, err := Decode(&buf) + if err != nil { + t.Error(tc.filename, err) + continue + } + if m0.Bounds() != m1.Bounds() { + t.Errorf("%s, bounds differ: %v and %v", tc.filename, m0.Bounds(), m1.Bounds()) + continue + } + // Compare the average delta to the tolerance level. + avgDelta := averageDelta(m0, m1) + if avgDelta > tc.tolerance { + t.Errorf("%s: average delta is too high. expected: %d, got %d", tc.filename, tc.tolerance, avgDelta) + continue + } + } +} + +func TestSubImage(t *testing.T) { + m0, err := readImg("../testdata/video-001.gif") + if err != nil { + t.Fatalf("readImg: %v", err) + } + m0 = m0.(*image.Paletted).SubImage(image.Rect(0, 0, 50, 30)) + var buf bytes.Buffer + err = Encode(&buf, m0, nil) + if err != nil { + t.Fatalf("Encode: %v", err) + } + m1, err := Decode(&buf) + if err != nil { + t.Fatalf("Decode: %v", err) + } + if m0.Bounds() != m1.Bounds() { + t.Fatalf("bounds differ: %v and %v", m0.Bounds(), m1.Bounds()) + } + if averageDelta(m0, m1) != 0 { + t.Fatalf("images differ") + } +} + +var frames = []string{ + "../testdata/video-001.gif", + "../testdata/video-005.gray.gif", +} + +func TestEncodeAll(t *testing.T) { + g0 := &GIF{ + Image: make([]*image.Paletted, len(frames)), + Delay: make([]int, len(frames)), + LoopCount: 5, + } + for i, f := range frames { + m, err := readGIF(f) + if err != nil { + t.Fatal(f, err) + } + g0.Image[i] = m.Image[0] + } + var buf bytes.Buffer + if err := EncodeAll(&buf, g0); err != nil { + t.Fatal("EncodeAll:", err) + } + g1, err := DecodeAll(&buf) + if err != nil { + t.Fatal("DecodeAll:", err) + } + if g0.LoopCount != g1.LoopCount { + t.Errorf("loop counts differ: %d and %d", g0.LoopCount, g1.LoopCount) + } + for i := range g0.Image { + m0, m1 := g0.Image[i], g1.Image[i] + if m0.Bounds() != m1.Bounds() { + t.Errorf("%s, bounds differ: %v and %v", frames[i], m0.Bounds(), m1.Bounds()) + } + d0, d1 := g0.Delay[i], g1.Delay[i] + if d0 != d1 { + t.Errorf("%s: delay values differ: %d and %d", frames[i], d0, d1) + } + } + + g1.Delay = make([]int, 1) + if err := EncodeAll(ioutil.Discard, g1); err == nil { + t.Error("expected error from mismatched delay and image slice lengths") + } + if err := EncodeAll(ioutil.Discard, &GIF{}); err == nil { + t.Error("expected error from providing empty gif") + } +} + +func BenchmarkEncode(b *testing.B) { + b.StopTimer() + + bo := image.Rect(0, 0, 640, 480) + rnd := rand.New(rand.NewSource(123)) + + // Restrict to a 256-color paletted image to avoid quantization path. + palette := make(color.Palette, 256) + for i := range palette { + palette[i] = color.RGBA{ + uint8(rnd.Intn(256)), + uint8(rnd.Intn(256)), + uint8(rnd.Intn(256)), + 255, + } + } + img := image.NewPaletted(image.Rect(0, 0, 640, 480), palette) + for y := bo.Min.Y; y < bo.Max.Y; y++ { + for x := bo.Min.X; x < bo.Max.X; x++ { + img.Set(x, y, palette[rnd.Intn(256)]) + } + } + + b.SetBytes(640 * 480 * 4) + b.StartTimer() + for i := 0; i < b.N; i++ { + Encode(ioutil.Discard, img, nil) + } +} + +func BenchmarkQuantizedEncode(b *testing.B) { + b.StopTimer() + img := image.NewRGBA(image.Rect(0, 0, 640, 480)) + bo := img.Bounds() + rnd := rand.New(rand.NewSource(123)) + for y := bo.Min.Y; y < bo.Max.Y; y++ { + for x := bo.Min.X; x < bo.Max.X; x++ { + img.SetRGBA(x, y, color.RGBA{ + uint8(rnd.Intn(256)), + uint8(rnd.Intn(256)), + uint8(rnd.Intn(256)), + 255, + }) + } + } + b.SetBytes(640 * 480 * 4) + b.StartTimer() + for i := 0; i < b.N; i++ { + Encode(ioutil.Discard, img, nil) + } +} |