aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/lucasb-eyer/go-colorful/colors.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/lucasb-eyer/go-colorful/colors.go')
-rw-r--r--vendor/github.com/lucasb-eyer/go-colorful/colors.go871
1 files changed, 445 insertions, 426 deletions
diff --git a/vendor/github.com/lucasb-eyer/go-colorful/colors.go b/vendor/github.com/lucasb-eyer/go-colorful/colors.go
index f2fcf8c..7469cf7 100644
--- a/vendor/github.com/lucasb-eyer/go-colorful/colors.go
+++ b/vendor/github.com/lucasb-eyer/go-colorful/colors.go
@@ -1,52 +1,52 @@
// The colorful package provides all kinds of functions for working with colors.
package colorful
-import(
- "fmt"
- "math"
- "image/color"
+import (
+ "fmt"
+ "image/color"
+ "math"
)
// A color is stored internally using sRGB (standard RGB) values in the range 0-1
type Color struct {
- R, G, B float64
+ R, G, B float64
}
// Implement the Go color.Color interface.
func (col Color) RGBA() (r, g, b, a uint32) {
- r = uint32(col.R*65535.0+0.5)
- g = uint32(col.G*65535.0+0.5)
- b = uint32(col.B*65535.0+0.5)
- a = 0xFFFF
- return
+ r = uint32(col.R*65535.0 + 0.5)
+ g = uint32(col.G*65535.0 + 0.5)
+ b = uint32(col.B*65535.0 + 0.5)
+ a = 0xFFFF
+ return
}
// Constructs a colorful.Color from something implementing color.Color
func MakeColor(col color.Color) Color {
- r, g, b, a := col.RGBA()
+ r, g, b, a := col.RGBA()
- // Since color.Color is alpha pre-multiplied, we need to divide the
- // RGB values by alpha again in order to get back the original RGB.
- r *= 0xffff
- r /= a
- g *= 0xffff
- g /= a
- b *= 0xffff
- b /= a
+ // Since color.Color is alpha pre-multiplied, we need to divide the
+ // RGB values by alpha again in order to get back the original RGB.
+ r *= 0xffff
+ r /= a
+ g *= 0xffff
+ g /= a
+ b *= 0xffff
+ b /= a
- return Color{float64(r)/65535.0, float64(g)/65535.0, float64(b)/65535.0}
+ return Color{float64(r) / 65535.0, float64(g) / 65535.0, float64(b) / 65535.0}
}
// Might come in handy sometimes to reduce boilerplate code.
func (col Color) RGB255() (r, g, b uint8) {
- r = uint8(col.R*255.0+0.5)
- g = uint8(col.G*255.0+0.5)
- b = uint8(col.B*255.0+0.5)
- return
+ r = uint8(col.R*255.0 + 0.5)
+ g = uint8(col.G*255.0 + 0.5)
+ b = uint8(col.B*255.0 + 0.5)
+ return
}
// This is the tolerance used when comparing colors using AlmostEqualRgb.
-const Delta = 1.0/255.0
+const Delta = 1.0 / 255.0
// This is the default reference white point.
var D65 = [3]float64{0.95047, 1.00000, 1.08883}
@@ -56,58 +56,57 @@ var D50 = [3]float64{0.96422, 1.00000, 0.82521}
// Checks whether the color exists in RGB space, i.e. all values are in [0..1]
func (c Color) IsValid() bool {
- return 0.0 <= c.R && c.R <= 1.0 &&
- 0.0 <= c.G && c.G <= 1.0 &&
- 0.0 <= c.B && c.B <= 1.0
+ return 0.0 <= c.R && c.R <= 1.0 &&
+ 0.0 <= c.G && c.G <= 1.0 &&
+ 0.0 <= c.B && c.B <= 1.0
}
func clamp01(v float64) float64 {
- return math.Max(0.0, math.Min(v, 1.0))
+ return math.Max(0.0, math.Min(v, 1.0))
}
// Returns Clamps the color into valid range, clamping each value to [0..1]
// If the color is valid already, this is a no-op.
func (c Color) Clamped() Color {
- return Color{clamp01(c.R), clamp01(c.G), clamp01(c.B)}
+ return Color{clamp01(c.R), clamp01(c.G), clamp01(c.B)}
}
func sq(v float64) float64 {
- return v * v;
+ return v * v
}
func cub(v float64) float64 {
- return v * v * v;
+ return v * v * v
}
// DistanceRgb computes the distance between two colors in RGB space.
// This is not a good measure! Rather do it in Lab space.
func (c1 Color) DistanceRgb(c2 Color) float64 {
- return math.Sqrt(sq(c1.R-c2.R) + sq(c1.G-c2.G) + sq(c1.B-c2.B))
+ return math.Sqrt(sq(c1.R-c2.R) + sq(c1.G-c2.G) + sq(c1.B-c2.B))
}
// Check for equality between colors within the tolerance Delta (1/255).
func (c1 Color) AlmostEqualRgb(c2 Color) bool {
- return math.Abs(c1.R - c2.R) +
- math.Abs(c1.G - c2.G) +
- math.Abs(c1.B - c2.B) < 3.0*Delta
+ return math.Abs(c1.R-c2.R)+
+ math.Abs(c1.G-c2.G)+
+ math.Abs(c1.B-c2.B) < 3.0*Delta
}
// You don't really want to use this, do you? Go for BlendLab, BlendLuv or BlendHcl.
func (c1 Color) BlendRgb(c2 Color, t float64) Color {
- return Color{c1.R + t*(c2.R - c1.R),
- c1.G + t*(c2.G - c1.G),
- c1.B + t*(c2.B - c1.B)}
+ return Color{c1.R + t*(c2.R-c1.R),
+ c1.G + t*(c2.G-c1.G),
+ c1.B + t*(c2.B-c1.B)}
}
// Utility used by Hxx color-spaces for interpolating between two angles in [0,360].
func interp_angle(a0, a1, t float64) float64 {
- // Based on the answer here: http://stackoverflow.com/a/14498790/2366315
- // With potential proof that it works here: http://math.stackexchange.com/a/2144499
- delta := math.Mod(math.Mod(a1 - a0, 360.0) + 540, 360.0) - 180.0
- return math.Mod(a0 + t*delta + 360.0, 360.0)
+ // Based on the answer here: http://stackoverflow.com/a/14498790/2366315
+ // With potential proof that it works here: http://math.stackexchange.com/a/2144499
+ delta := math.Mod(math.Mod(a1-a0, 360.0)+540, 360.0) - 180.0
+ return math.Mod(a0+t*delta+360.0, 360.0)
}
-
/// HSV ///
///////////
// From http://en.wikipedia.org/wiki/HSL_and_HSV
@@ -115,54 +114,74 @@ func interp_angle(a0, a1, t float64) float64 {
// Hsv returns the Hue [0..360], Saturation and Value [0..1] of the color.
func (col Color) Hsv() (h, s, v float64) {
- min := math.Min(math.Min(col.R, col.G), col.B)
- v = math.Max(math.Max(col.R, col.G), col.B)
- C := v - min
-
- s = 0.0
- if v != 0.0 {
- s = C / v
- }
-
- h = 0.0 // We use 0 instead of undefined as in wp.
- if min != v {
- if v == col.R { h = math.Mod((col.G - col.B) / C, 6.0) }
- if v == col.G { h = (col.B - col.R) / C + 2.0 }
- if v == col.B { h = (col.R - col.G) / C + 4.0 }
- h *= 60.0
- if h < 0.0 { h += 360.0 }
- }
- return
+ min := math.Min(math.Min(col.R, col.G), col.B)
+ v = math.Max(math.Max(col.R, col.G), col.B)
+ C := v - min
+
+ s = 0.0
+ if v != 0.0 {
+ s = C / v
+ }
+
+ h = 0.0 // We use 0 instead of undefined as in wp.
+ if min != v {
+ if v == col.R {
+ h = math.Mod((col.G-col.B)/C, 6.0)
+ }
+ if v == col.G {
+ h = (col.B-col.R)/C + 2.0
+ }
+ if v == col.B {
+ h = (col.R-col.G)/C + 4.0
+ }
+ h *= 60.0
+ if h < 0.0 {
+ h += 360.0
+ }
+ }
+ return
}
// Hsv creates a new Color given a Hue in [0..360], a Saturation and a Value in [0..1]
func Hsv(H, S, V float64) Color {
- Hp := H/60.0
- C := V*S
- X := C*(1.0-math.Abs(math.Mod(Hp, 2.0)-1.0))
-
- m := V-C;
- r, g, b := 0.0, 0.0, 0.0
-
- switch {
- case 0.0 <= Hp && Hp < 1.0: r = C; g = X
- case 1.0 <= Hp && Hp < 2.0: r = X; g = C
- case 2.0 <= Hp && Hp < 3.0: g = C; b = X
- case 3.0 <= Hp && Hp < 4.0: g = X; b = C
- case 4.0 <= Hp && Hp < 5.0: r = X; b = C
- case 5.0 <= Hp && Hp < 6.0: r = C; b = X
- }
-
- return Color{m+r, m+g, m+b}
+ Hp := H / 60.0
+ C := V * S
+ X := C * (1.0 - math.Abs(math.Mod(Hp, 2.0)-1.0))
+
+ m := V - C
+ r, g, b := 0.0, 0.0, 0.0
+
+ switch {
+ case 0.0 <= Hp && Hp < 1.0:
+ r = C
+ g = X
+ case 1.0 <= Hp && Hp < 2.0:
+ r = X
+ g = C
+ case 2.0 <= Hp && Hp < 3.0:
+ g = C
+ b = X
+ case 3.0 <= Hp && Hp < 4.0:
+ g = X
+ b = C
+ case 4.0 <= Hp && Hp < 5.0:
+ r = X
+ b = C
+ case 5.0 <= Hp && Hp < 6.0:
+ r = C
+ b = X
+ }
+
+ return Color{m + r, m + g, m + b}
}
// You don't really want to use this, do you? Go for BlendLab, BlendLuv or BlendHcl.
func (c1 Color) BlendHsv(c2 Color, t float64) Color {
- h1, s1, v1 := c1.Hsv()
- h2, s2, v2 := c2.Hsv()
+ h1, s1, v1 := c1.Hsv()
+ h2, s2, v2 := c2.Hsv()
- // We know that h are both in [0..360]
- return Hsv(interp_angle(h1, h2, t), s1 + t*(s2 - s1), v1 + t*(v2 - v1))
+ // We know that h are both in [0..360]
+ return Hsv(interp_angle(h1, h2, t), s1+t*(s2-s1), v1+t*(v2-v1))
}
/// HSL ///
@@ -170,117 +189,117 @@ func (c1 Color) BlendHsv(c2 Color, t float64) Color {
// Hsl returns the Hue [0..360], Saturation [0..1], and Luminance (lightness) [0..1] of the color.
func (col Color) Hsl() (h, s, l float64) {
- min := math.Min(math.Min(col.R, col.G), col.B)
- max := math.Max(math.Max(col.R, col.G), col.B)
+ min := math.Min(math.Min(col.R, col.G), col.B)
+ max := math.Max(math.Max(col.R, col.G), col.B)
- l = (max + min) / 2
+ l = (max + min) / 2
- if min == max {
- s = 0
- h = 0
- } else {
- if l < 0.5 {
- s = (max - min) / (max + min)
- } else {
- s = (max - min) / (2.0 - max - min)
- }
+ if min == max {
+ s = 0
+ h = 0
+ } else {
+ if l < 0.5 {
+ s = (max - min) / (max + min)
+ } else {
+ s = (max - min) / (2.0 - max - min)
+ }
- if max == col.R {
- h = (col.G - col.B) / (max - min)
- } else if max == col.G {
- h = 2.0 + (col.B-col.R)/(max-min)
- } else {
- h = 4.0 + (col.R-col.G)/(max-min)
- }
+ if max == col.R {
+ h = (col.G - col.B) / (max - min)
+ } else if max == col.G {
+ h = 2.0 + (col.B-col.R)/(max-min)
+ } else {
+ h = 4.0 + (col.R-col.G)/(max-min)
+ }
- h *= 60
+ h *= 60
- if h < 0 {
- h += 360
- }
- }
+ if h < 0 {
+ h += 360
+ }
+ }
- return
+ return
}
// Hsl creates a new Color given a Hue in [0..360], a Saturation [0..1], and a Luminance (lightness) in [0..1]
func Hsl(h, s, l float64) Color {
- if s == 0 {
- return Color{l, l, l}
- }
-
- var r, g, b float64
- var t1 float64
- var t2 float64
- var tr float64
- var tg float64
- var tb float64
-
- if l < 0.5 {
- t1 = l * (1.0 + s)
- } else {
- t1 = l + s - l*s
- }
-
- t2 = 2*l - t1
- h = h / 360
- tr = h + 1.0/3.0
- tg = h
- tb = h - 1.0/3.0
-
- if tr < 0 {
- tr += 1
- }
- if tr > 1 {
- tr -= 1
- }
- if tg < 0 {
- tg += 1
- }
- if tg > 1 {
- tg -= 1
- }
- if tb < 0 {
- tb += 1
- }
- if tb > 1 {
- tb -= 1
- }
-
- // Red
- if 6*tr < 1 {
- r = t2 + (t1-t2)*6*tr
- } else if 2*tr < 1 {
- r = t1
- } else if 3*tr < 2 {
- r = t2 + (t1-t2)*(2.0/3.0-tr)*6
- } else {
- r = t2
- }
-
- // Green
- if 6*tg < 1 {
- g = t2 + (t1-t2)*6*tg
- } else if 2*tg < 1 {
- g = t1
- } else if 3*tg < 2 {
- g = t2 + (t1-t2)*(2.0/3.0-tg)*6
- } else {
- g = t2
- }
-
- // Blue
- if 6*tb < 1 {
- b = t2 + (t1-t2)*6*tb
- } else if 2*tb < 1 {
- b = t1
- } else if 3*tb < 2 {
- b = t2 + (t1-t2)*(2.0/3.0-tb)*6
- } else {
- b = t2
- }
-
- return Color{r, g, b}
+ if s == 0 {
+ return Color{l, l, l}
+ }
+
+ var r, g, b float64
+ var t1 float64
+ var t2 float64
+ var tr float64
+ var tg float64
+ var tb float64
+
+ if l < 0.5 {
+ t1 = l * (1.0 + s)
+ } else {
+ t1 = l + s - l*s
+ }
+
+ t2 = 2*l - t1
+ h = h / 360
+ tr = h + 1.0/3.0
+ tg = h
+ tb = h - 1.0/3.0
+
+ if tr < 0 {
+ tr++
+ }
+ if tr > 1 {
+ tr--
+ }
+ if tg < 0 {
+ tg++
+ }
+ if tg > 1 {
+ tg--
+ }
+ if tb < 0 {
+ tb++
+ }
+ if tb > 1 {
+ tb--
+ }
+
+ // Red
+ if 6*tr < 1 {
+ r = t2 + (t1-t2)*6*tr
+ } else if 2*tr < 1 {
+ r = t1
+ } else if 3*tr < 2 {
+ r = t2 + (t1-t2)*(2.0/3.0-tr)*6
+ } else {
+ r = t2
+ }
+
+ // Green
+ if 6*tg < 1 {
+ g = t2 + (t1-t2)*6*tg
+ } else if 2*tg < 1 {
+ g = t1
+ } else if 3*tg < 2 {
+ g = t2 + (t1-t2)*(2.0/3.0-tg)*6
+ } else {
+ g = t2
+ }
+
+ // Blue
+ if 6*tb < 1 {
+ b = t2 + (t1-t2)*6*tb
+ } else if 2*tb < 1 {
+ b = t1
+ } else if 3*tb < 2 {
+ b = t2 + (t1-t2)*(2.0/3.0-tb)*6
+ } else {
+ b = t2
+ }
+
+ return Color{r, g, b}
}
/// Hex ///
@@ -288,29 +307,29 @@ func Hsl(h, s, l float64) Color {
// Hex returns the hex "html" representation of the color, as in #ff0080.
func (col Color) Hex() string {
- // Add 0.5 for rounding
- return fmt.Sprintf("#%02x%02x%02x", uint8(col.R*255.0+0.5), uint8(col.G*255.0+0.5), uint8(col.B*255.0+0.5))
+ // Add 0.5 for rounding
+ return fmt.Sprintf("#%02x%02x%02x", uint8(col.R*255.0+0.5), uint8(col.G*255.0+0.5), uint8(col.B*255.0+0.5))
}
// Hex parses a "html" hex color-string, either in the 3 "#f0c" or 6 "#ff1034" digits form.
func Hex(scol string) (Color, error) {
- format := "#%02x%02x%02x"
- factor := 1.0/255.0
- if len(scol) == 4 {
- format = "#%1x%1x%1x"
- factor = 1.0/15.0
- }
+ format := "#%02x%02x%02x"
+ factor := 1.0 / 255.0
+ if len(scol) == 4 {
+ format = "#%1x%1x%1x"
+ factor = 1.0 / 15.0
+ }
- var r, g, b uint8
- n, err := fmt.Sscanf(scol, format, &r, &g, &b)
- if err != nil {
- return Color{}, err
- }
- if n != 3 {
- return Color{}, fmt.Errorf("color: %v is not a hex-color", scol)
- }
+ var r, g, b uint8
+ n, err := fmt.Sscanf(scol, format, &r, &g, &b)
+ if err != nil {
+ return Color{}, err
+ }
+ if n != 3 {
+ return Color{}, fmt.Errorf("color: %v is not a hex-color", scol)
+ }
- return Color{float64(r)*factor, float64(g)*factor, float64(b)*factor}, nil
+ return Color{float64(r) * factor, float64(g) * factor, float64(b) * factor}, nil
}
/// Linear ///
@@ -319,98 +338,98 @@ func Hex(scol string) (Color, error) {
// http://www.brucelindbloom.com/Eqn_RGB_to_XYZ.html
func linearize(v float64) float64 {
- if v <= 0.04045 {
- return v / 12.92
- }
- return math.Pow((v + 0.055)/1.055, 2.4)
+ if v <= 0.04045 {
+ return v / 12.92
+ }
+ return math.Pow((v+0.055)/1.055, 2.4)
}
// LinearRgb converts the color into the linear RGB space (see http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/).
func (col Color) LinearRgb() (r, g, b float64) {
- r = linearize(col.R)
- g = linearize(col.G)
- b = linearize(col.B)
- return
+ r = linearize(col.R)
+ g = linearize(col.G)
+ b = linearize(col.B)
+ return
}
// A much faster and still quite precise linearization using a 6th-order Taylor approximation.
// See the accompanying Jupyter notebook for derivation of the constants.
func linearize_fast(v float64) float64 {
- v1 := v - 0.5
- v2 := v1*v1
- v3 := v2*v1
- v4 := v2*v2
- //v5 := v3*v2
- return -0.248750514614486 + 0.925583310193438*v + 1.16740237321695*v2 + 0.280457026598666*v3 - 0.0757991963780179*v4 //+ 0.0437040411548932*v5
+ v1 := v - 0.5
+ v2 := v1 * v1
+ v3 := v2 * v1
+ v4 := v2 * v2
+ //v5 := v3*v2
+ return -0.248750514614486 + 0.925583310193438*v + 1.16740237321695*v2 + 0.280457026598666*v3 - 0.0757991963780179*v4 //+ 0.0437040411548932*v5
}
// FastLinearRgb is much faster than and almost as accurate as LinearRgb.
// BUT it is important to NOTE that they only produce good results for valid colors r,g,b in [0,1].
func (col Color) FastLinearRgb() (r, g, b float64) {
- r = linearize_fast(col.R)
- g = linearize_fast(col.G)
- b = linearize_fast(col.B)
- return
+ r = linearize_fast(col.R)
+ g = linearize_fast(col.G)
+ b = linearize_fast(col.B)
+ return
}
func delinearize(v float64) float64 {
- if v <= 0.0031308 {
- return 12.92 * v
- }
- return 1.055 * math.Pow(v, 1.0/2.4) - 0.055
+ if v <= 0.0031308 {
+ return 12.92 * v
+ }
+ return 1.055*math.Pow(v, 1.0/2.4) - 0.055
}
// LinearRgb creates an sRGB color out of the given linear RGB color (see http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/).
func LinearRgb(r, g, b float64) Color {
- return Color{delinearize(r), delinearize(g), delinearize(b)}
+ return Color{delinearize(r), delinearize(g), delinearize(b)}
}
func delinearize_fast(v float64) float64 {
- // This function (fractional root) is much harder to linearize, so we need to split.
- if v > 0.2 {
- v1 := v - 0.6
- v2 := v1*v1
- v3 := v2*v1
- v4 := v2*v2
- v5 := v3*v2
- return 0.442430344268235 + 0.592178981271708*v - 0.287864782562636*v2 + 0.253214392068985*v3 - 0.272557158129811*v4 + 0.325554383321718*v5
- } else if v > 0.03 {
- v1 := v - 0.115
- v2 := v1*v1
- v3 := v2*v1
- v4 := v2*v2
- v5 := v3*v2
- return 0.194915592891669 + 1.55227076330229*v - 3.93691860257828*v2 + 18.0679839248761*v3 - 101.468750302746*v4 + 632.341487393927*v5
- } else {
- v1 := v - 0.015
- v2 := v1*v1
- v3 := v2*v1
- v4 := v2*v2
- v5 := v3*v2
- // You can clearly see from the involved constants that the low-end is highly nonlinear.
- return 0.0519565234928877 + 5.09316778537561*v - 99.0338180489702*v2 + 3484.52322764895*v3 - 150028.083412663*v4 + 7168008.42971613*v5
- }
+ // This function (fractional root) is much harder to linearize, so we need to split.
+ if v > 0.2 {
+ v1 := v - 0.6
+ v2 := v1 * v1
+ v3 := v2 * v1
+ v4 := v2 * v2
+ v5 := v3 * v2
+ return 0.442430344268235 + 0.592178981271708*v - 0.287864782562636*v2 + 0.253214392068985*v3 - 0.272557158129811*v4 + 0.325554383321718*v5
+ } else if v > 0.03 {
+ v1 := v - 0.115
+ v2 := v1 * v1
+ v3 := v2 * v1
+ v4 := v2 * v2
+ v5 := v3 * v2
+ return 0.194915592891669 + 1.55227076330229*v - 3.93691860257828*v2 + 18.0679839248761*v3 - 101.468750302746*v4 + 632.341487393927*v5
+ } else {
+ v1 := v - 0.015
+ v2 := v1 * v1
+ v3 := v2 * v1
+ v4 := v2 * v2
+ v5 := v3 * v2
+ // You can clearly see from the involved constants that the low-end is highly nonlinear.
+ return 0.0519565234928877 + 5.09316778537561*v - 99.0338180489702*v2 + 3484.52322764895*v3 - 150028.083412663*v4 + 7168008.42971613*v5
+ }
}
// FastLinearRgb is much faster than and almost as accurate as LinearRgb.
// BUT it is important to NOTE that they only produce good results for valid inputs r,g,b in [0,1].
func FastLinearRgb(r, g, b float64) Color {
- return Color{delinearize_fast(r), delinearize_fast(g), delinearize_fast(b)}
+ return Color{delinearize_fast(r), delinearize_fast(g), delinearize_fast(b)}
}
// XyzToLinearRgb converts from CIE XYZ-space to Linear RGB space.
func XyzToLinearRgb(x, y, z float64) (r, g, b float64) {
- r = 3.2404542*x - 1.5371385*y - 0.4985314*z
- g = -0.9692660*x + 1.8760108*y + 0.0415560*z
- b = 0.0556434*x - 0.2040259*y + 1.0572252*z
- return
+ r = 3.2404542*x - 1.5371385*y - 0.4985314*z
+ g = -0.9692660*x + 1.8760108*y + 0.0415560*z
+ b = 0.0556434*x - 0.2040259*y + 1.0572252*z
+ return
}
func LinearRgbToXyz(r, g, b float64) (x, y, z float64) {
- x = 0.4124564*r + 0.3575761*g + 0.1804375*b
- y = 0.2126729*r + 0.7151522*g + 0.0721750*b
- z = 0.0193339*r + 0.1191920*g + 0.9503041*b
- return
+ x = 0.4124564*r + 0.3575761*g + 0.1804375*b
+ y = 0.2126729*r + 0.7151522*g + 0.0721750*b
+ z = 0.0193339*r + 0.1191920*g + 0.9503041*b
+ return
}
/// XYZ ///
@@ -418,11 +437,11 @@ func LinearRgbToXyz(r, g, b float64) (x, y, z float64) {
// http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/
func (col Color) Xyz() (x, y, z float64) {
- return LinearRgbToXyz(col.LinearRgb())
+ return LinearRgbToXyz(col.LinearRgb())
}
func Xyz(x, y, z float64) Color {
- return LinearRgb(XyzToLinearRgb(x, y, z))
+ return LinearRgb(XyzToLinearRgb(x, y, z))
}
/// xyY ///
@@ -432,43 +451,43 @@ func Xyz(x, y, z float64) Color {
// Well, the name is bad, since it's xyY but Golang needs me to start with a
// capital letter to make the method public.
func XyzToXyy(X, Y, Z float64) (x, y, Yout float64) {
- return XyzToXyyWhiteRef(X, Y, Z, D65)
+ return XyzToXyyWhiteRef(X, Y, Z, D65)
}
func XyzToXyyWhiteRef(X, Y, Z float64, wref [3]float64) (x, y, Yout float64) {
- Yout = Y
- N := X + Y + Z
- if math.Abs(N) < 1e-14 {
- // When we have black, Bruce Lindbloom recommends to use
- // the reference white's chromacity for x and y.
- x = wref[0] / (wref[0] + wref[1] + wref[2])
- y = wref[1] / (wref[0] + wref[1] + wref[2])
- } else {
- x = X / N
- y = Y / N
- }
- return
+ Yout = Y
+ N := X + Y + Z
+ if math.Abs(N) < 1e-14 {
+ // When we have black, Bruce Lindbloom recommends to use
+ // the reference white's chromacity for x and y.
+ x = wref[0] / (wref[0] + wref[1] + wref[2])
+ y = wref[1] / (wref[0] + wref[1] + wref[2])
+ } else {
+ x = X / N
+ y = Y / N
+ }
+ return
}
func XyyToXyz(x, y, Y float64) (X, Yout, Z float64) {
- Yout = Y
+ Yout = Y
- if -1e-14 < y && y < 1e-14 {
- X = 0.0
- Z = 0.0
- } else {
- X = Y / y * x
- Z = Y / y * (1.0 - x - y)
- }
+ if -1e-14 < y && y < 1e-14 {
+ X = 0.0
+ Z = 0.0
+ } else {
+ X = Y / y * x
+ Z = Y / y * (1.0 - x - y)
+ }
- return
+ return
}
// Converts the given color to CIE xyY space using D65 as reference white.
// (Note that the reference white is only used for black input.)
// x, y and Y are in [0..1]
func (col Color) Xyy() (x, y, Y float64) {
- return XyzToXyy(col.Xyz())
+ return XyzToXyy(col.Xyz())
}
// Converts the given color to CIE xyY space, taking into account
@@ -476,14 +495,14 @@ func (col Color) Xyy() (x, y, Y float64) {
// (Note that the reference white is only used for black input.)
// x, y and Y are in [0..1]
func (col Color) XyyWhiteRef(wref [3]float64) (x, y, Y float64) {
- X, Y2, Z := col.Xyz()
- return XyzToXyyWhiteRef(X, Y2, Z, wref)
+ X, Y2, Z := col.Xyz()
+ return XyzToXyyWhiteRef(X, Y2, Z, wref)
}
// Generates a color by using data given in CIE xyY space.
// x, y and Y are in [0..1]
func Xyy(x, y, Y float64) Color {
- return Xyz(XyyToXyz(x, y, Y))
+ return Xyz(XyyToXyz(x, y, Y))
}
/// L*a*b* ///
@@ -492,131 +511,131 @@ func Xyy(x, y, Y float64) Color {
// For L*a*b*, we need to L*a*b*<->XYZ->RGB and the first one is device dependent.
func lab_f(t float64) float64 {
- if t > 6.0/29.0 * 6.0/29.0 * 6.0/29.0 {
- return math.Cbrt(t)
- }
- return t/3.0 * 29.0/6.0 * 29.0/6.0 + 4.0/29.0
+ if t > 6.0/29.0*6.0/29.0*6.0/29.0 {
+ return math.Cbrt(t)
+ }
+ return t/3.0*29.0/6.0*29.0/6.0 + 4.0/29.0
}
func XyzToLab(x, y, z float64) (l, a, b float64) {
- // Use D65 white as reference point by default.
- // http://www.fredmiranda.com/forum/topic/1035332
- // http://en.wikipedia.org/wiki/Standard_illuminant
- return XyzToLabWhiteRef(x, y, z, D65)
+ // Use D65 white as reference point by default.
+ // http://www.fredmiranda.com/forum/topic/1035332
+ // http://en.wikipedia.org/wiki/Standard_illuminant
+ return XyzToLabWhiteRef(x, y, z, D65)
}
func XyzToLabWhiteRef(x, y, z float64, wref [3]float64) (l, a, b float64) {
- fy := lab_f(y/wref[1])
- l = 1.16*fy - 0.16
- a = 5.0*(lab_f(x/wref[0]) - fy)
- b = 2.0*(fy - lab_f(z/wref[2]))
- return
+ fy := lab_f(y / wref[1])
+ l = 1.16*fy - 0.16
+ a = 5.0 * (lab_f(x/wref[0]) - fy)
+ b = 2.0 * (fy - lab_f(z/wref[2]))
+ return
}
func lab_finv(t float64) float64 {
- if t > 6.0/29.0 {
- return t * t * t
- }
- return 3.0 * 6.0/29.0 * 6.0/29.0 * (t - 4.0/29.0)
+ if t > 6.0/29.0 {
+ return t * t * t
+ }
+ return 3.0 * 6.0 / 29.0 * 6.0 / 29.0 * (t - 4.0/29.0)
}
func LabToXyz(l, a, b float64) (x, y, z float64) {
- // D65 white (see above).
- return LabToXyzWhiteRef(l, a, b, D65)
+ // D65 white (see above).
+ return LabToXyzWhiteRef(l, a, b, D65)
}
func LabToXyzWhiteRef(l, a, b float64, wref [3]float64) (x, y, z float64) {
- l2 := (l + 0.16) / 1.16
- x = wref[0] * lab_finv(l2 + a/5.0)
- y = wref[1] * lab_finv(l2)
- z = wref[2] * lab_finv(l2 - b/2.0)
- return
+ l2 := (l + 0.16) / 1.16
+ x = wref[0] * lab_finv(l2+a/5.0)
+ y = wref[1] * lab_finv(l2)
+ z = wref[2] * lab_finv(l2-b/2.0)
+ return
}
// Converts the given color to CIE L*a*b* space using D65 as reference white.
func (col Color) Lab() (l, a, b float64) {
- return XyzToLab(col.Xyz())
+ return XyzToLab(col.Xyz())
}
// Converts the given color to CIE L*a*b* space, taking into account
// a given reference white. (i.e. the monitor's white)
func (col Color) LabWhiteRef(wref [3]float64) (l, a, b float64) {
- x, y, z := col.Xyz()
- return XyzToLabWhiteRef(x, y, z, wref)
+ x, y, z := col.Xyz()
+ return XyzToLabWhiteRef(x, y, z, wref)
}
// Generates a color by using data given in CIE L*a*b* space using D65 as reference white.
// WARNING: many combinations of `l`, `a`, and `b` values do not have corresponding
// valid RGB values, check the FAQ in the README if you're unsure.
func Lab(l, a, b float64) Color {
- return Xyz(LabToXyz(l, a, b))
+ return Xyz(LabToXyz(l, a, b))
}
// Generates a color by using data given in CIE L*a*b* space, taking
// into account a given reference white. (i.e. the monitor's white)
func LabWhiteRef(l, a, b float64, wref [3]float64) Color {
- return Xyz(LabToXyzWhiteRef(l, a, b, wref))
+ return Xyz(LabToXyzWhiteRef(l, a, b, wref))
}
// DistanceLab is a good measure of visual similarity between two colors!
// A result of 0 would mean identical colors, while a result of 1 or higher
// means the colors differ a lot.
func (c1 Color) DistanceLab(c2 Color) float64 {
- l1, a1, b1 := c1.Lab()
- l2, a2, b2 := c2.Lab()
- return math.Sqrt(sq(l1-l2) + sq(a1-a2) + sq(b1-b2))
+ l1, a1, b1 := c1.Lab()
+ l2, a2, b2 := c2.Lab()
+ return math.Sqrt(sq(l1-l2) + sq(a1-a2) + sq(b1-b2))
}
// That's actually the same, but I don't want to break code.
func (c1 Color) DistanceCIE76(c2 Color) float64 {
- return c1.DistanceLab(c2)
+ return c1.DistanceLab(c2)
}
// Uses the CIE94 formula to calculate color distance. More accurate than
// DistanceLab, but also more work.
func (cl Color) DistanceCIE94(cr Color) float64 {
- l1, a1, b1 := cl.Lab()
- l2, a2, b2 := cr.Lab()
+ l1, a1, b1 := cl.Lab()
+ l2, a2, b2 := cr.Lab()
- // NOTE: Since all those formulas expect L,a,b values 100x larger than we
- // have them in this library, we either need to adjust all constants
- // in the formula, or convert the ranges of L,a,b before, and then
- // scale the distances down again. The latter is less error-prone.
- l1, a1, b1 = l1*100.0, a1*100.0, b1*100.0
- l2, a2, b2 = l2*100.0, a2*100.0, b2*100.0
+ // NOTE: Since all those formulas expect L,a,b values 100x larger than we
+ // have them in this library, we either need to adjust all constants
+ // in the formula, or convert the ranges of L,a,b before, and then
+ // scale the distances down again. The latter is less error-prone.
+ l1, a1, b1 = l1*100.0, a1*100.0, b1*100.0
+ l2, a2, b2 = l2*100.0, a2*100.0, b2*100.0
- kl := 1.0 // 2.0 for textiles
- kc := 1.0
- kh := 1.0
- k1 := 0.045 // 0.048 for textiles
- k2 := 0.015 // 0.014 for textiles.
+ kl := 1.0 // 2.0 for textiles
+ kc := 1.0
+ kh := 1.0
+ k1 := 0.045 // 0.048 for textiles
+ k2 := 0.015 // 0.014 for textiles.
- deltaL := l1 - l2
- c1 := math.Sqrt(sq(a1) + sq(b1))
- c2 := math.Sqrt(sq(a2) + sq(b2))
- deltaCab := c1 - c2
+ deltaL := l1 - l2
+ c1 := math.Sqrt(sq(a1) + sq(b1))
+ c2 := math.Sqrt(sq(a2) + sq(b2))
+ deltaCab := c1 - c2
- // Not taking Sqrt here for stability, and it's unnecessary.
- deltaHab2 := sq(a1-a2) + sq(b1-b2) - sq(deltaCab)
- sl := 1.0
- sc := 1.0 + k1*c1
- sh := 1.0 + k2*c1
+ // Not taking Sqrt here for stability, and it's unnecessary.
+ deltaHab2 := sq(a1-a2) + sq(b1-b2) - sq(deltaCab)
+ sl := 1.0
+ sc := 1.0 + k1*c1
+ sh := 1.0 + k2*c1
- vL2 := sq(deltaL/(kl*sl))
- vC2 := sq(deltaCab/(kc*sc))
- vH2 := deltaHab2/sq(kh*sh)
+ vL2 := sq(deltaL / (kl * sl))
+ vC2 := sq(deltaCab / (kc * sc))
+ vH2 := deltaHab2 / sq(kh*sh)
- return math.Sqrt(vL2 + vC2 + vH2)*0.01 // See above.
+ return math.Sqrt(vL2+vC2+vH2) * 0.01 // See above.
}
// BlendLab blends two colors in the L*a*b* color-space, which should result in a smoother blend.
// t == 0 results in c1, t == 1 results in c2
func (c1 Color) BlendLab(c2 Color, t float64) Color {
- l1, a1, b1 := c1.Lab()
- l2, a2, b2 := c2.Lab()
- return Lab(l1 + t*(l2 - l1),
- a1 + t*(a2 - a1),
- b1 + t*(b2 - b1))
+ l1, a1, b1 := c1.Lab()
+ l2, a2, b2 := c2.Lab()
+ return Lab(l1+t*(l2-l1),
+ a1+t*(a2-a1),
+ b1+t*(b2-b1))
}
/// L*u*v* ///
@@ -625,74 +644,74 @@ func (c1 Color) BlendLab(c2 Color, t float64) Color {
// For L*u*v*, we need to L*u*v*<->XYZ<->RGB and the first one is device dependent.
func XyzToLuv(x, y, z float64) (l, a, b float64) {
- // Use D65 white as reference point by default.
- // http://www.fredmiranda.com/forum/topic/1035332
- // http://en.wikipedia.org/wiki/Standard_illuminant
- return XyzToLuvWhiteRef(x, y, z, D65)
+ // Use D65 white as reference point by default.
+ // http://www.fredmiranda.com/forum/topic/1035332
+ // http://en.wikipedia.org/wiki/Standard_illuminant
+ return XyzToLuvWhiteRef(x, y, z, D65)
}
func XyzToLuvWhiteRef(x, y, z float64, wref [3]float64) (l, u, v float64) {
- if y/wref[1] <= 6.0/29.0 * 6.0/29.0 * 6.0/29.0 {
- l = y/wref[1] * 29.0/3.0 * 29.0/3.0 * 29.0/3.0
- } else {
- l = 1.16 * math.Cbrt(y/wref[1]) - 0.16
- }
- ubis, vbis := xyz_to_uv(x, y, z)
- un, vn := xyz_to_uv(wref[0], wref[1], wref[2])
- u = 13.0*l * (ubis - un)
- v = 13.0*l * (vbis - vn)
- return
+ if y/wref[1] <= 6.0/29.0*6.0/29.0*6.0/29.0 {
+ l = y / wref[1] * 29.0 / 3.0 * 29.0 / 3.0 * 29.0 / 3.0
+ } else {
+ l = 1.16*math.Cbrt(y/wref[1]) - 0.16
+ }
+ ubis, vbis := xyz_to_uv(x, y, z)
+ un, vn := xyz_to_uv(wref[0], wref[1], wref[2])
+ u = 13.0 * l * (ubis - un)
+ v = 13.0 * l * (vbis - vn)
+ return
}
// For this part, we do as R's graphics.hcl does, not as wikipedia does.
// Or is it the same?
func xyz_to_uv(x, y, z float64) (u, v float64) {
- denom := x + 15.0*y + 3.0*z
- if denom == 0.0 {
- u, v = 0.0, 0.0
- } else {
- u = 4.0*x/denom
- v = 9.0*y/denom
- }
- return
+ denom := x + 15.0*y + 3.0*z
+ if denom == 0.0 {
+ u, v = 0.0, 0.0
+ } else {
+ u = 4.0 * x / denom
+ v = 9.0 * y / denom
+ }
+ return
}
func LuvToXyz(l, u, v float64) (x, y, z float64) {
- // D65 white (see above).
- return LuvToXyzWhiteRef(l, u, v, D65)
+ // D65 white (see above).
+ return LuvToXyzWhiteRef(l, u, v, D65)
}
func LuvToXyzWhiteRef(l, u, v float64, wref [3]float64) (x, y, z float64) {
- //y = wref[1] * lab_finv((l + 0.16) / 1.16)
- if l <= 0.08 {
- y = wref[1] * l * 100.0 * 3.0/29.0 * 3.0/29.0 * 3.0/29.0
- } else {
- y = wref[1] * cub((l+0.16)/1.16)
- }
- un, vn := xyz_to_uv(wref[0], wref[1], wref[2])
- if l != 0.0 {
- ubis := u/(13.0*l) + un
- vbis := v/(13.0*l) + vn
- x = y*9.0*ubis/(4.0*vbis)
- z = y*(12.0-3.0*ubis-20.0*vbis)/(4.0*vbis)
- } else {
- x, y = 0.0, 0.0
- }
- return
+ //y = wref[1] * lab_finv((l + 0.16) / 1.16)
+ if l <= 0.08 {
+ y = wref[1] * l * 100.0 * 3.0 / 29.0 * 3.0 / 29.0 * 3.0 / 29.0
+ } else {
+ y = wref[1] * cub((l+0.16)/1.16)
+ }
+ un, vn := xyz_to_uv(wref[0], wref[1], wref[2])
+ if l != 0.0 {
+ ubis := u/(13.0*l) + un
+ vbis := v/(13.0*l) + vn
+ x = y * 9.0 * ubis / (4.0 * vbis)
+ z = y * (12.0 - 3.0*ubis - 20.0*vbis) / (4.0 * vbis)
+ } else {
+ x, y = 0.0, 0.0
+ }
+ return
}
// Converts the given color to CIE L*u*v* space using D65 as reference white.
// L* is in [0..1] and both u* and v* are in about [-1..1]
func (col Color) Luv() (l, u, v float64) {
- return XyzToLuv(col.Xyz())
+ return XyzToLuv(col.Xyz())
}
// Converts the given color to CIE L*u*v* space, taking into account
// a given reference white. (i.e. the monitor's white)
// L* is in [0..1] and both u* and v* are in about [-1..1]
func (col Color) LuvWhiteRef(wref [3]float64) (l, u, v float64) {
- x, y, z := col.Xyz()
- return XyzToLuvWhiteRef(x, y, z, wref)
+ x, y, z := col.Xyz()
+ return XyzToLuvWhiteRef(x, y, z, wref)
}
// Generates a color by using data given in CIE L*u*v* space using D65 as reference white.
@@ -700,33 +719,33 @@ func (col Color) LuvWhiteRef(wref [3]float64) (l, u, v float64) {
// WARNING: many combinations of `l`, `a`, and `b` values do not have corresponding
// valid RGB values, check the FAQ in the README if you're unsure.
func Luv(l, u, v float64) Color {
- return Xyz(LuvToXyz(l, u, v))
+ return Xyz(LuvToXyz(l, u, v))
}
// Generates a color by using data given in CIE L*u*v* space, taking
// into account a given reference white. (i.e. the monitor's white)
// L* is in [0..1] and both u* and v* are in about [-1..1]
func LuvWhiteRef(l, u, v float64, wref [3]float64) Color {
- return Xyz(LuvToXyzWhiteRef(l, u, v, wref))
+ return Xyz(LuvToXyzWhiteRef(l, u, v, wref))
}
// DistanceLuv is a good measure of visual similarity between two colors!
// A result of 0 would mean identical colors, while a result of 1 or higher
// means the colors differ a lot.
func (c1 Color) DistanceLuv(c2 Color) float64 {
- l1, u1, v1 := c1.Luv()
- l2, u2, v2 := c2.Luv()
- return math.Sqrt(sq(l1-l2) + sq(u1-u2) + sq(v1-v2))
+ l1, u1, v1 := c1.Luv()
+ l2, u2, v2 := c2.Luv()
+ return math.Sqrt(sq(l1-l2) + sq(u1-u2) + sq(v1-v2))
}
// BlendLuv blends two colors in the CIE-L*u*v* color-space, which should result in a smoother blend.
// t == 0 results in c1, t == 1 results in c2
func (c1 Color) BlendLuv(c2 Color, t float64) Color {
- l1, u1, v1 := c1.Luv()
- l2, u2, v2 := c2.Luv()
- return Luv(l1 + t*(l2 - l1),
- u1 + t*(u2 - u1),
- v1 + t*(v2 - v1))
+ l1, u1, v1 := c1.Luv()
+ l2, u2, v2 := c2.Luv()
+ return Luv(l1+t*(l2-l1),
+ u1+t*(u2-u1),
+ v1+t*(v2-v1))
}
/// HCL ///
@@ -739,27 +758,27 @@ func (c1 Color) BlendLuv(c2 Color, t float64) Color {
// Converts the given color to HCL space using D65 as reference white.
// H values are in [0..360], C and L values are in [0..1] although C can overshoot 1.0
func (col Color) Hcl() (h, c, l float64) {
- return col.HclWhiteRef(D65)
+ return col.HclWhiteRef(D65)
}
func LabToHcl(L, a, b float64) (h, c, l float64) {
- // Oops, floating point workaround necessary if a ~= b and both are very small (i.e. almost zero).
- if math.Abs(b - a) > 1e-4 && math.Abs(a) > 1e-4 {
- h = math.Mod(57.29577951308232087721*math.Atan2(b, a) + 360.0, 360.0) // Rad2Deg
- } else {
- h = 0.0
- }
- c = math.Sqrt(sq(a) + sq(b))
- l = L
- return
+ // Oops, floating point workaround necessary if a ~= b and both are very small (i.e. almost zero).
+ if math.Abs(b-a) > 1e-4 && math.Abs(a) > 1e-4 {
+ h = math.Mod(57.29577951308232087721*math.Atan2(b, a)+360.0, 360.0) // Rad2Deg
+ } else {
+ h = 0.0
+ }
+ c = math.Sqrt(sq(a) + sq(b))
+ l = L
+ return
}
// Converts the given color to HCL space, taking into account
// a given reference white. (i.e. the monitor's white)
// H values are in [0..360], C and L values are in [0..1]
func (col Color) HclWhiteRef(wref [3]float64) (h, c, l float64) {
- L, a, b := col.LabWhiteRef(wref)
- return LabToHcl(L, a, b)
+ L, a, b := col.LabWhiteRef(wref)
+ return LabToHcl(L, a, b)
}
// Generates a color by using data given in HCL space using D65 as reference white.
@@ -767,31 +786,31 @@ func (col Color) HclWhiteRef(wref [3]float64) (h, c, l float64) {
// WARNING: many combinations of `l`, `a`, and `b` values do not have corresponding
// valid RGB values, check the FAQ in the README if you're unsure.
func Hcl(h, c, l float64) Color {
- return HclWhiteRef(h, c, l, D65)
+ return HclWhiteRef(h, c, l, D65)
}
func HclToLab(h, c, l float64) (L, a, b float64) {
- H := 0.01745329251994329576*h // Deg2Rad
- a = c*math.Cos(H)
- b = c*math.Sin(H)
- L = l
- return
+ H := 0.01745329251994329576 * h // Deg2Rad
+ a = c * math.Cos(H)
+ b = c * math.Sin(H)
+ L = l
+ return
}
// Generates a color by using data given in HCL space, taking
// into account a given reference white. (i.e. the monitor's white)
// H values are in [0..360], C and L values are in [0..1]
func HclWhiteRef(h, c, l float64, wref [3]float64) Color {
- L, a, b := HclToLab(h, c, l)
- return LabWhiteRef(L, a, b, wref)
+ L, a, b := HclToLab(h, c, l)
+ return LabWhiteRef(L, a, b, wref)
}
// BlendHcl blends two colors in the CIE-L*C*h° color-space, which should result in a smoother blend.
// t == 0 results in c1, t == 1 results in c2
func (col1 Color) BlendHcl(col2 Color, t float64) Color {
- h1, c1, l1 := col1.Hcl()
- h2, c2, l2 := col2.Hcl()
+ h1, c1, l1 := col1.Hcl()
+ h2, c2, l2 := col2.Hcl()
- // We know that h are both in [0..360]
- return Hcl(interp_angle(h1, h2, t), c1 + t*(c2 - c1), l1 + t*(l2 - l1))
+ // We know that h are both in [0..360]
+ return Hcl(interp_angle(h1, h2, t), c1+t*(c2-c1), l1+t*(l2-l1))
}