Remplissage du dépôt
This commit is contained in:
87
cmd/mtautocolors/images.go
Normal file
87
cmd/mtautocolors/images.go
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright 2015 by Sascha L. Teichmann
|
||||
// Use of this source code is governed by the MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"os"
|
||||
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
)
|
||||
|
||||
func loadRGBA(filename string) (*image.RGBA, error) {
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var img image.Image
|
||||
if img, _, err = image.Decode(bufio.NewReader(file)); err != nil {
|
||||
return nil, fmt.Errorf("Decoding '%s' failed: %s", filename, err)
|
||||
}
|
||||
|
||||
if rgba, ok := img.(*image.RGBA); ok {
|
||||
return rgba, nil
|
||||
}
|
||||
bounds := img.Bounds()
|
||||
rgba := image.NewRGBA(bounds)
|
||||
draw.Draw(rgba, bounds, img, image.ZP, draw.Src)
|
||||
|
||||
return rgba, nil
|
||||
}
|
||||
|
||||
func averageColor(filename string) (color.Color, error) {
|
||||
|
||||
img, err := loadRGBA(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bounds := img.Bounds()
|
||||
if bounds.Empty() {
|
||||
return color.Black, nil
|
||||
}
|
||||
y := img.PixOffset(bounds.Min.X, bounds.Min.Y)
|
||||
yEnd := img.PixOffset(bounds.Min.X, bounds.Max.Y)
|
||||
w := bounds.Dx() * 4
|
||||
pix := img.Pix
|
||||
|
||||
var r, g, b, a uint64
|
||||
|
||||
for ; y < yEnd; y += img.Stride {
|
||||
for pos, end := y, y+w; pos < end; pos += 4 {
|
||||
pa := uint64(pix[pos+3])
|
||||
r += pa * uint64(pix[pos])
|
||||
g += pa * uint64(pix[pos+1])
|
||||
b += pa * uint64(pix[pos+2])
|
||||
a += pa
|
||||
}
|
||||
}
|
||||
|
||||
r /= 255
|
||||
g /= 255
|
||||
b /= 255
|
||||
|
||||
if s := a / 255; s > 0 {
|
||||
r /= s
|
||||
g /= s
|
||||
b /= s
|
||||
}
|
||||
|
||||
col := color.RGBA{
|
||||
R: uint8(r),
|
||||
G: uint8(g),
|
||||
B: uint8(b),
|
||||
A: uint8(a / uint64(bounds.Dx()*bounds.Dy()))}
|
||||
|
||||
return &col, nil
|
||||
}
|
237
cmd/mtautocolors/main.go
Normal file
237
cmd/mtautocolors/main.go
Normal file
@ -0,0 +1,237 @@
|
||||
// Copyright 2015 by Sascha L. Teichmann
|
||||
// Use of this source code is governed by the MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"image/color"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var nodesSplit = regexp.MustCompile(" +")
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr,
|
||||
"Usage: %s [<options>] <nodes.txt> [<data source directory> ...]\n",
|
||||
os.Args[0])
|
||||
fmt.Fprintln(os.Stderr, "Options:")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func main() {
|
||||
var predef string
|
||||
var transparent string
|
||||
var workers int
|
||||
|
||||
flag.Usage = usage
|
||||
flag.StringVar(&predef, "predefined", "", "predefined colors")
|
||||
flag.StringVar(&predef, "p", "", "predefined colors (shorthand)")
|
||||
flag.StringVar(&transparent, "transparent", "glasslike,liquid", "transparent nodes")
|
||||
flag.StringVar(&transparent, "t", "glasslike,liquid", "transparent nodes (shorthand)")
|
||||
flag.IntVar(&workers, "workers", 0, "number of image processing workers")
|
||||
flag.IntVar(&workers, "w", 0, "number of image processing workers (shorthand)")
|
||||
flag.Parse()
|
||||
|
||||
nargs := flag.NArg()
|
||||
if nargs < 1 {
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var err error
|
||||
var predefs PredefCols
|
||||
|
||||
if predef != "" {
|
||||
if predefs, err = LoadPredefCols(predef); err != nil {
|
||||
log.Fatalf("Cannot load predefined colors: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
var roots []string
|
||||
if nargs > 1 {
|
||||
roots = flag.Args()[1:]
|
||||
} else {
|
||||
roots = []string{"."}
|
||||
}
|
||||
|
||||
files, err := buildFileIndex(roots)
|
||||
if err != nil {
|
||||
log.Fatalf("error while building file index: %s\n", err)
|
||||
}
|
||||
|
||||
drawTypes := strings.Split(transparent, ",")
|
||||
|
||||
if err = process(flag.Arg(0), drawTypes, files, predefs, workers); err != nil {
|
||||
log.Fatalf("error while generating colors: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func contains(haystack []string, needle string) bool {
|
||||
for _, straw := range haystack {
|
||||
if strings.Contains(needle, straw) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type outLine struct {
|
||||
name string
|
||||
col color.Color
|
||||
alpha bool
|
||||
}
|
||||
|
||||
func asByte(x uint32) byte {
|
||||
return byte(x >> 8)
|
||||
}
|
||||
|
||||
func (ol *outLine) print(out io.Writer) {
|
||||
r, g, b, a := ol.col.RGBA()
|
||||
ba := asByte(a)
|
||||
if ol.alpha && ba < 255 {
|
||||
fmt.Fprintf(out, "%s %d %d %d %d\n",
|
||||
ol.name, asByte(r), asByte(g), asByte(b), ba)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s %d %d %d\n",
|
||||
ol.name, asByte(r), asByte(g), asByte(b))
|
||||
}
|
||||
}
|
||||
|
||||
func process(
|
||||
nodesFile string,
|
||||
drawTypes []string,
|
||||
files map[string]string,
|
||||
predefs PredefCols,
|
||||
workers int) error {
|
||||
|
||||
file, err := os.Open(nodesFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
lineCh := make(chan []string)
|
||||
|
||||
if workers < 1 {
|
||||
workers = runtime.NumCPU()
|
||||
}
|
||||
|
||||
var outLineMu sync.Mutex
|
||||
var outLines []outLine
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < workers; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
lines:
|
||||
for line := range lineCh {
|
||||
name, drawType, texture := line[0], line[1], line[2]
|
||||
|
||||
var c color.Color
|
||||
switch col := predefs.findCol(name); {
|
||||
case col != nil && col.Complete():
|
||||
c = col
|
||||
case col == nil || !col.Complete():
|
||||
tfile := files[texture]
|
||||
if tfile == "" {
|
||||
log.Printf("WARN: node '%s' missing texture '%s'\n", name, texture)
|
||||
continue lines
|
||||
}
|
||||
avg, err := averageColor(tfile)
|
||||
if err != nil {
|
||||
log.Printf("WARN: node '%s' defect image: %s\n", name, err)
|
||||
continue lines
|
||||
}
|
||||
if col != nil {
|
||||
c = col.Apply(avg)
|
||||
} else {
|
||||
c = avg
|
||||
}
|
||||
}
|
||||
alpha := contains(drawTypes, drawType)
|
||||
outLineMu.Lock()
|
||||
outLines = append(outLines, outLine{
|
||||
name: name,
|
||||
col: c,
|
||||
alpha: alpha,
|
||||
})
|
||||
outLineMu.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for lineNo := 1; scanner.Scan(); lineNo++ {
|
||||
parts := nodesSplit.Split(scanner.Text(), 3)
|
||||
if len(parts) < 3 {
|
||||
log.Printf("WARN: line %d too short.\n", lineNo)
|
||||
} else {
|
||||
lineCh <- parts
|
||||
}
|
||||
}
|
||||
close(lineCh)
|
||||
wg.Wait()
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// To make it more deterministic.
|
||||
sort.Slice(outLines, func(i, j int) bool {
|
||||
return outLines[i].name < outLines[j].name
|
||||
})
|
||||
|
||||
out := bufio.NewWriter(os.Stdout)
|
||||
for i := range outLines {
|
||||
outLines[i].print(out)
|
||||
}
|
||||
return out.Flush()
|
||||
}
|
||||
|
||||
func buildFileIndex(roots []string) (map[string]string, error) {
|
||||
index := make(map[string]string)
|
||||
|
||||
acceptedExts := map[string]bool{
|
||||
".png": true,
|
||||
".jpg": true,
|
||||
".jpeg": true}
|
||||
|
||||
walkFn := func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.Mode().IsRegular() {
|
||||
return nil
|
||||
}
|
||||
name := info.Name()
|
||||
if !acceptedExts[strings.ToLower(filepath.Ext(name))] {
|
||||
return nil
|
||||
}
|
||||
if _, found := index[name]; !found {
|
||||
index[name] = path
|
||||
} else {
|
||||
log.Printf("WARN: more than one file for '%s'\n", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, root := range roots {
|
||||
if err := filepath.Walk(root, walkFn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return index, nil
|
||||
}
|
116
cmd/mtautocolors/predefined.go
Normal file
116
cmd/mtautocolors/predefined.go
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright 2015 by Sascha L. Teichmann
|
||||
// Use of this source code is governed by the MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"image/color"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Component struct {
|
||||
Value uint8
|
||||
Used bool
|
||||
}
|
||||
|
||||
type Expr struct{ *regexp.Regexp }
|
||||
|
||||
type Col struct {
|
||||
E Expr `json:"expr"`
|
||||
R Component `json:"r"`
|
||||
G Component `json:"g"`
|
||||
B Component `json:"b"`
|
||||
A Component `json:"a"`
|
||||
}
|
||||
|
||||
type PredefCols []Col
|
||||
|
||||
func (e *Expr) UnmarshalJSON(data []byte) error {
|
||||
unquoted := string(data[1 : len(data)-1])
|
||||
expr, err := regexp.Compile(unquoted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = Expr{expr}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Component) UnmarshalJSON(data []byte) error {
|
||||
v, err := strconv.ParseUint(string(data), 10, 8)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Value = uint8(v)
|
||||
c.Used = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadPredefCols(filename string) (PredefCols, error) {
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
decoder := json.NewDecoder(bufio.NewReader(file))
|
||||
|
||||
var predef PredefCols
|
||||
if err = decoder.Decode(&predef); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return predef, nil
|
||||
}
|
||||
|
||||
func (pd *PredefCols) findCol(name string) *Col {
|
||||
for i, n := 0, len(*pd); i < n; i++ {
|
||||
if (*pd)[i].E.MatchString(name) {
|
||||
return &(*pd)[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Col) Complete() bool {
|
||||
return c.R.Used && c.G.Used && c.B.Used && c.A.Used
|
||||
}
|
||||
|
||||
func (c *Col) RGBA() (r, g, b, a uint32) {
|
||||
r = uint32(c.R.Value)
|
||||
r |= r << 8
|
||||
g = uint32(c.G.Value)
|
||||
g |= g << 8
|
||||
b = uint32(c.B.Value)
|
||||
b |= b << 8
|
||||
a = uint32(c.A.Value)
|
||||
a |= a << 8
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Col) Apply(other color.Color) color.Color {
|
||||
r, g, b, a := other.RGBA()
|
||||
x := color.RGBA{
|
||||
R: uint8(r >> 8),
|
||||
G: uint8(g >> 8),
|
||||
B: uint8(b >> 8),
|
||||
A: uint8(a >> 8)}
|
||||
if c.R.Used {
|
||||
x.R = c.R.Value
|
||||
}
|
||||
if c.G.Used {
|
||||
x.G = c.G.Value
|
||||
}
|
||||
if c.B.Used {
|
||||
x.B = c.B.Value
|
||||
}
|
||||
if c.A.Used {
|
||||
x.A = c.A.Value
|
||||
}
|
||||
return &x
|
||||
}
|
Reference in New Issue
Block a user