github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/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/ioutil"
    13  	"math/rand"
    14  	"os"
    15  	"strings"
    16  	"testing"
    17  )
    18  
    19  // TestDecodeProgressive tests that decoding the baseline and progressive
    20  // versions of the same image result in exactly the same pixel data, in YCbCr
    21  // space for color images, and Y space for grayscale images.
    22  func TestDecodeProgressive(t *testing.T) {
    23  	testCases := []string{
    24  		"../testdata/video-001",
    25  		"../testdata/video-001.q50.420",
    26  		"../testdata/video-001.q50.422",
    27  		"../testdata/video-001.q50.440",
    28  		"../testdata/video-001.q50.444",
    29  		"../testdata/video-005.gray.q50",
    30  		"../testdata/video-005.gray.q50.2x2",
    31  	}
    32  	for _, tc := range testCases {
    33  		m0, err := decodeFile(tc + ".jpeg")
    34  		if err != nil {
    35  			t.Errorf("%s: %v", tc+".jpeg", err)
    36  			continue
    37  		}
    38  		m1, err := decodeFile(tc + ".progressive.jpeg")
    39  		if err != nil {
    40  			t.Errorf("%s: %v", tc+".progressive.jpeg", err)
    41  			continue
    42  		}
    43  		if m0.Bounds() != m1.Bounds() {
    44  			t.Errorf("%s: bounds differ: %v and %v", tc, m0.Bounds(), m1.Bounds())
    45  			continue
    46  		}
    47  		switch m0 := m0.(type) {
    48  		case *image.YCbCr:
    49  			m1 := m1.(*image.YCbCr)
    50  			if err := check(m0.Bounds(), m0.Y, m1.Y, m0.YStride, m1.YStride); err != nil {
    51  				t.Errorf("%s (Y): %v", tc, err)
    52  				continue
    53  			}
    54  			if err := check(m0.Bounds(), m0.Cb, m1.Cb, m0.CStride, m1.CStride); err != nil {
    55  				t.Errorf("%s (Cb): %v", tc, err)
    56  				continue
    57  			}
    58  			if err := check(m0.Bounds(), m0.Cr, m1.Cr, m0.CStride, m1.CStride); err != nil {
    59  				t.Errorf("%s (Cr): %v", tc, err)
    60  				continue
    61  			}
    62  		case *image.Gray:
    63  			m1 := m1.(*image.Gray)
    64  			if err := check(m0.Bounds(), m0.Pix, m1.Pix, m0.Stride, m1.Stride); err != nil {
    65  				t.Errorf("%s: %v", tc, err)
    66  				continue
    67  			}
    68  		default:
    69  			t.Errorf("%s: unexpected image type %T", tc, m0)
    70  			continue
    71  		}
    72  	}
    73  }
    74  
    75  func decodeFile(filename string) (image.Image, error) {
    76  	f, err := os.Open(filename)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	defer f.Close()
    81  	return Decode(f)
    82  
    83  }
    84  
    85  // check checks that the two pix data are equal, within the given bounds.
    86  func check(bounds image.Rectangle, pix0, pix1 []byte, stride0, stride1 int) error {
    87  	if len(pix0) != len(pix1) {
    88  		return fmt.Errorf("len(pix) %d and %d differ", len(pix0), len(pix1))
    89  	}
    90  	if stride0 != stride1 {
    91  		return fmt.Errorf("strides %d and %d differ", stride0, stride1)
    92  	}
    93  	if stride0%8 != 0 {
    94  		return fmt.Errorf("stride %d is not a multiple of 8", stride0)
    95  	}
    96  	// Compare the two pix data, one 8x8 block at a time.
    97  	for y := 0; y < len(pix0)/stride0; y += 8 {
    98  		for x := 0; x < stride0; x += 8 {
    99  			if x >= bounds.Max.X || y >= bounds.Max.Y {
   100  				// We don't care if the two pix data differ if the 8x8 block is
   101  				// entirely outside of the image's bounds. For example, this can
   102  				// occur with a 4:2:0 chroma subsampling and a 1x1 image. Baseline
   103  				// decoding works on the one 16x16 MCU as a whole; progressive
   104  				// decoding's first pass works on that 16x16 MCU as a whole but
   105  				// refinement passes only process one 8x8 block within the MCU.
   106  				continue
   107  			}
   108  
   109  			for j := 0; j < 8; j++ {
   110  				for i := 0; i < 8; i++ {
   111  					index := (y+j)*stride0 + (x + i)
   112  					if pix0[index] != pix1[index] {
   113  						return fmt.Errorf("blocks at (%d, %d) differ:\n%sand\n%s", x, y,
   114  							pixString(pix0, stride0, x, y),
   115  							pixString(pix1, stride1, x, y),
   116  						)
   117  					}
   118  				}
   119  			}
   120  		}
   121  	}
   122  	return nil
   123  }
   124  
   125  func pixString(pix []byte, stride, x, y int) string {
   126  	s := bytes.NewBuffer(nil)
   127  	for j := 0; j < 8; j++ {
   128  		fmt.Fprintf(s, "\t")
   129  		for i := 0; i < 8; i++ {
   130  			fmt.Fprintf(s, "%02x ", pix[(y+j)*stride+(x+i)])
   131  		}
   132  		fmt.Fprintf(s, "\n")
   133  	}
   134  	return s.String()
   135  }
   136  
   137  func TestExtraneousData(t *testing.T) {
   138  	// Encode a 1x1 red image.
   139  	src := image.NewRGBA(image.Rect(0, 0, 1, 1))
   140  	src.Set(0, 0, color.RGBA{0xff, 0x00, 0x00, 0xff})
   141  	buf := new(bytes.Buffer)
   142  	if err := Encode(buf, src, nil); err != nil {
   143  		t.Fatalf("encode: %v", err)
   144  	}
   145  	enc := buf.String()
   146  	// Sanity check that the encoded JPEG is long enough, that it ends in a
   147  	// "\xff\xd9" EOI marker, and that it contains a "\xff\xda" SOS marker
   148  	// somewhere in the final 64 bytes.
   149  	if len(enc) < 64 {
   150  		t.Fatalf("encoded JPEG is too short: %d bytes", len(enc))
   151  	}
   152  	if got, want := enc[len(enc)-2:], "\xff\xd9"; got != want {
   153  		t.Fatalf("encoded JPEG ends with %q, want %q", got, want)
   154  	}
   155  	if s := enc[len(enc)-64:]; !strings.Contains(s, "\xff\xda") {
   156  		t.Fatalf("encoded JPEG does not contain a SOS marker (ff da) near the end: % x", s)
   157  	}
   158  	// Test that adding some random junk between the SOS marker and the
   159  	// EOI marker does not affect the decoding.
   160  	rnd := rand.New(rand.NewSource(1))
   161  	for i, nerr := 0, 0; i < 1000 && nerr < 10; i++ {
   162  		buf.Reset()
   163  		// Write all but the trailing "\xff\xd9" EOI marker.
   164  		buf.WriteString(enc[:len(enc)-2])
   165  		// Write some random extraneous data.
   166  		for n := rnd.Intn(10); n > 0; n-- {
   167  			if x := byte(rnd.Intn(256)); x != 0xff {
   168  				buf.WriteByte(x)
   169  			} else {
   170  				// The JPEG format escapes a SOS 0xff data byte as "\xff\x00".
   171  				buf.WriteString("\xff\x00")
   172  			}
   173  		}
   174  		// Write the "\xff\xd9" EOI marker.
   175  		buf.WriteString("\xff\xd9")
   176  
   177  		// Check that we can still decode the resultant image.
   178  		got, err := Decode(buf)
   179  		if err != nil {
   180  			t.Errorf("could not decode image #%d: %v", i, err)
   181  			nerr++
   182  			continue
   183  		}
   184  		if got.Bounds() != src.Bounds() {
   185  			t.Errorf("image #%d, bounds differ: %v and %v", i, got.Bounds(), src.Bounds())
   186  			nerr++
   187  			continue
   188  		}
   189  		if averageDelta(got, src) > 2<<8 {
   190  			t.Errorf("image #%d changed too much after a round trip", i)
   191  			nerr++
   192  			continue
   193  		}
   194  	}
   195  }
   196  
   197  func benchmarkDecode(b *testing.B, filename string) {
   198  	b.StopTimer()
   199  	data, err := ioutil.ReadFile(filename)
   200  	if err != nil {
   201  		b.Fatal(err)
   202  	}
   203  	cfg, err := DecodeConfig(bytes.NewReader(data))
   204  	if err != nil {
   205  		b.Fatal(err)
   206  	}
   207  	b.SetBytes(int64(cfg.Width * cfg.Height * 4))
   208  	b.StartTimer()
   209  	for i := 0; i < b.N; i++ {
   210  		Decode(bytes.NewReader(data))
   211  	}
   212  }
   213  
   214  func BenchmarkDecodeBaseline(b *testing.B) {
   215  	benchmarkDecode(b, "../testdata/video-001.jpeg")
   216  }
   217  
   218  func BenchmarkDecodeProgressive(b *testing.B) {
   219  	benchmarkDecode(b, "../testdata/video-001.progressive.jpeg")
   220  }