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 }