summaryrefslogtreecommitdiff
path: root/libgo/go/image
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/image')
-rw-r--r--libgo/go/image/color/palette/gen.go96
-rw-r--r--libgo/go/image/color/palette/generate.go8
-rw-r--r--libgo/go/image/color/palette/palette.go3
-rw-r--r--libgo/go/image/gif/reader.go7
-rw-r--r--libgo/go/image/gif/reader_test.go64
-rw-r--r--libgo/go/image/gif/writer.go18
-rw-r--r--libgo/go/image/gif/writer_test.go25
-rw-r--r--libgo/go/image/image.go32
-rw-r--r--libgo/go/image/jpeg/huffman.go257
-rw-r--r--libgo/go/image/jpeg/reader.go210
-rw-r--r--libgo/go/image/jpeg/reader_test.go45
-rw-r--r--libgo/go/image/jpeg/scan.go57
-rw-r--r--libgo/go/image/jpeg/writer.go133
-rw-r--r--libgo/go/image/jpeg/writer_test.go28
-rw-r--r--libgo/go/image/png/paeth.go41
-rw-r--r--libgo/go/image/png/paeth_test.go8
-rw-r--r--libgo/go/image/png/reader.go219
-rw-r--r--libgo/go/image/png/reader_test.go16
-rw-r--r--libgo/go/image/png/writer.go62
-rw-r--r--libgo/go/image/png/writer_test.go29
-rw-r--r--libgo/go/image/ycbcr.go4
21 files changed, 1011 insertions, 351 deletions
diff --git a/libgo/go/image/color/palette/gen.go b/libgo/go/image/color/palette/gen.go
index 4f4d88345a8..2b5fdaaf2b3 100644
--- a/libgo/go/image/color/palette/gen.go
+++ b/libgo/go/image/color/palette/gen.go
@@ -7,29 +7,49 @@
package main
// This program generates palette.go. Invoke it as
-// go run gen.go | gofmt > palette.go
+// go run gen.go -output palette.go
import (
+ "bytes"
+ "flag"
"fmt"
+ "go/format"
+ "io"
+ "io/ioutil"
+ "log"
)
+var filename = flag.String("output", "palette.go", "output file name")
+
func main() {
- fmt.Println(`// Copyright 2013 The Go Authors. All rights reserved.
+ flag.Parse()
+
+ var buf bytes.Buffer
+
+ fmt.Fprintln(&buf, `// 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.`)
- fmt.Println()
- fmt.Println("// generated by go run gen.go; DO NOT EDIT")
- fmt.Println()
- fmt.Println("// Package palette provides standard color palettes.")
- fmt.Println("package palette")
- fmt.Println()
- fmt.Println(`import "image/color"`)
- fmt.Println()
- printPlan9()
- printWebSafe()
+ fmt.Fprintln(&buf)
+ fmt.Fprintln(&buf, "// generated by go run gen.go -output palette.go; DO NOT EDIT")
+ fmt.Fprintln(&buf)
+ fmt.Fprintln(&buf, "package palette")
+ fmt.Fprintln(&buf)
+ fmt.Fprintln(&buf, `import "image/color"`)
+ fmt.Fprintln(&buf)
+ printPlan9(&buf)
+ printWebSafe(&buf)
+
+ data, err := format.Source(buf.Bytes())
+ if err != nil {
+ log.Fatal(err)
+ }
+ err = ioutil.WriteFile(*filename, data, 0644)
+ if err != nil {
+ log.Fatal(err)
+ }
}
-func printPlan9() {
+func printPlan9(w io.Writer) {
c, lines := [3]int{}, [256]string{}
for r, i := 0, 0; r != 4; r++ {
for v := 0; v != 4; v, i = v+1, i+16 {
@@ -58,27 +78,27 @@ func printPlan9() {
}
}
}
- fmt.Println("// Plan9 is a 256-color palette that partitions the 24-bit RGB space")
- fmt.Println("// into 4×4×4 subdivision, with 4 shades in each subcube. Compared to the")
- fmt.Println("// WebSafe, the idea is to reduce the color resolution by dicing the")
- fmt.Println("// color cube into fewer cells, and to use the extra space to increase the")
- fmt.Println("// intensity resolution. This results in 16 gray shades (4 gray subcubes with")
- fmt.Println("// 4 samples in each), 13 shades of each primary and secondary color (3")
- fmt.Println("// subcubes with 4 samples plus black) and a reasonable selection of colors")
- fmt.Println("// covering the rest of the color cube. The advantage is better representation")
- fmt.Println("// of continuous tones.")
- fmt.Println("//")
- fmt.Println("// This palette was used in the Plan 9 Operating System, described at")
- fmt.Println("// http://plan9.bell-labs.com/magic/man2html/6/color")
- fmt.Println("var Plan9 = []color.Color{")
+ fmt.Fprintln(w, "// Plan9 is a 256-color palette that partitions the 24-bit RGB space")
+ fmt.Fprintln(w, "// into 4×4×4 subdivision, with 4 shades in each subcube. Compared to the")
+ fmt.Fprintln(w, "// WebSafe, the idea is to reduce the color resolution by dicing the")
+ fmt.Fprintln(w, "// color cube into fewer cells, and to use the extra space to increase the")
+ fmt.Fprintln(w, "// intensity resolution. This results in 16 gray shades (4 gray subcubes with")
+ fmt.Fprintln(w, "// 4 samples in each), 13 shades of each primary and secondary color (3")
+ fmt.Fprintln(w, "// subcubes with 4 samples plus black) and a reasonable selection of colors")
+ fmt.Fprintln(w, "// covering the rest of the color cube. The advantage is better representation")
+ fmt.Fprintln(w, "// of continuous tones.")
+ fmt.Fprintln(w, "//")
+ fmt.Fprintln(w, "// This palette was used in the Plan 9 Operating System, described at")
+ fmt.Fprintln(w, "// http://plan9.bell-labs.com/magic/man2html/6/color")
+ fmt.Fprintln(w, "var Plan9 = []color.Color{")
for _, line := range lines {
- fmt.Println(line)
+ fmt.Fprintln(w, line)
}
- fmt.Println("}")
- fmt.Println()
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
}
-func printWebSafe() {
+func printWebSafe(w io.Writer) {
lines := [6 * 6 * 6]string{}
for r := 0; r < 6; r++ {
for g := 0; g < 6; g++ {
@@ -88,14 +108,14 @@ func printWebSafe() {
}
}
}
- fmt.Println("// WebSafe is a 216-color palette that was popularized by early versions")
- fmt.Println("// of Netscape Navigator. It is also known as the Netscape Color Cube.")
- fmt.Println("//")
- fmt.Println("// See http://en.wikipedia.org/wiki/Web_colors#Web-safe_colors for details.")
- fmt.Println("var WebSafe = []color.Color{")
+ fmt.Fprintln(w, "// WebSafe is a 216-color palette that was popularized by early versions")
+ fmt.Fprintln(w, "// of Netscape Navigator. It is also known as the Netscape Color Cube.")
+ fmt.Fprintln(w, "//")
+ fmt.Fprintln(w, "// See http://en.wikipedia.org/wiki/Web_colors#Web-safe_colors for details.")
+ fmt.Fprintln(w, "var WebSafe = []color.Color{")
for _, line := range lines {
- fmt.Println(line)
+ fmt.Fprintln(w, line)
}
- fmt.Println("}")
- fmt.Println()
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
}
diff --git a/libgo/go/image/color/palette/generate.go b/libgo/go/image/color/palette/generate.go
new file mode 100644
index 00000000000..64c2ec0d9ab
--- /dev/null
+++ b/libgo/go/image/color/palette/generate.go
@@ -0,0 +1,8 @@
+// Copyright 2014 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.
+
+//go:generate go run gen.go -output palette.go
+
+// Package palette provides standard color palettes.
+package palette
diff --git a/libgo/go/image/color/palette/palette.go b/libgo/go/image/color/palette/palette.go
index f761e5368d9..0bf2c8e1aa5 100644
--- a/libgo/go/image/color/palette/palette.go
+++ b/libgo/go/image/color/palette/palette.go
@@ -2,9 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// generated by go run gen.go; DO NOT EDIT
+// generated by go run gen.go -output palette.go; DO NOT EDIT
-// Package palette provides standard color palettes.
package palette
import "image/color"
diff --git a/libgo/go/image/gif/reader.go b/libgo/go/image/gif/reader.go
index 926710a4562..5a863e204f3 100644
--- a/libgo/go/image/gif/reader.go
+++ b/libgo/go/image/gif/reader.go
@@ -171,7 +171,8 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
if err != nil {
return err
}
- if d.imageFields&fColorMapFollows != 0 {
+ useLocalColorMap := d.imageFields&fColorMapFollows != 0
+ if useLocalColorMap {
m.Palette, err = d.readColorMap()
if err != nil {
return err
@@ -180,6 +181,10 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
m.Palette = d.globalColorMap
}
if d.hasTransparentIndex && int(d.transparentIndex) < len(m.Palette) {
+ if !useLocalColorMap {
+ // Clone the global color map.
+ m.Palette = append(color.Palette(nil), d.globalColorMap...)
+ }
m.Palette[d.transparentIndex] = color.RGBA{}
}
litWidth, err := d.r.ReadByte()
diff --git a/libgo/go/image/gif/reader_test.go b/libgo/go/image/gif/reader_test.go
index fc2041e9970..7b6f504367c 100644
--- a/libgo/go/image/gif/reader_test.go
+++ b/libgo/go/image/gif/reader_test.go
@@ -22,16 +22,16 @@ const (
trailerStr = "\x3b"
)
-func TestDecode(t *testing.T) {
- // lzwEncode returns an LZW encoding (with 2-bit literals) of n zeroes.
- lzwEncode := func(n int) []byte {
- b := &bytes.Buffer{}
- w := lzw.NewWriter(b, lzw.LSB, 2)
- w.Write(make([]byte, n))
- w.Close()
- return b.Bytes()
- }
+// lzwEncode returns an LZW encoding (with 2-bit literals) of n zeroes.
+func lzwEncode(n int) []byte {
+ b := &bytes.Buffer{}
+ w := lzw.NewWriter(b, lzw.LSB, 2)
+ w.Write(make([]byte, n))
+ w.Close()
+ return b.Bytes()
+}
+func TestDecode(t *testing.T) {
testCases := []struct {
nPix int // The number of pixels in the image data.
extra bool // Whether to write an extra block after the LZW-encoded data.
@@ -90,6 +90,52 @@ func TestDecode(t *testing.T) {
}
}
+func TestTransparentIndex(t *testing.T) {
+ b := &bytes.Buffer{}
+ b.WriteString(headerStr)
+ b.WriteString(paletteStr)
+ for transparentIndex := 0; transparentIndex < 3; transparentIndex++ {
+ if transparentIndex < 2 {
+ // Write the graphic control for the transparent index.
+ b.WriteString("\x21\xf9\x00\x01\x00\x00")
+ b.WriteByte(byte(transparentIndex))
+ b.WriteByte(0)
+ }
+ // Write an image with bounds 2x1, as per TestDecode.
+ b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02")
+ enc := lzwEncode(2)
+ if len(enc) > 0xff {
+ t.Fatalf("compressed length %d is too large", len(enc))
+ }
+ b.WriteByte(byte(len(enc)))
+ b.Write(enc)
+ b.WriteByte(0x00)
+ }
+ b.WriteString(trailerStr)
+
+ g, err := DecodeAll(b)
+ if err != nil {
+ t.Fatalf("DecodeAll: %v", err)
+ }
+ c0 := color.RGBA{paletteStr[0], paletteStr[1], paletteStr[2], 0xff}
+ c1 := color.RGBA{paletteStr[3], paletteStr[4], paletteStr[5], 0xff}
+ cz := color.RGBA{}
+ wants := []color.Palette{
+ {cz, c1},
+ {c0, cz},
+ {c0, c1},
+ }
+ if len(g.Image) != len(wants) {
+ t.Fatalf("got %d images, want %d", len(g.Image), len(wants))
+ }
+ for i, want := range wants {
+ got := g.Image[i].Palette
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("palette #%d:\ngot %v\nwant %v", i, got, want)
+ }
+ }
+}
+
// testGIF is a simple GIF that we can modify to test different scenarios.
var testGIF = []byte{
'G', 'I', 'F', '8', '9', 'a',
diff --git a/libgo/go/image/gif/writer.go b/libgo/go/image/gif/writer.go
index 15cd40fadf6..49abde704c8 100644
--- a/libgo/go/image/gif/writer.go
+++ b/libgo/go/image/gif/writer.go
@@ -233,10 +233,20 @@ func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) {
e.writeByte(uint8(litWidth)) // LZW Minimum Code Size.
lzww := lzw.NewWriter(blockWriter{e: e}, lzw.LSB, litWidth)
- _, e.err = lzww.Write(pm.Pix)
- if e.err != nil {
- lzww.Close()
- return
+ if dx := b.Dx(); dx == pm.Stride {
+ _, e.err = lzww.Write(pm.Pix)
+ if e.err != nil {
+ lzww.Close()
+ return
+ }
+ } else {
+ for i, y := 0, b.Min.Y; y < b.Max.Y; i, y = i+pm.Stride, y+1 {
+ _, e.err = lzww.Write(pm.Pix[i : i+dx])
+ if e.err != nil {
+ lzww.Close()
+ return
+ }
+ }
}
lzww.Close()
e.writeByte(0x00) // Block Terminator.
diff --git a/libgo/go/image/gif/writer_test.go b/libgo/go/image/gif/writer_test.go
index c1ada769c2c..93306ffdb34 100644
--- a/libgo/go/image/gif/writer_test.go
+++ b/libgo/go/image/gif/writer_test.go
@@ -102,6 +102,29 @@ func TestWriter(t *testing.T) {
}
}
+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",
@@ -116,7 +139,7 @@ func TestEncodeAll(t *testing.T) {
for i, f := range frames {
m, err := readGIF(f)
if err != nil {
- t.Error(f, err)
+ t.Fatal(f, err)
}
g0.Image[i] = m.Image[0]
}
diff --git a/libgo/go/image/image.go b/libgo/go/image/image.go
index 32a89ef34ca..6b8e5c4877e 100644
--- a/libgo/go/image/image.go
+++ b/libgo/go/image/image.go
@@ -72,6 +72,10 @@ func (p *RGBA) ColorModel() color.Model { return color.RGBAModel }
func (p *RGBA) Bounds() Rectangle { return p.Rect }
func (p *RGBA) At(x, y int) color.Color {
+ return p.RGBAAt(x, y)
+}
+
+func (p *RGBA) RGBAAt(x, y int) color.RGBA {
if !(Point{x, y}.In(p.Rect)) {
return color.RGBA{}
}
@@ -167,6 +171,10 @@ func (p *RGBA64) ColorModel() color.Model { return color.RGBA64Model }
func (p *RGBA64) Bounds() Rectangle { return p.Rect }
func (p *RGBA64) At(x, y int) color.Color {
+ return p.RGBA64At(x, y)
+}
+
+func (p *RGBA64) RGBA64At(x, y int) color.RGBA64 {
if !(Point{x, y}.In(p.Rect)) {
return color.RGBA64{}
}
@@ -275,6 +283,10 @@ func (p *NRGBA) ColorModel() color.Model { return color.NRGBAModel }
func (p *NRGBA) Bounds() Rectangle { return p.Rect }
func (p *NRGBA) At(x, y int) color.Color {
+ return p.NRGBAAt(x, y)
+}
+
+func (p *NRGBA) NRGBAAt(x, y int) color.NRGBA {
if !(Point{x, y}.In(p.Rect)) {
return color.NRGBA{}
}
@@ -370,6 +382,10 @@ func (p *NRGBA64) ColorModel() color.Model { return color.NRGBA64Model }
func (p *NRGBA64) Bounds() Rectangle { return p.Rect }
func (p *NRGBA64) At(x, y int) color.Color {
+ return p.NRGBA64At(x, y)
+}
+
+func (p *NRGBA64) NRGBA64At(x, y int) color.NRGBA64 {
if !(Point{x, y}.In(p.Rect)) {
return color.NRGBA64{}
}
@@ -478,6 +494,10 @@ func (p *Alpha) ColorModel() color.Model { return color.AlphaModel }
func (p *Alpha) Bounds() Rectangle { return p.Rect }
func (p *Alpha) At(x, y int) color.Color {
+ return p.AlphaAt(x, y)
+}
+
+func (p *Alpha) AlphaAt(x, y int) color.Alpha {
if !(Point{x, y}.In(p.Rect)) {
return color.Alpha{}
}
@@ -566,6 +586,10 @@ func (p *Alpha16) ColorModel() color.Model { return color.Alpha16Model }
func (p *Alpha16) Bounds() Rectangle { return p.Rect }
func (p *Alpha16) At(x, y int) color.Color {
+ return p.Alpha16At(x, y)
+}
+
+func (p *Alpha16) Alpha16At(x, y int) color.Alpha16 {
if !(Point{x, y}.In(p.Rect)) {
return color.Alpha16{}
}
@@ -657,6 +681,10 @@ func (p *Gray) ColorModel() color.Model { return color.GrayModel }
func (p *Gray) Bounds() Rectangle { return p.Rect }
func (p *Gray) At(x, y int) color.Color {
+ return p.GrayAt(x, y)
+}
+
+func (p *Gray) GrayAt(x, y int) color.Gray {
if !(Point{x, y}.In(p.Rect)) {
return color.Gray{}
}
@@ -732,6 +760,10 @@ func (p *Gray16) ColorModel() color.Model { return color.Gray16Model }
func (p *Gray16) Bounds() Rectangle { return p.Rect }
func (p *Gray16) At(x, y int) color.Color {
+ return p.Gray16At(x, y)
+}
+
+func (p *Gray16) Gray16At(x, y int) color.Gray16 {
if !(Point{x, y}.In(p.Rect)) {
return color.Gray16{}
}
diff --git a/libgo/go/image/jpeg/huffman.go b/libgo/go/image/jpeg/huffman.go
index f53d873a538..d4ff4cfa0ce 100644
--- a/libgo/go/image/jpeg/huffman.go
+++ b/libgo/go/image/jpeg/huffman.go
@@ -4,94 +4,96 @@
package jpeg
-import "io"
+import (
+ "io"
+)
-// Each code is at most 16 bits long.
+// maxCodeLength is the maximum (inclusive) number of bits in a Huffman code.
const maxCodeLength = 16
-// Each decoded value is a uint8, so there are at most 256 such values.
-const maxNumValues = 256
+// maxNCodes is the maximum (inclusive) number of codes in a Huffman tree.
+const maxNCodes = 256
-// Bit stream for the Huffman decoder.
-// The n least significant bits of a form the unread bits, to be read in MSB to LSB order.
-type bits struct {
- a uint32 // accumulator.
- m uint32 // mask. m==1<<(n-1) when n>0, with m==0 when n==0.
- n int // the number of unread bits in a.
-}
+// lutSize is the log-2 size of the Huffman decoder's look-up table.
+const lutSize = 8
-// Huffman table decoder, specified in section C.
+// huffman is a Huffman decoder, specified in section C.
type huffman struct {
- l [maxCodeLength]int
- length int // sum of l[i].
- val [maxNumValues]uint8 // the decoded values, as sorted by their encoding.
- size [maxNumValues]int // size[i] is the number of bits to encode val[i].
- code [maxNumValues]int // code[i] is the encoding of val[i].
- minCode [maxCodeLength]int // min codes of length i, or -1 if no codes of that length.
- maxCode [maxCodeLength]int // max codes of length i, or -1 if no codes of that length.
- valIndex [maxCodeLength]int // index into val of minCode[i].
+ // length is the number of codes in the tree.
+ nCodes int32
+ // lut is the look-up table for the next lutSize bits in the bit-stream.
+ // The high 8 bits of the uint16 are the encoded value. The low 8 bits
+ // are 1 plus the code length, or 0 if the value is too large to fit in
+ // lutSize bits.
+ lut [1 << lutSize]uint16
+ // vals are the decoded values, sorted by their encoding.
+ vals [maxNCodes]uint8
+ // minCodes[i] is the minimum code of length i, or -1 if there are no
+ // codes of that length.
+ minCodes [maxCodeLength]int32
+ // maxCodes[i] is the maximum code of length i, or -1 if there are no
+ // codes of that length.
+ maxCodes [maxCodeLength]int32
+ // valsIndices[i] is the index into vals of minCodes[i].
+ valsIndices [maxCodeLength]int32
}
-// Reads bytes from the io.Reader to ensure that bits.n is at least n.
-func (d *decoder) ensureNBits(n int) error {
- for d.b.n < n {
- c, err := d.r.ReadByte()
+// errShortHuffmanData means that an unexpected EOF occurred while decoding
+// Huffman data.
+var errShortHuffmanData = FormatError("short Huffman data")
+
+// ensureNBits reads bytes from the byte buffer to ensure that d.bits.n is at
+// least n. For best performance (avoiding function calls inside hot loops),
+// the caller is the one responsible for first checking that d.bits.n < n.
+func (d *decoder) ensureNBits(n int32) error {
+ for {
+ c, err := d.readByteStuffedByte()
if err != nil {
if err == io.EOF {
- return FormatError("short Huffman data")
+ return errShortHuffmanData
}
return err
}
- d.b.a = d.b.a<<8 | uint32(c)
- d.b.n += 8
- if d.b.m == 0 {
- d.b.m = 1 << 7
+ d.bits.a = d.bits.a<<8 | uint32(c)
+ d.bits.n += 8
+ if d.bits.m == 0 {
+ d.bits.m = 1 << 7
} else {
- d.b.m <<= 8
- }
- // Byte stuffing, specified in section F.1.2.3.
- if c == 0xff {
- c, err = d.r.ReadByte()
- if err != nil {
- if err == io.EOF {
- return FormatError("short Huffman data")
- }
- return err
- }
- if c != 0x00 {
- return FormatError("missing 0xff00 sequence")
- }
+ d.bits.m <<= 8
+ }
+ if d.bits.n >= n {
+ break
}
}
return nil
}
-// The composition of RECEIVE and EXTEND, specified in section F.2.2.1.
+// receiveExtend is the composition of RECEIVE and EXTEND, specified in section
+// F.2.2.1.
func (d *decoder) receiveExtend(t uint8) (int32, error) {
- if d.b.n < int(t) {
- if err := d.ensureNBits(int(t)); err != nil {
+ if d.bits.n < int32(t) {
+ if err := d.ensureNBits(int32(t)); err != nil {
return 0, err
}
}
- d.b.n -= int(t)
- d.b.m >>= t
+ d.bits.n -= int32(t)
+ d.bits.m >>= t
s := int32(1) << t
- x := int32(d.b.a>>uint8(d.b.n)) & (s - 1)
+ x := int32(d.bits.a>>uint8(d.bits.n)) & (s - 1)
if x < s>>1 {
x += ((-1) << t) + 1
}
return x, nil
}
-// Processes a Define Huffman Table marker, and initializes a huffman struct from its contents.
-// Specified in section B.2.4.2.
+// processDHT processes a Define Huffman Table marker, and initializes a huffman
+// struct from its contents. Specified in section B.2.4.2.
func (d *decoder) processDHT(n int) error {
for n > 0 {
if n < 17 {
return FormatError("DHT has wrong length")
}
- _, err := io.ReadFull(d.r, d.tmp[0:17])
- if err != nil {
+ if err := d.readFull(d.tmp[:17]); err != nil {
return err
}
tc := d.tmp[0] >> 4
@@ -104,89 +106,112 @@ func (d *decoder) processDHT(n int) error {
}
h := &d.huff[tc][th]
- // Read l and val (and derive length).
- h.length = 0
- for i := 0; i < maxCodeLength; i++ {
- h.l[i] = int(d.tmp[i+1])
- h.length += h.l[i]
+ // Read nCodes and h.vals (and derive h.nCodes).
+ // nCodes[i] is the number of codes with code length i.
+ // h.nCodes is the total number of codes.
+ h.nCodes = 0
+ var nCodes [maxCodeLength]int32
+ for i := range nCodes {
+ nCodes[i] = int32(d.tmp[i+1])
+ h.nCodes += nCodes[i]
}
- if h.length == 0 {
+ if h.nCodes == 0 {
return FormatError("Huffman table has zero length")
}
- if h.length > maxNumValues {
+ if h.nCodes > maxNCodes {
return FormatError("Huffman table has excessive length")
}
- n -= h.length + 17
+ n -= int(h.nCodes) + 17
if n < 0 {
return FormatError("DHT has wrong length")
}
- _, err = io.ReadFull(d.r, h.val[0:h.length])
- if err != nil {
+ if err := d.readFull(h.vals[:h.nCodes]); err != nil {
return err
}
- // Derive size.
- k := 0
- for i := 0; i < maxCodeLength; i++ {
- for j := 0; j < h.l[i]; j++ {
- h.size[k] = i + 1
- k++
+ // Derive the look-up table.
+ for i := range h.lut {
+ h.lut[i] = 0
+ }
+ var x, code uint32
+ for i := uint32(0); i < lutSize; i++ {
+ code <<= 1
+ for j := int32(0); j < nCodes[i]; j++ {
+ // The codeLength is 1+i, so shift code by 8-(1+i) to
+ // calculate the high bits for every 8-bit sequence
+ // whose codeLength's high bits matches code.
+ // The high 8 bits of lutValue are the encoded value.
+ // The low 8 bits are 1 plus the codeLength.
+ base := uint8(code << (7 - i))
+ lutValue := uint16(h.vals[x])<<8 | uint16(2+i)
+ for k := uint8(0); k < 1<<(7-i); k++ {
+ h.lut[base|k] = lutValue
+ }
+ code++
+ x++
}
}
- // Derive code.
- code := 0
- size := h.size[0]
- for i := 0; i < h.length; i++ {
- if size != h.size[i] {
- code <<= uint8(h.size[i] - size)
- size = h.size[i]
- }
- h.code[i] = code
- code++
- }
-
- // Derive minCode, maxCode, and valIndex.
- k = 0
- index := 0
- for i := 0; i < maxCodeLength; i++ {
- if h.l[i] == 0 {
- h.minCode[i] = -1
- h.maxCode[i] = -1
- h.valIndex[i] = -1
+ // Derive minCodes, maxCodes, and valsIndices.
+ var c, index int32
+ for i, n := range nCodes {
+ if n == 0 {
+ h.minCodes[i] = -1
+ h.maxCodes[i] = -1
+ h.valsIndices[i] = -1
} else {
- h.minCode[i] = k
- h.maxCode[i] = k + h.l[i] - 1
- h.valIndex[i] = index
- k += h.l[i]
- index += h.l[i]
+ h.minCodes[i] = c
+ h.maxCodes[i] = c + n - 1
+ h.valsIndices[i] = index
+ c += n
+ index += n
}
- k <<= 1
+ c <<= 1
}
}
return nil
}
-// Returns the next Huffman-coded value from the bit stream, decoded according to h.
-// TODO(nigeltao): This decoding algorithm is simple, but slow. A lookahead table, instead of always
-// peeling off only 1 bit at time, ought to be faster.
+// decodeHuffman returns the next Huffman-coded value from the bit-stream,
+// decoded according to h.
func (d *decoder) decodeHuffman(h *huffman) (uint8, error) {
- if h.length == 0 {
+ if h.nCodes == 0 {
return 0, FormatError("uninitialized Huffman table")
}
- for i, code := 0, 0; i < maxCodeLength; i++ {
- if d.b.n == 0 {
+
+ if d.bits.n < 8 {
+ if err := d.ensureNBits(8); err != nil {
+ if err != errMissingFF00 && err != errShortHuffmanData {
+ return 0, err
+ }
+ // There are no more bytes of data in this segment, but we may still
+ // be able to read the next symbol out of the previously read bits.
+ // First, undo the readByte that the ensureNBits call made.
+ d.unreadByteStuffedByte()
+ goto slowPath
+ }
+ }
+ if v := h.lut[(d.bits.a>>uint32(d.bits.n-lutSize))&0xff]; v != 0 {
+ n := (v & 0xff) - 1
+ d.bits.n -= int32(n)
+ d.bits.m >>= n
+ return uint8(v >> 8), nil
+ }
+
+slowPath:
+ for i, code := 0, int32(0); i < maxCodeLength; i++ {
+ if d.bits.n == 0 {
if err := d.ensureNBits(1); err != nil {
return 0, err
}
}
- if d.b.a&d.b.m != 0 {
+ if d.bits.a&d.bits.m != 0 {
code |= 1
}
- d.b.n--
- d.b.m >>= 1
- if code <= h.maxCode[i] {
- return h.val[h.valIndex[i]+code-h.minCode[i]], nil
+ d.bits.n--
+ d.bits.m >>= 1
+ if code <= h.maxCodes[i] {
+ return h.vals[h.valsIndices[i]+code-h.minCodes[i]], nil
}
code <<= 1
}
@@ -194,26 +219,26 @@ func (d *decoder) decodeHuffman(h *huffman) (uint8, error) {
}
func (d *decoder) decodeBit() (bool, error) {
- if d.b.n == 0 {
+ if d.bits.n == 0 {
if err := d.ensureNBits(1); err != nil {
return false, err
}
}
- ret := d.b.a&d.b.m != 0
- d.b.n--
- d.b.m >>= 1
+ ret := d.bits.a&d.bits.m != 0
+ d.bits.n--
+ d.bits.m >>= 1
return ret, nil
}
-func (d *decoder) decodeBits(n int) (uint32, error) {
- if d.b.n < n {
+func (d *decoder) decodeBits(n int32) (uint32, error) {
+ if d.bits.n < n {
if err := d.ensureNBits(n); err != nil {
return 0, err
}
}
- ret := d.b.a >> uint(d.b.n-n)
- ret &= (1 << uint(n)) - 1
- d.b.n -= n
- d.b.m >>= uint(n)
+ ret := d.bits.a >> uint32(d.bits.n-n)
+ ret &= (1 << uint32(n)) - 1
+ d.bits.n -= n
+ d.bits.m >>= uint32(n)
return ret, nil
}
diff --git a/libgo/go/image/jpeg/reader.go b/libgo/go/image/jpeg/reader.go
index 356d56220a7..6d8b1d1d036 100644
--- a/libgo/go/image/jpeg/reader.go
+++ b/libgo/go/image/jpeg/reader.go
@@ -8,7 +8,6 @@
package jpeg
import (
- "bufio"
"image"
"image/color"
"io"
@@ -84,15 +83,36 @@ var unzig = [blockSize]int{
53, 60, 61, 54, 47, 55, 62, 63,
}
-// If the passed in io.Reader does not also have ReadByte, then Decode will introduce its own buffering.
+// Reader is deprecated.
type Reader interface {
+ io.ByteReader
io.Reader
- ReadByte() (c byte, err error)
+}
+
+// bits holds the unprocessed bits that have been taken from the byte-stream.
+// The n least significant bits of a form the unread bits, to be read in MSB to
+// LSB order.
+type bits struct {
+ a uint32 // accumulator.
+ m uint32 // mask. m==1<<(n-1) when n>0, with m==0 when n==0.
+ n int32 // the number of unread bits in a.
}
type decoder struct {
- r Reader
- b bits
+ r io.Reader
+ bits bits
+ // bytes is a byte buffer, similar to a bufio.Reader, except that it
+ // has to be able to unread more than 1 byte, due to byte stuffing.
+ // Byte stuffing is specified in section F.1.2.3.
+ bytes struct {
+ // buf[i:j] are the buffered bytes read from the underlying
+ // io.Reader that haven't yet been passed further on.
+ buf [4096]byte
+ i, j int
+ // nUnreadable is the number of bytes to back up i after
+ // overshooting. It can be 0, 1 or 2.
+ nUnreadable int
+ }
width, height int
img1 *image.Gray
img3 *image.YCbCr
@@ -104,21 +124,160 @@ type decoder struct {
progCoeffs [nColorComponent][]block // Saved state between progressive-mode scans.
huff [maxTc + 1][maxTh + 1]huffman
quant [maxTq + 1]block // Quantization tables, in zig-zag order.
- tmp [1024]byte
+ tmp [blockSize + 1]byte
+}
+
+// fill fills up the d.bytes.buf buffer from the underlying io.Reader. It
+// should only be called when there are no unread bytes in d.bytes.
+func (d *decoder) fill() error {
+ if d.bytes.i != d.bytes.j {
+ panic("jpeg: fill called when unread bytes exist")
+ }
+ // Move the last 2 bytes to the start of the buffer, in case we need
+ // to call unreadByteStuffedByte.
+ if d.bytes.j > 2 {
+ d.bytes.buf[0] = d.bytes.buf[d.bytes.j-2]
+ d.bytes.buf[1] = d.bytes.buf[d.bytes.j-1]
+ d.bytes.i, d.bytes.j = 2, 2
+ }
+ // Fill in the rest of the buffer.
+ n, err := d.r.Read(d.bytes.buf[d.bytes.j:])
+ d.bytes.j += n
+ if n > 0 {
+ err = nil
+ }
+ return err
+}
+
+// unreadByteStuffedByte undoes the most recent readByteStuffedByte call,
+// giving a byte of data back from d.bits to d.bytes. The Huffman look-up table
+// requires at least 8 bits for look-up, which means that Huffman decoding can
+// sometimes overshoot and read one or two too many bytes. Two-byte overshoot
+// can happen when expecting to read a 0xff 0x00 byte-stuffed byte.
+func (d *decoder) unreadByteStuffedByte() {
+ if d.bytes.nUnreadable == 0 {
+ panic("jpeg: unreadByteStuffedByte call cannot be fulfilled")
+ }
+ d.bytes.i -= d.bytes.nUnreadable
+ d.bytes.nUnreadable = 0
+ if d.bits.n >= 8 {
+ d.bits.a >>= 8
+ d.bits.n -= 8
+ d.bits.m >>= 8
+ }
+}
+
+// readByte returns the next byte, whether buffered or not buffered. It does
+// not care about byte stuffing.
+func (d *decoder) readByte() (x byte, err error) {
+ for d.bytes.i == d.bytes.j {
+ if err = d.fill(); err != nil {
+ return 0, err
+ }
+ }
+ x = d.bytes.buf[d.bytes.i]
+ d.bytes.i++
+ d.bytes.nUnreadable = 0
+ return x, nil
+}
+
+// errMissingFF00 means that readByteStuffedByte encountered an 0xff byte (a
+// marker byte) that wasn't the expected byte-stuffed sequence 0xff, 0x00.
+var errMissingFF00 = FormatError("missing 0xff00 sequence")
+
+// readByteStuffedByte is like readByte but is for byte-stuffed Huffman data.
+func (d *decoder) readByteStuffedByte() (x byte, err error) {
+ // Take the fast path if d.bytes.buf contains at least two bytes.
+ if d.bytes.i+2 <= d.bytes.j {
+ x = d.bytes.buf[d.bytes.i]
+ d.bytes.i++
+ d.bytes.nUnreadable = 1
+ if x != 0xff {
+ return x, err
+ }
+ if d.bytes.buf[d.bytes.i] != 0x00 {
+ return 0, errMissingFF00
+ }
+ d.bytes.i++
+ d.bytes.nUnreadable = 2
+ return 0xff, nil
+ }
+
+ x, err = d.readByte()
+ if err != nil {
+ return 0, err
+ }
+ if x != 0xff {
+ d.bytes.nUnreadable = 1
+ return x, nil
+ }
+
+ x, err = d.readByte()
+ if err != nil {
+ d.bytes.nUnreadable = 1
+ return 0, err
+ }
+ d.bytes.nUnreadable = 2
+ if x != 0x00 {
+ return 0, errMissingFF00
+ }
+ return 0xff, nil
}
-// Reads and ignores the next n bytes.
+// readFull reads exactly len(p) bytes into p. It does not care about byte
+// stuffing.
+func (d *decoder) readFull(p []byte) error {
+ // Unread the overshot bytes, if any.
+ if d.bytes.nUnreadable != 0 {
+ if d.bits.n >= 8 {
+ d.unreadByteStuffedByte()
+ }
+ d.bytes.nUnreadable = 0
+ }
+
+ for {
+ n := copy(p, d.bytes.buf[d.bytes.i:d.bytes.j])
+ p = p[n:]
+ d.bytes.i += n
+ if len(p) == 0 {
+ break
+ }
+ if err := d.fill(); err != nil {
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ return err
+ }
+ }
+ return nil
+}
+
+// ignore ignores the next n bytes.
func (d *decoder) ignore(n int) error {
- for n > 0 {
- m := len(d.tmp)
+ // Unread the overshot bytes, if any.
+ if d.bytes.nUnreadable != 0 {
+ if d.bits.n >= 8 {
+ d.unreadByteStuffedByte()
+ }
+ d.bytes.nUnreadable = 0
+ }
+
+ for {
+ m := d.bytes.j - d.bytes.i
if m > n {
m = n
}
- _, err := io.ReadFull(d.r, d.tmp[0:m])
- if err != nil {
+ d.bytes.i += m
+ n -= m
+ if n == 0 {
+ break
+ }
+ if err := d.fill(); err != nil {
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
return err
}
- n -= m
}
return nil
}
@@ -133,8 +292,7 @@ func (d *decoder) processSOF(n int) error {
default:
return UnsupportedError("SOF has wrong length")
}
- _, err := io.ReadFull(d.r, d.tmp[:n])
- if err != nil {
+ if err := d.readFull(d.tmp[:n]); err != nil {
return err
}
// We only support 8-bit precision.
@@ -187,8 +345,7 @@ func (d *decoder) processSOF(n int) error {
func (d *decoder) processDQT(n int) error {
const qtLength = 1 + blockSize
for ; n >= qtLength; n -= qtLength {
- _, err := io.ReadFull(d.r, d.tmp[0:qtLength])
- if err != nil {
+ if err := d.readFull(d.tmp[:qtLength]); err != nil {
return err
}
pq := d.tmp[0] >> 4
@@ -214,8 +371,7 @@ func (d *decoder) processDRI(n int) error {
if n != 2 {
return FormatError("DRI has wrong length")
}
- _, err := io.ReadFull(d.r, d.tmp[0:2])
- if err != nil {
+ if err := d.readFull(d.tmp[:2]); err != nil {
return err
}
d.ri = int(d.tmp[0])<<8 + int(d.tmp[1])
@@ -224,15 +380,10 @@ func (d *decoder) processDRI(n int) error {
// decode reads a JPEG image from r and returns it as an image.Image.
func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) {
- if rr, ok := r.(Reader); ok {
- d.r = rr
- } else {
- d.r = bufio.NewReader(r)
- }
+ d.r = r
// Check for the Start Of Image marker.
- _, err := io.ReadFull(d.r, d.tmp[0:2])
- if err != nil {
+ if err := d.readFull(d.tmp[:2]); err != nil {
return nil, err
}
if d.tmp[0] != 0xff || d.tmp[1] != soiMarker {
@@ -241,7 +392,7 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) {
// Process the remaining segments until the End Of Image marker.
for {
- _, err := io.ReadFull(d.r, d.tmp[0:2])
+ err := d.readFull(d.tmp[:2])
if err != nil {
return nil, err
}
@@ -267,7 +418,7 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) {
// Note that extraneous 0xff bytes in e.g. SOS data are escaped as
// "\xff\x00", and so are detected a little further down below.
d.tmp[0] = d.tmp[1]
- d.tmp[1], err = d.r.ReadByte()
+ d.tmp[1], err = d.readByte()
if err != nil {
return nil, err
}
@@ -280,7 +431,7 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) {
for marker == 0xff {
// Section B.1.1.2 says, "Any marker may optionally be preceded by any
// number of fill bytes, which are bytes assigned code X'FF'".
- marker, err = d.r.ReadByte()
+ marker, err = d.readByte()
if err != nil {
return nil, err
}
@@ -300,8 +451,7 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) {
// Read the 16-bit length of the segment. The value includes the 2 bytes for the
// length itself, so we subtract 2 to get the number of remaining bytes.
- _, err = io.ReadFull(d.r, d.tmp[0:2])
- if err != nil {
+ if err = d.readFull(d.tmp[:2]); err != nil {
return nil, err
}
n := int(d.tmp[0])<<8 + int(d.tmp[1]) - 2
diff --git a/libgo/go/image/jpeg/reader_test.go b/libgo/go/image/jpeg/reader_test.go
index 926bb043448..4de2e8ee737 100644
--- a/libgo/go/image/jpeg/reader_test.go
+++ b/libgo/go/image/jpeg/reader_test.go
@@ -9,6 +9,7 @@ import (
"fmt"
"image"
"image/color"
+ "io"
"io/ioutil"
"math/rand"
"os"
@@ -86,7 +87,51 @@ func decodeFile(filename string) (image.Image, error) {
}
defer f.Close()
return Decode(f)
+}
+
+type eofReader struct {
+ data []byte // deliver from Read without EOF
+ dataEOF []byte // then deliver from Read with EOF on last chunk
+ lenAtEOF int
+}
+
+func (r *eofReader) Read(b []byte) (n int, err error) {
+ if len(r.data) > 0 {
+ n = copy(b, r.data)
+ r.data = r.data[n:]
+ } else {
+ n = copy(b, r.dataEOF)
+ r.dataEOF = r.dataEOF[n:]
+ if len(r.dataEOF) == 0 {
+ err = io.EOF
+ if r.lenAtEOF == -1 {
+ r.lenAtEOF = n
+ }
+ }
+ }
+ return
+}
+func TestDecodeEOF(t *testing.T) {
+ // Check that if reader returns final data and EOF at same time, jpeg handles it.
+ data, err := ioutil.ReadFile("../testdata/video-001.jpeg")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ n := len(data)
+ for i := 0; i < n; {
+ r := &eofReader{data[:n-i], data[n-i:], -1}
+ _, err := Decode(r)
+ if err != nil {
+ t.Errorf("Decode with Read() = %d, EOF: %v", r.lenAtEOF, err)
+ }
+ if i == 0 {
+ i = 1
+ } else {
+ i *= 2
+ }
+ }
}
// check checks that the two pix data are equal, within the given bounds.
diff --git a/libgo/go/image/jpeg/scan.go b/libgo/go/image/jpeg/scan.go
index 559235d5127..2bd1d9d531d 100644
--- a/libgo/go/image/jpeg/scan.go
+++ b/libgo/go/image/jpeg/scan.go
@@ -6,7 +6,6 @@ package jpeg
import (
"image"
- "io"
)
// makeImg allocates and initializes the destination image.
@@ -41,8 +40,7 @@ func (d *decoder) processSOS(n int) error {
if n < 6 || 4+2*d.nComp < n || n%2 != 0 {
return FormatError("SOS has wrong length")
}
- _, err := io.ReadFull(d.r, d.tmp[:n])
- if err != nil {
+ if err := d.readFull(d.tmp[:n]); err != nil {
return err
}
nComp := int(d.tmp[0])
@@ -67,7 +65,13 @@ func (d *decoder) processSOS(n int) error {
}
scan[i].compIndex = uint8(compIndex)
scan[i].td = d.tmp[2+2*i] >> 4
+ if scan[i].td > maxTh {
+ return FormatError("bad Td value")
+ }
scan[i].ta = d.tmp[2+2*i] & 0x0f
+ if scan[i].ta > maxTh {
+ return FormatError("bad Ta value")
+ }
}
// zigStart and zigEnd are the spectral selection bounds.
@@ -119,18 +123,17 @@ func (d *decoder) processSOS(n int) error {
}
}
- d.b = bits{}
+ d.bits = bits{}
mcu, expectedRST := 0, uint8(rst0Marker)
var (
// b is the decoded coefficients, in natural (not zig-zag) order.
b block
dc [nColorComponent]int32
- // mx0 and my0 are the location of the current (in terms of 8x8 blocks).
+ // bx and by are the location of the current (in terms of 8x8 blocks).
// For example, with 4:2:0 chroma subsampling, the block whose top left
// pixel co-ordinates are (16, 8) is the third block in the first row:
- // mx0 is 2 and my0 is 0, even though the pixel is in the second MCU.
- // TODO(nigeltao): rename mx0 and my0 to bx and by?
- mx0, my0 int
+ // bx is 2 and by is 0, even though the pixel is in the second MCU.
+ bx, by int
blockCount int
)
for my := 0; my < myy; my++ {
@@ -165,26 +168,26 @@ func (d *decoder) processSOS(n int) error {
// 0 1 2
// 3 4 5
if nComp != 1 {
- mx0, my0 = d.comp[compIndex].h*mx, d.comp[compIndex].v*my
+ bx, by = d.comp[compIndex].h*mx, d.comp[compIndex].v*my
if h0 == 1 {
- my0 += j
+ by += j
} else {
- mx0 += j % 2
- my0 += j / 2
+ bx += j % 2
+ by += j / 2
}
} else {
q := mxx * d.comp[compIndex].h
- mx0 = blockCount % q
- my0 = blockCount / q
+ bx = blockCount % q
+ by = blockCount / q
blockCount++
- if mx0*8 >= d.width || my0*8 >= d.height {
+ if bx*8 >= d.width || by*8 >= d.height {
continue
}
}
// Load the previous partially decoded coefficients, if applicable.
if d.progressive {
- b = d.progCoeffs[compIndex][my0*mxx*d.comp[compIndex].h+mx0]
+ b = d.progCoeffs[compIndex][by*mxx*d.comp[compIndex].h+bx]
} else {
b = block{}
}
@@ -217,8 +220,9 @@ func (d *decoder) processSOS(n int) error {
d.eobRun--
} else {
// Decode the AC coefficients, as specified in section F.2.2.2.
+ huff := &d.huff[acTable][scan[i].ta]
for ; zig <= zigEnd; zig++ {
- value, err := d.decodeHuffman(&d.huff[acTable][scan[i].ta])
+ value, err := d.decodeHuffman(huff)
if err != nil {
return err
}
@@ -238,7 +242,7 @@ func (d *decoder) processSOS(n int) error {
if val0 != 0x0f {
d.eobRun = uint16(1 << val0)
if val0 != 0 {
- bits, err := d.decodeBits(int(val0))
+ bits, err := d.decodeBits(int32(val0))
if err != nil {
return err
}
@@ -256,7 +260,7 @@ func (d *decoder) processSOS(n int) error {
if d.progressive {
if zigEnd != blockSize-1 || al != 0 {
// We haven't completely decoded this 8x8 block. Save the coefficients.
- d.progCoeffs[compIndex][my0*mxx*d.comp[compIndex].h+mx0] = b
+ d.progCoeffs[compIndex][by*mxx*d.comp[compIndex].h+bx] = b
// At this point, we could execute the rest of the loop body to dequantize and
// perform the inverse DCT, to save early stages of a progressive image to the
// *image.YCbCr buffers (the whole point of progressive encoding), but in Go,
@@ -273,15 +277,15 @@ func (d *decoder) processSOS(n int) error {
idct(&b)
dst, stride := []byte(nil), 0
if d.nComp == nGrayComponent {
- dst, stride = d.img1.Pix[8*(my0*d.img1.Stride+mx0):], d.img1.Stride
+ dst, stride = d.img1.Pix[8*(by*d.img1.Stride+bx):], d.img1.Stride
} else {
switch compIndex {
case 0:
- dst, stride = d.img3.Y[8*(my0*d.img3.YStride+mx0):], d.img3.YStride
+ dst, stride = d.img3.Y[8*(by*d.img3.YStride+bx):], d.img3.YStride
case 1:
- dst, stride = d.img3.Cb[8*(my0*d.img3.CStride+mx0):], d.img3.CStride
+ dst, stride = d.img3.Cb[8*(by*d.img3.CStride+bx):], d.img3.CStride
case 2:
- dst, stride = d.img3.Cr[8*(my0*d.img3.CStride+mx0):], d.img3.CStride
+ dst, stride = d.img3.Cr[8*(by*d.img3.CStride+bx):], d.img3.CStride
default:
return UnsupportedError("too many components")
}
@@ -308,8 +312,7 @@ func (d *decoder) processSOS(n int) error {
if d.ri > 0 && mcu%d.ri == 0 && mcu < mxx*myy {
// A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input,
// but this one assumes well-formed input, and hence the restart marker follows immediately.
- _, err := io.ReadFull(d.r, d.tmp[0:2])
- if err != nil {
+ if err := d.readFull(d.tmp[:2]); err != nil {
return err
}
if d.tmp[0] != 0xff || d.tmp[1] != expectedRST {
@@ -320,7 +323,7 @@ func (d *decoder) processSOS(n int) error {
expectedRST = rst0Marker
}
// Reset the Huffman decoder.
- d.b = bits{}
+ d.bits = bits{}
// Reset the DC components, as per section F.2.1.3.1.
dc = [nColorComponent]int32{}
// Reset the progressive decoder state, as per section G.1.2.2.
@@ -368,7 +371,7 @@ func (d *decoder) refine(b *block, h *huffman, zigStart, zigEnd, delta int32) er
if val0 != 0x0f {
d.eobRun = uint16(1 << val0)
if val0 != 0 {
- bits, err := d.decodeBits(int(val0))
+ bits, err := d.decodeBits(int32(val0))
if err != nil {
return err
}
diff --git a/libgo/go/image/jpeg/writer.go b/libgo/go/image/jpeg/writer.go
index c58fbf30555..91bbde3bf80 100644
--- a/libgo/go/image/jpeg/writer.go
+++ b/libgo/go/image/jpeg/writer.go
@@ -249,7 +249,7 @@ func (e *encoder) writeByte(b byte) {
e.err = e.w.WriteByte(b)
}
-// emit emits the least significant nBits bits of bits to the bitstream.
+// emit emits the least significant nBits bits of bits to the bit-stream.
// The precondition is bits < 1<<nBits && nBits <= 16.
func (e *encoder) emit(bits, nBits uint32) {
nBits += e.nBits
@@ -312,32 +312,44 @@ func (e *encoder) writeDQT() {
}
// writeSOF0 writes the Start Of Frame (Baseline) marker.
-func (e *encoder) writeSOF0(size image.Point) {
- const markerlen = 8 + 3*nColorComponent
+func (e *encoder) writeSOF0(size image.Point, nComponent int) {
+ markerlen := 8 + 3*nComponent
e.writeMarkerHeader(sof0Marker, markerlen)
e.buf[0] = 8 // 8-bit color.
e.buf[1] = uint8(size.Y >> 8)
e.buf[2] = uint8(size.Y & 0xff)
e.buf[3] = uint8(size.X >> 8)
e.buf[4] = uint8(size.X & 0xff)
- e.buf[5] = nColorComponent
- for i := 0; i < nColorComponent; i++ {
- e.buf[3*i+6] = uint8(i + 1)
- // We use 4:2:0 chroma subsampling.
- e.buf[3*i+7] = "\x22\x11\x11"[i]
- e.buf[3*i+8] = "\x00\x01\x01"[i]
+ e.buf[5] = uint8(nComponent)
+ if nComponent == 1 {
+ e.buf[6] = 1
+ // No subsampling for grayscale image.
+ e.buf[7] = 0x11
+ e.buf[8] = 0x00
+ } else {
+ for i := 0; i < nComponent; i++ {
+ e.buf[3*i+6] = uint8(i + 1)
+ // We use 4:2:0 chroma subsampling.
+ e.buf[3*i+7] = "\x22\x11\x11"[i]
+ e.buf[3*i+8] = "\x00\x01\x01"[i]
+ }
}
- e.write(e.buf[:3*(nColorComponent-1)+9])
+ e.write(e.buf[:3*(nComponent-1)+9])
}
// writeDHT writes the Define Huffman Table marker.
-func (e *encoder) writeDHT() {
+func (e *encoder) writeDHT(nComponent int) {
markerlen := 2
- for _, s := range theHuffmanSpec {
+ specs := theHuffmanSpec[:]
+ if nComponent == 1 {
+ // Drop the Chrominance tables.
+ specs = specs[:2]
+ }
+ for _, s := range specs {
markerlen += 1 + 16 + len(s.value)
}
e.writeMarkerHeader(dhtMarker, markerlen)
- for i, s := range theHuffmanSpec {
+ for i, s := range specs {
e.writeByte("\x00\x10\x01\x11"[i])
e.write(s.count[:])
e.write(s.value)
@@ -345,8 +357,8 @@ func (e *encoder) writeDHT() {
}
// writeBlock writes a block of pixel data using the given quantization table,
-// returning the post-quantized DC value of the DCT-transformed block.
-// b is in natural (not zig-zag) order.
+// returning the post-quantized DC value of the DCT-transformed block. b is in
+// natural (not zig-zag) order.
func (e *encoder) writeBlock(b *block, q quantIndex, prevDC int32) int32 {
fdct(b)
// Emit the DC delta.
@@ -390,6 +402,20 @@ func toYCbCr(m image.Image, p image.Point, yBlock, cbBlock, crBlock *block) {
}
}
+// grayToY stores the 8x8 region of m whose top-left corner is p in yBlock.
+func grayToY(m *image.Gray, p image.Point, yBlock *block) {
+ b := m.Bounds()
+ xmax := b.Max.X - 1
+ ymax := b.Max.Y - 1
+ pix := m.Pix
+ for j := 0; j < 8; j++ {
+ for i := 0; i < 8; i++ {
+ idx := m.PixOffset(min(p.X+i, xmax), min(p.Y+j, ymax))
+ yBlock[8*j+i] = int32(pix[idx])
+ }
+ }
+}
+
// rgbaToYCbCr is a specialized version of toYCbCr for image.RGBA images.
func rgbaToYCbCr(m *image.RGBA, p image.Point, yBlock, cbBlock, crBlock *block) {
b := m.Bounds()
@@ -430,7 +456,18 @@ func scale(dst *block, src *[4]block) {
}
}
-// sosHeader is the SOS marker "\xff\xda" followed by 12 bytes:
+// sosHeaderY is the SOS marker "\xff\xda" followed by 8 bytes:
+// - the marker length "\x00\x08",
+// - the number of components "\x01",
+// - component 1 uses DC table 0 and AC table 0 "\x01\x00",
+// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
+// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
+// should be 0x00, 0x3f, 0x00<<4 | 0x00.
+var sosHeaderY = []byte{
+ 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00,
+}
+
+// sosHeaderYCbCr is the SOS marker "\xff\xda" followed by 12 bytes:
// - the marker length "\x00\x0c",
// - the number of components "\x03",
// - component 1 uses DC table 0 and AC table 0 "\x01\x00",
@@ -439,14 +476,19 @@ func scale(dst *block, src *[4]block) {
// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
// should be 0x00, 0x3f, 0x00<<4 | 0x00.
-var sosHeader = []byte{
+var sosHeaderYCbCr = []byte{
0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02,
0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
}
// writeSOS writes the StartOfScan marker.
func (e *encoder) writeSOS(m image.Image) {
- e.write(sosHeader)
+ switch m.(type) {
+ case *image.Gray:
+ e.write(sosHeaderY)
+ default:
+ e.write(sosHeaderYCbCr)
+ }
var (
// Scratch buffers to hold the YCbCr values.
// The blocks are in natural (not zig-zag) order.
@@ -456,24 +498,36 @@ func (e *encoder) writeSOS(m image.Image) {
prevDCY, prevDCCb, prevDCCr int32
)
bounds := m.Bounds()
- rgba, _ := m.(*image.RGBA)
- for y := bounds.Min.Y; y < bounds.Max.Y; y += 16 {
- for x := bounds.Min.X; x < bounds.Max.X; x += 16 {
- for i := 0; i < 4; i++ {
- xOff := (i & 1) * 8
- yOff := (i & 2) * 4
- p := image.Pt(x+xOff, y+yOff)
- if rgba != nil {
- rgbaToYCbCr(rgba, p, &b, &cb[i], &cr[i])
- } else {
- toYCbCr(m, p, &b, &cb[i], &cr[i])
- }
+ switch m := m.(type) {
+ // TODO(wathiede): switch on m.ColorModel() instead of type.
+ case *image.Gray:
+ for y := bounds.Min.Y; y < bounds.Max.Y; y += 8 {
+ for x := bounds.Min.X; x < bounds.Max.X; x += 8 {
+ p := image.Pt(x, y)
+ grayToY(m, p, &b)
prevDCY = e.writeBlock(&b, 0, prevDCY)
}
- scale(&b, &cb)
- prevDCCb = e.writeBlock(&b, 1, prevDCCb)
- scale(&b, &cr)
- prevDCCr = e.writeBlock(&b, 1, prevDCCr)
+ }
+ default:
+ rgba, _ := m.(*image.RGBA)
+ for y := bounds.Min.Y; y < bounds.Max.Y; y += 16 {
+ for x := bounds.Min.X; x < bounds.Max.X; x += 16 {
+ for i := 0; i < 4; i++ {
+ xOff := (i & 1) * 8
+ yOff := (i & 2) * 4
+ p := image.Pt(x+xOff, y+yOff)
+ if rgba != nil {
+ rgbaToYCbCr(rgba, p, &b, &cb[i], &cr[i])
+ } else {
+ toYCbCr(m, p, &b, &cb[i], &cr[i])
+ }
+ prevDCY = e.writeBlock(&b, 0, prevDCY)
+ }
+ scale(&b, &cb)
+ prevDCCb = e.writeBlock(&b, 1, prevDCCb)
+ scale(&b, &cr)
+ prevDCCr = e.writeBlock(&b, 1, prevDCCr)
+ }
}
}
// Pad the last byte with 1's.
@@ -532,6 +586,13 @@ func Encode(w io.Writer, m image.Image, o *Options) error {
e.quant[i][j] = uint8(x)
}
}
+ // Compute number of components based on input image type.
+ nComponent := 3
+ switch m.(type) {
+ // TODO(wathiede): switch on m.ColorModel() instead of type.
+ case *image.Gray:
+ nComponent = 1
+ }
// Write the Start Of Image marker.
e.buf[0] = 0xff
e.buf[1] = 0xd8
@@ -539,9 +600,9 @@ func Encode(w io.Writer, m image.Image, o *Options) error {
// Write the quantization tables.
e.writeDQT()
// Write the image dimensions.
- e.writeSOF0(b.Size())
+ e.writeSOF0(b.Size(), nComponent)
// Write the Huffman tables.
- e.writeDHT()
+ e.writeDHT(nComponent)
// Write the image data.
e.writeSOS(m)
// Write the End Of Image marker.
diff --git a/libgo/go/image/jpeg/writer_test.go b/libgo/go/image/jpeg/writer_test.go
index 514b455dce5..3df3cfcc5bb 100644
--- a/libgo/go/image/jpeg/writer_test.go
+++ b/libgo/go/image/jpeg/writer_test.go
@@ -160,6 +160,34 @@ func TestWriter(t *testing.T) {
}
}
+// TestWriteGrayscale tests that a grayscale images survives a round-trip
+// through encode/decode cycle.
+func TestWriteGrayscale(t *testing.T) {
+ m0 := image.NewGray(image.Rect(0, 0, 32, 32))
+ for i := range m0.Pix {
+ m0.Pix[i] = uint8(i)
+ }
+ var buf bytes.Buffer
+ if err := Encode(&buf, m0, nil); err != nil {
+ t.Fatal(err)
+ }
+ m1, err := Decode(&buf)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if m0.Bounds() != m1.Bounds() {
+ t.Fatalf("bounds differ: %v and %v", m0.Bounds(), m1.Bounds())
+ }
+ if _, ok := m1.(*image.Gray); !ok {
+ t.Errorf("got %T, want *image.Gray", m1)
+ }
+ // Compare the average delta to the tolerance level.
+ want := int64(2 << 8)
+ if got := averageDelta(m0, m1); got > want {
+ t.Errorf("average delta too high; got %d, want <= %d", got, want)
+ }
+}
+
// averageDelta returns the average delta in RGB space. The two images must
// have the same bounds.
func averageDelta(m0, m1 image.Image) int64 {
diff --git a/libgo/go/image/png/paeth.go b/libgo/go/image/png/paeth.go
index 37978aa662d..9ed6300c865 100644
--- a/libgo/go/image/png/paeth.go
+++ b/libgo/go/image/png/paeth.go
@@ -4,6 +4,21 @@
package png
+// intSize is either 32 or 64.
+const intSize = 32 << (^uint(0) >> 63)
+
+func abs(x int) int {
+ // m := -1 if x < 0. m := 0 otherwise.
+ m := x >> (intSize - 1)
+
+ // In two's complement representation, the negative number
+ // of any number (except the smallest one) can be computed
+ // by flipping all the bits and add 1. This is faster than
+ // code with a branch.
+ // See Hacker's Delight, section 2-4.
+ return (x ^ m) - m
+}
+
// paeth implements the Paeth filter function, as per the PNG specification.
func paeth(a, b, c uint8) uint8 {
// This is an optimized version of the sample code in the PNG spec.
@@ -16,16 +31,9 @@ func paeth(a, b, c uint8) uint8 {
pc := int(c)
pa := int(b) - pc
pb := int(a) - pc
- pc = pa + pb
- if pa < 0 {
- pa = -pa
- }
- if pb < 0 {
- pb = -pb
- }
- if pc < 0 {
- pc = -pc
- }
+ pc = abs(pa + pb)
+ pa = abs(pa)
+ pb = abs(pb)
if pa <= pb && pa <= pc {
return a
} else if pb <= pc {
@@ -44,16 +52,9 @@ func filterPaeth(cdat, pdat []byte, bytesPerPixel int) {
b = int(pdat[j])
pa = b - c
pb = a - c
- pc = pa + pb
- if pa < 0 {
- pa = -pa
- }
- if pb < 0 {
- pb = -pb
- }
- if pc < 0 {
- pc = -pc
- }
+ pc = abs(pa + pb)
+ pa = abs(pa)
+ pb = abs(pb)
if pa <= pb && pa <= pc {
// No-op.
} else if pb <= pc {
diff --git a/libgo/go/image/png/paeth_test.go b/libgo/go/image/png/paeth_test.go
index bb084861ae9..cfc1896cd7f 100644
--- a/libgo/go/image/png/paeth_test.go
+++ b/libgo/go/image/png/paeth_test.go
@@ -10,7 +10,7 @@ import (
"testing"
)
-func abs(x int) int {
+func slowAbs(x int) int {
if x < 0 {
return -x
}
@@ -21,9 +21,9 @@ func abs(x int) int {
// It is a straight port of the sample code in the PNG spec, section 9.4.
func slowPaeth(a, b, c uint8) uint8 {
p := int(a) + int(b) - int(c)
- pa := abs(p - int(a))
- pb := abs(p - int(b))
- pc := abs(p - int(c))
+ pa := slowAbs(p - int(a))
+ pb := slowAbs(p - int(b))
+ pc := slowAbs(p - int(c))
if pa <= pb && pa <= pc {
return a
} else if pb <= pc {
diff --git a/libgo/go/image/png/reader.go b/libgo/go/image/png/reader.go
index dfe2991024d..0a40ca161d9 100644
--- a/libgo/go/image/png/reader.go
+++ b/libgo/go/image/png/reader.go
@@ -57,6 +57,29 @@ const (
nFilter = 5
)
+// Interlace type.
+const (
+ itNone = 0
+ itAdam7 = 1
+)
+
+// interlaceScan defines the placement and size of a pass for Adam7 interlacing.
+type interlaceScan struct {
+ xFactor, yFactor, xOffset, yOffset int
+}
+
+// interlacing defines Adam7 interlacing, with 7 passes of reduced images.
+// See http://www.w3.org/TR/PNG/#8Interlace
+var interlacing = []interlaceScan{
+ {8, 8, 0, 0},
+ {8, 8, 4, 0},
+ {4, 8, 0, 4},
+ {4, 4, 2, 0},
+ {2, 4, 0, 2},
+ {2, 2, 1, 0},
+ {1, 2, 0, 1},
+}
+
// Decoding stage.
// The PNG specification says that the IHDR, PLTE (if present), IDAT and IEND
// chunks must appear in that order. There may be multiple IDAT chunks, and
@@ -84,6 +107,7 @@ type decoder struct {
stage int
idatLength uint32
tmp [3 * 256]byte
+ interlace int
}
// A FormatError reports that the input is not a valid PNG.
@@ -113,9 +137,16 @@ func (d *decoder) parseIHDR(length uint32) error {
return err
}
d.crc.Write(d.tmp[:13])
- if d.tmp[10] != 0 || d.tmp[11] != 0 || d.tmp[12] != 0 {
- return UnsupportedError("compression, filter or interlace method")
+ if d.tmp[10] != 0 {
+ return UnsupportedError("compression method")
}
+ if d.tmp[11] != 0 {
+ return UnsupportedError("filter method")
+ }
+ if d.tmp[12] != itNone && d.tmp[12] != itAdam7 {
+ return FormatError("invalid interlace method")
+ }
+ d.interlace = int(d.tmp[12])
w := int32(binary.BigEndian.Uint32(d.tmp[0:4]))
h := int32(binary.BigEndian.Uint32(d.tmp[4:8]))
if w < 0 || h < 0 {
@@ -287,7 +318,42 @@ func (d *decoder) decode() (image.Image, error) {
return nil, err
}
defer r.Close()
- bitsPerPixel := 0
+ var img image.Image
+ if d.interlace == itNone {
+ img, err = d.readImagePass(r, 0, false)
+ } else if d.interlace == itAdam7 {
+ // Allocate a blank image of the full size.
+ img, err = d.readImagePass(nil, 0, true)
+ for pass := 0; pass < 7; pass++ {
+ imagePass, err := d.readImagePass(r, pass, false)
+ if err != nil {
+ return nil, err
+ }
+ d.mergePassInto(img, imagePass, pass)
+ }
+ }
+
+ // Check for EOF, to verify the zlib checksum.
+ n := 0
+ for i := 0; n == 0 && err == nil; i++ {
+ if i == 100 {
+ return nil, io.ErrNoProgress
+ }
+ n, err = r.Read(d.tmp[:1])
+ }
+ if err != nil && err != io.EOF {
+ return nil, FormatError(err.Error())
+ }
+ if n != 0 || d.idatLength != 0 {
+ return nil, FormatError("too much pixel data")
+ }
+
+ return img, nil
+}
+
+// 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
pixOffset := 0
var (
gray *image.Gray
@@ -299,52 +365,63 @@ func (d *decoder) decode() (image.Image, error) {
nrgba64 *image.NRGBA64
img image.Image
)
+ width, height := d.width, d.height
+ if d.interlace == itAdam7 && !allocateOnly {
+ p := interlacing[pass]
+ // Add the multiplication factor and subtract one, effectively rounding up.
+ width = (width - p.xOffset + p.xFactor - 1) / p.xFactor
+ height = (height - p.yOffset + p.yFactor - 1) / p.yFactor
+ }
switch d.cb {
case cbG1, cbG2, cbG4, cbG8:
bitsPerPixel = d.depth
- gray = image.NewGray(image.Rect(0, 0, d.width, d.height))
+ gray = image.NewGray(image.Rect(0, 0, width, height))
img = gray
case cbGA8:
bitsPerPixel = 16
- nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height))
+ nrgba = image.NewNRGBA(image.Rect(0, 0, width, height))
img = nrgba
case cbTC8:
bitsPerPixel = 24
- rgba = image.NewRGBA(image.Rect(0, 0, d.width, d.height))
+ 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, d.width, d.height), d.palette)
+ paletted = image.NewPaletted(image.Rect(0, 0, width, height), d.palette)
img = paletted
case cbTCA8:
bitsPerPixel = 32
- nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height))
+ nrgba = image.NewNRGBA(image.Rect(0, 0, width, height))
img = nrgba
case cbG16:
bitsPerPixel = 16
- gray16 = image.NewGray16(image.Rect(0, 0, d.width, d.height))
+ gray16 = image.NewGray16(image.Rect(0, 0, width, height))
img = gray16
case cbGA16:
bitsPerPixel = 32
- nrgba64 = image.NewNRGBA64(image.Rect(0, 0, d.width, d.height))
+ nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height))
img = nrgba64
case cbTC16:
bitsPerPixel = 48
- rgba64 = image.NewRGBA64(image.Rect(0, 0, d.width, d.height))
+ rgba64 = image.NewRGBA64(image.Rect(0, 0, width, height))
img = rgba64
case cbTCA16:
bitsPerPixel = 64
- nrgba64 = image.NewNRGBA64(image.Rect(0, 0, d.width, d.height))
+ nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height))
img = nrgba64
}
+ if allocateOnly {
+ return img, nil
+ }
bytesPerPixel := (bitsPerPixel + 7) / 8
- // cr and pr are the bytes for the current and previous row.
// The +1 is for the per-row filter type, which is at cr[0].
- cr := make([]uint8, 1+(bitsPerPixel*d.width+7)/8)
- pr := make([]uint8, 1+(bitsPerPixel*d.width+7)/8)
+ rowSize := 1 + (bitsPerPixel*width+7)/8
+ // cr and pr are the bytes for the current and previous row.
+ cr := make([]uint8, rowSize)
+ pr := make([]uint8, rowSize)
- for y := 0; y < d.height; y++ {
+ for y := 0; y < height; y++ {
// Read the decompressed bytes.
_, err := io.ReadFull(r, cr)
if err != nil {
@@ -381,25 +458,25 @@ func (d *decoder) decode() (image.Image, error) {
// Convert from bytes to colors.
switch d.cb {
case cbG1:
- for x := 0; x < d.width; x += 8 {
+ for x := 0; x < width; x += 8 {
b := cdat[x/8]
- for x2 := 0; x2 < 8 && x+x2 < d.width; x2++ {
+ 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 < d.width; x += 4 {
+ for x := 0; x < width; x += 4 {
b := cdat[x/4]
- for x2 := 0; x2 < 4 && x+x2 < d.width; x2++ {
+ 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 < d.width; x += 2 {
+ for x := 0; x < width; x += 2 {
b := cdat[x/2]
- for x2 := 0; x2 < 2 && x+x2 < d.width; x2++ {
+ for x2 := 0; x2 < 2 && x+x2 < width; x2++ {
gray.SetGray(x+x2, y, color.Gray{(b >> 4) * 0x11})
b <<= 4
}
@@ -408,13 +485,13 @@ func (d *decoder) decode() (image.Image, error) {
copy(gray.Pix[pixOffset:], cdat)
pixOffset += gray.Stride
case cbGA8:
- for x := 0; x < d.width; x++ {
+ for x := 0; x < width; x++ {
ycol := cdat[2*x+0]
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 < d.width; x++ {
+ for x := 0; x < width; x++ {
pix[i+0] = cdat[j+0]
pix[i+1] = cdat[j+1]
pix[i+2] = cdat[j+2]
@@ -424,9 +501,9 @@ func (d *decoder) decode() (image.Image, error) {
}
pixOffset += rgba.Stride
case cbP1:
- for x := 0; x < d.width; x += 8 {
+ for x := 0; x < width; x += 8 {
b := cdat[x/8]
- for x2 := 0; x2 < 8 && x+x2 < d.width; x2++ {
+ for x2 := 0; x2 < 8 && x+x2 < width; x2++ {
idx := b >> 7
if len(paletted.Palette) <= int(idx) {
paletted.Palette = paletted.Palette[:int(idx)+1]
@@ -436,9 +513,9 @@ func (d *decoder) decode() (image.Image, error) {
}
}
case cbP2:
- for x := 0; x < d.width; x += 4 {
+ for x := 0; x < width; x += 4 {
b := cdat[x/4]
- for x2 := 0; x2 < 4 && x+x2 < d.width; x2++ {
+ for x2 := 0; x2 < 4 && x+x2 < width; x2++ {
idx := b >> 6
if len(paletted.Palette) <= int(idx) {
paletted.Palette = paletted.Palette[:int(idx)+1]
@@ -448,9 +525,9 @@ func (d *decoder) decode() (image.Image, error) {
}
}
case cbP4:
- for x := 0; x < d.width; x += 2 {
+ for x := 0; x < width; x += 2 {
b := cdat[x/2]
- for x2 := 0; x2 < 2 && x+x2 < d.width; x2++ {
+ for x2 := 0; x2 < 2 && x+x2 < width; x2++ {
idx := b >> 4
if len(paletted.Palette) <= int(idx) {
paletted.Palette = paletted.Palette[:int(idx)+1]
@@ -461,7 +538,7 @@ func (d *decoder) decode() (image.Image, error) {
}
case cbP8:
if len(paletted.Palette) != 255 {
- for x := 0; x < d.width; x++ {
+ for x := 0; x < width; x++ {
if len(paletted.Palette) <= int(cdat[x]) {
paletted.Palette = paletted.Palette[:int(cdat[x])+1]
}
@@ -473,25 +550,25 @@ func (d *decoder) decode() (image.Image, error) {
copy(nrgba.Pix[pixOffset:], cdat)
pixOffset += nrgba.Stride
case cbG16:
- for x := 0; x < d.width; x++ {
+ 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 < d.width; x++ {
+ for x := 0; x < width; x++ {
ycol := uint16(cdat[4*x+0])<<8 | uint16(cdat[4*x+1])
acol := uint16(cdat[4*x+2])<<8 | uint16(cdat[4*x+3])
nrgba64.SetNRGBA64(x, y, color.NRGBA64{ycol, ycol, ycol, acol})
}
case cbTC16:
- for x := 0; x < d.width; x++ {
+ 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 < d.width; x++ {
+ for x := 0; x < width; x++ {
rcol := uint16(cdat[8*x+0])<<8 | uint16(cdat[8*x+1])
gcol := uint16(cdat[8*x+2])<<8 | uint16(cdat[8*x+3])
bcol := uint16(cdat[8*x+4])<<8 | uint16(cdat[8*x+5])
@@ -504,22 +581,66 @@ func (d *decoder) decode() (image.Image, error) {
pr, cr = cr, pr
}
- // Check for EOF, to verify the zlib checksum.
- n := 0
- for i := 0; n == 0 && err == nil; i++ {
- if i == 100 {
- return nil, io.ErrNoProgress
+ return img, nil
+}
+
+// mergePassInto merges a single pass into a full sized image.
+func (d *decoder) mergePassInto(dst image.Image, src image.Image, pass int) {
+ p := interlacing[pass]
+ var (
+ srcPix []uint8
+ dstPix []uint8
+ stride int
+ rect image.Rectangle
+ bytesPerPixel int
+ )
+ switch target := dst.(type) {
+ case *image.Alpha:
+ srcPix = src.(*image.Alpha).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 1
+ case *image.Alpha16:
+ srcPix = src.(*image.Alpha16).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 2
+ case *image.Gray:
+ srcPix = src.(*image.Gray).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 1
+ case *image.Gray16:
+ srcPix = src.(*image.Gray16).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 2
+ case *image.NRGBA:
+ srcPix = src.(*image.NRGBA).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 4
+ case *image.NRGBA64:
+ srcPix = src.(*image.NRGBA64).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 8
+ case *image.Paletted:
+ srcPix = src.(*image.Paletted).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 1
+ case *image.RGBA:
+ srcPix = src.(*image.RGBA).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 4
+ case *image.RGBA64:
+ srcPix = src.(*image.RGBA64).Pix
+ dstPix, stride, rect = target.Pix, target.Stride, target.Rect
+ bytesPerPixel = 8
+ }
+ s, bounds := 0, src.Bounds()
+ for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
+ dBase := (y*p.yFactor+p.yOffset-rect.Min.Y)*stride + (p.xOffset-rect.Min.X)*bytesPerPixel
+ for x := bounds.Min.X; x < bounds.Max.X; x++ {
+ d := dBase + x*p.xFactor*bytesPerPixel
+ copy(dstPix[d:], srcPix[s:s+bytesPerPixel])
+ s += bytesPerPixel
}
- n, err = r.Read(pr[:1])
}
- if err != nil && err != io.EOF {
- return nil, FormatError(err.Error())
- }
- if n != 0 || d.idatLength != 0 {
- return nil, FormatError("too much pixel data")
- }
-
- return img, nil
}
func (d *decoder) parseIDAT(length uint32) (err error) {
diff --git a/libgo/go/image/png/reader_test.go b/libgo/go/image/png/reader_test.go
index ac0d949a9d3..ce772eb6f09 100644
--- a/libgo/go/image/png/reader_test.go
+++ b/libgo/go/image/png/reader_test.go
@@ -30,6 +30,7 @@ var filenames = []string{
"basn3p01",
"basn3p02",
"basn3p04",
+ "basn3p04-31i",
"basn3p08",
"basn3p08-trns",
"basn4a08",
@@ -186,6 +187,13 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
c = 0
}
}
+ if c != 0 {
+ for c != 8/bitdepth {
+ b = b << uint(bitdepth)
+ c++
+ }
+ fmt.Fprintf(w, "%02x", b)
+ }
}
io.WriteString(w, "\n")
}
@@ -235,8 +243,8 @@ func TestReader(t *testing.T) {
// Compare the two, in SNG format, line by line.
for {
- pdone := pb.Scan()
- sdone := sb.Scan()
+ pdone := !pb.Scan()
+ sdone := !sb.Scan()
if pdone && sdone {
break
}
@@ -348,3 +356,7 @@ func BenchmarkDecodePaletted(b *testing.B) {
func BenchmarkDecodeRGB(b *testing.B) {
benchmarkDecode(b, "testdata/benchRGB.png", 4)
}
+
+func BenchmarkDecodeInterlacing(b *testing.B) {
+ benchmarkDecode(b, "testdata/benchRGB-interlace.png", 4)
+}
diff --git a/libgo/go/image/png/writer.go b/libgo/go/image/png/writer.go
index 629452cbfa1..df23270ee97 100644
--- a/libgo/go/image/png/writer.go
+++ b/libgo/go/image/png/writer.go
@@ -14,7 +14,13 @@ import (
"strconv"
)
+// Encoder configures encoding PNG images.
+type Encoder struct {
+ CompressionLevel CompressionLevel
+}
+
type encoder struct {
+ enc *Encoder
w io.Writer
m image.Image
cb int
@@ -24,6 +30,18 @@ type encoder struct {
tmp [4 * 256]byte
}
+type CompressionLevel int
+
+const (
+ DefaultCompression CompressionLevel = 0
+ NoCompression CompressionLevel = -1
+ BestSpeed CompressionLevel = -2
+ BestCompression CompressionLevel = -3
+
+ // Positive CompressionLevel values are reserved to mean a numeric zlib
+ // compression level, although that is not implemented yet.
+)
+
// Big-endian.
func writeUint32(b []uint8, u uint32) {
b[0] = uint8(u >> 24)
@@ -188,7 +206,7 @@ func filter(cr *[nFilter][]byte, pr []byte, bpp int) int {
// The Paeth filter.
sum = 0
for i := 0; i < bpp; i++ {
- cdat4[i] = cdat0[i] - paeth(0, pdat[i], 0)
+ cdat4[i] = cdat0[i] - pdat[i]
sum += abs8(cdat4[i])
}
for i := bpp; i < n; i++ {
@@ -255,8 +273,11 @@ func filter(cr *[nFilter][]byte, pr []byte, bpp int) int {
return filter
}
-func writeImage(w io.Writer, m image.Image, cb int) error {
- zw := zlib.NewWriter(w)
+func writeImage(w io.Writer, m image.Image, cb int, level int) error {
+ zw, err := zlib.NewWriterLevel(w, level)
+ if err != nil {
+ return err
+ }
defer zw.Close()
bpp := 0 // Bytes per pixel.
@@ -399,7 +420,10 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
}
// Apply the filter.
- f := filter(&cr, pr, bpp)
+ f := ftNone
+ if level != zlib.NoCompression {
+ f = filter(&cr, pr, bpp)
+ }
// Write the compressed bytes.
if _, err := zw.Write(cr[f]); err != nil {
@@ -419,18 +443,41 @@ func (e *encoder) writeIDATs() {
}
var bw *bufio.Writer
bw = bufio.NewWriterSize(e, 1<<15)
- e.err = writeImage(bw, e.m, e.cb)
+ e.err = writeImage(bw, e.m, e.cb, levelToZlib(e.enc.CompressionLevel))
if e.err != nil {
return
}
e.err = bw.Flush()
}
+// This function is required because we want the zero value of
+// Encoder.CompressionLevel to map to zlib.DefaultCompression.
+func levelToZlib(l CompressionLevel) int {
+ switch l {
+ case DefaultCompression:
+ return zlib.DefaultCompression
+ case NoCompression:
+ return zlib.NoCompression
+ case BestSpeed:
+ return zlib.BestSpeed
+ case BestCompression:
+ return zlib.BestCompression
+ default:
+ return zlib.DefaultCompression
+ }
+}
+
func (e *encoder) writeIEND() { e.writeChunk(nil, "IEND") }
-// Encode writes the Image m to w in PNG format. Any Image may be encoded, but
-// images that are not image.NRGBA might be encoded lossily.
+// Encode writes the Image m to w in PNG format. Any Image may be
+// encoded, but images that are not image.NRGBA might be encoded lossily.
func Encode(w io.Writer, m image.Image) error {
+ var e Encoder
+ return e.Encode(w, m)
+}
+
+// Encode writes the Image m to w in PNG format.
+func (enc *Encoder) Encode(w io.Writer, m image.Image) error {
// Obviously, negative widths and heights are invalid. Furthermore, the PNG
// spec section 11.2.2 says that zero is invalid. Excessively large images are
// also rejected.
@@ -440,6 +487,7 @@ func Encode(w io.Writer, m image.Image) error {
}
var e encoder
+ e.enc = enc
e.w = w
e.m = m
diff --git a/libgo/go/image/png/writer_test.go b/libgo/go/image/png/writer_test.go
index 3116fc9ff94..d67a815698f 100644
--- a/libgo/go/image/png/writer_test.go
+++ b/libgo/go/image/png/writer_test.go
@@ -40,11 +40,7 @@ func encodeDecode(m image.Image) (image.Image, error) {
if err != nil {
return nil, err
}
- m, err = Decode(&b)
- if err != nil {
- return nil, err
- }
- return m, nil
+ return Decode(&b)
}
func TestWriter(t *testing.T) {
@@ -81,6 +77,29 @@ func TestWriter(t *testing.T) {
}
}
+func TestWriterLevels(t *testing.T) {
+ m := image.NewNRGBA(image.Rect(0, 0, 100, 100))
+
+ var b1, b2 bytes.Buffer
+ if err := (&Encoder{}).Encode(&b1, m); err != nil {
+ t.Fatal(err)
+ }
+ noenc := &Encoder{CompressionLevel: NoCompression}
+ if err := noenc.Encode(&b2, m); err != nil {
+ t.Fatal(err)
+ }
+
+ if b2.Len() <= b1.Len() {
+ t.Error("DefaultCompression encoding was larger than NoCompression encoding")
+ }
+ if _, err := Decode(&b1); err != nil {
+ t.Error("cannot decode DefaultCompression")
+ }
+ if _, err := Decode(&b2); err != nil {
+ t.Error("cannot decode NoCompression")
+ }
+}
+
func TestSubImage(t *testing.T) {
m0 := image.NewRGBA(image.Rect(0, 0, 256, 256))
for y := 0; y < 256; y++ {
diff --git a/libgo/go/image/ycbcr.go b/libgo/go/image/ycbcr.go
index 5b73bef7895..7c773f2f0a4 100644
--- a/libgo/go/image/ycbcr.go
+++ b/libgo/go/image/ycbcr.go
@@ -60,6 +60,10 @@ func (p *YCbCr) Bounds() Rectangle {
}
func (p *YCbCr) At(x, y int) color.Color {
+ return p.YCbCrAt(x, y)
+}
+
+func (p *YCbCr) YCbCrAt(x, y int) color.YCbCr {
if !(Point{x, y}.In(p.Rect)) {
return color.YCbCr{}
}