diff options
Diffstat (limited to 'libgo/go/image')
-rw-r--r-- | libgo/go/image/color/palette/gen.go | 96 | ||||
-rw-r--r-- | libgo/go/image/color/palette/generate.go | 8 | ||||
-rw-r--r-- | libgo/go/image/color/palette/palette.go | 3 | ||||
-rw-r--r-- | libgo/go/image/gif/reader.go | 7 | ||||
-rw-r--r-- | libgo/go/image/gif/reader_test.go | 64 | ||||
-rw-r--r-- | libgo/go/image/gif/writer.go | 18 | ||||
-rw-r--r-- | libgo/go/image/gif/writer_test.go | 25 | ||||
-rw-r--r-- | libgo/go/image/image.go | 32 | ||||
-rw-r--r-- | libgo/go/image/jpeg/huffman.go | 257 | ||||
-rw-r--r-- | libgo/go/image/jpeg/reader.go | 210 | ||||
-rw-r--r-- | libgo/go/image/jpeg/reader_test.go | 45 | ||||
-rw-r--r-- | libgo/go/image/jpeg/scan.go | 57 | ||||
-rw-r--r-- | libgo/go/image/jpeg/writer.go | 133 | ||||
-rw-r--r-- | libgo/go/image/jpeg/writer_test.go | 28 | ||||
-rw-r--r-- | libgo/go/image/png/paeth.go | 41 | ||||
-rw-r--r-- | libgo/go/image/png/paeth_test.go | 8 | ||||
-rw-r--r-- | libgo/go/image/png/reader.go | 219 | ||||
-rw-r--r-- | libgo/go/image/png/reader_test.go | 16 | ||||
-rw-r--r-- | libgo/go/image/png/writer.go | 62 | ||||
-rw-r--r-- | libgo/go/image/png/writer_test.go | 29 | ||||
-rw-r--r-- | libgo/go/image/ycbcr.go | 4 |
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{} } |