123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- // Copyright 2012 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.
- package jpeg
- import (
- "bytes"
- "fmt"
- "image"
- "image/color"
- "io"
- "io/ioutil"
- "math/rand"
- "os"
- "strings"
- "testing"
- )
- // TestDecodeProgressive tests that decoding the baseline and progressive
- // versions of the same image result in exactly the same pixel data, in YCbCr
- // space for color images, and Y space for grayscale images.
- func TestDecodeProgressive(t *testing.T) {
- testCases := []string{
- "../testdata/video-001",
- "../testdata/video-001.q50.420",
- "../testdata/video-001.q50.422",
- "../testdata/video-001.q50.440",
- "../testdata/video-001.q50.444",
- "../testdata/video-005.gray.q50",
- "../testdata/video-005.gray.q50.2x2",
- "../testdata/video-001.separate.dc.progression",
- }
- for _, tc := range testCases {
- m0, err := decodeFile(tc + ".jpeg")
- if err != nil {
- t.Errorf("%s: %v", tc+".jpeg", err)
- continue
- }
- m1, err := decodeFile(tc + ".progressive.jpeg")
- if err != nil {
- t.Errorf("%s: %v", tc+".progressive.jpeg", err)
- continue
- }
- if m0.Bounds() != m1.Bounds() {
- t.Errorf("%s: bounds differ: %v and %v", tc, m0.Bounds(), m1.Bounds())
- continue
- }
- // All of the video-*.jpeg files are 150x103.
- if m0.Bounds() != image.Rect(0, 0, 150, 103) {
- t.Errorf("%s: bad bounds: %v", tc, m0.Bounds())
- continue
- }
- switch m0 := m0.(type) {
- case *image.YCbCr:
- m1 := m1.(*image.YCbCr)
- if err := check(m0.Bounds(), m0.Y, m1.Y, m0.YStride, m1.YStride); err != nil {
- t.Errorf("%s (Y): %v", tc, err)
- continue
- }
- if err := check(m0.Bounds(), m0.Cb, m1.Cb, m0.CStride, m1.CStride); err != nil {
- t.Errorf("%s (Cb): %v", tc, err)
- continue
- }
- if err := check(m0.Bounds(), m0.Cr, m1.Cr, m0.CStride, m1.CStride); err != nil {
- t.Errorf("%s (Cr): %v", tc, err)
- continue
- }
- case *image.Gray:
- m1 := m1.(*image.Gray)
- if err := check(m0.Bounds(), m0.Pix, m1.Pix, m0.Stride, m1.Stride); err != nil {
- t.Errorf("%s: %v", tc, err)
- continue
- }
- default:
- t.Errorf("%s: unexpected image type %T", tc, m0)
- continue
- }
- }
- }
- func decodeFile(filename string) (image.Image, error) {
- f, err := os.Open(filename)
- if err != nil {
- return nil, err
- }
- 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.
- func check(bounds image.Rectangle, pix0, pix1 []byte, stride0, stride1 int) error {
- if stride0 <= 0 || stride0%8 != 0 {
- return fmt.Errorf("bad stride %d", stride0)
- }
- if stride1 <= 0 || stride1%8 != 0 {
- return fmt.Errorf("bad stride %d", stride1)
- }
- // Compare the two pix data, one 8x8 block at a time.
- for y := 0; y < len(pix0)/stride0 && y < len(pix1)/stride1; y += 8 {
- for x := 0; x < stride0 && x < stride1; x += 8 {
- if x >= bounds.Max.X || y >= bounds.Max.Y {
- // We don't care if the two pix data differ if the 8x8 block is
- // entirely outside of the image's bounds. For example, this can
- // occur with a 4:2:0 chroma subsampling and a 1x1 image. Baseline
- // decoding works on the one 16x16 MCU as a whole; progressive
- // decoding's first pass works on that 16x16 MCU as a whole but
- // refinement passes only process one 8x8 block within the MCU.
- continue
- }
- for j := 0; j < 8; j++ {
- for i := 0; i < 8; i++ {
- index0 := (y+j)*stride0 + (x + i)
- index1 := (y+j)*stride1 + (x + i)
- if pix0[index0] != pix1[index1] {
- return fmt.Errorf("blocks at (%d, %d) differ:\n%sand\n%s", x, y,
- pixString(pix0, stride0, x, y),
- pixString(pix1, stride1, x, y),
- )
- }
- }
- }
- }
- }
- return nil
- }
- func pixString(pix []byte, stride, x, y int) string {
- s := bytes.NewBuffer(nil)
- for j := 0; j < 8; j++ {
- fmt.Fprintf(s, "\t")
- for i := 0; i < 8; i++ {
- fmt.Fprintf(s, "%02x ", pix[(y+j)*stride+(x+i)])
- }
- fmt.Fprintf(s, "\n")
- }
- 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)
- if err != nil {
- b.Fatal(err)
- }
- cfg, err := DecodeConfig(bytes.NewReader(data))
- if err != nil {
- b.Fatal(err)
- }
- b.SetBytes(int64(cfg.Width * cfg.Height * 4))
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- Decode(bytes.NewReader(data))
- }
- }
- func BenchmarkDecodeBaseline(b *testing.B) {
- benchmarkDecode(b, "../testdata/video-001.jpeg")
- }
- func BenchmarkDecodeProgressive(b *testing.B) {
- benchmarkDecode(b, "../testdata/video-001.progressive.jpeg")
- }
|