]> Cypherpunks repositories - gostls13.git/commitdiff
image/gif: fix transparency loss when encoding a wrapped *image.Paletted
authorkawakami <kawakami.ozone@gmail.com>
Wed, 15 May 2019 18:11:44 +0000 (03:11 +0900)
committerBenny Siegert <bsiegert@gmail.com>
Wed, 22 May 2019 20:41:27 +0000 (20:41 +0000)
This keeps transparency of a wrapped image.Image even after it is encoded.

Fixes #30995

Change-Id: I1f7ac98b1741f83ed740f6eda6c36b7e9b16e5af
Reviewed-on: https://go-review.googlesource.com/c/go/+/177377
Reviewed-by: Hayato Kawakami <kawakami.ozone@gmail.com>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
Run-TryBot: Benny Siegert <bsiegert@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/image/gif/writer.go
src/image/gif/writer_test.go
src/image/testdata/triangle-001.gif [new file with mode: 0644]

index 5819df0dd0ede00a31955eb8d9f9f9e2a9966051..7220446de5ebf308f38b94faefd49426ce6c3c99 100644 (file)
@@ -433,8 +433,18 @@ func Encode(w io.Writer, m image.Image, o *Options) error {
                opts.Drawer = draw.FloydSteinberg
        }
 
-       pm, ok := m.(*image.Paletted)
-       if !ok || len(pm.Palette) > opts.NumColors {
+       pm, _ := m.(*image.Paletted)
+       if pm == nil {
+               if cp, ok := m.ColorModel().(color.Palette); ok {
+                       pm = image.NewPaletted(b, cp)
+                       for y := b.Min.Y; y < b.Max.Y; y++ {
+                               for x := b.Min.X; x < b.Max.X; x++ {
+                                       pm.Set(x, y, cp.Convert(m.At(x, y)))
+                               }
+                       }
+               }
+       }
+       if pm == nil || len(pm.Palette) > opts.NumColors {
                // Set pm to be a palettedized copy of m, including its bounds, which
                // might not start at (0, 0).
                //
index 91275bb90769ff092791ec680e2a56acf005f38c..0bc24d1beec47476c4ada6a9c6613480a7600b7e 100644 (file)
@@ -48,11 +48,17 @@ func delta(u0, u1 uint32) int64 {
 // have the same bounds.
 func averageDelta(m0, m1 image.Image) int64 {
        b := m0.Bounds()
+       return averageDeltaBound(m0, m1, b, b)
+}
+
+// averageDeltaBounds returns the average delta in RGB space. The average delta is
+// calulated in the specified bounds.
+func averageDeltaBound(m0, m1 image.Image, b0, b1 image.Rectangle) int64 {
        var sum, n int64
-       for y := b.Min.Y; y < b.Max.Y; y++ {
-               for x := b.Min.X; x < b.Max.X; x++ {
+       for y := b0.Min.Y; y < b0.Max.Y; y++ {
+               for x := b0.Min.X; x < b0.Max.X; x++ {
                        c0 := m0.At(x, y)
-                       c1 := m1.At(x, y)
+                       c1 := m1.At(x-b0.Min.X+b1.Min.X, y-b0.Min.Y+b1.Min.Y)
                        r0, g0, b0, _ := c0.RGBA()
                        r1, g1, b1, _ := c1.RGBA()
                        sum += delta(r0, r1)
@@ -581,6 +587,75 @@ func TestEncodeCroppedSubImages(t *testing.T) {
        }
 }
 
+type offsetImage struct {
+       image.Image
+       Rect image.Rectangle
+}
+
+func (i offsetImage) Bounds() image.Rectangle {
+       return i.Rect
+}
+
+func TestEncodeWrappedImage(t *testing.T) {
+       m0, err := readImg("../testdata/video-001.gif")
+       if err != nil {
+               t.Fatalf("readImg: %v", err)
+       }
+
+       // Case 1: Enocde a wrapped image.Image
+       buf := new(bytes.Buffer)
+       w0 := offsetImage{m0, m0.Bounds()}
+       err = Encode(buf, w0, nil)
+       if err != nil {
+               t.Fatalf("Encode: %v", err)
+       }
+       w1, err := Decode(buf)
+       if err != nil {
+               t.Fatalf("Dencode: %v", err)
+       }
+       avgDelta := averageDelta(m0, w1)
+       if avgDelta > 0 {
+               t.Fatalf("Wrapped: average delta is too high. expected: 0, got %d", avgDelta)
+       }
+
+       // Case 2: Enocde a wrapped image.Image with offset
+       b0 := image.Rectangle{
+               Min: image.Point{
+                       X: 128,
+                       Y: 64,
+               },
+               Max: image.Point{
+                       X: 256,
+                       Y: 128,
+               },
+       }
+       w0 = offsetImage{m0, b0}
+       buf = new(bytes.Buffer)
+       err = Encode(buf, w0, nil)
+       if err != nil {
+               t.Fatalf("Encode: %v", err)
+       }
+       w1, err = Decode(buf)
+       if err != nil {
+               t.Fatalf("Dencode: %v", err)
+       }
+
+       b1 := image.Rectangle{
+               Min: image.Point{
+                       X: 0,
+                       Y: 0,
+               },
+               Max: image.Point{
+                       X: 128,
+                       Y: 64,
+               },
+       }
+       avgDelta = averageDeltaBound(m0, w1, b0, b1)
+       if avgDelta > 0 {
+               t.Fatalf("Wrapped and offset: average delta is too high. expected: 0, got %d", avgDelta)
+       }
+}
+
 func BenchmarkEncode(b *testing.B) {
        bo := image.Rect(0, 0, 640, 480)
        rnd := rand.New(rand.NewSource(123))
diff --git a/src/image/testdata/triangle-001.gif b/src/image/testdata/triangle-001.gif
new file mode 100644 (file)
index 0000000..f3d45bb
Binary files /dev/null and b/src/image/testdata/triangle-001.gif differ