github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/image/jpeg/reader_test.go (about)

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package jpeg
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"image"
    11  	"image/color"
    12  	"io"
    13  	"io/ioutil"
    14  	"math/rand"
    15  	"os"
    16  	"strings"
    17  	"testing"
    18  )
    19  
    20  // TestDecodeProgressive tests that decoding the baseline and progressive
    21  // versions of the same image result in exactly the same pixel data, in YCbCr
    22  // space for color images, and Y space for grayscale images.
    23  func TestDecodeProgressive(t *testing.T) {
    24  	testCases := []string{
    25  		"../testdata/video-001",
    26  		"../testdata/video-001.q50.410",
    27  		"../testdata/video-001.q50.411",
    28  		"../testdata/video-001.q50.420",
    29  		"../testdata/video-001.q50.422",
    30  		"../testdata/video-001.q50.440",
    31  		"../testdata/video-001.q50.444",
    32  		"../testdata/video-005.gray.q50",
    33  		"../testdata/video-005.gray.q50.2x2",
    34  		"../testdata/video-001.separate.dc.progression",
    35  	}
    36  	for _, tc := range testCases {
    37  		m0, err := decodeFile(tc + ".jpeg")
    38  		if err != nil {
    39  			t.Errorf("%s: %v", tc+".jpeg", err)
    40  			continue
    41  		}
    42  		m1, err := decodeFile(tc + ".progressive.jpeg")
    43  		if err != nil {
    44  			t.Errorf("%s: %v", tc+".progressive.jpeg", err)
    45  			continue
    46  		}
    47  		if m0.Bounds() != m1.Bounds() {
    48  			t.Errorf("%s: bounds differ: %v and %v", tc, m0.Bounds(), m1.Bounds())
    49  			continue
    50  		}
    51  		// All of the video-*.jpeg files are 150x103.
    52  		if m0.Bounds() != image.Rect(0, 0, 150, 103) {
    53  			t.Errorf("%s: bad bounds: %v", tc, m0.Bounds())
    54  			continue
    55  		}
    56  
    57  		switch m0 := m0.(type) {
    58  		case *image.YCbCr:
    59  			m1 := m1.(*image.YCbCr)
    60  			if err := check(m0.Bounds(), m0.Y, m1.Y, m0.YStride, m1.YStride); err != nil {
    61  				t.Errorf("%s (Y): %v", tc, err)
    62  				continue
    63  			}
    64  			if err := check(m0.Bounds(), m0.Cb, m1.Cb, m0.CStride, m1.CStride); err != nil {
    65  				t.Errorf("%s (Cb): %v", tc, err)
    66  				continue
    67  			}
    68  			if err := check(m0.Bounds(), m0.Cr, m1.Cr, m0.CStride, m1.CStride); err != nil {
    69  				t.Errorf("%s (Cr): %v", tc, err)
    70  				continue
    71  			}
    72  		case *image.Gray:
    73  			m1 := m1.(*image.Gray)
    74  			if err := check(m0.Bounds(), m0.Pix, m1.Pix, m0.Stride, m1.Stride); err != nil {
    75  				t.Errorf("%s: %v", tc, err)
    76  				continue
    77  			}
    78  		default:
    79  			t.Errorf("%s: unexpected image type %T", tc, m0)
    80  			continue
    81  		}
    82  	}
    83  }
    84  
    85  func decodeFile(filename string) (image.Image, error) {
    86  	f, err := os.Open(filename)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	defer f.Close()
    91  	return Decode(f)
    92  }
    93  
    94  type eofReader struct {
    95  	data     []byte // deliver from Read without EOF
    96  	dataEOF  []byte // then deliver from Read with EOF on last chunk
    97  	lenAtEOF int
    98  }
    99  
   100  func (r *eofReader) Read(b []byte) (n int, err error) {
   101  	if len(r.data) > 0 {
   102  		n = copy(b, r.data)
   103  		r.data = r.data[n:]
   104  	} else {
   105  		n = copy(b, r.dataEOF)
   106  		r.dataEOF = r.dataEOF[n:]
   107  		if len(r.dataEOF) == 0 {
   108  			err = io.EOF
   109  			if r.lenAtEOF == -1 {
   110  				r.lenAtEOF = n
   111  			}
   112  		}
   113  	}
   114  	return
   115  }
   116  
   117  func TestDecodeEOF(t *testing.T) {
   118  	// Check that if reader returns final data and EOF at same time, jpeg handles it.
   119  	data, err := ioutil.ReadFile("../testdata/video-001.jpeg")
   120  	if err != nil {
   121  		t.Fatal(err)
   122  	}
   123  
   124  	n := len(data)
   125  	for i := 0; i < n; {
   126  		r := &eofReader{data[:n-i], data[n-i:], -1}
   127  		_, err := Decode(r)
   128  		if err != nil {
   129  			t.Errorf("Decode with Read() = %d, EOF: %v", r.lenAtEOF, err)
   130  		}
   131  		if i == 0 {
   132  			i = 1
   133  		} else {
   134  			i *= 2
   135  		}
   136  	}
   137  }
   138  
   139  // check checks that the two pix data are equal, within the given bounds.
   140  func check(bounds image.Rectangle, pix0, pix1 []byte, stride0, stride1 int) error {
   141  	if stride0 <= 0 || stride0%8 != 0 {
   142  		return fmt.Errorf("bad stride %d", stride0)
   143  	}
   144  	if stride1 <= 0 || stride1%8 != 0 {
   145  		return fmt.Errorf("bad stride %d", stride1)
   146  	}
   147  	// Compare the two pix data, one 8x8 block at a time.
   148  	for y := 0; y < len(pix0)/stride0 && y < len(pix1)/stride1; y += 8 {
   149  		for x := 0; x < stride0 && x < stride1; x += 8 {
   150  			if x >= bounds.Max.X || y >= bounds.Max.Y {
   151  				// We don't care if the two pix data differ if the 8x8 block is
   152  				// entirely outside of the image's bounds. For example, this can
   153  				// occur with a 4:2:0 chroma subsampling and a 1x1 image. Baseline
   154  				// decoding works on the one 16x16 MCU as a whole; progressive
   155  				// decoding's first pass works on that 16x16 MCU as a whole but
   156  				// refinement passes only process one 8x8 block within the MCU.
   157  				continue
   158  			}
   159  
   160  			for j := 0; j < 8; j++ {
   161  				for i := 0; i < 8; i++ {
   162  					index0 := (y+j)*stride0 + (x + i)
   163  					index1 := (y+j)*stride1 + (x + i)
   164  					if pix0[index0] != pix1[index1] {
   165  						return fmt.Errorf("blocks at (%d, %d) differ:\n%sand\n%s", x, y,
   166  							pixString(pix0, stride0, x, y),
   167  							pixString(pix1, stride1, x, y),
   168  						)
   169  					}
   170  				}
   171  			}
   172  		}
   173  	}
   174  	return nil
   175  }
   176  
   177  func pixString(pix []byte, stride, x, y int) string {
   178  	s := bytes.NewBuffer(nil)
   179  	for j := 0; j < 8; j++ {
   180  		fmt.Fprintf(s, "\t")
   181  		for i := 0; i < 8; i++ {
   182  			fmt.Fprintf(s, "%02x ", pix[(y+j)*stride+(x+i)])
   183  		}
   184  		fmt.Fprintf(s, "\n")
   185  	}
   186  	return s.String()
   187  }
   188  
   189  func TestTruncatedSOSDataDoesntPanic(t *testing.T) {
   190  	b, err := ioutil.ReadFile("../testdata/video-005.gray.q50.jpeg")
   191  	if err != nil {
   192  		t.Fatal(err)
   193  	}
   194  	sosMarker := []byte{0xff, 0xda}
   195  	i := bytes.Index(b, sosMarker)
   196  	if i < 0 {
   197  		t.Fatal("SOS marker not found")
   198  	}
   199  	i += len(sosMarker)
   200  	j := i + 10
   201  	if j > len(b) {
   202  		j = len(b)
   203  	}
   204  	for ; i < j; i++ {
   205  		Decode(bytes.NewReader(b[:i]))
   206  	}
   207  }
   208  
   209  func TestExtraneousData(t *testing.T) {
   210  	// Encode a 1x1 red image.
   211  	src := image.NewRGBA(image.Rect(0, 0, 1, 1))
   212  	src.Set(0, 0, color.RGBA{0xff, 0x00, 0x00, 0xff})
   213  	buf := new(bytes.Buffer)
   214  	if err := Encode(buf, src, nil); err != nil {
   215  		t.Fatalf("encode: %v", err)
   216  	}
   217  	enc := buf.String()
   218  	// Sanity check that the encoded JPEG is long enough, that it ends in a
   219  	// "\xff\xd9" EOI marker, and that it contains a "\xff\xda" SOS marker
   220  	// somewhere in the final 64 bytes.
   221  	if len(enc) < 64 {
   222  		t.Fatalf("encoded JPEG is too short: %d bytes", len(enc))
   223  	}
   224  	if got, want := enc[len(enc)-2:], "\xff\xd9"; got != want {
   225  		t.Fatalf("encoded JPEG ends with %q, want %q", got, want)
   226  	}
   227  	if s := enc[len(enc)-64:]; !strings.Contains(s, "\xff\xda") {
   228  		t.Fatalf("encoded JPEG does not contain a SOS marker (ff da) near the end: % x", s)
   229  	}
   230  	// Test that adding some random junk between the SOS marker and the
   231  	// EOI marker does not affect the decoding.
   232  	rnd := rand.New(rand.NewSource(1))
   233  	for i, nerr := 0, 0; i < 1000 && nerr < 10; i++ {
   234  		buf.Reset()
   235  		// Write all but the trailing "\xff\xd9" EOI marker.
   236  		buf.WriteString(enc[:len(enc)-2])
   237  		// Write some random extraneous data.
   238  		for n := rnd.Intn(10); n > 0; n-- {
   239  			if x := byte(rnd.Intn(256)); x != 0xff {
   240  				buf.WriteByte(x)
   241  			} else {
   242  				// The JPEG format escapes a SOS 0xff data byte as "\xff\x00".
   243  				buf.WriteString("\xff\x00")
   244  			}
   245  		}
   246  		// Write the "\xff\xd9" EOI marker.
   247  		buf.WriteString("\xff\xd9")
   248  
   249  		// Check that we can still decode the resultant image.
   250  		got, err := Decode(buf)
   251  		if err != nil {
   252  			t.Errorf("could not decode image #%d: %v", i, err)
   253  			nerr++
   254  			continue
   255  		}
   256  		if got.Bounds() != src.Bounds() {
   257  			t.Errorf("image #%d, bounds differ: %v and %v", i, got.Bounds(), src.Bounds())
   258  			nerr++
   259  			continue
   260  		}
   261  		if averageDelta(got, src) > 2<<8 {
   262  			t.Errorf("image #%d changed too much after a round trip", i)
   263  			nerr++
   264  			continue
   265  		}
   266  	}
   267  }
   268  
   269  func benchmarkDecode(b *testing.B, filename string) {
   270  	b.StopTimer()
   271  	data, err := ioutil.ReadFile(filename)
   272  	if err != nil {
   273  		b.Fatal(err)
   274  	}
   275  	cfg, err := DecodeConfig(bytes.NewReader(data))
   276  	if err != nil {
   277  		b.Fatal(err)
   278  	}
   279  	b.SetBytes(int64(cfg.Width * cfg.Height * 4))
   280  	b.StartTimer()
   281  	for i := 0; i < b.N; i++ {
   282  		Decode(bytes.NewReader(data))
   283  	}
   284  }
   285  
   286  func BenchmarkDecodeBaseline(b *testing.B) {
   287  	benchmarkDecode(b, "../testdata/video-001.jpeg")
   288  }
   289  
   290  func BenchmarkDecodeProgressive(b *testing.B) {
   291  	benchmarkDecode(b, "../testdata/video-001.progressive.jpeg")
   292  }