...

Source file src/github.com/lucasb-eyer/go-colorful/colors_test.go

Documentation: github.com/lucasb-eyer/go-colorful

     1  package colorful
     2  
     3  import (
     4  	"image/color"
     5  	"math"
     6  	"math/rand"
     7  	"strings"
     8  	"testing"
     9  )
    10  
    11  var bench_result float64 // Dummy for benchmarks to avoid optimization
    12  
    13  // Checks whether the relative error is below eps
    14  func almosteq_eps(v1, v2, eps float64) bool {
    15  	if math.Abs(v1) > delta {
    16  		return math.Abs((v1-v2)/v1) < eps
    17  	}
    18  	return true
    19  }
    20  
    21  // Checks whether the relative error is below the 8bit RGB delta, which should be good enough.
    22  const delta = 1.0 / 256.0
    23  
    24  func almosteq(v1, v2 float64) bool {
    25  	return almosteq_eps(v1, v2, delta)
    26  }
    27  
    28  // Note: the XYZ, L*a*b*, etc. are using D65 white and D50 white if postfixed by "50".
    29  // See http://www.brucelindbloom.com/index.html?ColorCalcHelp.html
    30  // For d50 white, no "adaptation" and the sRGB model are used in colorful
    31  // HCL values form http://www.easyrgb.com/index.php?X=CALC and missing ones hand-computed from lab ones
    32  var vals = []struct {
    33  	c      Color
    34  	hsl    [3]float64
    35  	hsv    [3]float64
    36  	hex    string
    37  	xyz    [3]float64
    38  	xyy    [3]float64
    39  	lab    [3]float64
    40  	lab50  [3]float64
    41  	luv    [3]float64
    42  	luv50  [3]float64
    43  	hcl    [3]float64
    44  	hcl50  [3]float64
    45  	rgba   [4]uint32
    46  	rgb255 [3]uint8
    47  }{
    48  	{Color{1.0, 1.0, 1.0}, [3]float64{0.0, 0.0, 1.00}, [3]float64{0.0, 0.0, 1.0}, "#ffffff", [3]float64{0.950470, 1.000000, 1.088830}, [3]float64{0.312727, 0.329023, 1.000000}, [3]float64{1.000000, 0.000000, 0.000000}, [3]float64{1.000000, -0.023881, -0.193622}, [3]float64{1.00000, 0.00000, 0.00000}, [3]float64{1.00000, -0.14716, -0.25658}, [3]float64{0.0000, 0.000000, 1.000000}, [3]float64{262.9688, 0.195089, 1.000000}, [4]uint32{65535, 65535, 65535, 65535}, [3]uint8{255, 255, 255}},
    49  	{Color{0.5, 1.0, 1.0}, [3]float64{180.0, 1.0, 0.75}, [3]float64{180.0, 0.5, 1.0}, "#80ffff", [3]float64{0.626296, 0.832848, 1.073634}, [3]float64{0.247276, 0.328828, 0.832848}, [3]float64{0.931390, -0.353319, -0.108946}, [3]float64{0.931390, -0.374100, -0.301663}, [3]float64{0.93139, -0.53909, -0.11630}, [3]float64{0.93139, -0.67615, -0.35528}, [3]float64{197.1371, 0.369735, 0.931390}, [3]float64{218.8817, 0.480574, 0.931390}, [4]uint32{32768, 65535, 65535, 65535}, [3]uint8{128, 255, 255}},
    50  	{Color{1.0, 0.5, 1.0}, [3]float64{300.0, 1.0, 0.75}, [3]float64{300.0, 0.5, 1.0}, "#ff80ff", [3]float64{0.669430, 0.437920, 0.995150}, [3]float64{0.318397, 0.208285, 0.437920}, [3]float64{0.720892, 0.651673, -0.422133}, [3]float64{0.720892, 0.630425, -0.610035}, [3]float64{0.72089, 0.60047, -0.77626}, [3]float64{0.72089, 0.49438, -0.96123}, [3]float64{327.0661, 0.776450, 0.720892}, [3]float64{315.9417, 0.877257, 0.720892}, [4]uint32{65535, 32768, 65535, 65535}, [3]uint8{255, 128, 255}},
    51  	{Color{1.0, 1.0, 0.5}, [3]float64{60.0, 1.0, 0.75}, [3]float64{60.0, 0.5, 1.0}, "#ffff80", [3]float64{0.808654, 0.943273, 0.341930}, [3]float64{0.386203, 0.450496, 0.943273}, [3]float64{0.977637, -0.165795, 0.602017}, [3]float64{0.977637, -0.188424, 0.470410}, [3]float64{0.97764, 0.05759, 0.79816}, [3]float64{0.97764, -0.08628, 0.54731}, [3]float64{105.3975, 0.624430, 0.977637}, [3]float64{111.8287, 0.506743, 0.977637}, [4]uint32{65535, 65535, 32768, 65535}, [3]uint8{255, 255, 128}},
    52  	{Color{0.5, 0.5, 1.0}, [3]float64{240.0, 1.0, 0.75}, [3]float64{240.0, 0.5, 1.0}, "#8080ff", [3]float64{0.345256, 0.270768, 0.979954}, [3]float64{0.216329, 0.169656, 0.270768}, [3]float64{0.590453, 0.332846, -0.637099}, [3]float64{0.590453, 0.315806, -0.824040}, [3]float64{0.59045, -0.07568, -1.04877}, [3]float64{0.59045, -0.16257, -1.20027}, [3]float64{297.5843, 0.718805, 0.590453}, [3]float64{290.9689, 0.882482, 0.590453}, [4]uint32{32768, 32768, 65535, 65535}, [3]uint8{128, 128, 255}},
    53  	{Color{1.0, 0.5, 0.5}, [3]float64{0.0, 1.0, 0.75}, [3]float64{0.0, 0.5, 1.0}, "#ff8080", [3]float64{0.527613, 0.381193, 0.248250}, [3]float64{0.455996, 0.329451, 0.381193}, [3]float64{0.681085, 0.483884, 0.228328}, [3]float64{0.681085, 0.464258, 0.110043}, [3]float64{0.68108, 0.92148, 0.19879}, [3]float64{0.68106, 0.82106, 0.02393}, [3]float64{25.2610, 0.535049, 0.681085}, [3]float64{13.3347, 0.477121, 0.681085}, [4]uint32{65535, 32768, 32768, 65535}, [3]uint8{255, 128, 128}},
    54  	{Color{0.5, 1.0, 0.5}, [3]float64{120.0, 1.0, 0.75}, [3]float64{120.0, 0.5, 1.0}, "#80ff80", [3]float64{0.484480, 0.776121, 0.326734}, [3]float64{0.305216, 0.488946, 0.776121}, [3]float64{0.906026, -0.600870, 0.498993}, [3]float64{0.906026, -0.619946, 0.369365}, [3]float64{0.90603, -0.58869, 0.76102}, [3]float64{0.90603, -0.72202, 0.52855}, [3]float64{140.2920, 0.781050, 0.906026}, [3]float64{149.2134, 0.721640, 0.906026}, [4]uint32{32768, 65535, 32768, 65535}, [3]uint8{128, 255, 128}},
    55  	{Color{0.5, 0.5, 0.5}, [3]float64{0.0, 0.0, 0.50}, [3]float64{0.0, 0.0, 0.5}, "#808080", [3]float64{0.203440, 0.214041, 0.233054}, [3]float64{0.312727, 0.329023, 0.214041}, [3]float64{0.533890, 0.000000, 0.000000}, [3]float64{0.533890, -0.014285, -0.115821}, [3]float64{0.53389, 0.00000, 0.00000}, [3]float64{0.53389, -0.07857, -0.13699}, [3]float64{0.0000, 0.000000, 0.533890}, [3]float64{262.9688, 0.116699, 0.533890}, [4]uint32{32768, 32768, 32768, 65535}, [3]uint8{128, 128, 128}},
    56  	{Color{0.0, 1.0, 1.0}, [3]float64{180.0, 1.0, 0.50}, [3]float64{180.0, 1.0, 1.0}, "#00ffff", [3]float64{0.538014, 0.787327, 1.069496}, [3]float64{0.224656, 0.328760, 0.787327}, [3]float64{0.911132, -0.480875, -0.141312}, [3]float64{0.911132, -0.500630, -0.333781}, [3]float64{0.91113, -0.70477, -0.15204}, [3]float64{0.91113, -0.83886, -0.38582}, [3]float64{196.3762, 0.501209, 0.911132}, [3]float64{213.6923, 0.601698, 0.911132}, [4]uint32{0, 65535, 65535, 65535}, [3]uint8{0, 255, 255}},
    57  	{Color{1.0, 0.0, 1.0}, [3]float64{300.0, 1.0, 0.50}, [3]float64{300.0, 1.0, 1.0}, "#ff00ff", [3]float64{0.592894, 0.284848, 0.969638}, [3]float64{0.320938, 0.154190, 0.284848}, [3]float64{0.603242, 0.982343, -0.608249}, [3]float64{0.603242, 0.961939, -0.794531}, [3]float64{0.60324, 0.84071, -1.08683}, [3]float64{0.60324, 0.75194, -1.24161}, [3]float64{328.2350, 1.155407, 0.603242}, [3]float64{320.4444, 1.247640, 0.603242}, [4]uint32{65535, 0, 65535, 65535}, [3]uint8{255, 0, 255}},
    58  	{Color{1.0, 1.0, 0.0}, [3]float64{60.0, 1.0, 0.50}, [3]float64{60.0, 1.0, 1.0}, "#ffff00", [3]float64{0.770033, 0.927825, 0.138526}, [3]float64{0.419320, 0.505246, 0.927825}, [3]float64{0.971393, -0.215537, 0.944780}, [3]float64{0.971393, -0.237800, 0.847398}, [3]float64{0.97139, 0.07706, 1.06787}, [3]float64{0.97139, -0.06590, 0.81862}, [3]float64{102.8512, 0.969054, 0.971393}, [3]float64{105.6754, 0.880131, 0.971393}, [4]uint32{65535, 65535, 0, 65535}, [3]uint8{255, 255, 0}},
    59  	{Color{0.0, 0.0, 1.0}, [3]float64{240.0, 1.0, 0.50}, [3]float64{240.0, 1.0, 1.0}, "#0000ff", [3]float64{0.180437, 0.072175, 0.950304}, [3]float64{0.150000, 0.060000, 0.072175}, [3]float64{0.322970, 0.791875, -1.078602}, [3]float64{0.322970, 0.778150, -1.263638}, [3]float64{0.32297, -0.09405, -1.30342}, [3]float64{0.32297, -0.14158, -1.38629}, [3]float64{306.2849, 1.338076, 0.322970}, [3]float64{301.6248, 1.484014, 0.322970}, [4]uint32{0, 0, 65535, 65535}, [3]uint8{0, 0, 255}},
    60  	{Color{0.0, 1.0, 0.0}, [3]float64{120.0, 1.0, 0.50}, [3]float64{120.0, 1.0, 1.0}, "#00ff00", [3]float64{0.357576, 0.715152, 0.119192}, [3]float64{0.300000, 0.600000, 0.715152}, [3]float64{0.877347, -0.861827, 0.831793}, [3]float64{0.877347, -0.879067, 0.739170}, [3]float64{0.87735, -0.83078, 1.07398}, [3]float64{0.87735, -0.95989, 0.84887}, [3]float64{136.0160, 1.197759, 0.877347}, [3]float64{139.9409, 1.148534, 0.877347}, [4]uint32{0, 65535, 0, 65535}, [3]uint8{0, 255, 0}},
    61  	{Color{1.0, 0.0, 0.0}, [3]float64{0.0, 1.0, 0.50}, [3]float64{0.0, 1.0, 1.0}, "#ff0000", [3]float64{0.412456, 0.212673, 0.019334}, [3]float64{0.640000, 0.330000, 0.212673}, [3]float64{0.532408, 0.800925, 0.672032}, [3]float64{0.532408, 0.782845, 0.621518}, [3]float64{0.53241, 1.75015, 0.37756}, [3]float64{0.53241, 1.67180, 0.24096}, [3]float64{39.9990, 1.045518, 0.532408}, [3]float64{38.4469, 0.999566, 0.532408}, [4]uint32{65535, 0, 0, 65535}, [3]uint8{255, 0, 0}},
    62  	{Color{0.0, 0.0, 0.0}, [3]float64{0.0, 0.0, 0.00}, [3]float64{0.0, 0.0, 0.0}, "#000000", [3]float64{0.000000, 0.000000, 0.000000}, [3]float64{0.312727, 0.329023, 0.000000}, [3]float64{0.000000, 0.000000, 0.000000}, [3]float64{0.000000, 0.000000, 0.000000}, [3]float64{0.00000, 0.00000, 0.00000}, [3]float64{0.00000, 0.00000, 0.00000}, [3]float64{0.0000, 0.000000, 0.000000}, [3]float64{0.0000, 0.000000, 0.000000}, [4]uint32{0, 0, 0, 65535}, [3]uint8{0, 0, 0}},
    63  }
    64  
    65  // For testing short-hex values, since the above contains colors which don't
    66  // have corresponding short hexes.
    67  var shorthexvals = []struct {
    68  	c   Color
    69  	hex string
    70  }{
    71  	{Color{1.0, 1.0, 1.0}, "#fff"},
    72  	{Color{0.6, 1.0, 1.0}, "#9ff"},
    73  	{Color{1.0, 0.6, 1.0}, "#f9f"},
    74  	{Color{1.0, 1.0, 0.6}, "#ff9"},
    75  	{Color{0.6, 0.6, 1.0}, "#99f"},
    76  	{Color{1.0, 0.6, 0.6}, "#f99"},
    77  	{Color{0.6, 1.0, 0.6}, "#9f9"},
    78  	{Color{0.6, 0.6, 0.6}, "#999"},
    79  	{Color{0.0, 1.0, 1.0}, "#0ff"},
    80  	{Color{1.0, 0.0, 1.0}, "#f0f"},
    81  	{Color{1.0, 1.0, 0.0}, "#ff0"},
    82  	{Color{0.0, 0.0, 1.0}, "#00f"},
    83  	{Color{0.0, 1.0, 0.0}, "#0f0"},
    84  	{Color{1.0, 0.0, 0.0}, "#f00"},
    85  	{Color{0.0, 0.0, 0.0}, "#000"},
    86  }
    87  
    88  /// RGBA ///
    89  ////////////
    90  
    91  func TestRGBAConversion(t *testing.T) {
    92  	for i, tt := range vals {
    93  		r, g, b, a := tt.c.RGBA()
    94  		if r != tt.rgba[0] || g != tt.rgba[1] || b != tt.rgba[2] || a != tt.rgba[3] {
    95  			t.Errorf("%v. %v.RGBA() => (%v), want %v (delta %v)", i, tt.c, []uint32{r, g, b, a}, tt.rgba, delta)
    96  		}
    97  	}
    98  }
    99  
   100  /// RGB255 ///
   101  ////////////
   102  
   103  func TestRGB255Conversion(t *testing.T) {
   104  	for i, tt := range vals {
   105  		r, g, b := tt.c.RGB255()
   106  		if r != tt.rgb255[0] || g != tt.rgb255[1] || b != tt.rgb255[2] {
   107  			t.Errorf("%v. %v.RGB255() => (%v), want %v (delta %v)", i, tt.c, []uint8{r, g, b}, tt.rgb255, delta)
   108  		}
   109  	}
   110  }
   111  
   112  /// HSV ///
   113  ///////////
   114  
   115  func TestHsvCreation(t *testing.T) {
   116  	for i, tt := range vals {
   117  		c := Hsv(tt.hsv[0], tt.hsv[1], tt.hsv[2])
   118  		if !c.AlmostEqualRgb(tt.c) {
   119  			t.Errorf("%v. Hsv(%v) => (%v), want %v (delta %v)", i, tt.hsv, c, tt.c, delta)
   120  		}
   121  	}
   122  }
   123  
   124  func TestHsvConversion(t *testing.T) {
   125  	for i, tt := range vals {
   126  		h, s, v := tt.c.Hsv()
   127  		if !almosteq(h, tt.hsv[0]) || !almosteq(s, tt.hsv[1]) || !almosteq(v, tt.hsv[2]) {
   128  			t.Errorf("%v. %v.Hsv() => (%v), want %v (delta %v)", i, tt.c, []float64{h, s, v}, tt.hsv, delta)
   129  		}
   130  	}
   131  }
   132  
   133  /// HSL ///
   134  ///////////
   135  
   136  func TestHslCreation(t *testing.T) {
   137  	for i, tt := range vals {
   138  		c := Hsl(tt.hsl[0], tt.hsl[1], tt.hsl[2])
   139  		if !c.AlmostEqualRgb(tt.c) {
   140  			t.Errorf("%v. Hsl(%v) => (%v), want %v (delta %v)", i, tt.hsl, c, tt.c, delta)
   141  		}
   142  	}
   143  }
   144  
   145  func TestHslConversion(t *testing.T) {
   146  	for i, tt := range vals {
   147  		h, s, l := tt.c.Hsl()
   148  		if !almosteq(h, tt.hsl[0]) || !almosteq(s, tt.hsl[1]) || !almosteq(l, tt.hsl[2]) {
   149  			t.Errorf("%v. %v.Hsl() => (%v), want %v (delta %v)", i, tt.c, []float64{h, s, l}, tt.hsl, delta)
   150  		}
   151  	}
   152  }
   153  
   154  /// Hex ///
   155  ///////////
   156  
   157  func TestHexCreation(t *testing.T) {
   158  	for i, tt := range vals {
   159  		c, err := Hex(tt.hex)
   160  		if err != nil || !c.AlmostEqualRgb(tt.c) {
   161  			t.Errorf("%v. Hex(%v) => (%v), want %v (delta %v)", i, tt.hex, c, tt.c, delta)
   162  		}
   163  	}
   164  }
   165  
   166  func TestHEXCreation(t *testing.T) {
   167  	for i, tt := range vals {
   168  		c, err := Hex(strings.ToUpper(tt.hex))
   169  		if err != nil || !c.AlmostEqualRgb(tt.c) {
   170  			t.Errorf("%v. HEX(%v) => (%v), want %v (delta %v)", i, strings.ToUpper(tt.hex), c, tt.c, delta)
   171  		}
   172  	}
   173  }
   174  
   175  func TestShortHexCreation(t *testing.T) {
   176  	for i, tt := range shorthexvals {
   177  		c, err := Hex(tt.hex)
   178  		if err != nil || !c.AlmostEqualRgb(tt.c) {
   179  			t.Errorf("%v. Hex(%v) => (%v), want %v (delta %v)", i, tt.hex, c, tt.c, delta)
   180  		}
   181  	}
   182  }
   183  
   184  func TestShortHEXCreation(t *testing.T) {
   185  	for i, tt := range shorthexvals {
   186  		c, err := Hex(strings.ToUpper(tt.hex))
   187  		if err != nil || !c.AlmostEqualRgb(tt.c) {
   188  			t.Errorf("%v. Hex(%v) => (%v), want %v (delta %v)", i, strings.ToUpper(tt.hex), c, tt.c, delta)
   189  		}
   190  	}
   191  }
   192  
   193  func TestHexConversion(t *testing.T) {
   194  	for i, tt := range vals {
   195  		hex := tt.c.Hex()
   196  		if hex != tt.hex {
   197  			t.Errorf("%v. %v.Hex() => (%v), want %v (delta %v)", i, tt.c, hex, tt.hex, delta)
   198  		}
   199  	}
   200  }
   201  
   202  /// Linear ///
   203  //////////////
   204  
   205  // LinearRgb itself is implicitly tested by XYZ conversions below (they use it).
   206  // So what we do here is just test that the FastLinearRgb approximation is "good enough"
   207  func TestFastLinearRgb(t *testing.T) {
   208  	const eps = 6.0 / 255.0 // We want that "within 6 RGB values total" is "good enough".
   209  
   210  	for r := 0.0; r < 256.0; r++ {
   211  		for g := 0.0; g < 256.0; g++ {
   212  			for b := 0.0; b < 256.0; b++ {
   213  				c := Color{r / 255.0, g / 255.0, b / 255.0}
   214  				r_want, g_want, b_want := c.LinearRgb()
   215  				r_appr, g_appr, b_appr := c.FastLinearRgb()
   216  				dr, dg, db := math.Abs(r_want-r_appr), math.Abs(g_want-g_appr), math.Abs(b_want-b_appr)
   217  				if dr+dg+db > eps {
   218  					t.Errorf("FastLinearRgb not precise enough for %v: differences are (%v, %v, %v), allowed total difference is %v", c, dr, dg, db, eps)
   219  					return
   220  				}
   221  
   222  				c_want := LinearRgb(r/255.0, g/255.0, b/255.0)
   223  				c_appr := FastLinearRgb(r/255.0, g/255.0, b/255.0)
   224  				dr, dg, db = math.Abs(c_want.R-c_appr.R), math.Abs(c_want.G-c_appr.G), math.Abs(c_want.B-c_appr.B)
   225  				if dr+dg+db > eps {
   226  					t.Errorf("FastLinearRgb not precise enough for (%v, %v, %v): differences are (%v, %v, %v), allowed total difference is %v", r, g, b, dr, dg, db, eps)
   227  					return
   228  				}
   229  			}
   230  		}
   231  	}
   232  }
   233  
   234  // Also include some benchmarks to make sure the `Fast` versions are actually significantly faster!
   235  // (Sounds silly, but the original ones weren't!)
   236  
   237  func BenchmarkColorToLinear(bench *testing.B) {
   238  	var r, g, b float64
   239  	for n := 0; n < bench.N; n++ {
   240  		r, g, b = Color{rand.Float64(), rand.Float64(), rand.Float64()}.LinearRgb()
   241  	}
   242  	bench_result = r + g + b
   243  }
   244  
   245  func BenchmarkFastColorToLinear(bench *testing.B) {
   246  	var r, g, b float64
   247  	for n := 0; n < bench.N; n++ {
   248  		r, g, b = Color{rand.Float64(), rand.Float64(), rand.Float64()}.FastLinearRgb()
   249  	}
   250  	bench_result = r + g + b
   251  }
   252  
   253  func BenchmarkLinearToColor(bench *testing.B) {
   254  	var c Color
   255  	for n := 0; n < bench.N; n++ {
   256  		c = LinearRgb(rand.Float64(), rand.Float64(), rand.Float64())
   257  	}
   258  	bench_result = c.R + c.G + c.B
   259  }
   260  
   261  func BenchmarkFastLinearToColor(bench *testing.B) {
   262  	var c Color
   263  	for n := 0; n < bench.N; n++ {
   264  		c = FastLinearRgb(rand.Float64(), rand.Float64(), rand.Float64())
   265  	}
   266  	bench_result = c.R + c.G + c.B
   267  }
   268  
   269  /// XYZ ///
   270  ///////////
   271  func TestXyzCreation(t *testing.T) {
   272  	for i, tt := range vals {
   273  		c := Xyz(tt.xyz[0], tt.xyz[1], tt.xyz[2])
   274  		if !c.AlmostEqualRgb(tt.c) {
   275  			t.Errorf("%v. Xyz(%v) => (%v), want %v (delta %v)", i, tt.xyz, c, tt.c, delta)
   276  		}
   277  	}
   278  }
   279  
   280  func TestXyzConversion(t *testing.T) {
   281  	for i, tt := range vals {
   282  		x, y, z := tt.c.Xyz()
   283  		if !almosteq(x, tt.xyz[0]) || !almosteq(y, tt.xyz[1]) || !almosteq(z, tt.xyz[2]) {
   284  			t.Errorf("%v. %v.Xyz() => (%v), want %v (delta %v)", i, tt.c, [3]float64{x, y, z}, tt.xyz, delta)
   285  		}
   286  	}
   287  }
   288  
   289  /// xyY ///
   290  ///////////
   291  func TestXyyCreation(t *testing.T) {
   292  	for i, tt := range vals {
   293  		c := Xyy(tt.xyy[0], tt.xyy[1], tt.xyy[2])
   294  		if !c.AlmostEqualRgb(tt.c) {
   295  			t.Errorf("%v. Xyy(%v) => (%v), want %v (delta %v)", i, tt.xyy, c, tt.c, delta)
   296  		}
   297  	}
   298  }
   299  
   300  func TestXyyConversion(t *testing.T) {
   301  	for i, tt := range vals {
   302  		x, y, Y := tt.c.Xyy()
   303  		if !almosteq(x, tt.xyy[0]) || !almosteq(y, tt.xyy[1]) || !almosteq(Y, tt.xyy[2]) {
   304  			t.Errorf("%v. %v.Xyy() => (%v), want %v (delta %v)", i, tt.c, [3]float64{x, y, Y}, tt.xyy, delta)
   305  		}
   306  	}
   307  }
   308  
   309  /// L*a*b* ///
   310  //////////////
   311  func TestLabCreation(t *testing.T) {
   312  	for i, tt := range vals {
   313  		c := Lab(tt.lab[0], tt.lab[1], tt.lab[2])
   314  		if !c.AlmostEqualRgb(tt.c) {
   315  			t.Errorf("%v. Lab(%v) => (%v), want %v (delta %v)", i, tt.lab, c, tt.c, delta)
   316  		}
   317  	}
   318  }
   319  
   320  func TestLabConversion(t *testing.T) {
   321  	for i, tt := range vals {
   322  		l, a, b := tt.c.Lab()
   323  		if !almosteq(l, tt.lab[0]) || !almosteq(a, tt.lab[1]) || !almosteq(b, tt.lab[2]) {
   324  			t.Errorf("%v. %v.Lab() => (%v), want %v (delta %v)", i, tt.c, [3]float64{l, a, b}, tt.lab, delta)
   325  		}
   326  	}
   327  }
   328  
   329  func TestLabWhiteRefCreation(t *testing.T) {
   330  	for i, tt := range vals {
   331  		c := LabWhiteRef(tt.lab50[0], tt.lab50[1], tt.lab50[2], D50)
   332  		if !c.AlmostEqualRgb(tt.c) {
   333  			t.Errorf("%v. LabWhiteRef(%v, D50) => (%v), want %v (delta %v)", i, tt.lab50, c, tt.c, delta)
   334  		}
   335  	}
   336  }
   337  
   338  func TestLabWhiteRefConversion(t *testing.T) {
   339  	for i, tt := range vals {
   340  		l, a, b := tt.c.LabWhiteRef(D50)
   341  		if !almosteq(l, tt.lab50[0]) || !almosteq(a, tt.lab50[1]) || !almosteq(b, tt.lab50[2]) {
   342  			t.Errorf("%v. %v.LabWhiteRef(D50) => (%v), want %v (delta %v)", i, tt.c, [3]float64{l, a, b}, tt.lab50, delta)
   343  		}
   344  	}
   345  }
   346  
   347  /// L*u*v* ///
   348  //////////////
   349  func TestLuvCreation(t *testing.T) {
   350  	for i, tt := range vals {
   351  		c := Luv(tt.luv[0], tt.luv[1], tt.luv[2])
   352  		if !c.AlmostEqualRgb(tt.c) {
   353  			t.Errorf("%v. Luv(%v) => (%v), want %v (delta %v)", i, tt.luv, c, tt.c, delta)
   354  		}
   355  	}
   356  }
   357  
   358  func TestLuvConversion(t *testing.T) {
   359  	for i, tt := range vals {
   360  		l, u, v := tt.c.Luv()
   361  		if !almosteq(l, tt.luv[0]) || !almosteq(u, tt.luv[1]) || !almosteq(v, tt.luv[2]) {
   362  			t.Errorf("%v. %v.Luv() => (%v), want %v (delta %v)", i, tt.c, [3]float64{l, u, v}, tt.luv, delta)
   363  		}
   364  	}
   365  }
   366  
   367  func TestLuvWhiteRefCreation(t *testing.T) {
   368  	for i, tt := range vals {
   369  		c := LuvWhiteRef(tt.luv50[0], tt.luv50[1], tt.luv50[2], D50)
   370  		if !c.AlmostEqualRgb(tt.c) {
   371  			t.Errorf("%v. LuvWhiteRef(%v, D50) => (%v), want %v (delta %v)", i, tt.luv50, c, tt.c, delta)
   372  		}
   373  	}
   374  }
   375  
   376  func TestLuvWhiteRefConversion(t *testing.T) {
   377  	for i, tt := range vals {
   378  		l, u, v := tt.c.LuvWhiteRef(D50)
   379  		if !almosteq(l, tt.luv50[0]) || !almosteq(u, tt.luv50[1]) || !almosteq(v, tt.luv50[2]) {
   380  			t.Errorf("%v. %v.LuvWhiteRef(D50) => (%v), want %v (delta %v)", i, tt.c, [3]float64{l, u, v}, tt.luv50, delta)
   381  		}
   382  	}
   383  }
   384  
   385  /// HCL ///
   386  ///////////
   387  // CIE-L*a*b* in polar coordinates.
   388  func TestHclCreation(t *testing.T) {
   389  	for i, tt := range vals {
   390  		c := Hcl(tt.hcl[0], tt.hcl[1], tt.hcl[2])
   391  		if !c.AlmostEqualRgb(tt.c) {
   392  			t.Errorf("%v. Hcl(%v) => (%v), want %v (delta %v)", i, tt.hcl, c, tt.c, delta)
   393  		}
   394  	}
   395  }
   396  
   397  func TestHclConversion(t *testing.T) {
   398  	for i, tt := range vals {
   399  		h, c, l := tt.c.Hcl()
   400  		if !almosteq(h, tt.hcl[0]) || !almosteq(c, tt.hcl[1]) || !almosteq(l, tt.hcl[2]) {
   401  			t.Errorf("%v. %v.Hcl() => (%v), want %v (delta %v)", i, tt.c, [3]float64{h, c, l}, tt.hcl, delta)
   402  		}
   403  	}
   404  }
   405  
   406  func TestHclWhiteRefCreation(t *testing.T) {
   407  	for i, tt := range vals {
   408  		c := HclWhiteRef(tt.hcl50[0], tt.hcl50[1], tt.hcl50[2], D50)
   409  		if !c.AlmostEqualRgb(tt.c) {
   410  			t.Errorf("%v. HclWhiteRef(%v, D50) => (%v), want %v (delta %v)", i, tt.hcl50, c, tt.c, delta)
   411  		}
   412  	}
   413  }
   414  
   415  func TestHclWhiteRefConversion(t *testing.T) {
   416  	for i, tt := range vals {
   417  		h, c, l := tt.c.HclWhiteRef(D50)
   418  		if !almosteq(h, tt.hcl50[0]) || !almosteq(c, tt.hcl50[1]) || !almosteq(l, tt.hcl50[2]) {
   419  			t.Errorf("%v. %v.HclWhiteRef(D50) => (%v), want %v (delta %v)", i, tt.c, [3]float64{h, c, l}, tt.hcl50, delta)
   420  		}
   421  	}
   422  }
   423  
   424  /// Test distances ///
   425  //////////////////////
   426  
   427  // Ground-truth from http://www.brucelindbloom.com/index.html?ColorDifferenceCalcHelp.html
   428  var dists = []struct {
   429  	c1  Color
   430  	c2  Color
   431  	d76 float64 // That's also dLab
   432  	d94 float64
   433  	d00 float64
   434  }{
   435  	{Color{1.0, 1.0, 1.0}, Color{1.0, 1.0, 1.0}, 0.0, 0.0, 0.0},
   436  	{Color{0.0, 0.0, 0.0}, Color{0.0, 0.0, 0.0}, 0.0, 0.0, 0.0},
   437  
   438  	// Just pairs of values of the table way above.
   439  	{Lab(1.000000, 0.000000, 0.000000), Lab(0.931390, -0.353319, -0.108946), 0.37604638, 0.37604638, 0.23528129},
   440  	{Lab(0.720892, 0.651673, -0.422133), Lab(0.977637, -0.165795, 0.602017), 1.33531088, 0.65466377, 0.75175896},
   441  	{Lab(0.590453, 0.332846, -0.637099), Lab(0.681085, 0.483884, 0.228328), 0.88317072, 0.42541075, 0.37688153},
   442  	{Lab(0.906026, -0.600870, 0.498993), Lab(0.533890, 0.000000, 0.000000), 0.86517280, 0.41038323, 0.39960503},
   443  	{Lab(0.911132, -0.480875, -0.141312), Lab(0.603242, 0.982343, -0.608249), 1.56647162, 0.87431457, 0.57983482},
   444  	{Lab(0.971393, -0.215537, 0.944780), Lab(0.322970, 0.791875, -1.078602), 2.35146891, 1.11858192, 1.03426977},
   445  	{Lab(0.877347, -0.861827, 0.831793), Lab(0.532408, 0.800925, 0.672032), 1.70565338, 0.68800270, 0.86608245},
   446  }
   447  
   448  func TestLabDistance(t *testing.T) {
   449  	for i, tt := range dists {
   450  		d := tt.c1.DistanceCIE76(tt.c2)
   451  		if !almosteq(d, tt.d76) {
   452  			t.Errorf("%v. %v.DistanceCIE76(%v) => (%v), want %v (delta %v)", i, tt.c1, tt.c2, d, tt.d76, delta)
   453  		}
   454  	}
   455  }
   456  
   457  func TestCIE94Distance(t *testing.T) {
   458  	for i, tt := range dists {
   459  		d := tt.c1.DistanceCIE94(tt.c2)
   460  		if !almosteq(d, tt.d94) {
   461  			t.Errorf("%v. %v.DistanceCIE94(%v) => (%v), want %v (delta %v)", i, tt.c1, tt.c2, d, tt.d94, delta)
   462  		}
   463  	}
   464  }
   465  
   466  func TestCIEDE2000Distance(t *testing.T) {
   467  	for i, tt := range dists {
   468  		d := tt.c1.DistanceCIEDE2000(tt.c2)
   469  		if !almosteq(d, tt.d00) {
   470  			t.Errorf("%v. %v.DistanceCIEDE2000(%v) => (%v), want %v (delta %v)", i, tt.c1, tt.c2, d, tt.d00, delta)
   471  		}
   472  	}
   473  }
   474  
   475  /// Test utilities ///
   476  //////////////////////
   477  
   478  func TestClamp(t *testing.T) {
   479  	c_orig := Color{1.1, -0.1, 0.5}
   480  	c_want := Color{1.0, 0.0, 0.5}
   481  	if c_orig.Clamped() != c_want {
   482  		t.Errorf("%v.Clamped() => %v, want %v", c_orig, c_orig.Clamped(), c_want)
   483  	}
   484  }
   485  
   486  func TestMakeColor(t *testing.T) {
   487  	c_orig_nrgba := color.NRGBA{123, 45, 67, 255}
   488  	c_ours, ok := MakeColor(c_orig_nrgba)
   489  	r, g, b := c_ours.RGB255()
   490  	if r != 123 || g != 45 || b != 67 || !ok {
   491  		t.Errorf("NRGBA->Colorful->RGB255 error: %v became (%v, %v, %v, %t)", c_orig_nrgba, r, g, b, ok)
   492  	}
   493  
   494  	c_orig_nrgba64 := color.NRGBA64{123 << 8, 45 << 8, 67 << 8, 0xffff}
   495  	c_ours, ok = MakeColor(c_orig_nrgba64)
   496  	r, g, b = c_ours.RGB255()
   497  	if r != 123 || g != 45 || b != 67 || !ok {
   498  		t.Errorf("NRGBA64->Colorful->RGB255 error: %v became (%v, %v, %v, %t)", c_orig_nrgba64, r, g, b, ok)
   499  	}
   500  
   501  	c_orig_gray := color.Gray{123}
   502  	c_ours, ok = MakeColor(c_orig_gray)
   503  	r, g, b = c_ours.RGB255()
   504  	if r != 123 || g != 123 || b != 123 || !ok {
   505  		t.Errorf("Gray->Colorful->RGB255 error: %v became (%v, %v, %v, %t)", c_orig_gray, r, g, b, ok)
   506  	}
   507  
   508  	c_orig_gray16 := color.Gray16{123 << 8}
   509  	c_ours, ok = MakeColor(c_orig_gray16)
   510  	r, g, b = c_ours.RGB255()
   511  	if r != 123 || g != 123 || b != 123 || !ok {
   512  		t.Errorf("Gray16->Colorful->RGB255 error: %v became (%v, %v, %v, %t)", c_orig_gray16, r, g, b, ok)
   513  	}
   514  
   515  	c_orig_rgba := color.RGBA{255, 255, 255, 0}
   516  	c_ours, ok = MakeColor(c_orig_rgba)
   517  	r, g, b = c_ours.RGB255()
   518  	if r != 0 || g != 0 || b != 0 || ok {
   519  		t.Errorf("RGBA->Colorful->RGB255 error: %v became (%v, %v, %v, %t)", c_orig_rgba, r, g, b, ok)
   520  	}
   521  }
   522  
   523  /// Issues raised on github ///
   524  ///////////////////////////////
   525  
   526  // https://github.com/lucasb-eyer/go-colorful/issues/11
   527  func TestIssue11(t *testing.T) {
   528  	c1hex := "#1a1a46"
   529  	c2hex := "#666666"
   530  
   531  	c1, _ := Hex(c1hex)
   532  	c2, _ := Hex(c2hex)
   533  
   534  	blend := c1.BlendHsv(c2, 0).Hex()
   535  	if blend != c1hex {
   536  		t.Errorf("Issue11: %v --Hsv-> %v = %v, want %v", c1hex, c2hex, blend, c1hex)
   537  	}
   538  	blend = c1.BlendHsv(c2, 1).Hex()
   539  	if blend != c2hex {
   540  		t.Errorf("Issue11: %v --Hsv-> %v = %v, want %v", c1hex, c2hex, blend, c2hex)
   541  	}
   542  
   543  	blend = c1.BlendLuv(c2, 0).Hex()
   544  	if blend != c1hex {
   545  		t.Errorf("Issue11: %v --Luv-> %v = %v, want %v", c1hex, c2hex, blend, c1hex)
   546  	}
   547  	blend = c1.BlendLuv(c2, 1).Hex()
   548  	if blend != c2hex {
   549  		t.Errorf("Issue11: %v --Luv-> %v = %v, want %v", c1hex, c2hex, blend, c2hex)
   550  	}
   551  
   552  	blend = c1.BlendRgb(c2, 0).Hex()
   553  	if blend != c1hex {
   554  		t.Errorf("Issue11: %v --Rgb-> %v = %v, want %v", c1hex, c2hex, blend, c1hex)
   555  	}
   556  	blend = c1.BlendRgb(c2, 1).Hex()
   557  	if blend != c2hex {
   558  		t.Errorf("Issue11: %v --Rgb-> %v = %v, want %v", c1hex, c2hex, blend, c2hex)
   559  	}
   560  
   561  	blend = c1.BlendLab(c2, 0).Hex()
   562  	if blend != c1hex {
   563  		t.Errorf("Issue11: %v --Lab-> %v = %v, want %v", c1hex, c2hex, blend, c1hex)
   564  	}
   565  	blend = c1.BlendLab(c2, 1).Hex()
   566  	if blend != c2hex {
   567  		t.Errorf("Issue11: %v --Lab-> %v = %v, want %v", c1hex, c2hex, blend, c2hex)
   568  	}
   569  
   570  	blend = c1.BlendHcl(c2, 0).Hex()
   571  	if blend != c1hex {
   572  		t.Errorf("Issue11: %v --Hcl-> %v = %v, want %v", c1hex, c2hex, blend, c1hex)
   573  	}
   574  	blend = c1.BlendHcl(c2, 1).Hex()
   575  	if blend != c2hex {
   576  		t.Errorf("Issue11: %v --Hcl-> %v = %v, want %v", c1hex, c2hex, blend, c2hex)
   577  	}
   578  
   579  	blend = c1.BlendLuvLCh(c2, 0).Hex()
   580  	if blend != c1hex {
   581  		t.Errorf("Issue11: %v --LuvLCh-> %v = %v, want %v", c1hex, c2hex, blend, c1hex)
   582  	}
   583  	blend = c1.BlendLuvLCh(c2, 1).Hex()
   584  	if blend != c2hex {
   585  		t.Errorf("Issue11: %v --LuvLCh-> %v = %v, want %v", c1hex, c2hex, blend, c2hex)
   586  	}
   587  }
   588  
   589  // For testing angular interpolation internal function
   590  // NOTE: They are being tested in both directions.
   591  var anglevals = []struct {
   592  	a0 float64
   593  	a1 float64
   594  	t  float64
   595  	at float64
   596  }{
   597  	{0.0, 1.0, 0.0, 0.0},
   598  	{0.0, 1.0, 0.25, 0.25},
   599  	{0.0, 1.0, 0.5, 0.5},
   600  	{0.0, 1.0, 1.0, 1.0},
   601  	{0.0, 90.0, 0.0, 0.0},
   602  	{0.0, 90.0, 0.25, 22.5},
   603  	{0.0, 90.0, 0.5, 45.0},
   604  	{0.0, 90.0, 1.0, 90.0},
   605  	{0.0, 178.0, 0.0, 0.0}, // Exact 0-180 is ambiguous.
   606  	{0.0, 178.0, 0.25, 44.5},
   607  	{0.0, 178.0, 0.5, 89.0},
   608  	{0.0, 178.0, 1.0, 178.0},
   609  	{0.0, 182.0, 0.0, 0.0}, // Exact 0-180 is ambiguous.
   610  	{0.0, 182.0, 0.25, 315.5},
   611  	{0.0, 182.0, 0.5, 271.0},
   612  	{0.0, 182.0, 1.0, 182.0},
   613  	{0.0, 270.0, 0.0, 0.0},
   614  	{0.0, 270.0, 0.25, 337.5},
   615  	{0.0, 270.0, 0.5, 315.0},
   616  	{0.0, 270.0, 1.0, 270.0},
   617  	{0.0, 359.0, 0.0, 0.0},
   618  	{0.0, 359.0, 0.25, 359.75},
   619  	{0.0, 359.0, 0.5, 359.5},
   620  	{0.0, 359.0, 1.0, 359.0},
   621  }
   622  
   623  func TestInterpolation(t *testing.T) {
   624  	// Forward
   625  	for i, tt := range anglevals {
   626  		res := interp_angle(tt.a0, tt.a1, tt.t)
   627  		if !almosteq_eps(res, tt.at, 1e-15) {
   628  			t.Errorf("%v. interp_angle(%v, %v, %v) => (%v), want %v", i, tt.a0, tt.a1, tt.t, res, tt.at)
   629  		}
   630  	}
   631  	// Backward
   632  	for i, tt := range anglevals {
   633  		res := interp_angle(tt.a1, tt.a0, 1.0-tt.t)
   634  		if !almosteq_eps(res, tt.at, 1e-15) {
   635  			t.Errorf("%v. interp_angle(%v, %v, %v) => (%v), want %v", i, tt.a1, tt.a0, 1.0-tt.t, res, tt.at)
   636  		}
   637  	}
   638  }
   639  

View as plain text