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