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 }