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