github.com/flyinox/gosm@v0.0.0-20171117061539-16768cb62077/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  	"time"
    19  )
    20  
    21  // TestDecodeProgressive tests that decoding the baseline and progressive
    22  // versions of the same image result in exactly the same pixel data, in YCbCr
    23  // space for color images, and Y space for grayscale images.
    24  func TestDecodeProgressive(t *testing.T) {
    25  	testCases := []string{
    26  		"../testdata/video-001",
    27  		"../testdata/video-001.q50.410",
    28  		"../testdata/video-001.q50.411",
    29  		"../testdata/video-001.q50.420",
    30  		"../testdata/video-001.q50.422",
    31  		"../testdata/video-001.q50.440",
    32  		"../testdata/video-001.q50.444",
    33  		"../testdata/video-005.gray.q50",
    34  		"../testdata/video-005.gray.q50.2x2",
    35  		"../testdata/video-001.separate.dc.progression",
    36  	}
    37  	for _, tc := range testCases {
    38  		m0, err := decodeFile(tc + ".jpeg")
    39  		if err != nil {
    40  			t.Errorf("%s: %v", tc+".jpeg", err)
    41  			continue
    42  		}
    43  		m1, err := decodeFile(tc + ".progressive.jpeg")
    44  		if err != nil {
    45  			t.Errorf("%s: %v", tc+".progressive.jpeg", err)
    46  			continue
    47  		}
    48  		if m0.Bounds() != m1.Bounds() {
    49  			t.Errorf("%s: bounds differ: %v and %v", tc, m0.Bounds(), m1.Bounds())
    50  			continue
    51  		}
    52  		// All of the video-*.jpeg files are 150x103.
    53  		if m0.Bounds() != image.Rect(0, 0, 150, 103) {
    54  			t.Errorf("%s: bad bounds: %v", tc, m0.Bounds())
    55  			continue
    56  		}
    57  
    58  		switch m0 := m0.(type) {
    59  		case *image.YCbCr:
    60  			m1 := m1.(*image.YCbCr)
    61  			if err := check(m0.Bounds(), m0.Y, m1.Y, m0.YStride, m1.YStride); err != nil {
    62  				t.Errorf("%s (Y): %v", tc, err)
    63  				continue
    64  			}
    65  			if err := check(m0.Bounds(), m0.Cb, m1.Cb, m0.CStride, m1.CStride); err != nil {
    66  				t.Errorf("%s (Cb): %v", tc, err)
    67  				continue
    68  			}
    69  			if err := check(m0.Bounds(), m0.Cr, m1.Cr, m0.CStride, m1.CStride); err != nil {
    70  				t.Errorf("%s (Cr): %v", tc, err)
    71  				continue
    72  			}
    73  		case *image.Gray:
    74  			m1 := m1.(*image.Gray)
    75  			if err := check(m0.Bounds(), m0.Pix, m1.Pix, m0.Stride, m1.Stride); err != nil {
    76  				t.Errorf("%s: %v", tc, err)
    77  				continue
    78  			}
    79  		default:
    80  			t.Errorf("%s: unexpected image type %T", tc, m0)
    81  			continue
    82  		}
    83  	}
    84  }
    85  
    86  func decodeFile(filename string) (image.Image, error) {
    87  	f, err := os.Open(filename)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	defer f.Close()
    92  	return Decode(f)
    93  }
    94  
    95  type eofReader struct {
    96  	data     []byte // deliver from Read without EOF
    97  	dataEOF  []byte // then deliver from Read with EOF on last chunk
    98  	lenAtEOF int
    99  }
   100  
   101  func (r *eofReader) Read(b []byte) (n int, err error) {
   102  	if len(r.data) > 0 {
   103  		n = copy(b, r.data)
   104  		r.data = r.data[n:]
   105  	} else {
   106  		n = copy(b, r.dataEOF)
   107  		r.dataEOF = r.dataEOF[n:]
   108  		if len(r.dataEOF) == 0 {
   109  			err = io.EOF
   110  			if r.lenAtEOF == -1 {
   111  				r.lenAtEOF = n
   112  			}
   113  		}
   114  	}
   115  	return
   116  }
   117  
   118  func TestDecodeEOF(t *testing.T) {
   119  	// Check that if reader returns final data and EOF at same time, jpeg handles it.
   120  	data, err := ioutil.ReadFile("../testdata/video-001.jpeg")
   121  	if err != nil {
   122  		t.Fatal(err)
   123  	}
   124  
   125  	n := len(data)
   126  	for i := 0; i < n; {
   127  		r := &eofReader{data[:n-i], data[n-i:], -1}
   128  		_, err := Decode(r)
   129  		if err != nil {
   130  			t.Errorf("Decode with Read() = %d, EOF: %v", r.lenAtEOF, err)
   131  		}
   132  		if i == 0 {
   133  			i = 1
   134  		} else {
   135  			i *= 2
   136  		}
   137  	}
   138  }
   139  
   140  // check checks that the two pix data are equal, within the given bounds.
   141  func check(bounds image.Rectangle, pix0, pix1 []byte, stride0, stride1 int) error {
   142  	if stride0 <= 0 || stride0%8 != 0 {
   143  		return fmt.Errorf("bad stride %d", stride0)
   144  	}
   145  	if stride1 <= 0 || stride1%8 != 0 {
   146  		return fmt.Errorf("bad stride %d", stride1)
   147  	}
   148  	// Compare the two pix data, one 8x8 block at a time.
   149  	for y := 0; y < len(pix0)/stride0 && y < len(pix1)/stride1; y += 8 {
   150  		for x := 0; x < stride0 && x < stride1; x += 8 {
   151  			if x >= bounds.Max.X || y >= bounds.Max.Y {
   152  				// We don't care if the two pix data differ if the 8x8 block is
   153  				// entirely outside of the image's bounds. For example, this can
   154  				// occur with a 4:2:0 chroma subsampling and a 1x1 image. Baseline
   155  				// decoding works on the one 16x16 MCU as a whole; progressive
   156  				// decoding's first pass works on that 16x16 MCU as a whole but
   157  				// refinement passes only process one 8x8 block within the MCU.
   158  				continue
   159  			}
   160  
   161  			for j := 0; j < 8; j++ {
   162  				for i := 0; i < 8; i++ {
   163  					index0 := (y+j)*stride0 + (x + i)
   164  					index1 := (y+j)*stride1 + (x + i)
   165  					if pix0[index0] != pix1[index1] {
   166  						return fmt.Errorf("blocks at (%d, %d) differ:\n%sand\n%s", x, y,
   167  							pixString(pix0, stride0, x, y),
   168  							pixString(pix1, stride1, x, y),
   169  						)
   170  					}
   171  				}
   172  			}
   173  		}
   174  	}
   175  	return nil
   176  }
   177  
   178  func pixString(pix []byte, stride, x, y int) string {
   179  	s := bytes.NewBuffer(nil)
   180  	for j := 0; j < 8; j++ {
   181  		fmt.Fprintf(s, "\t")
   182  		for i := 0; i < 8; i++ {
   183  			fmt.Fprintf(s, "%02x ", pix[(y+j)*stride+(x+i)])
   184  		}
   185  		fmt.Fprintf(s, "\n")
   186  	}
   187  	return s.String()
   188  }
   189  
   190  func TestTruncatedSOSDataDoesntPanic(t *testing.T) {
   191  	b, err := ioutil.ReadFile("../testdata/video-005.gray.q50.jpeg")
   192  	if err != nil {
   193  		t.Fatal(err)
   194  	}
   195  	sosMarker := []byte{0xff, 0xda}
   196  	i := bytes.Index(b, sosMarker)
   197  	if i < 0 {
   198  		t.Fatal("SOS marker not found")
   199  	}
   200  	i += len(sosMarker)
   201  	j := i + 10
   202  	if j > len(b) {
   203  		j = len(b)
   204  	}
   205  	for ; i < j; i++ {
   206  		Decode(bytes.NewReader(b[:i]))
   207  	}
   208  }
   209  
   210  func TestLargeImageWithShortData(t *testing.T) {
   211  	// This input is an invalid JPEG image, based on the fuzzer-generated image
   212  	// in issue 10413. It is only 504 bytes, and shouldn't take long for Decode
   213  	// to return an error. The Start Of Frame marker gives the image dimensions
   214  	// as 8192 wide and 8192 high, so even if an unreadByteStuffedByte bug
   215  	// doesn't technically lead to an infinite loop, such a bug can still cause
   216  	// an unreasonably long loop for such a short input.
   217  	const input = "" +
   218  		"\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x00\x00\x01" +
   219  		"\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x10\x0b\x0c\x0e\x0c\x0a\x10" +
   220  		"\x0e\x89\x0e\x12\x11\x10\x13\x18\xff\xd8\xff\xe0\x00\x10\x4a\x46" +
   221  		"\x49\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43" +
   222  		"\x00\x10\x0b\x0c\x0e\x0c\x0a\x10\x0e\x0d\x0e\x12\x11\x10\x13\x18" +
   223  		"\x28\x1a\x18\x16\x16\x18\x31\x23\x25\x1d\x28\x3a\x33\x3d\x3c\x39" +
   224  		"\x33\x38\x37\x40\x48\x5c\x4e\x40\x44\x57\x45\x37\x38\x50\x6d\x51" +
   225  		"\x57\x5f\x62\x67\x68\x67\x3e\x4d\x71\x79\x70\x64\x78\x5c\x65\x67" +
   226  		"\x63\xff\xc0\x00\x0b\x08\x20\x00\x20\x00\x01\x01\x11\x00\xff\xc4" +
   227  		"\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00" +
   228  		"\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\xff" +
   229  		"\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04" +
   230  		"\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x01\x06" +
   231  		"\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\xd8\xff\xdd" +
   232  		"\x42\xb1\xc1\x15\x52\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17" +
   233  		"\x18\x19\x1a\x25\x26\x27\x28\x29\x2a\x34\x35\x36\x37\x38\x39\x3a" +
   234  		"\x43\x44\x45\x46\x47\x48\x49\x4a\x53\x54\x55\x56\x57\x58\x59\x5a" +
   235  		"\x00\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75\x76\x77\x78\x79" +
   236  		"\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98" +
   237  		"\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6" +
   238  		"\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xff\xd8\xff\xe0\x00\x10" +
   239  		"\x4a\x46\x49\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb" +
   240  		"\x00\x43\x00\x10\x0b\x0c\x0e\x0c\x0a\x10\x0e\x0d\x0e\x12\x11\x10" +
   241  		"\x13\x18\x28\x1a\x18\x16\x16\x18\x31\x23\x25\x1d\xc8\xc9\xca\xd2" +
   242  		"\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8" +
   243  		"\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x08" +
   244  		"\x01\x01\x00\x00\x3f\x00\xb9\xeb\x50\xb0\xdb\xc8\xa8\xe4\x63\x80" +
   245  		"\xdd\x31\xd6\x9d\xbb\xf2\xc5\x42\x1f\x6c\x6f\xf4\x34\xdd\x3c\xfc" +
   246  		"\xac\xe7\x3d\x80\xa9\xcc\x87\x34\xb3\x37\xfa\x2b\x9f\x6a\xad\x63" +
   247  		"\x20\x36\x9f\x78\x64\x75\xe6\xab\x7d\xb2\xde\x29\x70\xd3\x20\x27" +
   248  		"\xde\xaf\xa4\xf0\xca\x9f\x24\xa8\xdf\x46\xa8\x24\x84\x96\xe3\x77" +
   249  		"\xf9\x2e\xe0\x0a\x62\x7f\xdf\xd9"
   250  	c := make(chan error, 1)
   251  	go func() {
   252  		_, err := Decode(strings.NewReader(input))
   253  		c <- err
   254  	}()
   255  	select {
   256  	case err := <-c:
   257  		if err == nil {
   258  			t.Fatalf("got nil error, want non-nil")
   259  		}
   260  	case <-time.After(3 * time.Second):
   261  		t.Fatalf("timed out")
   262  	}
   263  }
   264  
   265  func TestExtraneousData(t *testing.T) {
   266  	// Encode a 1x1 red image.
   267  	src := image.NewRGBA(image.Rect(0, 0, 1, 1))
   268  	src.Set(0, 0, color.RGBA{0xff, 0x00, 0x00, 0xff})
   269  	buf := new(bytes.Buffer)
   270  	if err := Encode(buf, src, nil); err != nil {
   271  		t.Fatalf("encode: %v", err)
   272  	}
   273  	enc := buf.String()
   274  	// Sanity check that the encoded JPEG is long enough, that it ends in a
   275  	// "\xff\xd9" EOI marker, and that it contains a "\xff\xda" SOS marker
   276  	// somewhere in the final 64 bytes.
   277  	if len(enc) < 64 {
   278  		t.Fatalf("encoded JPEG is too short: %d bytes", len(enc))
   279  	}
   280  	if got, want := enc[len(enc)-2:], "\xff\xd9"; got != want {
   281  		t.Fatalf("encoded JPEG ends with %q, want %q", got, want)
   282  	}
   283  	if s := enc[len(enc)-64:]; !strings.Contains(s, "\xff\xda") {
   284  		t.Fatalf("encoded JPEG does not contain a SOS marker (ff da) near the end: % x", s)
   285  	}
   286  	// Test that adding some random junk between the SOS marker and the
   287  	// EOI marker does not affect the decoding.
   288  	rnd := rand.New(rand.NewSource(1))
   289  	for i, nerr := 0, 0; i < 1000 && nerr < 10; i++ {
   290  		buf.Reset()
   291  		// Write all but the trailing "\xff\xd9" EOI marker.
   292  		buf.WriteString(enc[:len(enc)-2])
   293  		// Write some random extraneous data.
   294  		for n := rnd.Intn(10); n > 0; n-- {
   295  			if x := byte(rnd.Intn(256)); x != 0xff {
   296  				buf.WriteByte(x)
   297  			} else {
   298  				// The JPEG format escapes a SOS 0xff data byte as "\xff\x00".
   299  				buf.WriteString("\xff\x00")
   300  			}
   301  		}
   302  		// Write the "\xff\xd9" EOI marker.
   303  		buf.WriteString("\xff\xd9")
   304  
   305  		// Check that we can still decode the resultant image.
   306  		got, err := Decode(buf)
   307  		if err != nil {
   308  			t.Errorf("could not decode image #%d: %v", i, err)
   309  			nerr++
   310  			continue
   311  		}
   312  		if got.Bounds() != src.Bounds() {
   313  			t.Errorf("image #%d, bounds differ: %v and %v", i, got.Bounds(), src.Bounds())
   314  			nerr++
   315  			continue
   316  		}
   317  		if averageDelta(got, src) > 2<<8 {
   318  			t.Errorf("image #%d changed too much after a round trip", i)
   319  			nerr++
   320  			continue
   321  		}
   322  	}
   323  }
   324  
   325  func benchmarkDecode(b *testing.B, filename string) {
   326  	b.StopTimer()
   327  	data, err := ioutil.ReadFile(filename)
   328  	if err != nil {
   329  		b.Fatal(err)
   330  	}
   331  	cfg, err := DecodeConfig(bytes.NewReader(data))
   332  	if err != nil {
   333  		b.Fatal(err)
   334  	}
   335  	b.SetBytes(int64(cfg.Width * cfg.Height * 4))
   336  	b.StartTimer()
   337  	for i := 0; i < b.N; i++ {
   338  		Decode(bytes.NewReader(data))
   339  	}
   340  }
   341  
   342  func BenchmarkDecodeBaseline(b *testing.B) {
   343  	benchmarkDecode(b, "../testdata/video-001.jpeg")
   344  }
   345  
   346  func BenchmarkDecodeProgressive(b *testing.B) {
   347  	benchmarkDecode(b, "../testdata/video-001.progressive.jpeg")
   348  }