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