// Copyright 2014, 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 common import ( "bufio" "fmt" "image/color" "log" "os" "sort" "strconv" "strings" ) // DefaultTransparentDim sets the default dimming // factor of transparent nodes to 2%. const DefaultTransparentDim = 2.0 / 100.0 type Colors struct { Colors []color.RGBA NameIndex map[string]int32 NumTransparent int32 TransparentDim float32 } type namedColor struct { name string color color.RGBA } type sortByAlpha []namedColor func (colors sortByAlpha) Less(i, j int) bool { return colors[i].color.A < colors[j].color.A } func (colors sortByAlpha) Len() int { return len(colors) } func (colors sortByAlpha) Swap(i, j int) { colors[i], colors[j] = colors[j], colors[i] } func ParseColors(filename string) (colors *Colors, err error) { var file *os.File if file, err = os.Open(filename); err != nil { return } defer file.Close() cols := make([]namedColor, 0, 2200) scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "#") { continue } c := color.RGBA{A: 0xff} var name string if n, _ := fmt.Sscanf( line, "%s %d %d %d %d", &name, &c.R, &c.G, &c.B, &c.A); n > 0 { cols = append(cols, namedColor{name: name, color: c}) } } err = scanner.Err() // Sort transparent colors to front. Makes it easier to figure out // if an index corresponds to a transparent color (i < Transparent). sort.Sort(sortByAlpha(cols)) cs := make([]color.RGBA, len(cols)) nameIndex := make(map[string]int32, len(cols)) numTransparent := int32(0) for i, nc := range cols { if nc.color.A < 0xff { numTransparent++ } cs[i] = nc.color nameIndex[nc.name] = int32(i) } colors = &Colors{ Colors: cs, NameIndex: nameIndex, NumTransparent: numTransparent, TransparentDim: DefaultTransparentDim} return } func (colors *Colors) IsTransparent(index int32) bool { return index < colors.NumTransparent } func BlendColor(c1, c2 color.RGBA, a float32) color.RGBA { b := float32(1) - a return color.RGBA{ R: uint8(float32(c1.R)*a + float32(c2.R)*b), G: uint8(float32(c1.G)*a + float32(c2.G)*b), B: uint8(float32(c1.B)*a + float32(c2.B)*b), A: 0xff} } func (colors *Colors) BlendColors(span *Span, col color.RGBA, pos int32) color.RGBA { curr := span // Ignore colors below pos. for ; curr != nil && pos >= curr.To; curr = curr.Next { } if curr == nil { return col } dim := colors.TransparentDim for ; curr != nil; curr = curr.Next { c := colors.Colors[curr.Value] // At least alpha channel attenuation + dim% extra for each depth meter. base := float32(c.A) / 255.0 factor := min32f(1.0, base+float32(curr.To-curr.From)*dim) col = BlendColor(c, col, factor) } return col } func ParseColor(col string) (color.RGBA, error) { col = strings.TrimLeft(col, "#") rgb, err := strconv.ParseUint(col, 16, 32) if err != nil { return color.RGBA{}, err } return color.RGBA{ R: uint8(rgb >> 16), G: uint8(rgb >> 8), B: uint8(rgb), A: 0xff}, nil } func ParseColorDefault(col string, def color.RGBA) color.RGBA { c, err := ParseColor(col) if err != nil { log.Printf("WARN: cannot parse color '%s': %s\n", col, err) return def } return c } func ColorToHex(col color.RGBA) string { return fmt.Sprintf("#%02x%02x%02x", col.R, col.G, col.B) }