github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/image/webp/decode_test.go (about) 1 // Copyright 2014 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 webp 6 7 import ( 8 "bytes" 9 "fmt" 10 "image" 11 "image/png" 12 "io/ioutil" 13 "os" 14 "strings" 15 "testing" 16 17 "golang.org/x/image/webp/nycbcra" 18 ) 19 20 // hex is like fmt.Sprintf("% x", x) but also inserts dots every 16 bytes, to 21 // delineate VP8 macroblock boundaries. 22 func hex(x []byte) string { 23 buf := new(bytes.Buffer) 24 for len(x) > 0 { 25 n := len(x) 26 if n > 16 { 27 n = 16 28 } 29 fmt.Fprintf(buf, " . % x", x[:n]) 30 x = x[n:] 31 } 32 return buf.String() 33 } 34 35 func testDecodeLossy(t *testing.T, tc string, withAlpha bool) { 36 webpFilename := "../testdata/" + tc + ".lossy.webp" 37 pngFilename := webpFilename + ".ycbcr.png" 38 if withAlpha { 39 webpFilename = "../testdata/" + tc + ".lossy-with-alpha.webp" 40 pngFilename = webpFilename + ".nycbcra.png" 41 } 42 43 f0, err := os.Open(webpFilename) 44 if err != nil { 45 t.Errorf("%s: Open WEBP: %v", tc, err) 46 return 47 } 48 defer f0.Close() 49 img0, err := Decode(f0) 50 if err != nil { 51 t.Errorf("%s: Decode WEBP: %v", tc, err) 52 return 53 } 54 55 var ( 56 m0 *image.YCbCr 57 a0 *nycbcra.Image 58 ok bool 59 ) 60 if withAlpha { 61 a0, ok = img0.(*nycbcra.Image) 62 if ok { 63 m0 = &a0.YCbCr 64 } 65 } else { 66 m0, ok = img0.(*image.YCbCr) 67 } 68 if !ok || m0.SubsampleRatio != image.YCbCrSubsampleRatio420 { 69 t.Errorf("%s: decoded WEBP image is not a 4:2:0 YCbCr or 4:2:0 NYCbCrA", tc) 70 return 71 } 72 // w2 and h2 are the half-width and half-height, rounded up. 73 w, h := m0.Bounds().Dx(), m0.Bounds().Dy() 74 w2, h2 := int((w+1)/2), int((h+1)/2) 75 76 f1, err := os.Open(pngFilename) 77 if err != nil { 78 t.Errorf("%s: Open PNG: %v", tc, err) 79 return 80 } 81 defer f1.Close() 82 img1, err := png.Decode(f1) 83 if err != nil { 84 t.Errorf("%s: Open PNG: %v", tc, err) 85 return 86 } 87 88 // The split-into-YCbCr-planes golden image is a 2*w2 wide and h+h2 high 89 // (or 2*h+h2 high, if with Alpha) gray image arranged in IMC4 format: 90 // YYYY 91 // YYYY 92 // BBRR 93 // AAAA 94 // See http://www.fourcc.org/yuv.php#IMC4 95 pngW, pngH := 2*w2, h+h2 96 if withAlpha { 97 pngH += h 98 } 99 if got, want := img1.Bounds(), image.Rect(0, 0, pngW, pngH); got != want { 100 t.Errorf("%s: bounds0: got %v, want %v", tc, got, want) 101 return 102 } 103 m1, ok := img1.(*image.Gray) 104 if !ok { 105 t.Errorf("%s: decoded PNG image is not a Gray", tc) 106 return 107 } 108 109 type plane struct { 110 name string 111 m0Pix []uint8 112 m0Stride int 113 m1Rect image.Rectangle 114 } 115 planes := []plane{ 116 {"Y", m0.Y, m0.YStride, image.Rect(0, 0, w, h)}, 117 {"Cb", m0.Cb, m0.CStride, image.Rect(0*w2, h, 1*w2, h+h2)}, 118 {"Cr", m0.Cr, m0.CStride, image.Rect(1*w2, h, 2*w2, h+h2)}, 119 } 120 if withAlpha { 121 planes = append(planes, plane{ 122 "A", a0.A, a0.AStride, image.Rect(0, h+h2, w, 2*h+h2), 123 }) 124 } 125 126 for _, plane := range planes { 127 dx := plane.m1Rect.Dx() 128 nDiff, diff := 0, make([]byte, dx) 129 for j, y := 0, plane.m1Rect.Min.Y; y < plane.m1Rect.Max.Y; j, y = j+1, y+1 { 130 got := plane.m0Pix[j*plane.m0Stride:][:dx] 131 want := m1.Pix[y*m1.Stride+plane.m1Rect.Min.X:][:dx] 132 if bytes.Equal(got, want) { 133 continue 134 } 135 nDiff++ 136 if nDiff > 10 { 137 t.Errorf("%s: %s plane: more rows differ", tc, plane.name) 138 break 139 } 140 for i := range got { 141 diff[i] = got[i] - want[i] 142 } 143 t.Errorf("%s: %s plane: m0 row %d, m1 row %d\ngot %s\nwant%s\ndiff%s", 144 tc, plane.name, j, y, hex(got), hex(want), hex(diff)) 145 } 146 } 147 } 148 149 func TestDecodeVP8(t *testing.T) { 150 testCases := []string{ 151 "blue-purple-pink", 152 "blue-purple-pink-large.no-filter", 153 "blue-purple-pink-large.simple-filter", 154 "blue-purple-pink-large.normal-filter", 155 "video-001", 156 "yellow_rose", 157 } 158 159 for _, tc := range testCases { 160 testDecodeLossy(t, tc, false) 161 } 162 } 163 164 func TestDecodeVP8XAlpha(t *testing.T) { 165 testCases := []string{ 166 "yellow_rose", 167 } 168 169 for _, tc := range testCases { 170 testDecodeLossy(t, tc, true) 171 } 172 } 173 174 func TestDecodeVP8L(t *testing.T) { 175 testCases := []string{ 176 "blue-purple-pink", 177 "blue-purple-pink-large", 178 "gopher-doc.1bpp", 179 "gopher-doc.2bpp", 180 "gopher-doc.4bpp", 181 "gopher-doc.8bpp", 182 "tux", 183 "yellow_rose", 184 } 185 186 loop: 187 for _, tc := range testCases { 188 f0, err := os.Open("../testdata/" + tc + ".lossless.webp") 189 if err != nil { 190 t.Errorf("%s: Open WEBP: %v", tc, err) 191 continue 192 } 193 defer f0.Close() 194 img0, err := Decode(f0) 195 if err != nil { 196 t.Errorf("%s: Decode WEBP: %v", tc, err) 197 continue 198 } 199 m0, ok := img0.(*image.NRGBA) 200 if !ok { 201 t.Errorf("%s: WEBP image is %T, want *image.NRGBA", tc, img0) 202 continue 203 } 204 205 f1, err := os.Open("../testdata/" + tc + ".png") 206 if err != nil { 207 t.Errorf("%s: Open PNG: %v", tc, err) 208 continue 209 } 210 defer f1.Close() 211 img1, err := png.Decode(f1) 212 if err != nil { 213 t.Errorf("%s: Decode PNG: %v", tc, err) 214 continue 215 } 216 m1, ok := img1.(*image.NRGBA) 217 if !ok { 218 rgba1, ok := img1.(*image.RGBA) 219 if !ok { 220 t.Fatalf("%s: PNG image is %T, want *image.NRGBA", tc, img1) 221 continue 222 } 223 if !rgba1.Opaque() { 224 t.Fatalf("%s: PNG image is non-opaque *image.RGBA, want *image.NRGBA", tc) 225 continue 226 } 227 // The image is fully opaque, so we can re-interpret the RGBA pixels 228 // as NRGBA pixels. 229 m1 = &image.NRGBA{ 230 Pix: rgba1.Pix, 231 Stride: rgba1.Stride, 232 Rect: rgba1.Rect, 233 } 234 } 235 236 b0, b1 := m0.Bounds(), m1.Bounds() 237 if b0 != b1 { 238 t.Errorf("%s: bounds: got %v, want %v", tc, b0, b1) 239 continue 240 } 241 for i := range m0.Pix { 242 if m0.Pix[i] != m1.Pix[i] { 243 y := i / m0.Stride 244 x := (i - y*m0.Stride) / 4 245 i = 4 * (y*m0.Stride + x) 246 t.Errorf("%s: at (%d, %d):\ngot %02x %02x %02x %02x\nwant %02x %02x %02x %02x", 247 tc, x, y, 248 m0.Pix[i+0], m0.Pix[i+1], m0.Pix[i+2], m0.Pix[i+3], 249 m1.Pix[i+0], m1.Pix[i+1], m1.Pix[i+2], m1.Pix[i+3], 250 ) 251 continue loop 252 } 253 } 254 } 255 } 256 257 // TestDecodePartitionTooLarge tests that decoding a malformed WEBP image 258 // doesn't try to allocate an unreasonable amount of memory. This WEBP image 259 // claims a RIFF chunk length of 0x12345678 bytes (291 MiB) compressed, 260 // independent of the actual image size (0 pixels wide * 0 pixels high). 261 // 262 // This is based on golang.org/issue/10790. 263 func TestDecodePartitionTooLarge(t *testing.T) { 264 data := "RIFF\xff\xff\xff\x7fWEBPVP8 " + 265 "\x78\x56\x34\x12" + // RIFF chunk length. 266 "\xbd\x01\x00\x14\x00\x00\xb2\x34\x0a\x9d\x01\x2a\x96\x00\x67\x00" 267 _, err := Decode(strings.NewReader(data)) 268 if err == nil { 269 t.Fatal("got nil error, want non-nil") 270 } 271 if got, want := err.Error(), "too much data"; !strings.Contains(got, want) { 272 t.Fatalf("got error %q, want something containing %q", got, want) 273 } 274 } 275 276 func benchmarkDecode(b *testing.B, filename string) { 277 data, err := ioutil.ReadFile("../testdata/blue-purple-pink-large." + filename + ".webp") 278 if err != nil { 279 b.Fatal(err) 280 } 281 s := string(data) 282 cfg, err := DecodeConfig(strings.NewReader(s)) 283 if err != nil { 284 b.Fatal(err) 285 } 286 b.SetBytes(int64(cfg.Width * cfg.Height * 4)) 287 b.ResetTimer() 288 for i := 0; i < b.N; i++ { 289 Decode(strings.NewReader(s)) 290 } 291 } 292 293 func BenchmarkDecodeVP8NoFilter(b *testing.B) { benchmarkDecode(b, "no-filter.lossy") } 294 func BenchmarkDecodeVP8SimpleFilter(b *testing.B) { benchmarkDecode(b, "simple-filter.lossy") } 295 func BenchmarkDecodeVP8NormalFilter(b *testing.B) { benchmarkDecode(b, "normal-filter.lossy") } 296 func BenchmarkDecodeVP8L(b *testing.B) { benchmarkDecode(b, "lossless") }