aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/image
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2017-01-14 00:05:42 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2017-01-14 00:05:42 +0000
commitc2047754c300b68c05d65faa8dc2925fe67b71b4 (patch)
treee183ae81a1f48a02945cb6de463a70c5be1b06f6 /libgo/go/image
parent829afb8f05602bb31c9c597b24df7377fed4f059 (diff)
downloadgcc-c2047754c300b68c05d65faa8dc2925fe67b71b4.zip
gcc-c2047754c300b68c05d65faa8dc2925fe67b71b4.tar.gz
gcc-c2047754c300b68c05d65faa8dc2925fe67b71b4.tar.bz2
libgo: update to Go 1.8 release candidate 1
Compiler changes: * Change map assignment to use mapassign and assign value directly. * Change string iteration to use decoderune, faster for ASCII strings. * Change makeslice to take int, and use makeslice64 for larger values. * Add new noverflow field to hmap struct used for maps. Unresolved problems, to be fixed later: * Commented out test in go/types/sizes_test.go that doesn't compile. * Commented out reflect.TestStructOf test for padding after zero-sized field. Reviewed-on: https://go-review.googlesource.com/35231 gotools/: Updates for Go 1.8rc1. * Makefile.am (go_cmd_go_files): Add bug.go. (s-zdefaultcc): Write defaultPkgConfig. * Makefile.in: Rebuild. From-SVN: r244456
Diffstat (limited to 'libgo/go/image')
-rw-r--r--libgo/go/image/color/color.go23
-rw-r--r--libgo/go/image/color/ycbcr.go94
-rw-r--r--libgo/go/image/color/ycbcr_test.go63
-rw-r--r--libgo/go/image/draw/bench_test.go2
-rw-r--r--libgo/go/image/draw/draw.go16
-rw-r--r--libgo/go/image/draw/example_test.go50
-rw-r--r--libgo/go/image/gif/reader.go83
-rw-r--r--libgo/go/image/gif/reader_test.go17
-rw-r--r--libgo/go/image/png/example_test.go79
-rw-r--r--libgo/go/image/png/reader.go266
-rw-r--r--libgo/go/image/png/reader_test.go175
-rw-r--r--libgo/go/image/png/testdata/pngsuite/README21
-rw-r--r--libgo/go/image/png/writer.go5
13 files changed, 735 insertions, 159 deletions
diff --git a/libgo/go/image/color/color.go b/libgo/go/image/color/color.go
index 1044339..0832c59 100644
--- a/libgo/go/image/color/color.go
+++ b/libgo/go/image/color/color.go
@@ -246,8 +246,18 @@ func grayModel(c Color) Color {
return c
}
r, g, b, _ := c.RGBA()
- y := (299*r + 587*g + 114*b + 500) / 1000
- return Gray{uint8(y >> 8)}
+
+ // These coefficients (the fractions 0.299, 0.587 and 0.114) are the same
+ // as those given by the JFIF specification and used by func RGBToYCbCr in
+ // ycbcr.go.
+ //
+ // Note that 19595 + 38470 + 7471 equals 65536.
+ //
+ // The 24 is 16 + 8. The 16 is the same as used in RGBToYCbCr. The 8 is
+ // because the return value is 8 bit color, not 16 bit color.
+ y := (19595*r + 38470*g + 7471*b + 1<<15) >> 24
+
+ return Gray{uint8(y)}
}
func gray16Model(c Color) Color {
@@ -255,7 +265,14 @@ func gray16Model(c Color) Color {
return c
}
r, g, b, _ := c.RGBA()
- y := (299*r + 587*g + 114*b + 500) / 1000
+
+ // These coefficients (the fractions 0.299, 0.587 and 0.114) are the same
+ // as those given by the JFIF specification and used by func RGBToYCbCr in
+ // ycbcr.go.
+ //
+ // Note that 19595 + 38470 + 7471 equals 65536.
+ y := (19595*r + 38470*g + 7471*b + 1<<15) >> 16
+
return Gray16{uint16(y)}
}
diff --git a/libgo/go/image/color/ycbcr.go b/libgo/go/image/color/ycbcr.go
index 3df5d367..18d1a56 100644
--- a/libgo/go/image/color/ycbcr.go
+++ b/libgo/go/image/color/ycbcr.go
@@ -17,6 +17,8 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
b1 := int32(b)
// yy is in range [0,0xff].
+ //
+ // Note that 19595 + 38470 + 7471 equals 65536.
yy := (19595*r1 + 38470*g1 + 7471*b1 + 1<<15) >> 16
// The bit twiddling below is equivalent to
@@ -32,6 +34,8 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
// Note that the uint8 type conversion in the return
// statement will convert ^int32(0) to 0xff.
// The code below to compute cr uses a similar pattern.
+ //
+ // Note that -11056 - 21712 + 32768 equals 0.
cb := -11056*r1 - 21712*g1 + 32768*b1 + 257<<15
if uint32(cb)&0xff000000 == 0 {
cb >>= 16
@@ -39,6 +43,7 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
cb = ^(cb >> 31)
}
+ // Note that 32768 - 27440 - 5328 equals 0.
cr := 32768*r1 - 27440*g1 - 5328*b1 + 257<<15
if uint32(cr)&0xff000000 == 0 {
cr >>= 16
@@ -134,24 +139,39 @@ func (c YCbCr) RGBA() (uint32, uint32, uint32, uint32) {
yy1 := int32(c.Y) * 0x10100 // Convert 0x12 to 0x121200.
cb1 := int32(c.Cb) - 128
cr1 := int32(c.Cr) - 128
- r := (yy1 + 91881*cr1) >> 8
- g := (yy1 - 22554*cb1 - 46802*cr1) >> 8
- b := (yy1 + 116130*cb1) >> 8
- if r < 0 {
- r = 0
- } else if r > 0xffff {
- r = 0xffff
+
+ // The bit twiddling below is equivalent to
+ //
+ // r := (yy1 + 91881*cr1) >> 8
+ // if r < 0 {
+ // r = 0
+ // } else if r > 0xff {
+ // r = 0xffff
+ // }
+ //
+ // but uses fewer branches and is faster.
+ // The code below to compute g and b uses a similar pattern.
+ r := yy1 + 91881*cr1
+ if uint32(r)&0xff000000 == 0 {
+ r >>= 8
+ } else {
+ r = ^(r >> 31) & 0xffff
}
- if g < 0 {
- g = 0
- } else if g > 0xffff {
- g = 0xffff
+
+ g := yy1 - 22554*cb1 - 46802*cr1
+ if uint32(g)&0xff000000 == 0 {
+ g >>= 8
+ } else {
+ g = ^(g >> 31) & 0xffff
}
- if b < 0 {
- b = 0
- } else if b > 0xffff {
- b = 0xffff
+
+ b := yy1 + 116130*cb1
+ if uint32(b)&0xff000000 == 0 {
+ b >>= 8
+ } else {
+ b = ^(b >> 31) & 0xffff
}
+
return uint32(r), uint32(g), uint32(b), 0xffff
}
@@ -179,23 +199,37 @@ func (c NYCbCrA) RGBA() (uint32, uint32, uint32, uint32) {
yy1 := int32(c.Y) * 0x10100 // Convert 0x12 to 0x121200.
cb1 := int32(c.Cb) - 128
cr1 := int32(c.Cr) - 128
- r := (yy1 + 91881*cr1) >> 8
- g := (yy1 - 22554*cb1 - 46802*cr1) >> 8
- b := (yy1 + 116130*cb1) >> 8
- if r < 0 {
- r = 0
- } else if r > 0xffff {
- r = 0xffff
+
+ // The bit twiddling below is equivalent to
+ //
+ // r := (yy1 + 91881*cr1) >> 8
+ // if r < 0 {
+ // r = 0
+ // } else if r > 0xff {
+ // r = 0xffff
+ // }
+ //
+ // but uses fewer branches and is faster.
+ // The code below to compute g and b uses a similar pattern.
+ r := yy1 + 91881*cr1
+ if uint32(r)&0xff000000 == 0 {
+ r >>= 8
+ } else {
+ r = ^(r >> 31) & 0xffff
}
- if g < 0 {
- g = 0
- } else if g > 0xffff {
- g = 0xffff
+
+ g := yy1 - 22554*cb1 - 46802*cr1
+ if uint32(g)&0xff000000 == 0 {
+ g >>= 8
+ } else {
+ g = ^(g >> 31) & 0xffff
}
- if b < 0 {
- b = 0
- } else if b > 0xffff {
- b = 0xffff
+
+ b := yy1 + 116130*cb1
+ if uint32(b)&0xff000000 == 0 {
+ b >>= 8
+ } else {
+ b = ^(b >> 31) & 0xffff
}
// The second part of this method applies the alpha.
diff --git a/libgo/go/image/color/ycbcr_test.go b/libgo/go/image/color/ycbcr_test.go
index 561699f..85c1b98 100644
--- a/libgo/go/image/color/ycbcr_test.go
+++ b/libgo/go/image/color/ycbcr_test.go
@@ -172,7 +172,8 @@ func TestPalette(t *testing.T) {
}
}
-var sink uint8
+var sink8 uint8
+var sink32 uint32
func BenchmarkYCbCrToRGB(b *testing.B) {
// YCbCrToRGB does saturating arithmetic.
@@ -180,17 +181,17 @@ func BenchmarkYCbCrToRGB(b *testing.B) {
// different paths through the generated code.
b.Run("0", func(b *testing.B) {
for i := 0; i < b.N; i++ {
- sink, sink, sink = YCbCrToRGB(0, 0, 0)
+ sink8, sink8, sink8 = YCbCrToRGB(0, 0, 0)
}
})
b.Run("128", func(b *testing.B) {
for i := 0; i < b.N; i++ {
- sink, sink, sink = YCbCrToRGB(128, 128, 128)
+ sink8, sink8, sink8 = YCbCrToRGB(128, 128, 128)
}
})
b.Run("255", func(b *testing.B) {
for i := 0; i < b.N; i++ {
- sink, sink, sink = YCbCrToRGB(255, 255, 255)
+ sink8, sink8, sink8 = YCbCrToRGB(255, 255, 255)
}
})
}
@@ -201,17 +202,65 @@ func BenchmarkRGBToYCbCr(b *testing.B) {
// through the generated code.
b.Run("0", func(b *testing.B) {
for i := 0; i < b.N; i++ {
- sink, sink, sink = RGBToYCbCr(0, 0, 0)
+ sink8, sink8, sink8 = RGBToYCbCr(0, 0, 0)
}
})
b.Run("Cb", func(b *testing.B) {
for i := 0; i < b.N; i++ {
- sink, sink, sink = RGBToYCbCr(0, 0, 255)
+ sink8, sink8, sink8 = RGBToYCbCr(0, 0, 255)
}
})
b.Run("Cr", func(b *testing.B) {
for i := 0; i < b.N; i++ {
- sink, sink, sink = RGBToYCbCr(255, 0, 0)
+ sink8, sink8, sink8 = RGBToYCbCr(255, 0, 0)
+ }
+ })
+}
+
+func BenchmarkYCbCrToRGBA(b *testing.B) {
+ // RGB does saturating arithmetic.
+ // Low, middle, and high values can take
+ // different paths through the generated code.
+ b.Run("0", func(b *testing.B) {
+ c := YCbCr{0, 0, 0}
+ for i := 0; i < b.N; i++ {
+ sink32, sink32, sink32, sink32 = c.RGBA()
+ }
+ })
+ b.Run("128", func(b *testing.B) {
+ c := YCbCr{128, 128, 128}
+ for i := 0; i < b.N; i++ {
+ sink32, sink32, sink32, sink32 = c.RGBA()
+ }
+ })
+ b.Run("255", func(b *testing.B) {
+ c := YCbCr{255, 255, 255}
+ for i := 0; i < b.N; i++ {
+ sink32, sink32, sink32, sink32 = c.RGBA()
+ }
+ })
+}
+
+func BenchmarkNYCbCrAToRGBA(b *testing.B) {
+ // RGBA does saturating arithmetic.
+ // Low, middle, and high values can take
+ // different paths through the generated code.
+ b.Run("0", func(b *testing.B) {
+ c := NYCbCrA{YCbCr{0, 0, 0}, 0xff}
+ for i := 0; i < b.N; i++ {
+ sink32, sink32, sink32, sink32 = c.RGBA()
+ }
+ })
+ b.Run("128", func(b *testing.B) {
+ c := NYCbCrA{YCbCr{128, 128, 128}, 0xff}
+ for i := 0; i < b.N; i++ {
+ sink32, sink32, sink32, sink32 = c.RGBA()
+ }
+ })
+ b.Run("255", func(b *testing.B) {
+ c := NYCbCrA{YCbCr{255, 255, 255}, 0xff}
+ for i := 0; i < b.N; i++ {
+ sink32, sink32, sink32, sink32 = c.RGBA()
}
})
}
diff --git a/libgo/go/image/draw/bench_test.go b/libgo/go/image/draw/bench_test.go
index 7b89f95..a41d7e7 100644
--- a/libgo/go/image/draw/bench_test.go
+++ b/libgo/go/image/draw/bench_test.go
@@ -74,7 +74,7 @@ func bench(b *testing.B, dcm, scm, mcm color.Model, op Op) {
var src image.Image
switch scm {
case nil:
- src = &image.Uniform{C: color.RGBA{0x11, 0x22, 0x33, 0xff}}
+ src = &image.Uniform{C: color.RGBA{0x11, 0x22, 0x33, 0x44}}
case color.CMYKModel:
src1 := image.NewCMYK(image.Rect(0, 0, srcw, srch))
for y := 0; y < srch; y++ {
diff --git a/libgo/go/image/draw/draw.go b/libgo/go/image/draw/draw.go
index 6a16cd3..a31dd42 100644
--- a/libgo/go/image/draw/draw.go
+++ b/libgo/go/image/draw/draw.go
@@ -116,7 +116,12 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
if mask == nil {
switch src0 := src.(type) {
case *image.Uniform:
- drawFillOver(dst0, r, src0)
+ sr, sg, sb, sa := src0.RGBA()
+ if sa == 0xffff {
+ drawFillSrc(dst0, r, sr, sg, sb, sa)
+ } else {
+ drawFillOver(dst0, r, sr, sg, sb, sa)
+ }
return
case *image.RGBA:
drawCopyOver(dst0, r, src0, sp)
@@ -150,7 +155,8 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
if mask == nil {
switch src0 := src.(type) {
case *image.Uniform:
- drawFillSrc(dst0, r, src0)
+ sr, sg, sb, sa := src0.RGBA()
+ drawFillSrc(dst0, r, sr, sg, sb, sa)
return
case *image.RGBA:
drawCopySrc(dst0, r, src0, sp)
@@ -232,8 +238,7 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
}
}
-func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform) {
- sr, sg, sb, sa := src.RGBA()
+func drawFillOver(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) {
// The 0x101 is here for the same reason as in drawRGBA.
a := (m - sa) * 0x101
i0 := dst.PixOffset(r.Min.X, r.Min.Y)
@@ -255,8 +260,7 @@ func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform) {
}
}
-func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.Uniform) {
- sr, sg, sb, sa := src.RGBA()
+func drawFillSrc(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) {
sr8 := uint8(sr >> 8)
sg8 := uint8(sg >> 8)
sb8 := uint8(sb >> 8)
diff --git a/libgo/go/image/draw/example_test.go b/libgo/go/image/draw/example_test.go
new file mode 100644
index 0000000..d381c1c
--- /dev/null
+++ b/libgo/go/image/draw/example_test.go
@@ -0,0 +1,50 @@
+// Copyright 2016 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.
+
+// +build ignore
+
+package draw_test
+
+import (
+ "fmt"
+ "image"
+ "image/color"
+ "image/draw"
+ "math"
+)
+
+func ExampleDrawer_floydSteinberg() {
+ const width = 130
+ const height = 50
+
+ im := image.NewGray(image.Rectangle{Max: image.Point{X: width, Y: height}})
+ for x := 0; x < width; x++ {
+ for y := 0; y < height; y++ {
+ dist := math.Sqrt(math.Pow(float64(x-width/2), 2)/3+math.Pow(float64(y-height/2), 2)) / (height / 1.5) * 255
+ var gray uint8
+ if dist > 255 {
+ gray = 255
+ } else {
+ gray = uint8(dist)
+ }
+ im.SetGray(x, y, color.Gray{Y: 255 - gray})
+ }
+ }
+ pi := image.NewPaletted(im.Bounds(), []color.Color{
+ color.Gray{Y: 255},
+ color.Gray{Y: 160},
+ color.Gray{Y: 70},
+ color.Gray{Y: 35},
+ color.Gray{Y: 0},
+ })
+
+ draw.FloydSteinberg.Draw(pi, im.Bounds(), im, image.ZP)
+ shade := []string{" ", "░", "▒", "▓", "█"}
+ for i, p := range pi.Pix {
+ fmt.Print(shade[p])
+ if (i+1)%width == 0 {
+ fmt.Print("\n")
+ }
+ }
+}
diff --git a/libgo/go/image/gif/reader.go b/libgo/go/image/gif/reader.go
index 6181a94..e6111281 100644
--- a/libgo/go/image/gif/reader.go
+++ b/libgo/go/image/gif/reader.go
@@ -63,6 +63,22 @@ const (
eApplication = 0xFF // Application
)
+func readFull(r io.Reader, b []byte) error {
+ _, err := io.ReadFull(r, b)
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ return err
+}
+
+func readByte(r io.ByteReader) (byte, error) {
+ b, err := r.ReadByte()
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ return b, err
+}
+
// decoder is the type used to decode a GIF file.
type decoder struct {
r reader
@@ -124,7 +140,7 @@ func (b *blockReader) Read(p []byte) (int, error) {
return 0, b.err
}
b.slice = b.tmp[:blockLen]
- if _, b.err = io.ReadFull(b.r, b.slice); b.err != nil {
+ if b.err = readFull(b.r, b.slice); b.err != nil {
return 0, b.err
}
}
@@ -151,9 +167,9 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
}
for {
- c, err := d.r.ReadByte()
+ c, err := readByte(d.r)
if err != nil {
- return err
+ return fmt.Errorf("gif: reading frames: %v", err)
}
switch c {
case sExtension:
@@ -198,9 +214,9 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
m.Palette = p
}
}
- litWidth, err := d.r.ReadByte()
+ litWidth, err := readByte(d.r)
if err != nil {
- return err
+ return fmt.Errorf("gif: reading image data: %v", err)
}
if litWidth < 2 || litWidth > 8 {
return fmt.Errorf("gif: pixel size in decode out of range: %d", litWidth)
@@ -209,9 +225,9 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
br := &blockReader{r: d.r}
lzwr := lzw.NewReader(br, lzw.LSB, int(litWidth))
defer lzwr.Close()
- if _, err = io.ReadFull(lzwr, m.Pix); err != nil {
+ if err = readFull(lzwr, m.Pix); err != nil {
if err != io.ErrUnexpectedEOF {
- return err
+ return fmt.Errorf("gif: reading image data: %v", err)
}
return errNotEnough
}
@@ -228,13 +244,13 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
// See https://golang.org/issue/9856 for an example GIF.
if n, err := lzwr.Read(d.tmp[:1]); n != 0 || (err != io.EOF && err != io.ErrUnexpectedEOF) {
if err != nil {
- return err
+ return fmt.Errorf("gif: reading image data: %v", err)
}
return errTooMuch
}
if n, err := br.Read(d.tmp[:1]); n != 0 || err != io.EOF {
if err != nil {
- return err
+ return fmt.Errorf("gif: reading image data: %v", err)
}
return errTooMuch
}
@@ -264,7 +280,7 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
case sTrailer:
if len(d.image) == 0 {
- return io.ErrUnexpectedEOF
+ return fmt.Errorf("gif: missing image data")
}
return nil
@@ -275,13 +291,13 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
}
func (d *decoder) readHeaderAndScreenDescriptor() error {
- _, err := io.ReadFull(d.r, d.tmp[:13])
+ err := readFull(d.r, d.tmp[:13])
if err != nil {
- return err
+ return fmt.Errorf("gif: reading header: %v", err)
}
d.vers = string(d.tmp[:6])
if d.vers != "GIF87a" && d.vers != "GIF89a" {
- return fmt.Errorf("gif: can't recognize format %s", d.vers)
+ return fmt.Errorf("gif: can't recognize format %q", d.vers)
}
d.width = int(d.tmp[6]) + int(d.tmp[7])<<8
d.height = int(d.tmp[8]) + int(d.tmp[9])<<8
@@ -298,9 +314,9 @@ func (d *decoder) readHeaderAndScreenDescriptor() error {
func (d *decoder) readColorTable(fields byte) (color.Palette, error) {
n := 1 << (1 + uint(fields&fColorTableBitsMask))
- _, err := io.ReadFull(d.r, d.tmp[:3*n])
+ err := readFull(d.r, d.tmp[:3*n])
if err != nil {
- return nil, fmt.Errorf("gif: short read on color table: %s", err)
+ return nil, fmt.Errorf("gif: reading color table: %s", err)
}
j, p := 0, make(color.Palette, n)
for i := range p {
@@ -311,9 +327,9 @@ func (d *decoder) readColorTable(fields byte) (color.Palette, error) {
}
func (d *decoder) readExtension() error {
- extension, err := d.r.ReadByte()
+ extension, err := readByte(d.r)
if err != nil {
- return err
+ return fmt.Errorf("gif: reading extension: %v", err)
}
size := 0
switch extension {
@@ -324,9 +340,9 @@ func (d *decoder) readExtension() error {
case eComment:
// nothing to do but read the data.
case eApplication:
- b, err := d.r.ReadByte()
+ b, err := readByte(d.r)
if err != nil {
- return err
+ return fmt.Errorf("gif: reading extension: %v", err)
}
// The spec requires size be 11, but Adobe sometimes uses 10.
size = int(b)
@@ -334,8 +350,8 @@ func (d *decoder) readExtension() error {
return fmt.Errorf("gif: unknown extension 0x%.2x", extension)
}
if size > 0 {
- if _, err := io.ReadFull(d.r, d.tmp[:size]); err != nil {
- return err
+ if err := readFull(d.r, d.tmp[:size]); err != nil {
+ return fmt.Errorf("gif: reading extension: %v", err)
}
}
@@ -343,8 +359,11 @@ func (d *decoder) readExtension() error {
// this extension defines a loop count.
if extension == eApplication && string(d.tmp[:size]) == "NETSCAPE2.0" {
n, err := d.readBlock()
- if n == 0 || err != nil {
- return err
+ if err != nil {
+ return fmt.Errorf("gif: reading extension: %v", err)
+ }
+ if n == 0 {
+ return nil
}
if n == 3 && d.tmp[0] == 1 {
d.loopCount = int(d.tmp[1]) | int(d.tmp[2])<<8
@@ -352,14 +371,17 @@ func (d *decoder) readExtension() error {
}
for {
n, err := d.readBlock()
- if n == 0 || err != nil {
- return err
+ if err != nil {
+ return fmt.Errorf("gif: reading extension: %v", err)
+ }
+ if n == 0 {
+ return nil
}
}
}
func (d *decoder) readGraphicControl() error {
- if _, err := io.ReadFull(d.r, d.tmp[:6]); err != nil {
+ if err := readFull(d.r, d.tmp[:6]); err != nil {
return fmt.Errorf("gif: can't read graphic control: %s", err)
}
if d.tmp[0] != 4 {
@@ -379,7 +401,7 @@ func (d *decoder) readGraphicControl() error {
}
func (d *decoder) newImageFromDescriptor() (*image.Paletted, error) {
- if _, err := io.ReadFull(d.r, d.tmp[:9]); err != nil {
+ if err := readFull(d.r, d.tmp[:9]); err != nil {
return nil, fmt.Errorf("gif: can't read image descriptor: %s", err)
}
left := int(d.tmp[0]) + int(d.tmp[1])<<8
@@ -399,11 +421,14 @@ func (d *decoder) newImageFromDescriptor() (*image.Paletted, error) {
}
func (d *decoder) readBlock() (int, error) {
- n, err := d.r.ReadByte()
+ n, err := readByte(d.r)
if n == 0 || err != nil {
return 0, err
}
- return io.ReadFull(d.r, d.tmp[:n])
+ if err := readFull(d.r, d.tmp[:n]); err != nil {
+ return 0, err
+ }
+ return int(n), nil
}
// interlaceScan defines the ordering for a pass of the interlace algorithm.
diff --git a/libgo/go/image/gif/reader_test.go b/libgo/go/image/gif/reader_test.go
index 90c8149..1267ba0 100644
--- a/libgo/go/image/gif/reader_test.go
+++ b/libgo/go/image/gif/reader_test.go
@@ -10,6 +10,7 @@ import (
"image"
"image/color"
"reflect"
+ "strings"
"testing"
)
@@ -292,3 +293,19 @@ func TestLoopCount(t *testing.T) {
t.Errorf("loop count mismatch: %d vs %d", img.LoopCount, img1.LoopCount)
}
}
+
+func TestUnexpectedEOF(t *testing.T) {
+ for i := len(testGIF) - 1; i >= 0; i-- {
+ _, err := Decode(bytes.NewReader(testGIF[:i]))
+ if err == errNotEnough {
+ continue
+ }
+ text := ""
+ if err != nil {
+ text = err.Error()
+ }
+ if !strings.HasPrefix(text, "gif:") || !strings.HasSuffix(text, ": unexpected EOF") {
+ t.Errorf("Decode(testGIF[:%d]) = %v, want gif: ...: unexpected EOF", i, err)
+ }
+ }
+}
diff --git a/libgo/go/image/png/example_test.go b/libgo/go/image/png/example_test.go
new file mode 100644
index 0000000..2a03be5
--- /dev/null
+++ b/libgo/go/image/png/example_test.go
@@ -0,0 +1,79 @@
+// Copyright 2016 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.
+
+// +build ignore
+
+package png_test
+
+import (
+ "encoding/base64"
+ "fmt"
+ "image"
+ "image/color"
+ "image/png"
+ "io"
+ "log"
+ "os"
+ "strings"
+)
+
+const gopher = `iVBORw0KGgoAAAANSUhEUgAAAEsAAAA8CAAAAAALAhhPAAAFfUlEQVRYw62XeWwUVRzHf2+OPbo9d7tsWyiyaZti6eWGAhISoIGKECEKCAiJJkYTiUgTMYSIosYYBBIUIxoSPIINEBDi2VhwkQrVsj1ESgu9doHWdrul7ba73WNm3vOPtsseM9MdwvvrzTs+8/t95ze/33sI5BqiabU6m9En8oNjduLnAEDLUsQXFF8tQ5oxK3vmnNmDSMtrncks9Hhtt/qeWZapHb1ha3UqYSWVl2ZmpWgaXMXGohQAvmeop3bjTRtv6SgaK/Pb9/bFzUrYslbFAmHPp+3WhAYdr+7GN/YnpN46Opv55VDsJkoEpMrY/vO2BIYQ6LLvm0ThY3MzDzzeSJeeWNyTkgnIE5ePKsvKlcg/0T9QMzXalwXMlj54z4c0rh/mzEfr+FgWEz2w6uk8dkzFAgcARAgNp1ZYef8bH2AgvuStbc2/i6CiWGj98y2tw2l4FAXKkQBIf+exyRnteY83LfEwDQAYCoK+P6bxkZm/0966LxcAAILHB56kgD95PPxltuYcMtFTWw/FKkY/6Opf3GGd9ZF+Qp6mzJxzuRSractOmJrH1u8XTvWFHINNkLQLMR+XHXvfPPHw967raE1xxwtA36IMRfkAAG29/7mLuQcb2WOnsJReZGfpiHsSBX81cvMKywYZHhX5hFPtOqPGWZCXnhWGAu6lX91ElKXSalcLXu3UaOXVay57ZSe5f6Gpx7J2MXAsi7EqSp09b/MirKSyJfnfEEgeDjl8FgDAfvewP03zZ+AJ0m9aFRM8eEHBDRKjfcreDXnZdQuAxXpT2NRJ7xl3UkLBhuVGU16gZiGOgZmrSbRdqkILuL/yYoSXHHkl9KXgqNu3PB8oRg0geC5vFmLjad6mUyTKLmF3OtraWDIfACyXqmephaDABawfpi6tqqBZytfQMqOz6S09iWXhktrRaB8Xz4Yi/8gyABDm5NVe6qq/3VzPrcjELWrebVuyY2T7ar4zQyybUCtsQ5Es1FGaZVrRVQwAgHGW2ZCRZshI5bGQi7HesyE972pOSeMM0dSktlzxRdrlqb3Osa6CCS8IJoQQQgBAbTAa5l5epO34rJszibJI8rxLfGzcp1dRosutGeb2VDNgqYrwTiPNsLxXiPi3dz7LiS1WBRBDBOnqEjyy3aQb+/bLiJzz9dIkscVBBLxMfSEac7kO4Fpkngi0ruNBeSOal+u8jgOuqPz12nryMLCniEjtOOOmpt+KEIqsEdocJjYXwrh9OZqWJQyPCTo67LNS/TdxLAv6R5ZNK9npEjbYdT33gRo4o5oTqR34R+OmaSzDBWsAIPhuRcgyoteNi9gF0KzNYWVItPf2TLoXEg+7isNC7uJkgo1iQWOfRSP9NR11RtbZZ3OMG/VhL6jvx+J1m87+RCfJChAtEBQkSBX2PnSiihc/Twh3j0h7qdYQAoRVsRGmq7HU2QRbaxVGa1D6nIOqaIWRjyRZpHMQKWKpZM5feA+lzC4ZFultV8S6T0mzQGhQohi5I8iw+CsqBSxhFMuwyLgSwbghGb0AiIKkSDmGZVmJSiKihsiyOAUs70UkywooYP0bii9GdH4sfr1UNysd3fUyLLMQN+rsmo3grHl9VNJHbbwxoa47Vw5gupIqrZcjPh9R4Nye3nRDk199V+aetmvVtDRE8/+cbgAAgMIWGb3UA0MGLE9SCbWX670TDy1y98c3D27eppUjsZ6fql3jcd5rUe7+ZIlLNQny3Rd+E5Tct3WVhTM5RBCEdiEK0b6B+/ca2gYU393nFj/n1AygRQxPIUA043M42u85+z2SnssKrPl8Mx76NL3E6eXc3be7OD+H4WHbJkKI8AU8irbITQjZ+0hQcPEgId/Fn/pl9crKH02+5o2b9T/eMx7pKoskYgAAAABJRU5ErkJggg==`
+
+// gopherPNG creates an io.Reader by decoding the base64 encoded image data string in the gopher constant.
+func gopherPNG() io.Reader { return base64.NewDecoder(base64.StdEncoding, strings.NewReader(gopher)) }
+
+func ExampleDecode() {
+ // This example uses png.Decode which can only decode PNG images.
+ // Consider using the general image.Decode as it can sniff and decode any registered image format.
+ img, err := png.Decode(gopherPNG())
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ levels := []string{" ", "░", "▒", "▓", "█"}
+
+ for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
+ for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
+ c := color.GrayModel.Convert(img.At(x, y)).(color.Gray)
+ level := c.Y / 51 // 51 * 5 = 255
+ if level == 5 {
+ level--
+ }
+ fmt.Print(levels[level])
+ }
+ fmt.Print("\n")
+ }
+}
+
+func ExampleEncode() {
+ const width, height = 256, 256
+
+ // Create a colored image of the given width and height.
+ img := image.NewNRGBA(image.Rect(0, 0, width, height))
+
+ for y := 0; y < height; y++ {
+ for x := 0; x < width; x++ {
+ img.Set(x, y, color.NRGBA{
+ R: uint8((x + y) & 255),
+ G: uint8((x + y) << 1 & 255),
+ B: uint8((x + y) << 2 & 255),
+ A: 255,
+ })
+ }
+ }
+
+ f, err := os.Create("image.png")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ if err := png.Encode(f, img); err != nil {
+ f.Close()
+ log.Fatal(err)
+ }
+
+ if err := f.Close(); err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/libgo/go/image/png/reader.go b/libgo/go/image/png/reader.go
index 2dd5ed8..32f78f0 100644
--- a/libgo/go/image/png/reader.go
+++ b/libgo/go/image/png/reader.go
@@ -113,6 +113,11 @@ type decoder struct {
idatLength uint32
tmp [3 * 256]byte
interlace int
+
+ // useTransparent and transparent are used for grayscale and truecolor
+ // transparency, as opposed to palette transparency.
+ useTransparent bool
+ transparent [6]byte
}
// A FormatError reports that the input is not a valid PNG.
@@ -252,20 +257,51 @@ func (d *decoder) parsePLTE(length uint32) error {
}
func (d *decoder) parsetRNS(length uint32) error {
- if length > 256 {
- return FormatError("bad tRNS length")
- }
- n, err := io.ReadFull(d.r, d.tmp[:length])
- if err != nil {
- return err
- }
- d.crc.Write(d.tmp[:n])
switch d.cb {
- case cbG8, cbG16:
- return UnsupportedError("grayscale transparency")
+ case cbG1, cbG2, cbG4, cbG8, cbG16:
+ if length != 2 {
+ return FormatError("bad tRNS length")
+ }
+ n, err := io.ReadFull(d.r, d.tmp[:length])
+ if err != nil {
+ return err
+ }
+ d.crc.Write(d.tmp[:n])
+
+ copy(d.transparent[:], d.tmp[:length])
+ switch d.cb {
+ case cbG1:
+ d.transparent[1] *= 0xff
+ case cbG2:
+ d.transparent[1] *= 0x55
+ case cbG4:
+ d.transparent[1] *= 0x11
+ }
+ d.useTransparent = true
+
case cbTC8, cbTC16:
- return UnsupportedError("truecolor transparency")
+ if length != 6 {
+ return FormatError("bad tRNS length")
+ }
+ n, err := io.ReadFull(d.r, d.tmp[:length])
+ if err != nil {
+ return err
+ }
+ d.crc.Write(d.tmp[:n])
+
+ copy(d.transparent[:], d.tmp[:length])
+ d.useTransparent = true
+
case cbP1, cbP2, cbP4, cbP8:
+ if length > 256 {
+ return FormatError("bad tRNS length")
+ }
+ n, err := io.ReadFull(d.r, d.tmp[:length])
+ if err != nil {
+ return err
+ }
+ d.crc.Write(d.tmp[:n])
+
if len(d.palette) < n {
d.palette = d.palette[:n]
}
@@ -273,7 +309,8 @@ func (d *decoder) parsetRNS(length uint32) error {
rgba := d.palette[i].(color.RGBA)
d.palette[i] = color.NRGBA{rgba.R, rgba.G, rgba.B, d.tmp[i]}
}
- case cbGA8, cbGA16, cbTCA8, cbTCA16:
+
+ default:
return FormatError("tRNS, color type mismatch")
}
return d.verifyChecksum()
@@ -366,7 +403,7 @@ func (d *decoder) decode() (image.Image, error) {
// readImagePass reads a single image pass, sized according to the pass number.
func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image.Image, error) {
- var bitsPerPixel int = 0
+ bitsPerPixel := 0
pixOffset := 0
var (
gray *image.Gray
@@ -394,16 +431,26 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image
switch d.cb {
case cbG1, cbG2, cbG4, cbG8:
bitsPerPixel = d.depth
- gray = image.NewGray(image.Rect(0, 0, width, height))
- img = gray
+ if d.useTransparent {
+ nrgba = image.NewNRGBA(image.Rect(0, 0, width, height))
+ img = nrgba
+ } else {
+ gray = image.NewGray(image.Rect(0, 0, width, height))
+ img = gray
+ }
case cbGA8:
bitsPerPixel = 16
nrgba = image.NewNRGBA(image.Rect(0, 0, width, height))
img = nrgba
case cbTC8:
bitsPerPixel = 24
- rgba = image.NewRGBA(image.Rect(0, 0, width, height))
- img = rgba
+ if d.useTransparent {
+ nrgba = image.NewNRGBA(image.Rect(0, 0, width, height))
+ img = nrgba
+ } else {
+ rgba = image.NewRGBA(image.Rect(0, 0, width, height))
+ img = rgba
+ }
case cbP1, cbP2, cbP4, cbP8:
bitsPerPixel = d.depth
paletted = image.NewPaletted(image.Rect(0, 0, width, height), d.palette)
@@ -414,16 +461,26 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image
img = nrgba
case cbG16:
bitsPerPixel = 16
- gray16 = image.NewGray16(image.Rect(0, 0, width, height))
- img = gray16
+ if d.useTransparent {
+ nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height))
+ img = nrgba64
+ } else {
+ gray16 = image.NewGray16(image.Rect(0, 0, width, height))
+ img = gray16
+ }
case cbGA16:
bitsPerPixel = 32
nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height))
img = nrgba64
case cbTC16:
bitsPerPixel = 48
- rgba64 = image.NewRGBA64(image.Rect(0, 0, width, height))
- img = rgba64
+ if d.useTransparent {
+ nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height))
+ img = nrgba64
+ } else {
+ rgba64 = image.NewRGBA64(image.Rect(0, 0, width, height))
+ img = rgba64
+ }
case cbTCA16:
bitsPerPixel = 64
nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height))
@@ -483,27 +540,75 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image
// Convert from bytes to colors.
switch d.cb {
case cbG1:
- for x := 0; x < width; x += 8 {
- b := cdat[x/8]
- for x2 := 0; x2 < 8 && x+x2 < width; x2++ {
- gray.SetGray(x+x2, y, color.Gray{(b >> 7) * 0xff})
- b <<= 1
+ if d.useTransparent {
+ ty := d.transparent[1]
+ for x := 0; x < width; x += 8 {
+ b := cdat[x/8]
+ for x2 := 0; x2 < 8 && x+x2 < width; x2++ {
+ ycol := (b >> 7) * 0xff
+ acol := uint8(0xff)
+ if ycol == ty {
+ acol = 0x00
+ }
+ nrgba.SetNRGBA(x+x2, y, color.NRGBA{ycol, ycol, ycol, acol})
+ b <<= 1
+ }
+ }
+ } else {
+ for x := 0; x < width; x += 8 {
+ b := cdat[x/8]
+ for x2 := 0; x2 < 8 && x+x2 < width; x2++ {
+ gray.SetGray(x+x2, y, color.Gray{(b >> 7) * 0xff})
+ b <<= 1
+ }
}
}
case cbG2:
- for x := 0; x < width; x += 4 {
- b := cdat[x/4]
- for x2 := 0; x2 < 4 && x+x2 < width; x2++ {
- gray.SetGray(x+x2, y, color.Gray{(b >> 6) * 0x55})
- b <<= 2
+ if d.useTransparent {
+ ty := d.transparent[1]
+ for x := 0; x < width; x += 4 {
+ b := cdat[x/4]
+ for x2 := 0; x2 < 4 && x+x2 < width; x2++ {
+ ycol := (b >> 6) * 0x55
+ acol := uint8(0xff)
+ if ycol == ty {
+ acol = 0x00
+ }
+ nrgba.SetNRGBA(x+x2, y, color.NRGBA{ycol, ycol, ycol, acol})
+ b <<= 2
+ }
+ }
+ } else {
+ for x := 0; x < width; x += 4 {
+ b := cdat[x/4]
+ for x2 := 0; x2 < 4 && x+x2 < width; x2++ {
+ gray.SetGray(x+x2, y, color.Gray{(b >> 6) * 0x55})
+ b <<= 2
+ }
}
}
case cbG4:
- for x := 0; x < width; x += 2 {
- b := cdat[x/2]
- for x2 := 0; x2 < 2 && x+x2 < width; x2++ {
- gray.SetGray(x+x2, y, color.Gray{(b >> 4) * 0x11})
- b <<= 4
+ if d.useTransparent {
+ ty := d.transparent[1]
+ for x := 0; x < width; x += 2 {
+ b := cdat[x/2]
+ for x2 := 0; x2 < 2 && x+x2 < width; x2++ {
+ ycol := (b >> 4) * 0x11
+ acol := uint8(0xff)
+ if ycol == ty {
+ acol = 0x00
+ }
+ nrgba.SetNRGBA(x+x2, y, color.NRGBA{ycol, ycol, ycol, acol})
+ b <<= 4
+ }
+ }
+ } else {
+ for x := 0; x < width; x += 2 {
+ b := cdat[x/2]
+ for x2 := 0; x2 < 2 && x+x2 < width; x2++ {
+ gray.SetGray(x+x2, y, color.Gray{(b >> 4) * 0x11})
+ b <<= 4
+ }
}
}
case cbG8:
@@ -515,16 +620,37 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image
nrgba.SetNRGBA(x, y, color.NRGBA{ycol, ycol, ycol, cdat[2*x+1]})
}
case cbTC8:
- pix, i, j := rgba.Pix, pixOffset, 0
- for x := 0; x < width; x++ {
- pix[i+0] = cdat[j+0]
- pix[i+1] = cdat[j+1]
- pix[i+2] = cdat[j+2]
- pix[i+3] = 0xff
- i += 4
- j += 3
+ if d.useTransparent {
+ pix, i, j := nrgba.Pix, pixOffset, 0
+ tr, tg, tb := d.transparent[1], d.transparent[3], d.transparent[5]
+ for x := 0; x < width; x++ {
+ r := cdat[j+0]
+ g := cdat[j+1]
+ b := cdat[j+2]
+ a := uint8(0xff)
+ if r == tr && g == tg && b == tb {
+ a = 0x00
+ }
+ pix[i+0] = r
+ pix[i+1] = g
+ pix[i+2] = b
+ pix[i+3] = a
+ i += 4
+ j += 3
+ }
+ pixOffset += nrgba.Stride
+ } else {
+ pix, i, j := rgba.Pix, pixOffset, 0
+ for x := 0; x < width; x++ {
+ pix[i+0] = cdat[j+0]
+ pix[i+1] = cdat[j+1]
+ pix[i+2] = cdat[j+2]
+ pix[i+3] = 0xff
+ i += 4
+ j += 3
+ }
+ pixOffset += rgba.Stride
}
- pixOffset += rgba.Stride
case cbP1:
for x := 0; x < width; x += 8 {
b := cdat[x/8]
@@ -575,9 +701,21 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image
copy(nrgba.Pix[pixOffset:], cdat)
pixOffset += nrgba.Stride
case cbG16:
- for x := 0; x < width; x++ {
- ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1])
- gray16.SetGray16(x, y, color.Gray16{ycol})
+ if d.useTransparent {
+ ty := uint16(d.transparent[0])<<8 | uint16(d.transparent[1])
+ for x := 0; x < width; x++ {
+ ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1])
+ acol := uint16(0xffff)
+ if ycol == ty {
+ acol = 0x0000
+ }
+ nrgba64.SetNRGBA64(x, y, color.NRGBA64{ycol, ycol, ycol, acol})
+ }
+ } else {
+ for x := 0; x < width; x++ {
+ ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1])
+ gray16.SetGray16(x, y, color.Gray16{ycol})
+ }
}
case cbGA16:
for x := 0; x < width; x++ {
@@ -586,11 +724,27 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image
nrgba64.SetNRGBA64(x, y, color.NRGBA64{ycol, ycol, ycol, acol})
}
case cbTC16:
- for x := 0; x < width; x++ {
- rcol := uint16(cdat[6*x+0])<<8 | uint16(cdat[6*x+1])
- gcol := uint16(cdat[6*x+2])<<8 | uint16(cdat[6*x+3])
- bcol := uint16(cdat[6*x+4])<<8 | uint16(cdat[6*x+5])
- rgba64.SetRGBA64(x, y, color.RGBA64{rcol, gcol, bcol, 0xffff})
+ if d.useTransparent {
+ tr := uint16(d.transparent[0])<<8 | uint16(d.transparent[1])
+ tg := uint16(d.transparent[2])<<8 | uint16(d.transparent[3])
+ tb := uint16(d.transparent[4])<<8 | uint16(d.transparent[5])
+ for x := 0; x < width; x++ {
+ rcol := uint16(cdat[6*x+0])<<8 | uint16(cdat[6*x+1])
+ gcol := uint16(cdat[6*x+2])<<8 | uint16(cdat[6*x+3])
+ bcol := uint16(cdat[6*x+4])<<8 | uint16(cdat[6*x+5])
+ acol := uint16(0xffff)
+ if rcol == tr && gcol == tg && bcol == tb {
+ acol = 0x0000
+ }
+ nrgba64.SetNRGBA64(x, y, color.NRGBA64{rcol, gcol, bcol, acol})
+ }
+ } else {
+ for x := 0; x < width; x++ {
+ rcol := uint16(cdat[6*x+0])<<8 | uint16(cdat[6*x+1])
+ gcol := uint16(cdat[6*x+2])<<8 | uint16(cdat[6*x+3])
+ bcol := uint16(cdat[6*x+4])<<8 | uint16(cdat[6*x+5])
+ rgba64.SetRGBA64(x, y, color.RGBA64{rcol, gcol, bcol, 0xffff})
+ }
}
case cbTCA16:
for x := 0; x < width; x++ {
@@ -709,7 +863,11 @@ func (d *decoder) parseChunk() error {
d.stage = dsSeenPLTE
return d.parsePLTE(length)
case "tRNS":
- if d.stage != dsSeenPLTE {
+ if cbPaletted(d.cb) {
+ if d.stage != dsSeenPLTE {
+ return chunkOrderError
+ }
+ } else if d.stage != dsSeenIHDR {
return chunkOrderError
}
d.stage = dsSeentRNS
diff --git a/libgo/go/image/png/reader_test.go b/libgo/go/image/png/reader_test.go
index 0bc4203..b9e9f4d 100644
--- a/libgo/go/image/png/reader_test.go
+++ b/libgo/go/image/png/reader_test.go
@@ -39,6 +39,21 @@ var filenames = []string{
"basn4a16",
"basn6a08",
"basn6a16",
+ "ftbbn0g01",
+ "ftbbn0g02",
+ "ftbbn0g04",
+ "ftbbn2c16",
+ "ftbbn3p08",
+ "ftbgn2c16",
+ "ftbgn3p08",
+ "ftbrn2c08",
+ "ftbwn0g16",
+ "ftbwn3p08",
+ "ftbyn3p08",
+ "ftp0n0g08",
+ "ftp0n2c08",
+ "ftp0n3p08",
+ "ftp1n3p08",
}
var filenamesPaletted = []string{
@@ -64,6 +79,50 @@ func readPNG(filename string) (image.Image, error) {
return Decode(f)
}
+// fakebKGDs maps from filenames to fake bKGD chunks for our approximation to
+// the sng command-line tool. Package png doesn't keep that metadata when
+// png.Decode returns an image.Image.
+var fakebKGDs = map[string]string{
+ "ftbbn0g01": "bKGD {gray: 0;}\n",
+ "ftbbn0g02": "bKGD {gray: 0;}\n",
+ "ftbbn0g04": "bKGD {gray: 0;}\n",
+ "ftbbn2c16": "bKGD {red: 0; green: 0; blue: 65535;}\n",
+ "ftbbn3p08": "bKGD {index: 245}\n",
+ "ftbgn2c16": "bKGD {red: 0; green: 65535; blue: 0;}\n",
+ "ftbgn3p08": "bKGD {index: 245}\n",
+ "ftbrn2c08": "bKGD {red: 255; green: 0; blue: 0;}\n",
+ "ftbwn0g16": "bKGD {gray: 65535;}\n",
+ "ftbwn3p08": "bKGD {index: 0}\n",
+ "ftbyn3p08": "bKGD {index: 245}\n",
+}
+
+// fakegAMAs maps from filenames to fake gAMA chunks for our approximation to
+// the sng command-line tool. Package png doesn't keep that metadata when
+// png.Decode returns an image.Image.
+var fakegAMAs = map[string]string{
+ "ftbbn0g01": "",
+ "ftbbn0g02": "gAMA {0.45455}\n",
+}
+
+// fakeIHDRUsings maps from filenames to fake IHDR "using" lines for our
+// approximation to the sng command-line tool. The PNG model is that
+// transparency (in the tRNS chunk) is separate to the color/grayscale/palette
+// color model (in the IHDR chunk). The Go model is that the concrete
+// image.Image type returned by png.Decode, such as image.RGBA (with all pixels
+// having 100% alpha) or image.NRGBA, encapsulates whether or not the image has
+// transparency. This map is a hack to work around the fact that the Go model
+// can't otherwise discriminate PNG's "IHDR says color (with no alpha) but tRNS
+// says alpha" and "IHDR says color with alpha".
+var fakeIHDRUsings = map[string]string{
+ "ftbbn0g01": " using grayscale;\n",
+ "ftbbn0g02": " using grayscale;\n",
+ "ftbbn0g04": " using grayscale;\n",
+ "ftbbn2c16": " using color;\n",
+ "ftbgn2c16": " using color;\n",
+ "ftbrn2c08": " using color;\n",
+ "ftbwn0g16": " using grayscale;\n",
+}
+
// An approximation of the sng command-line tool.
func sng(w io.WriteCloser, filename string, png image.Image) {
defer w.Close()
@@ -95,25 +154,35 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
// Write the filename and IHDR.
io.WriteString(w, "#SNG: from "+filename+".png\nIHDR {\n")
fmt.Fprintf(w, " width: %d; height: %d; bitdepth: %d;\n", bounds.Dx(), bounds.Dy(), bitdepth)
- switch {
- case cm == color.RGBAModel, cm == color.RGBA64Model:
- io.WriteString(w, " using color;\n")
- case cm == color.NRGBAModel, cm == color.NRGBA64Model:
- io.WriteString(w, " using color alpha;\n")
- case cm == color.GrayModel, cm == color.Gray16Model:
- io.WriteString(w, " using grayscale;\n")
- case cpm != nil:
- io.WriteString(w, " using color palette;\n")
- default:
- io.WriteString(w, "unknown PNG decoder color model\n")
+ if s, ok := fakeIHDRUsings[filename]; ok {
+ io.WriteString(w, s)
+ } else {
+ switch {
+ case cm == color.RGBAModel, cm == color.RGBA64Model:
+ io.WriteString(w, " using color;\n")
+ case cm == color.NRGBAModel, cm == color.NRGBA64Model:
+ io.WriteString(w, " using color alpha;\n")
+ case cm == color.GrayModel, cm == color.Gray16Model:
+ io.WriteString(w, " using grayscale;\n")
+ case cpm != nil:
+ io.WriteString(w, " using color palette;\n")
+ default:
+ io.WriteString(w, "unknown PNG decoder color model\n")
+ }
}
io.WriteString(w, "}\n")
- // We fake a gAMA output. The test files have a gAMA chunk but the go PNG parser ignores it
- // (the PNG spec section 11.3 says "Ancillary chunks may be ignored by a decoder").
- io.WriteString(w, "gAMA {1.0000}\n")
+ // We fake a gAMA chunk. The test files have a gAMA chunk but the go PNG
+ // parser ignores it (the PNG spec section 11.3 says "Ancillary chunks may
+ // be ignored by a decoder").
+ if s, ok := fakegAMAs[filename]; ok {
+ io.WriteString(w, s)
+ } else {
+ io.WriteString(w, "gAMA {1.0000}\n")
+ }
// Write the PLTE and tRNS (if applicable).
+ useTransparent := false
if cpm != nil {
lastAlpha := -1
io.WriteString(w, "PLTE {\n")
@@ -133,6 +202,9 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
fmt.Fprintf(w, " (%3d,%3d,%3d) # rgb = (0x%02x,0x%02x,0x%02x)\n", r, g, b, r, g, b)
}
io.WriteString(w, "}\n")
+ if s, ok := fakebKGDs[filename]; ok {
+ io.WriteString(w, s)
+ }
if lastAlpha != -1 {
io.WriteString(w, "tRNS {\n")
for i := 0; i <= lastAlpha; i++ {
@@ -142,6 +214,42 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
}
io.WriteString(w, "}\n")
}
+ } else if strings.HasPrefix(filename, "ft") {
+ if s, ok := fakebKGDs[filename]; ok {
+ io.WriteString(w, s)
+ }
+ // We fake a tRNS chunk. The test files' grayscale and truecolor
+ // transparent images all have their top left corner transparent.
+ switch c := png.At(0, 0).(type) {
+ case color.NRGBA:
+ if c.A == 0 {
+ useTransparent = true
+ io.WriteString(w, "tRNS {\n")
+ switch filename {
+ case "ftbbn0g01", "ftbbn0g02", "ftbbn0g04":
+ // The standard image package doesn't have a "gray with
+ // alpha" type. Instead, we use an image.NRGBA.
+ fmt.Fprintf(w, " gray: %d;\n", c.R)
+ default:
+ fmt.Fprintf(w, " red: %d; green: %d; blue: %d;\n", c.R, c.G, c.B)
+ }
+ io.WriteString(w, "}\n")
+ }
+ case color.NRGBA64:
+ if c.A == 0 {
+ useTransparent = true
+ io.WriteString(w, "tRNS {\n")
+ switch filename {
+ case "ftbwn0g16":
+ // The standard image package doesn't have a "gray16 with
+ // alpha" type. Instead, we use an image.NRGBA64.
+ fmt.Fprintf(w, " gray: %d;\n", c.R)
+ default:
+ fmt.Fprintf(w, " red: %d; green: %d; blue: %d;\n", c.R, c.G, c.B)
+ }
+ io.WriteString(w, "}\n")
+ }
+ }
}
// Write the IMAGE.
@@ -171,12 +279,30 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
case cm == color.NRGBAModel:
for x := bounds.Min.X; x < bounds.Max.X; x++ {
nrgba := png.At(x, y).(color.NRGBA)
- fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A)
+ switch filename {
+ case "ftbbn0g01", "ftbbn0g02", "ftbbn0g04":
+ fmt.Fprintf(w, "%02x", nrgba.R)
+ default:
+ if useTransparent {
+ fmt.Fprintf(w, "%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B)
+ } else {
+ fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A)
+ }
+ }
}
case cm == color.NRGBA64Model:
for x := bounds.Min.X; x < bounds.Max.X; x++ {
nrgba64 := png.At(x, y).(color.NRGBA64)
- fmt.Fprintf(w, "%04x%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B, nrgba64.A)
+ switch filename {
+ case "ftbwn0g16":
+ fmt.Fprintf(w, "%04x ", nrgba64.R)
+ default:
+ if useTransparent {
+ fmt.Fprintf(w, "%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B)
+ } else {
+ fmt.Fprintf(w, "%04x%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B, nrgba64.A)
+ }
+ }
}
case cpm != nil:
var b, c int
@@ -256,8 +382,23 @@ func TestReader(t *testing.T) {
}
ps := pb.Text()
ss := sb.Text()
+
+ // Newer versions of the sng command line tool append an optional
+ // color name to the RGB tuple. For example:
+ // # rgb = (0xff,0xff,0xff) grey100
+ // # rgb = (0x00,0x00,0xff) blue1
+ // instead of the older version's plainer:
+ // # rgb = (0xff,0xff,0xff)
+ // # rgb = (0x00,0x00,0xff)
+ // We strip any such name.
+ if strings.Contains(ss, "# rgb = (") && !strings.HasSuffix(ss, ")") {
+ if i := strings.LastIndex(ss, ") "); i >= 0 {
+ ss = ss[:i+1]
+ }
+ }
+
if ps != ss {
- t.Errorf("%s: Mismatch\n%sversus\n%s\n", fn, ps, ss)
+ t.Errorf("%s: Mismatch\n%s\nversus\n%s\n", fn, ps, ss)
break
}
}
diff --git a/libgo/go/image/png/testdata/pngsuite/README b/libgo/go/image/png/testdata/pngsuite/README
index c0f78bd..01d1d89 100644
--- a/libgo/go/image/png/testdata/pngsuite/README
+++ b/libgo/go/image/png/testdata/pngsuite/README
@@ -1,21 +1,20 @@
The *.png and README.original files in this directory are copied from
-libpng.org, specifically contrib/pngsuite/* in libpng-1.2.40.tar.gz.
+libpng.org, specifically contrib/pngsuite/* in libpng 1.6.26.
+
README.original gives the following license for those files:
Permission to use, copy, and distribute these images for any purpose
and without fee is hereby granted.
-
-The files basn0g01-30.png, basn0g02-29.png and basn0g04-31.png are in fact
-not part of pngsuite but were created from files in pngsuite. Their non-power-
-of-two sizes makes them useful for testing bit-depths smaller than a byte.
+The files basn0g01-30.png, basn0g02-29.png and basn0g04-31.png are in fact not
+part of pngsuite but were created from files in pngsuite. Their non-power-of-2
+sizes makes them useful for testing bit-depths smaller than a byte.
basn3a08.png was generated from basn6a08.png using the pngnq tool, which
converted it to the 8-bit paletted image with alpha values in tRNS chunk.
-The *.sng files in this directory were generated from the *.png files
-by the sng command-line tool and some hand editing. The files
-basn0g0{1,2,4}.sng were actually generated by first converting the PNG
-to a bitdepth of 8 and then running sng on them. basn4a08.sng was generated
-by from a 16-bit rgba version of basn4a08.png rather than the original
-gray + alpha.
+The *.sng files in this directory were generated from the *.png files by the
+sng command-line tool and some hand editing. The files basn0g0{1,2,4}.sng and
+ftbbn0g0{1,2,4}.sng were actually generated by first converting the PNG to a
+bitdepth of 8 and then running sng on them. basn4a08.sng was generated from a
+16-bit rgba version of basn4a08.png rather than the original gray + alpha.
diff --git a/libgo/go/image/png/writer.go b/libgo/go/image/png/writer.go
index df23270..dd87d81 100644
--- a/libgo/go/image/png/writer.go
+++ b/libgo/go/image/png/writer.go
@@ -420,8 +420,11 @@ func writeImage(w io.Writer, m image.Image, cb int, level int) error {
}
// Apply the filter.
+ // Skip filter for NoCompression and paletted images (cbP8) as
+ // "filters are rarely useful on palette images" and will result
+ // in larger files (see http://www.libpng.org/pub/png/book/chapter09.html).
f := ftNone
- if level != zlib.NoCompression {
+ if level != zlib.NoCompression && cb != cbP8 {
f = filter(&cr, pr, bpp)
}