git.lucas.co / go_mono
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 }