git clone https://git.lucas.co/go_mono.git
cmd/webp-manual-test/main.go (5.2K)
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 //go:build ignore
6 // +build ignore
7
8 // This build tag means that "go install golang.org/x/image/..." doesn't
9 // install this manual test. Use "go run main.go" to explicitly run it.
10
11 // Program webp-manual-test checks that the Go WEBP library's decodings match
12 // the C WEBP library's.
13 package main // import "golang.org/x/image/cmd/webp-manual-test"
14
15 import (
16 "bytes"
17 "encoding/hex"
18 "flag"
19 "fmt"
20 "image"
21 "io"
22 "log"
23 "os"
24 "os/exec"
25 "path/filepath"
26 "sort"
27 "strings"
28
29 "golang.org/x/image/webp"
30 )
31
32 var (
33 dwebp = flag.String("dwebp", "/usr/bin/dwebp", "path to the dwebp program "+
34 "installed from https://developers.google.com/speed/webp/download")
35 testdata = flag.String("testdata", "", "path to the libwebp-test-data directory "+
36 "checked out from https://chromium.googlesource.com/webm/libwebp-test-data")
37 )
38
39 func main() {
40 flag.Parse()
41 if err := checkDwebp(); err != nil {
42 flag.Usage()
43 log.Fatal(err)
44 }
45 if *testdata == "" {
46 flag.Usage()
47 log.Fatal("testdata flag was not specified")
48 }
49
50 f, err := os.Open(*testdata)
51 if err != nil {
52 log.Fatalf("Open: %v", err)
53 }
54 defer f.Close()
55 names, err := f.Readdirnames(-1)
56 if err != nil {
57 log.Fatalf("Readdirnames: %v", err)
58 }
59 sort.Strings(names)
60
61 nFail, nPass := 0, 0
62 for _, name := range names {
63 if !strings.HasSuffix(name, "webp") {
64 continue
65 }
66 if err := test(name); err != nil {
67 fmt.Printf("FAIL\t%s\t%v\n", name, err)
68 nFail++
69 } else {
70 fmt.Printf("PASS\t%s\n", name)
71 nPass++
72 }
73 }
74 fmt.Printf("%d PASS, %d FAIL, %d TOTAL\n", nPass, nFail, nPass+nFail)
75 if nFail != 0 {
76 os.Exit(1)
77 }
78 }
79
80 func checkDwebp() error {
81 if *dwebp == "" {
82 return fmt.Errorf("dwebp flag was not specified")
83 }
84 if _, err := os.Stat(*dwebp); err != nil {
85 return fmt.Errorf("could not find dwebp program at %q", *dwebp)
86 }
87 b, err := exec.Command(*dwebp, "-version").Output()
88 if err != nil {
89 return fmt.Errorf("could not determine the dwebp program version for %q: %v", *dwebp, err)
90 }
91 switch s := string(bytes.TrimSpace(b)); s {
92 case "0.4.0", "0.4.1", "0.4.2":
93 return fmt.Errorf("the dwebp program version %q for %q has a known bug "+
94 "(https://bugs.chromium.org/p/webp/issues/detail?id=239). Please use a newer version.", s, *dwebp)
95 }
96 return nil
97 }
98
99 // test tests a single WEBP image.
100 func test(name string) error {
101 filename := filepath.Join(*testdata, name)
102 f, err := os.Open(filename)
103 if err != nil {
104 return fmt.Errorf("Open: %v", err)
105 }
106 defer f.Close()
107
108 gotImage, err := webp.Decode(f)
109 if err != nil {
110 return fmt.Errorf("Decode: %v", err)
111 }
112 format, encode := "-pgm", encodePGM
113 if _, lossless := gotImage.(*image.NRGBA); lossless {
114 format, encode = "-pam", encodePAM
115 }
116 got, err := encode(gotImage)
117 if err != nil {
118 return fmt.Errorf("encode: %v", err)
119 }
120
121 stdout := new(bytes.Buffer)
122 stderr := new(bytes.Buffer)
123 c := exec.Command(*dwebp, filename, format, "-o", "/dev/stdout")
124 c.Stdout = stdout
125 c.Stderr = stderr
126 if err := c.Run(); err != nil {
127 os.Stderr.Write(stderr.Bytes())
128 return fmt.Errorf("executing dwebp: %v", err)
129 }
130 want := stdout.Bytes()
131
132 if len(got) != len(want) {
133 return fmt.Errorf("encodings have different length: got %d, want %d", len(got), len(want))
134 }
135 for i, g := range got {
136 if w := want[i]; g != w {
137 return fmt.Errorf("encodings differ at position 0x%x: got 0x%02x, want 0x%02x", i, g, w)
138 }
139 }
140 return nil
141 }
142
143 // encodePAM encodes gotImage in the PAM format.
144 func encodePAM(gotImage image.Image) ([]byte, error) {
145 m, ok := gotImage.(*image.NRGBA)
146 if !ok {
147 return nil, fmt.Errorf("lossless image did not decode to an *image.NRGBA")
148 }
149 b := m.Bounds()
150 w, h := b.Dx(), b.Dy()
151 buf := new(bytes.Buffer)
152 fmt.Fprintf(buf, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n", w, h)
153 for y := b.Min.Y; y < b.Max.Y; y++ {
154 o := m.PixOffset(b.Min.X, y)
155 buf.Write(m.Pix[o : o+4*w])
156 }
157 return buf.Bytes(), nil
158 }
159
160 // encodePGM encodes gotImage in the PGM format in the IMC4 layout.
161 func encodePGM(gotImage image.Image) ([]byte, error) {
162 var (
163 m *image.YCbCr
164 ma *image.NYCbCrA
165 )
166 switch g := gotImage.(type) {
167 case *image.YCbCr:
168 m = g
169 case *image.NYCbCrA:
170 m = &g.YCbCr
171 ma = g
172 default:
173 return nil, fmt.Errorf("lossy image did not decode to an *image.YCbCr")
174 }
175 if m.SubsampleRatio != image.YCbCrSubsampleRatio420 {
176 return nil, fmt.Errorf("lossy image did not decode to a 4:2:0 YCbCr")
177 }
178 b := m.Bounds()
179 w, h := b.Dx(), b.Dy()
180 w2, h2 := (w+1)/2, (h+1)/2
181 outW, outH := 2*w2, h+h2
182 if ma != nil {
183 outH += h
184 }
185 buf := new(bytes.Buffer)
186 fmt.Fprintf(buf, "P5\n%d %d\n255\n", outW, outH)
187 for y := b.Min.Y; y < b.Max.Y; y++ {
188 o := m.YOffset(b.Min.X, y)
189 buf.Write(m.Y[o : o+w])
190 if w&1 != 0 {
191 buf.WriteByte(0x00)
192 }
193 }
194 for y := b.Min.Y; y < b.Max.Y; y += 2 {
195 o := m.COffset(b.Min.X, y)
196 buf.Write(m.Cb[o : o+w2])
197 buf.Write(m.Cr[o : o+w2])
198 }
199 if ma != nil {
200 for y := b.Min.Y; y < b.Max.Y; y++ {
201 o := ma.AOffset(b.Min.X, y)
202 buf.Write(ma.A[o : o+w])
203 if w&1 != 0 {
204 buf.WriteByte(0x00)
205 }
206 }
207 }
208 return buf.Bytes(), nil
209 }
210
211 // dump can be useful for debugging.
212 func dump(w io.Writer, b []byte) {
213 h := hex.Dumper(w)
214 h.Write(b)
215 h.Close()
216 }