Home | History | Annotate | Download | only in skqp
      1 /*
      2  * Copyright 2017 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 package main
      8 
      9 import (
     10 	"encoding/json"
     11 	"errors"
     12 	"fmt"
     13 	"image"
     14 	"image/draw"
     15 	"image/png"
     16 	"log"
     17 	"net/http"
     18 	"os"
     19 	"path"
     20 	"sort"
     21 	"strings"
     22 	"sync"
     23 
     24 	"go.skia.org/infra/golden/go/search"
     25 )
     26 
     27 const (
     28 	min_png = "min.png"
     29 	max_png = "max.png"
     30 )
     31 
     32 type ExportTestRecordArray []search.ExportTestRecord
     33 
     34 func (a ExportTestRecordArray) Len() int           { return len(a) }
     35 func (a ExportTestRecordArray) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
     36 func (a ExportTestRecordArray) Less(i, j int) bool { return a[i].TestName < a[j].TestName }
     37 
     38 func in(v string, a []string) bool {
     39 	for _, u := range a {
     40 		if u == v {
     41 			return true
     42 		}
     43 	}
     44 	return false
     45 }
     46 
     47 func clampU8(v int) uint8 {
     48 	if v < 0 {
     49 		return 0
     50 	} else if v > 255 {
     51 		return 255
     52 	}
     53 	return uint8(v)
     54 }
     55 
     56 func processTest(testName string, imgUrls []string, output string) (bool, error) {
     57 	if strings.ContainsRune(testName, '/') {
     58 		return false, nil
     59 	}
     60 	output_directory := path.Join(output, testName)
     61 	var img_max image.NRGBA
     62 	var img_min image.NRGBA
     63 	for _, url := range imgUrls {
     64 		resp, err := http.Get(url)
     65 		if err != nil {
     66 			return false, err
     67 		}
     68 		img, err := png.Decode(resp.Body)
     69 		resp.Body.Close()
     70 		if err != nil {
     71 			return false, err
     72 		}
     73 		if img_max.Rect.Max.X == 0 {
     74 			// N.B. img_max.Pix may alias img.Pix (if they're already NRGBA).
     75 			img_max = toNrgba(img)
     76 			img_min = copyNrgba(img_max)
     77 			continue
     78 		}
     79 		w := img.Bounds().Max.X - img.Bounds().Min.X
     80 		h := img.Bounds().Max.Y - img.Bounds().Min.Y
     81 		if img_max.Rect.Max.X != w || img_max.Rect.Max.Y != h {
     82 			return false, errors.New("size mismatch")
     83 		}
     84 		img_nrgba := toNrgba(img)
     85 		for i, value := range img_nrgba.Pix {
     86 			if value > img_max.Pix[i] {
     87 				img_max.Pix[i] = value
     88 			} else if value < img_min.Pix[i] {
     89 				img_min.Pix[i] = value
     90 			}
     91 		}
     92 	}
     93 	if img_max.Rect.Max.X == 0 {
     94 		return false, nil
     95 	}
     96 
     97 	if err := os.Mkdir(output_directory, os.ModePerm); err != nil && !os.IsExist(err) {
     98 		return false, err
     99 	}
    100 	if err := writePngToFile(path.Join(output_directory, min_png), &img_min); err != nil {
    101 		return false, err
    102 	}
    103 	if err := writePngToFile(path.Join(output_directory, max_png), &img_max); err != nil {
    104 		return false, err
    105 	}
    106 	return true, nil
    107 }
    108 
    109 type LockedStringList struct {
    110 	List []string
    111 	mux sync.Mutex
    112 }
    113 
    114 func (l *LockedStringList) add(v string) {
    115 	l.mux.Lock()
    116 	defer l.mux.Unlock()
    117 	l.List = append(l.List, v)
    118 }
    119 
    120 
    121 func readMetaJsonFile(filename string) ([]search.ExportTestRecord, error) {
    122 	file, err := os.Open(filename)
    123 	if err != nil {
    124 		return nil, err
    125 	}
    126 	dec := json.NewDecoder(file)
    127 	var records []search.ExportTestRecord
    128 	err = dec.Decode(&records)
    129 	return records, err
    130 }
    131 
    132 func writePngToFile(path string, img image.Image) error {
    133 	file, err := os.Create(path)
    134 	if err != nil {
    135 		return err
    136 	}
    137 	defer file.Close()
    138 	return png.Encode(file, img)
    139 }
    140 
    141 // to_nrgb() may return a shallow copy of img if it's already NRGBA.
    142 func toNrgba(img image.Image) image.NRGBA {
    143 	switch v := img.(type) {
    144 	case *image.NRGBA:
    145 		return *v
    146 	}
    147 	nimg := *image.NewNRGBA(img.Bounds())
    148 	draw.Draw(&nimg, img.Bounds(), img, image.Point{0, 0}, draw.Src)
    149 	return nimg
    150 }
    151 
    152 func copyNrgba(src image.NRGBA) image.NRGBA {
    153 	dst := image.NRGBA{make([]uint8, len(src.Pix)), src.Stride, src.Rect}
    154 	copy(dst.Pix, src.Pix)
    155 	return dst
    156 }
    157 
    158 func main() {
    159 	if len(os.Args) != 3 {
    160 		log.Printf("Usage:\n  %s INPUT.json OUTPUT_DIRECTORY\n\n", os.Args[0])
    161 		os.Exit(1)
    162 	}
    163 	input := os.Args[1]
    164 	output := os.Args[2]
    165 	// output is removed and replaced with a clean directory.
    166 	if err := os.RemoveAll(output); err != nil && !os.IsNotExist(err) {
    167 		log.Fatal(err)
    168 	}
    169 	if err := os.MkdirAll(output, os.ModePerm); err != nil && !os.IsExist(err) {
    170 		log.Fatal(err)
    171 	}
    172 
    173 	records, err := readMetaJsonFile(input)
    174 	if err != nil {
    175 		log.Fatal(err)
    176 	}
    177 	sort.Sort(ExportTestRecordArray(records))
    178 
    179 	var results LockedStringList
    180 	var wg sync.WaitGroup
    181 	for _, record := range records {
    182 		var goodUrls []string
    183 		for _, digest := range record.Digests {
    184 			if (in("vk", digest.ParamSet["config"]) ||
    185 				in("gles", digest.ParamSet["config"])) &&
    186 				digest.Status == "positive" {
    187 				goodUrls = append(goodUrls, digest.URL)
    188 			}
    189 		}
    190 		wg.Add(1)
    191 		go func(testName string, imgUrls []string, output string, results* LockedStringList) {
    192 			defer wg.Done()
    193 			success, err := processTest(testName, imgUrls, output)
    194 			if err != nil {
    195 				log.Fatal(err)
    196 			}
    197 			if success {
    198 				results.add(testName)
    199 			}
    200 			fmt.Printf("\r%-60s", testName)
    201 		}(record.TestName, goodUrls, output, &results)
    202 	}
    203 	wg.Wait()
    204 	fmt.Printf("\r%60s\n", "")
    205 	sort.Strings(results.List)
    206 	modelFile, err := os.Create(path.Join(output, "models.txt"))
    207 	if err != nil {
    208 		log.Fatal(err)
    209 	}
    210 	for _, v := range results.List {
    211 		fmt.Fprintln(modelFile, v)
    212 	}
    213 }
    214