2017-12-11 22:46:26 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2017 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"image"
|
|
|
|
"image/draw"
|
|
|
|
"image/png"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"go.skia.org/infra/golden/go/search"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
min_png = "min.png"
|
|
|
|
max_png = "max.png"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ExportTestRecordArray []search.ExportTestRecord
|
|
|
|
|
|
|
|
func (a ExportTestRecordArray) Len() int { return len(a) }
|
|
|
|
func (a ExportTestRecordArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
func (a ExportTestRecordArray) Less(i, j int) bool { return a[i].TestName < a[j].TestName }
|
|
|
|
|
|
|
|
func in(v string, a []string) bool {
|
|
|
|
for _, u := range a {
|
|
|
|
if u == v {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2018-01-24 16:22:57 +00:00
|
|
|
func clampU8(v int) uint8 {
|
|
|
|
if v < 0 {
|
|
|
|
return 0
|
|
|
|
} else if v > 255 {
|
|
|
|
return 255
|
|
|
|
}
|
|
|
|
return uint8(v)
|
|
|
|
}
|
|
|
|
|
2017-12-11 22:46:26 +00:00
|
|
|
func processTest(testName string, imgUrls []string, output string) error {
|
|
|
|
if strings.ContainsRune(testName, '/') {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
output_directory := path.Join(output, testName)
|
|
|
|
var img_max image.NRGBA
|
|
|
|
var img_min image.NRGBA
|
|
|
|
for _, url := range imgUrls {
|
|
|
|
resp, err := http.Get(url)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
img, err := png.Decode(resp.Body)
|
|
|
|
resp.Body.Close()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if img_max.Rect.Max.X == 0 {
|
|
|
|
// N.B. img_max.Pix may alias img.Pix (if they're already NRGBA).
|
|
|
|
img_max = toNrgba(img)
|
|
|
|
img_min = copyNrgba(img_max)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
w := img.Bounds().Max.X - img.Bounds().Min.X
|
|
|
|
h := img.Bounds().Max.Y - img.Bounds().Min.Y
|
|
|
|
if img_max.Rect.Max.X != w || img_max.Rect.Max.Y != h {
|
|
|
|
return errors.New("size mismatch")
|
|
|
|
}
|
|
|
|
img_nrgba := toNrgba(img)
|
|
|
|
for i, value := range img_nrgba.Pix {
|
|
|
|
if value > img_max.Pix[i] {
|
|
|
|
img_max.Pix[i] = value
|
|
|
|
} else if value < img_min.Pix[i] {
|
|
|
|
img_min.Pix[i] = value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if img_max.Rect.Max.X == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2018-01-24 16:22:57 +00:00
|
|
|
|
2017-12-11 22:46:26 +00:00
|
|
|
if err := os.Mkdir(output_directory, os.ModePerm); err != nil && !os.IsExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := writePngToFile(path.Join(output_directory, min_png), &img_min); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := writePngToFile(path.Join(output_directory, max_png), &img_max); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func readMetaJsonFile(filename string) ([]search.ExportTestRecord, error) {
|
|
|
|
file, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
dec := json.NewDecoder(file)
|
|
|
|
var records []search.ExportTestRecord
|
|
|
|
err = dec.Decode(&records)
|
|
|
|
return records, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func writePngToFile(path string, img image.Image) error {
|
|
|
|
file, err := os.Create(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
return png.Encode(file, img)
|
|
|
|
}
|
|
|
|
|
|
|
|
// to_nrgb() may return a shallow copy of img if it's already NRGBA.
|
|
|
|
func toNrgba(img image.Image) image.NRGBA {
|
|
|
|
switch v := img.(type) {
|
|
|
|
case *image.NRGBA:
|
|
|
|
return *v
|
|
|
|
}
|
|
|
|
nimg := *image.NewNRGBA(img.Bounds())
|
|
|
|
draw.Draw(&nimg, img.Bounds(), img, image.Point{0, 0}, draw.Src)
|
|
|
|
return nimg
|
|
|
|
}
|
|
|
|
|
|
|
|
func copyNrgba(src image.NRGBA) image.NRGBA {
|
|
|
|
dst := image.NRGBA{make([]uint8, len(src.Pix)), src.Stride, src.Rect}
|
|
|
|
copy(dst.Pix, src.Pix)
|
|
|
|
return dst
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
if len(os.Args) != 3 {
|
|
|
|
log.Printf("Usage:\n %s INPUT.json OUTPUT_DIRECTORY\n\n", os.Args[0])
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
input := os.Args[1]
|
|
|
|
output := os.Args[2]
|
2017-12-18 21:59:56 +00:00
|
|
|
// output is removed and replaced with a clean directory.
|
|
|
|
if err := os.RemoveAll(output); err != nil && !os.IsNotExist(err) {
|
2017-12-11 22:46:26 +00:00
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2017-12-18 21:59:56 +00:00
|
|
|
if err := os.MkdirAll(output, os.ModePerm); err != nil && !os.IsExist(err) {
|
2017-12-11 22:46:26 +00:00
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2017-12-18 21:59:56 +00:00
|
|
|
records, err := readMetaJsonFile(input)
|
2017-12-11 22:46:26 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2017-12-18 21:59:56 +00:00
|
|
|
sort.Sort(ExportTestRecordArray(records))
|
2017-12-11 22:46:26 +00:00
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
for _, record := range records {
|
|
|
|
var goodUrls []string
|
|
|
|
for _, digest := range record.Digests {
|
|
|
|
if (in("vk", digest.ParamSet["config"]) ||
|
|
|
|
in("gles", digest.ParamSet["config"])) &&
|
|
|
|
digest.Status == "positive" {
|
|
|
|
goodUrls = append(goodUrls, digest.URL)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wg.Add(1)
|
|
|
|
go func(testName string, imgUrls []string, output string) {
|
|
|
|
defer wg.Done()
|
|
|
|
if err := processTest(testName, imgUrls, output); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
fmt.Printf("\r%-60s", testName)
|
|
|
|
}(record.TestName, goodUrls, output)
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
fmt.Printf("\r%60s\n", "")
|
|
|
|
}
|