diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2013-07-16 06:54:42 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2013-07-16 06:54:42 +0000 |
commit | be47d6eceffd2c5dbbc1566d5eea490527fb2bd4 (patch) | |
tree | 0e8fda573576bb4181dba29d0e88380a8c38fafd /libgo/go/image | |
parent | efb30cdeb003fd7c585ee0d7657340086abcbd9e (diff) | |
download | gcc-be47d6eceffd2c5dbbc1566d5eea490527fb2bd4.zip gcc-be47d6eceffd2c5dbbc1566d5eea490527fb2bd4.tar.gz gcc-be47d6eceffd2c5dbbc1566d5eea490527fb2bd4.tar.bz2 |
libgo: Update to Go 1.1.1.
From-SVN: r200974
Diffstat (limited to 'libgo/go/image')
-rw-r--r-- | libgo/go/image/gif/reader.go | 103 | ||||
-rw-r--r-- | libgo/go/image/gif/reader_test.go | 138 | ||||
-rw-r--r-- | libgo/go/image/jpeg/reader.go | 32 | ||||
-rw-r--r-- | libgo/go/image/jpeg/reader_test.go | 63 | ||||
-rw-r--r-- | libgo/go/image/jpeg/scan.go | 8 | ||||
-rw-r--r-- | libgo/go/image/jpeg/writer_test.go | 39 | ||||
-rw-r--r-- | libgo/go/image/png/reader.go | 5 | ||||
-rw-r--r-- | libgo/go/image/png/reader_test.go | 59 | ||||
-rw-r--r-- | libgo/go/image/png/writer.go | 2 |
9 files changed, 374 insertions, 75 deletions
diff --git a/libgo/go/image/gif/reader.go b/libgo/go/image/gif/reader.go index 8b36948..8e8531f 100644 --- a/libgo/go/image/gif/reader.go +++ b/libgo/go/image/gif/reader.go @@ -17,6 +17,11 @@ import ( "io" ) +var ( + errNotEnough = errors.New("gif: not enough image data") + errTooMuch = errors.New("gif: too much image data") +) + // If the io.Reader does not also have ReadByte, then decode will introduce its own buffering. type reader interface { io.Reader @@ -89,29 +94,35 @@ type decoder struct { // comprises (n, (n bytes)) blocks, with 1 <= n <= 255. It is the // reader given to the LZW decoder, which is thus immune to the // blocking. After the LZW decoder completes, there will be a 0-byte -// block remaining (0, ()), but under normal execution blockReader -// doesn't consume it, so it is handled in decode. +// block remaining (0, ()), which is consumed when checking that the +// blockReader is exhausted. type blockReader struct { r reader slice []byte + err error tmp [256]byte } func (b *blockReader) Read(p []byte) (int, error) { + if b.err != nil { + return 0, b.err + } if len(p) == 0 { return 0, nil } if len(b.slice) == 0 { - blockLen, err := b.r.ReadByte() - if err != nil { - return 0, err + var blockLen uint8 + blockLen, b.err = b.r.ReadByte() + if b.err != nil { + return 0, b.err } if blockLen == 0 { - return 0, io.EOF + b.err = io.EOF + return 0, b.err } b.slice = b.tmp[0:blockLen] - if _, err = io.ReadFull(b.r, b.slice); err != nil { - return 0, err + if _, b.err = io.ReadFull(b.r, b.slice); b.err != nil { + return 0, b.err } } n := copy(p, b.slice) @@ -142,35 +153,33 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { } } -Loop: - for err == nil { - var c byte - c, err = d.r.ReadByte() - if err == io.EOF { - break + for { + c, err := d.r.ReadByte() + if err != nil { + return err } switch c { case sExtension: - err = d.readExtension() + if err = d.readExtension(); err != nil { + return err + } case sImageDescriptor: - var m *image.Paletted - m, err = d.newImageFromDescriptor() + m, err := d.newImageFromDescriptor() if err != nil { - break + return err } if d.imageFields&fColorMapFollows != 0 { m.Palette, err = d.readColorMap() if err != nil { - break + return err } // TODO: do we set transparency in this map too? That would be // d.setTransparency(m.Palette) } else { m.Palette = d.globalColorMap } - var litWidth uint8 - litWidth, err = d.r.ReadByte() + litWidth, err := d.r.ReadByte() if err != nil { return err } @@ -178,18 +187,27 @@ Loop: return fmt.Errorf("gif: pixel size in decode out of range: %d", litWidth) } // A wonderfully Go-like piece of magic. - lzwr := lzw.NewReader(&blockReader{r: d.r}, lzw.LSB, int(litWidth)) + br := &blockReader{r: d.r} + lzwr := lzw.NewReader(br, lzw.LSB, int(litWidth)) if _, err = io.ReadFull(lzwr, m.Pix); err != nil { - break + if err != io.ErrUnexpectedEOF { + return err + } + return errNotEnough } - - // There should be a "0" block remaining; drain that. - c, err = d.r.ReadByte() - if err != nil { - return err + // Both lzwr and br should be exhausted. Reading from them + // should yield (0, io.EOF). + if n, err := lzwr.Read(d.tmp[:1]); n != 0 || err != io.EOF { + if err != nil { + return err + } + return errTooMuch } - if c != 0 { - return errors.New("gif: extra data after image") + if n, err := br.Read(d.tmp[:1]); n != 0 || err != io.EOF { + if err != nil { + return err + } + return errTooMuch } // Undo the interlacing if necessary. @@ -202,19 +220,15 @@ Loop: d.delayTime = 0 // TODO: is this correct, or should we hold on to the value? case sTrailer: - break Loop + if len(d.image) == 0 { + return io.ErrUnexpectedEOF + } + return nil default: - err = fmt.Errorf("gif: unknown block type: 0x%.2x", c) + return fmt.Errorf("gif: unknown block type: 0x%.2x", c) } } - if err != nil { - return err - } - if len(d.image) == 0 { - return io.ErrUnexpectedEOF - } - return nil } func (d *decoder) readHeaderAndScreenDescriptor() error { @@ -304,7 +318,6 @@ func (d *decoder) readExtension() error { return err } } - panic("unreachable") } func (d *decoder) readGraphicControl() error { @@ -335,7 +348,15 @@ func (d *decoder) newImageFromDescriptor() (*image.Paletted, error) { width := int(d.tmp[4]) + int(d.tmp[5])<<8 height := int(d.tmp[6]) + int(d.tmp[7])<<8 d.imageFields = d.tmp[8] - return image.NewPaletted(image.Rect(left, top, left+width, top+height), nil), nil + + // The GIF89a spec, Section 20 (Image Descriptor) says: + // "Each image must fit within the boundaries of the Logical + // Screen, as defined in the Logical Screen Descriptor." + bounds := image.Rect(left, top, left+width, top+height) + if bounds != bounds.Intersect(image.Rect(0, 0, d.width, d.height)) { + return nil, errors.New("gif: frame bounds larger than image bounds") + } + return image.NewPaletted(bounds, nil), nil } func (d *decoder) readBlock() (int, error) { diff --git a/libgo/go/image/gif/reader_test.go b/libgo/go/image/gif/reader_test.go new file mode 100644 index 0000000..dcc6c6d --- /dev/null +++ b/libgo/go/image/gif/reader_test.go @@ -0,0 +1,138 @@ +package gif + +import ( + "bytes" + "compress/lzw" + "image" + "image/color" + "reflect" + "testing" +) + +func TestDecode(t *testing.T) { + // header and trailer are parts of a valid 2x1 GIF image. + const ( + header = "GIF89a" + + "\x02\x00\x01\x00" + // width=2, height=1 + "\x80\x00\x00" + // headerFields=(a color map of 2 pixels), backgroundIndex, aspect + "\x10\x20\x30\x40\x50\x60" // the color map, also known as a palette + trailer = "\x3b" + ) + + // 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() + } + + 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. + wantErr error + }{ + {0, false, errNotEnough}, + {1, false, errNotEnough}, + {2, false, nil}, + {2, true, errTooMuch}, + {3, false, errTooMuch}, + } + for _, tc := range testCases { + b := &bytes.Buffer{} + b.WriteString(header) + // Write an image with bounds 2x1 but tc.nPix pixels. If tc.nPix != 2 + // then this should result in an invalid GIF image. First, write a + // magic 0x2c (image descriptor) byte, bounds=(0,0)-(2,1), a flags + // byte, and 2-bit LZW literals. + b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") + if tc.nPix > 0 { + enc := lzwEncode(tc.nPix) + if len(enc) > 0xff { + t.Errorf("nPix=%d, extra=%t: compressed length %d is too large", tc.nPix, tc.extra, len(enc)) + continue + } + b.WriteByte(byte(len(enc))) + b.Write(enc) + } + if tc.extra { + b.WriteString("\x01\x02") // A 1-byte payload with an 0x02 byte. + } + b.WriteByte(0x00) // An empty block signifies the end of the image data. + b.WriteString(trailer) + + got, err := Decode(b) + if err != tc.wantErr { + t.Errorf("nPix=%d, extra=%t\ngot %v\nwant %v", tc.nPix, tc.extra, err, tc.wantErr) + } + + if tc.wantErr != nil { + continue + } + want := &image.Paletted{ + Pix: []uint8{0, 0}, + Stride: 2, + Rect: image.Rect(0, 0, 2, 1), + Palette: color.Palette{ + color.RGBA{0x10, 0x20, 0x30, 0xff}, + color.RGBA{0x40, 0x50, 0x60, 0xff}, + }, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("nPix=%d, extra=%t\ngot %v\nwant %v", tc.nPix, tc.extra, got, want) + } + } +} + +// testGIF is a simple GIF that we can modify to test different scenarios. +var testGIF = []byte{ + 'G', 'I', 'F', '8', '9', 'a', + 1, 0, 1, 0, // w=1, h=1 (6) + 128, 0, 0, // headerFields, bg, aspect (10) + 0, 0, 0, 1, 1, 1, // color map and graphics control (13) + 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0xff, 0x00, // (19) + // frame 1 (0,0 - 1,1) + 0x2c, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, // (32) + 0x00, + 0x02, 0x02, 0x4c, 0x01, 0x00, // lzw pixels + // trailer + 0x3b, +} + +func try(t *testing.T, b []byte, want string) { + _, err := DecodeAll(bytes.NewReader(b)) + var got string + if err != nil { + got = err.Error() + } + if got != want { + t.Fatalf("got %v, want %v", got, want) + } +} + +func TestBounds(t *testing.T) { + // make a local copy of testGIF + gif := make([]byte, len(testGIF)) + copy(gif, testGIF) + // Make the bounds too big, just by one. + gif[32] = 2 + want := "gif: frame bounds larger than image bounds" + try(t, gif, want) + + // Make the bounds too small; does not trigger bounds + // check, but now there's too much data. + gif[32] = 0 + want = "gif: too much image data" + try(t, gif, want) + gif[32] = 1 + + // Make the bounds really big, expect an error. + want = "gif: frame bounds larger than image bounds" + for i := 0; i < 4; i++ { + gif[32+i] = 0xff + } + try(t, gif, want) +} diff --git a/libgo/go/image/jpeg/reader.go b/libgo/go/image/jpeg/reader.go index 1ee6bbc..862d8dc 100644 --- a/libgo/go/image/jpeg/reader.go +++ b/libgo/go/image/jpeg/reader.go @@ -245,10 +245,38 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) { if err != nil { return nil, err } - if d.tmp[0] != 0xff { - return nil, FormatError("missing 0xff marker start") + for d.tmp[0] != 0xff { + // Strictly speaking, this is a format error. However, libjpeg is + // liberal in what it accepts. As of version 9, next_marker in + // jdmarker.c treats this as a warning (JWRN_EXTRANEOUS_DATA) and + // continues to decode the stream. Even before next_marker sees + // extraneous data, jpeg_fill_bit_buffer in jdhuff.c reads as many + // bytes as it can, possibly past the end of a scan's data. It + // effectively puts back any markers that it overscanned (e.g. an + // "\xff\xd9" EOI marker), but it does not put back non-marker data, + // and thus it can silently ignore a small number of extraneous + // non-marker bytes before next_marker has a chance to see them (and + // print a warning). + // + // We are therefore also liberal in what we accept. Extraneous data + // is silently ignored. + // + // This is similar to, but not exactly the same as, the restart + // mechanism within a scan (the RST[0-7] markers). + // + // 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() + if err != nil { + return nil, err + } } marker := d.tmp[1] + if marker == 0 { + // Treat "\xff\x00" as extraneous data. + continue + } 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'". diff --git a/libgo/go/image/jpeg/reader_test.go b/libgo/go/image/jpeg/reader_test.go index b520a8a..e951e03 100644 --- a/libgo/go/image/jpeg/reader_test.go +++ b/libgo/go/image/jpeg/reader_test.go @@ -8,8 +8,11 @@ import ( "bytes" "fmt" "image" + "image/color" "io/ioutil" + "math/rand" "os" + "strings" "testing" ) @@ -131,6 +134,66 @@ func pixString(pix []byte, stride, x, y int) string { return s.String() } +func TestExtraneousData(t *testing.T) { + // Encode a 1x1 red image. + src := image.NewRGBA(image.Rect(0, 0, 1, 1)) + src.Set(0, 0, color.RGBA{0xff, 0x00, 0x00, 0xff}) + buf := new(bytes.Buffer) + if err := Encode(buf, src, nil); err != nil { + t.Fatalf("encode: %v", err) + } + enc := buf.String() + // Sanity check that the encoded JPEG is long enough, that it ends in a + // "\xff\xd9" EOI marker, and that it contains a "\xff\xda" SOS marker + // somewhere in the final 64 bytes. + if len(enc) < 64 { + t.Fatalf("encoded JPEG is too short: %d bytes", len(enc)) + } + if got, want := enc[len(enc)-2:], "\xff\xd9"; got != want { + t.Fatalf("encoded JPEG ends with %q, want %q", got, want) + } + if s := enc[len(enc)-64:]; !strings.Contains(s, "\xff\xda") { + t.Fatalf("encoded JPEG does not contain a SOS marker (ff da) near the end: % x", s) + } + // Test that adding some random junk between the SOS marker and the + // EOI marker does not affect the decoding. + rnd := rand.New(rand.NewSource(1)) + for i, nerr := 0, 0; i < 1000 && nerr < 10; i++ { + buf.Reset() + // Write all but the trailing "\xff\xd9" EOI marker. + buf.WriteString(enc[:len(enc)-2]) + // Write some random extraneous data. + for n := rnd.Intn(10); n > 0; n-- { + if x := byte(rnd.Intn(256)); x != 0xff { + buf.WriteByte(x) + } else { + // The JPEG format escapes a SOS 0xff data byte as "\xff\x00". + buf.WriteString("\xff\x00") + } + } + // Write the "\xff\xd9" EOI marker. + buf.WriteString("\xff\xd9") + + // Check that we can still decode the resultant image. + got, err := Decode(buf) + if err != nil { + t.Errorf("could not decode image #%d: %v", i, err) + nerr++ + continue + } + if got.Bounds() != src.Bounds() { + t.Errorf("image #%d, bounds differ: %v and %v", i, got.Bounds(), src.Bounds()) + nerr++ + continue + } + if averageDelta(got, src) > 2<<8 { + t.Errorf("image #%d changed too much after a round trip", i) + nerr++ + continue + } + } +} + func benchmarkDecode(b *testing.B, filename string) { b.StopTimer() data, err := ioutil.ReadFile(filename) diff --git a/libgo/go/image/jpeg/scan.go b/libgo/go/image/jpeg/scan.go index e3ae8ae..a69ed17 100644 --- a/libgo/go/image/jpeg/scan.go +++ b/libgo/go/image/jpeg/scan.go @@ -109,9 +109,11 @@ func (d *decoder) processSOS(n int) error { myy := (d.height + 8*v0 - 1) / (8 * v0) if d.img1 == nil && d.img3 == nil { d.makeImg(h0, v0, mxx, myy) - if d.progressive { - for i := 0; i < nComp; i++ { - compIndex := scan[i].compIndex + } + if d.progressive { + for i := 0; i < nComp; i++ { + compIndex := scan[i].compIndex + if d.progCoeffs[compIndex] == nil { d.progCoeffs[compIndex] = make([]block, mxx*myy*d.comp[compIndex].h*d.comp[compIndex].v) } } diff --git a/libgo/go/image/jpeg/writer_test.go b/libgo/go/image/jpeg/writer_test.go index 0b2143f..514b455 100644 --- a/libgo/go/image/jpeg/writer_test.go +++ b/libgo/go/image/jpeg/writer_test.go @@ -148,29 +148,38 @@ func TestWriter(t *testing.T) { t.Error(tc.filename, err) continue } - // Compute the average delta in RGB space. - b := m0.Bounds() - var sum, n int64 - for y := b.Min.Y; y < b.Max.Y; y++ { - for x := b.Min.X; x < b.Max.X; x++ { - c0 := m0.At(x, y) - c1 := m1.At(x, y) - r0, g0, b0, _ := c0.RGBA() - r1, g1, b1, _ := c1.RGBA() - sum += delta(r0, r1) - sum += delta(g0, g1) - sum += delta(b0, b1) - n += 3 - } + if m0.Bounds() != m1.Bounds() { + t.Errorf("%s, bounds differ: %v and %v", tc.filename, m0.Bounds(), m1.Bounds()) + continue } // Compare the average delta to the tolerance level. - if sum/n > tc.tolerance { + if averageDelta(m0, m1) > tc.tolerance { t.Errorf("%s, quality=%d: average delta is too high", tc.filename, tc.quality) continue } } } +// averageDelta returns the average delta in RGB space. The two images must +// have the same bounds. +func averageDelta(m0, m1 image.Image) int64 { + b := m0.Bounds() + var sum, n int64 + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { + c0 := m0.At(x, y) + c1 := m1.At(x, y) + r0, g0, b0, _ := c0.RGBA() + r1, g1, b1, _ := c1.RGBA() + sum += delta(r0, r1) + sum += delta(g0, g1) + sum += delta(b0, b1) + n += 3 + } + } + return sum / n +} + func BenchmarkEncode(b *testing.B) { b.StopTimer() img := image.NewRGBA(image.Rect(0, 0, 640, 480)) diff --git a/libgo/go/image/png/reader.go b/libgo/go/image/png/reader.go index ff83733..a6bf86e 100644 --- a/libgo/go/image/png/reader.go +++ b/libgo/go/image/png/reader.go @@ -652,10 +652,11 @@ func DecodeConfig(r io.Reader) (image.Config, error) { } return image.Config{}, err } - if d.stage == dsSeenIHDR && d.cb != cbP8 { + paletted := d.cb == cbP8 || d.cb == cbP4 || d.cb == cbP2 || d.cb == cbP1 + if d.stage == dsSeenIHDR && !paletted { break } - if d.stage == dsSeenPLTE && d.cb == cbP8 { + if d.stage == dsSeenPLTE && paletted { break } } diff --git a/libgo/go/image/png/reader_test.go b/libgo/go/image/png/reader_test.go index 8223f52..ac0d949 100644 --- a/libgo/go/image/png/reader_test.go +++ b/libgo/go/image/png/reader_test.go @@ -38,6 +38,14 @@ var filenames = []string{ "basn6a16", } +var filenamesPaletted = []string{ + "basn3p01", + "basn3p02", + "basn3p04", + "basn3p08", + "basn3p08-trns", +} + var filenamesShort = []string{ "basn0g01", "basn0g04-31", @@ -208,7 +216,7 @@ func TestReader(t *testing.T) { } piper, pipew := io.Pipe() - pb := bufio.NewReader(piper) + pb := bufio.NewScanner(piper) go sng(pipew, fn, img) defer piper.Close() @@ -219,7 +227,7 @@ func TestReader(t *testing.T) { continue } defer sf.Close() - sb := bufio.NewReader(sf) + sb := bufio.NewScanner(sf) if err != nil { t.Error(fn, err) continue @@ -227,24 +235,28 @@ func TestReader(t *testing.T) { // Compare the two, in SNG format, line by line. for { - ps, perr := pb.ReadString('\n') - ss, serr := sb.ReadString('\n') - if perr == io.EOF && serr == io.EOF { - break - } - if perr != nil { - t.Error(fn, perr) + pdone := pb.Scan() + sdone := sb.Scan() + if pdone && sdone { break } - if serr != nil { - t.Error(fn, serr) + if pdone || sdone { + t.Errorf("%s: Different sizes", fn) break } + ps := pb.Text() + ss := sb.Text() if ps != ss { t.Errorf("%s: Mismatch\n%sversus\n%s\n", fn, ps, ss) break } } + if pb.Err() != nil { + t.Error(fn, pb.Err()) + } + if sb.Err() != nil { + t.Error(fn, sb.Err()) + } } } @@ -274,6 +286,31 @@ func TestReaderError(t *testing.T) { } } +func TestPalettedDecodeConfig(t *testing.T) { + for _, fn := range filenamesPaletted { + f, err := os.Open("testdata/pngsuite/" + fn + ".png") + if err != nil { + t.Errorf("%s: open failed: %v", fn, err) + continue + } + defer f.Close() + cfg, err := DecodeConfig(f) + if err != nil { + t.Errorf("%s: %v", fn, err) + continue + } + pal, ok := cfg.ColorModel.(color.Palette) + if !ok { + t.Errorf("%s: expected paletted color model", fn) + continue + } + if pal == nil { + t.Errorf("%s: palette not initialized", fn) + continue + } + } +} + func benchmarkDecode(b *testing.B, filename string, bytesPerPixel int) { b.StopTimer() data, err := ioutil.ReadFile(filename) diff --git a/libgo/go/image/png/writer.go b/libgo/go/image/png/writer.go index 093d471..629452c 100644 --- a/libgo/go/image/png/writer.go +++ b/libgo/go/image/png/writer.go @@ -436,7 +436,7 @@ func Encode(w io.Writer, m image.Image) error { // also rejected. mw, mh := int64(m.Bounds().Dx()), int64(m.Bounds().Dy()) if mw <= 0 || mh <= 0 || mw >= 1<<32 || mh >= 1<<32 { - return FormatError("invalid image size: " + strconv.FormatInt(mw, 10) + "x" + strconv.FormatInt(mw, 10)) + return FormatError("invalid image size: " + strconv.FormatInt(mw, 10) + "x" + strconv.FormatInt(mh, 10)) } var e encoder |