Browse Source

Merge pull request #9 from koron/win-hsv-color-reducer

use HSV color space to reduce colors
mattn 10 years ago
parent
commit
51a7e7a8b1
1 changed files with 111 additions and 65 deletions
  1. 111 65
      colorable_windows.go

+ 111 - 65
colorable_windows.go

@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"fmt"
 	"io"
+	"math"
 	"os"
 	"strconv"
 	"strings"
@@ -402,7 +403,7 @@ loop:
 				continue
 			}
 			token := strings.Split(cs, ";")
-			for i := 0; i < len(token); i+=1 {
+			for i := 0; i < len(token); i += 1 {
 				ns := token[i]
 				if n, err = strconv.Atoi(ns); err == nil {
 					switch {
@@ -480,75 +481,13 @@ loop:
 }
 
 type consoleColor struct {
+	rgb       int
 	red       bool
 	green     bool
 	blue      bool
 	intensity bool
 }
 
-func minmax3(a, b, c int) (min, max int) {
-	if a < b {
-		if b < c {
-			return a, c
-		} else if a < c {
-			return a, b
-		} else {
-			return c, b
-		}
-	} else {
-		if a < c {
-			return b, c
-		} else if b < c {
-			return b, a
-		} else {
-			return c, a
-		}
-	}
-}
-
-func toConsoleColor(rgb int) (c consoleColor) {
-	r, g, b := (rgb&0xFF0000)>>16, (rgb&0x00FF00)>>8, rgb&0x0000FF
-	min, max := minmax3(r, g, b)
-	a := (min + max) / 2
-	if r < 128 && g < 128 && b < 128 {
-		if r >= a {
-			c.red = true
-		}
-		if g >= a {
-			c.green = true
-		}
-		if b >= a {
-			c.blue = true
-		}
-		// non-intensed white is lighter than intensed black, so swap those.
-		if c.red && c.green && c.blue {
-			c.red, c.green, c.blue = false, false, false
-			c.intensity = true
-		}
-	} else {
-		if min < 128 {
-			min = 128
-			a = (min + max) / 2
-		}
-		if r >= a {
-			c.red = true
-		}
-		if g >= a {
-			c.green = true
-		}
-		if b >= a {
-			c.blue = true
-		}
-		c.intensity = true
-		// intensed black is darker than non-intensed white, so swap those.
-		if !c.red && !c.green && !c.blue {
-			c.red, c.green, c.blue = true, true, true
-			c.intensity = false
-		}
-	}
-	return c
-}
-
 func (c consoleColor) foregroundAttr() (attr word) {
 	if c.red {
 		attr |= foregroundRed
@@ -581,14 +520,121 @@ func (c consoleColor) backgroundAttr() (attr word) {
 	return
 }
 
+var color16 = []consoleColor{
+	consoleColor{0x000000, false, false, false, false},
+	consoleColor{0x000080, false, false, true, false},
+	consoleColor{0x008000, false, true, false, false},
+	consoleColor{0x008080, false, true, true, false},
+	consoleColor{0x800000, true, false, false, false},
+	consoleColor{0x800080, true, false, true, false},
+	consoleColor{0x808000, true, true, false, false},
+	consoleColor{0xc0c0c0, true, true, true, false},
+	consoleColor{0x808080, false, false, false, true},
+	consoleColor{0x0000ff, false, false, true, true},
+	consoleColor{0x00ff00, false, true, false, true},
+	consoleColor{0x00ffff, false, true, true, true},
+	consoleColor{0xff0000, true, false, false, true},
+	consoleColor{0xff00ff, true, false, true, true},
+	consoleColor{0xffff00, true, true, false, true},
+	consoleColor{0xffffff, true, true, true, true},
+}
+
+type hsv struct {
+	h, s, v float32
+}
+
+func (a hsv) dist(b hsv) float32 {
+	dh := a.h - b.h
+	switch {
+	case dh > 0.5:
+		dh = 1 - dh
+	case dh < -0.5:
+		dh = -1 - dh
+	}
+	ds := a.s - b.s
+	dv := a.v - b.v
+	return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv)))
+}
+
+func toHSV(rgb int) hsv {
+	r, g, b := float32((rgb&0xFF0000)>>16)/256.0,
+		float32((rgb&0x00FF00)>>8)/256.0,
+		float32(rgb&0x0000FF)/256.0
+	min, max := minmax3f(r, g, b)
+	h := max - min
+	if h > 0 {
+		if max == r {
+			h = (g - b) / h
+			if h < 0 {
+				h += 6
+			}
+		} else if max == g {
+			h = 2 + (b-r)/h
+		} else {
+			h = 4 + (r-g)/h
+		}
+	}
+	h /= 6.0
+	s := max - min
+	if max != 0 {
+		s /= max
+	}
+	v := max
+	return hsv{h: h, s: s, v: v}
+}
+
+type hsvTable []hsv
+
+func toHSVTable(rgbTable []consoleColor) hsvTable {
+	t := make(hsvTable, len(rgbTable))
+	for i, c := range rgbTable {
+		t[i] = toHSV(c.rgb)
+	}
+	return t
+}
+
+func (t hsvTable) find(rgb int) consoleColor {
+	hsv := toHSV(rgb)
+	n := 7
+	l := float32(5.0)
+	for i, p := range t {
+		d := hsv.dist(p)
+		if d < l {
+			l, n = d, i
+		}
+	}
+	return color16[n]
+}
+
+func minmax3f(a, b, c float32) (min, max float32) {
+	if a < b {
+		if b < c {
+			return a, c
+		} else if a < c {
+			return a, b
+		} else {
+			return c, b
+		}
+	} else {
+		if a < c {
+			return b, c
+		} else if b < c {
+			return b, a
+		} else {
+			return c, a
+		}
+	}
+}
+
 var n256foreAttr []word
 var n256backAttr []word
 
 func n256setup() {
 	n256foreAttr = make([]word, 256)
 	n256backAttr = make([]word, 256)
+	t := toHSVTable(color16)
 	for i, rgb := range color256 {
-		c := toConsoleColor(rgb)
+		c := t.find(rgb)
 		n256foreAttr[i] = c.foregroundAttr()
 		n256backAttr[i] = c.backgroundAttr()
 	}