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