1
2 package colorful
3
4 import (
5 "fmt"
6 "image/color"
7 "math"
8 )
9
10
11 type Color struct {
12 R, G, B float64
13 }
14
15
16 func (col Color) RGBA() (r, g, b, a uint32) {
17 r = uint32(col.R*65535.0 + 0.5)
18 g = uint32(col.G*65535.0 + 0.5)
19 b = uint32(col.B*65535.0 + 0.5)
20 a = 0xFFFF
21 return
22 }
23
24
25 func MakeColor(col color.Color) (Color, bool) {
26 r, g, b, a := col.RGBA()
27 if a == 0 {
28 return Color{0, 0, 0}, false
29 }
30
31
32
33 r *= 0xffff
34 r /= a
35 g *= 0xffff
36 g /= a
37 b *= 0xffff
38 b /= a
39
40 return Color{float64(r) / 65535.0, float64(g) / 65535.0, float64(b) / 65535.0}, true
41 }
42
43
44 func (col Color) RGB255() (r, g, b uint8) {
45 r = uint8(col.R*255.0 + 0.5)
46 g = uint8(col.G*255.0 + 0.5)
47 b = uint8(col.B*255.0 + 0.5)
48 return
49 }
50
51
52 func (col Color) values() (float64, float64, float64) {
53 return col.R, col.G, col.B
54 }
55
56
57 const Delta = 1.0 / 255.0
58
59
60 var D65 = [3]float64{0.95047, 1.00000, 1.08883}
61
62
63 var D50 = [3]float64{0.96422, 1.00000, 0.82521}
64
65
66 func (c Color) IsValid() bool {
67 return 0.0 <= c.R && c.R <= 1.0 &&
68 0.0 <= c.G && c.G <= 1.0 &&
69 0.0 <= c.B && c.B <= 1.0
70 }
71
72
73 func clamp01(v float64) float64 {
74 return math.Max(0.0, math.Min(v, 1.0))
75 }
76
77
78
79 func (c Color) Clamped() Color {
80 return Color{clamp01(c.R), clamp01(c.G), clamp01(c.B)}
81 }
82
83 func sq(v float64) float64 {
84 return v * v
85 }
86
87 func cub(v float64) float64 {
88 return v * v * v
89 }
90
91
92
93 func (c1 Color) DistanceRgb(c2 Color) float64 {
94 return math.Sqrt(sq(c1.R-c2.R) + sq(c1.G-c2.G) + sq(c1.B-c2.B))
95 }
96
97
98
99
100 func (c1 Color) DistanceLinearRGB(c2 Color) float64 {
101 r1, g1, b1 := c1.LinearRgb()
102 r2, g2, b2 := c2.LinearRgb()
103 return math.Sqrt(sq(r1-r2) + sq(g1-g2) + sq(b1-b2))
104 }
105
106
107 func (c1 Color) AlmostEqualRgb(c2 Color) bool {
108 return math.Abs(c1.R-c2.R)+
109 math.Abs(c1.G-c2.G)+
110 math.Abs(c1.B-c2.B) < 3.0*Delta
111 }
112
113
114 func (c1 Color) BlendRgb(c2 Color, t float64) Color {
115 return Color{c1.R + t*(c2.R-c1.R),
116 c1.G + t*(c2.G-c1.G),
117 c1.B + t*(c2.B-c1.B)}
118 }
119
120
121 func interp_angle(a0, a1, t float64) float64 {
122
123
124 delta := math.Mod(math.Mod(a1-a0, 360.0)+540, 360.0) - 180.0
125 return math.Mod(a0+t*delta+360.0, 360.0)
126 }
127
128
129
130
131
132
133
134 func (col Color) Hsv() (h, s, v float64) {
135 min := math.Min(math.Min(col.R, col.G), col.B)
136 v = math.Max(math.Max(col.R, col.G), col.B)
137 C := v - min
138
139 s = 0.0
140 if v != 0.0 {
141 s = C / v
142 }
143
144 h = 0.0
145 if min != v {
146 if v == col.R {
147 h = math.Mod((col.G-col.B)/C, 6.0)
148 }
149 if v == col.G {
150 h = (col.B-col.R)/C + 2.0
151 }
152 if v == col.B {
153 h = (col.R-col.G)/C + 4.0
154 }
155 h *= 60.0
156 if h < 0.0 {
157 h += 360.0
158 }
159 }
160 return
161 }
162
163
164 func Hsv(H, S, V float64) Color {
165 Hp := H / 60.0
166 C := V * S
167 X := C * (1.0 - math.Abs(math.Mod(Hp, 2.0)-1.0))
168
169 m := V - C
170 r, g, b := 0.0, 0.0, 0.0
171
172 switch {
173 case 0.0 <= Hp && Hp < 1.0:
174 r = C
175 g = X
176 case 1.0 <= Hp && Hp < 2.0:
177 r = X
178 g = C
179 case 2.0 <= Hp && Hp < 3.0:
180 g = C
181 b = X
182 case 3.0 <= Hp && Hp < 4.0:
183 g = X
184 b = C
185 case 4.0 <= Hp && Hp < 5.0:
186 r = X
187 b = C
188 case 5.0 <= Hp && Hp < 6.0:
189 r = C
190 b = X
191 }
192
193 return Color{m + r, m + g, m + b}
194 }
195
196
197 func (c1 Color) BlendHsv(c2 Color, t float64) Color {
198 h1, s1, v1 := c1.Hsv()
199 h2, s2, v2 := c2.Hsv()
200
201
202 return Hsv(interp_angle(h1, h2, t), s1+t*(s2-s1), v1+t*(v2-v1))
203 }
204
205
206
207
208
209 func (col Color) Hsl() (h, s, l float64) {
210 min := math.Min(math.Min(col.R, col.G), col.B)
211 max := math.Max(math.Max(col.R, col.G), col.B)
212
213 l = (max + min) / 2
214
215 if min == max {
216 s = 0
217 h = 0
218 } else {
219 if l < 0.5 {
220 s = (max - min) / (max + min)
221 } else {
222 s = (max - min) / (2.0 - max - min)
223 }
224
225 if max == col.R {
226 h = (col.G - col.B) / (max - min)
227 } else if max == col.G {
228 h = 2.0 + (col.B-col.R)/(max-min)
229 } else {
230 h = 4.0 + (col.R-col.G)/(max-min)
231 }
232
233 h *= 60
234
235 if h < 0 {
236 h += 360
237 }
238 }
239
240 return
241 }
242
243
244 func Hsl(h, s, l float64) Color {
245 if s == 0 {
246 return Color{l, l, l}
247 }
248
249 var r, g, b float64
250 var t1 float64
251 var t2 float64
252 var tr float64
253 var tg float64
254 var tb float64
255
256 if l < 0.5 {
257 t1 = l * (1.0 + s)
258 } else {
259 t1 = l + s - l*s
260 }
261
262 t2 = 2*l - t1
263 h /= 360
264 tr = h + 1.0/3.0
265 tg = h
266 tb = h - 1.0/3.0
267
268 if tr < 0 {
269 tr++
270 }
271 if tr > 1 {
272 tr--
273 }
274 if tg < 0 {
275 tg++
276 }
277 if tg > 1 {
278 tg--
279 }
280 if tb < 0 {
281 tb++
282 }
283 if tb > 1 {
284 tb--
285 }
286
287
288 if 6*tr < 1 {
289 r = t2 + (t1-t2)*6*tr
290 } else if 2*tr < 1 {
291 r = t1
292 } else if 3*tr < 2 {
293 r = t2 + (t1-t2)*(2.0/3.0-tr)*6
294 } else {
295 r = t2
296 }
297
298
299 if 6*tg < 1 {
300 g = t2 + (t1-t2)*6*tg
301 } else if 2*tg < 1 {
302 g = t1
303 } else if 3*tg < 2 {
304 g = t2 + (t1-t2)*(2.0/3.0-tg)*6
305 } else {
306 g = t2
307 }
308
309
310 if 6*tb < 1 {
311 b = t2 + (t1-t2)*6*tb
312 } else if 2*tb < 1 {
313 b = t1
314 } else if 3*tb < 2 {
315 b = t2 + (t1-t2)*(2.0/3.0-tb)*6
316 } else {
317 b = t2
318 }
319
320 return Color{r, g, b}
321 }
322
323
324
325
326
327 func (col Color) Hex() string {
328
329 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))
330 }
331
332
333 func Hex(scol string) (Color, error) {
334 format := "#%02x%02x%02x"
335 factor := 1.0 / 255.0
336 if len(scol) == 4 {
337 format = "#%1x%1x%1x"
338 factor = 1.0 / 15.0
339 }
340
341 var r, g, b uint8
342 n, err := fmt.Sscanf(scol, format, &r, &g, &b)
343 if err != nil {
344 return Color{}, err
345 }
346 if n != 3 {
347 return Color{}, fmt.Errorf("color: %v is not a hex-color", scol)
348 }
349
350 return Color{float64(r) * factor, float64(g) * factor, float64(b) * factor}, nil
351 }
352
353
354
355
356
357
358 func linearize(v float64) float64 {
359 if v <= 0.04045 {
360 return v / 12.92
361 }
362 return math.Pow((v+0.055)/1.055, 2.4)
363 }
364
365
366 func (col Color) LinearRgb() (r, g, b float64) {
367 r = linearize(col.R)
368 g = linearize(col.G)
369 b = linearize(col.B)
370 return
371 }
372
373
374
375 func linearize_fast(v float64) float64 {
376 v1 := v - 0.5
377 v2 := v1 * v1
378 v3 := v2 * v1
379 v4 := v2 * v2
380
381 return -0.248750514614486 + 0.925583310193438*v + 1.16740237321695*v2 + 0.280457026598666*v3 - 0.0757991963780179*v4
382 }
383
384
385
386 func (col Color) FastLinearRgb() (r, g, b float64) {
387 r = linearize_fast(col.R)
388 g = linearize_fast(col.G)
389 b = linearize_fast(col.B)
390 return
391 }
392
393 func delinearize(v float64) float64 {
394 if v <= 0.0031308 {
395 return 12.92 * v
396 }
397 return 1.055*math.Pow(v, 1.0/2.4) - 0.055
398 }
399
400
401 func LinearRgb(r, g, b float64) Color {
402 return Color{delinearize(r), delinearize(g), delinearize(b)}
403 }
404
405 func delinearize_fast(v float64) float64 {
406
407 if v > 0.2 {
408 v1 := v - 0.6
409 v2 := v1 * v1
410 v3 := v2 * v1
411 v4 := v2 * v2
412 v5 := v3 * v2
413 return 0.442430344268235 + 0.592178981271708*v - 0.287864782562636*v2 + 0.253214392068985*v3 - 0.272557158129811*v4 + 0.325554383321718*v5
414 } else if v > 0.03 {
415 v1 := v - 0.115
416 v2 := v1 * v1
417 v3 := v2 * v1
418 v4 := v2 * v2
419 v5 := v3 * v2
420 return 0.194915592891669 + 1.55227076330229*v - 3.93691860257828*v2 + 18.0679839248761*v3 - 101.468750302746*v4 + 632.341487393927*v5
421 } else {
422 v1 := v - 0.015
423 v2 := v1 * v1
424 v3 := v2 * v1
425 v4 := v2 * v2
426 v5 := v3 * v2
427
428 return 0.0519565234928877 + 5.09316778537561*v - 99.0338180489702*v2 + 3484.52322764895*v3 - 150028.083412663*v4 + 7168008.42971613*v5
429 }
430 }
431
432
433
434 func FastLinearRgb(r, g, b float64) Color {
435 return Color{delinearize_fast(r), delinearize_fast(g), delinearize_fast(b)}
436 }
437
438
439 func XyzToLinearRgb(x, y, z float64) (r, g, b float64) {
440 r = 3.2409699419045214*x - 1.5373831775700935*y - 0.49861076029300328*z
441 g = -0.96924363628087983*x + 1.8759675015077207*y + 0.041555057407175613*z
442 b = 0.055630079696993609*x - 0.20397695888897657*y + 1.0569715142428786*z
443 return
444 }
445
446 func LinearRgbToXyz(r, g, b float64) (x, y, z float64) {
447 x = 0.41239079926595948*r + 0.35758433938387796*g + 0.18048078840183429*b
448 y = 0.21263900587151036*r + 0.71516867876775593*g + 0.072192315360733715*b
449 z = 0.019330818715591851*r + 0.11919477979462599*g + 0.95053215224966058*b
450 return
451 }
452
453
454
455
456
457 func (col Color) Xyz() (x, y, z float64) {
458 return LinearRgbToXyz(col.LinearRgb())
459 }
460
461 func Xyz(x, y, z float64) Color {
462 return LinearRgb(XyzToLinearRgb(x, y, z))
463 }
464
465
466
467
468
469
470
471 func XyzToXyy(X, Y, Z float64) (x, y, Yout float64) {
472 return XyzToXyyWhiteRef(X, Y, Z, D65)
473 }
474
475 func XyzToXyyWhiteRef(X, Y, Z float64, wref [3]float64) (x, y, Yout float64) {
476 Yout = Y
477 N := X + Y + Z
478 if math.Abs(N) < 1e-14 {
479
480
481 x = wref[0] / (wref[0] + wref[1] + wref[2])
482 y = wref[1] / (wref[0] + wref[1] + wref[2])
483 } else {
484 x = X / N
485 y = Y / N
486 }
487 return
488 }
489
490 func XyyToXyz(x, y, Y float64) (X, Yout, Z float64) {
491 Yout = Y
492
493 if -1e-14 < y && y < 1e-14 {
494 X = 0.0
495 Z = 0.0
496 } else {
497 X = Y / y * x
498 Z = Y / y * (1.0 - x - y)
499 }
500
501 return
502 }
503
504
505
506
507 func (col Color) Xyy() (x, y, Y float64) {
508 return XyzToXyy(col.Xyz())
509 }
510
511
512
513
514
515 func (col Color) XyyWhiteRef(wref [3]float64) (x, y, Y float64) {
516 X, Y2, Z := col.Xyz()
517 return XyzToXyyWhiteRef(X, Y2, Z, wref)
518 }
519
520
521
522 func Xyy(x, y, Y float64) Color {
523 return Xyz(XyyToXyz(x, y, Y))
524 }
525
526
527
528
529
530
531 func lab_f(t float64) float64 {
532 if t > 6.0/29.0*6.0/29.0*6.0/29.0 {
533 return math.Cbrt(t)
534 }
535 return t/3.0*29.0/6.0*29.0/6.0 + 4.0/29.0
536 }
537
538 func XyzToLab(x, y, z float64) (l, a, b float64) {
539
540
541
542 return XyzToLabWhiteRef(x, y, z, D65)
543 }
544
545 func XyzToLabWhiteRef(x, y, z float64, wref [3]float64) (l, a, b float64) {
546 fy := lab_f(y / wref[1])
547 l = 1.16*fy - 0.16
548 a = 5.0 * (lab_f(x/wref[0]) - fy)
549 b = 2.0 * (fy - lab_f(z/wref[2]))
550 return
551 }
552
553 func lab_finv(t float64) float64 {
554 if t > 6.0/29.0 {
555 return t * t * t
556 }
557 return 3.0 * 6.0 / 29.0 * 6.0 / 29.0 * (t - 4.0/29.0)
558 }
559
560 func LabToXyz(l, a, b float64) (x, y, z float64) {
561
562 return LabToXyzWhiteRef(l, a, b, D65)
563 }
564
565 func LabToXyzWhiteRef(l, a, b float64, wref [3]float64) (x, y, z float64) {
566 l2 := (l + 0.16) / 1.16
567 x = wref[0] * lab_finv(l2+a/5.0)
568 y = wref[1] * lab_finv(l2)
569 z = wref[2] * lab_finv(l2-b/2.0)
570 return
571 }
572
573
574 func (col Color) Lab() (l, a, b float64) {
575 return XyzToLab(col.Xyz())
576 }
577
578
579
580 func (col Color) LabWhiteRef(wref [3]float64) (l, a, b float64) {
581 x, y, z := col.Xyz()
582 return XyzToLabWhiteRef(x, y, z, wref)
583 }
584
585
586
587
588 func Lab(l, a, b float64) Color {
589 return Xyz(LabToXyz(l, a, b))
590 }
591
592
593
594 func LabWhiteRef(l, a, b float64, wref [3]float64) Color {
595 return Xyz(LabToXyzWhiteRef(l, a, b, wref))
596 }
597
598
599
600
601 func (c1 Color) DistanceLab(c2 Color) float64 {
602 l1, a1, b1 := c1.Lab()
603 l2, a2, b2 := c2.Lab()
604 return math.Sqrt(sq(l1-l2) + sq(a1-a2) + sq(b1-b2))
605 }
606
607
608 func (c1 Color) DistanceCIE76(c2 Color) float64 {
609 return c1.DistanceLab(c2)
610 }
611
612
613
614 func (cl Color) DistanceCIE94(cr Color) float64 {
615 l1, a1, b1 := cl.Lab()
616 l2, a2, b2 := cr.Lab()
617
618
619
620
621
622 l1, a1, b1 = l1*100.0, a1*100.0, b1*100.0
623 l2, a2, b2 = l2*100.0, a2*100.0, b2*100.0
624
625 kl := 1.0
626 kc := 1.0
627 kh := 1.0
628 k1 := 0.045
629 k2 := 0.015
630
631 deltaL := l1 - l2
632 c1 := math.Sqrt(sq(a1) + sq(b1))
633 c2 := math.Sqrt(sq(a2) + sq(b2))
634 deltaCab := c1 - c2
635
636
637 deltaHab2 := sq(a1-a2) + sq(b1-b2) - sq(deltaCab)
638 sl := 1.0
639 sc := 1.0 + k1*c1
640 sh := 1.0 + k2*c1
641
642 vL2 := sq(deltaL / (kl * sl))
643 vC2 := sq(deltaCab / (kc * sc))
644 vH2 := deltaHab2 / sq(kh*sh)
645
646 return math.Sqrt(vL2+vC2+vH2) * 0.01
647 }
648
649
650
651
652 func (cl Color) DistanceCIEDE2000(cr Color) float64 {
653 return cl.DistanceCIEDE2000klch(cr, 1.0, 1.0, 1.0)
654 }
655
656
657
658 func (cl Color) DistanceCIEDE2000klch(cr Color, kl, kc, kh float64) float64 {
659 l1, a1, b1 := cl.Lab()
660 l2, a2, b2 := cr.Lab()
661
662
663
664 l1, a1, b1 = l1*100.0, a1*100.0, b1*100.0
665 l2, a2, b2 = l2*100.0, a2*100.0, b2*100.0
666
667 cab1 := math.Sqrt(sq(a1) + sq(b1))
668 cab2 := math.Sqrt(sq(a2) + sq(b2))
669 cabmean := (cab1 + cab2) / 2
670
671 g := 0.5 * (1 - math.Sqrt(math.Pow(cabmean, 7)/(math.Pow(cabmean, 7)+math.Pow(25, 7))))
672 ap1 := (1 + g) * a1
673 ap2 := (1 + g) * a2
674 cp1 := math.Sqrt(sq(ap1) + sq(b1))
675 cp2 := math.Sqrt(sq(ap2) + sq(b2))
676
677 hp1 := 0.0
678 if b1 != ap1 || ap1 != 0 {
679 hp1 = math.Atan2(b1, ap1)
680 if hp1 < 0 {
681 hp1 += math.Pi * 2
682 }
683 hp1 *= 180 / math.Pi
684 }
685 hp2 := 0.0
686 if b2 != ap2 || ap2 != 0 {
687 hp2 = math.Atan2(b2, ap2)
688 if hp2 < 0 {
689 hp2 += math.Pi * 2
690 }
691 hp2 *= 180 / math.Pi
692 }
693
694 deltaLp := l2 - l1
695 deltaCp := cp2 - cp1
696 dhp := 0.0
697 cpProduct := cp1 * cp2
698 if cpProduct != 0 {
699 dhp = hp2 - hp1
700 if dhp > 180 {
701 dhp -= 360
702 } else if dhp < -180 {
703 dhp += 360
704 }
705 }
706 deltaHp := 2 * math.Sqrt(cpProduct) * math.Sin(dhp/2*math.Pi/180)
707
708 lpmean := (l1 + l2) / 2
709 cpmean := (cp1 + cp2) / 2
710 hpmean := hp1 + hp2
711 if cpProduct != 0 {
712 hpmean /= 2
713 if math.Abs(hp1-hp2) > 180 {
714 if hp1+hp2 < 360 {
715 hpmean += 180
716 } else {
717 hpmean -= 180
718 }
719 }
720 }
721
722 t := 1 - 0.17*math.Cos((hpmean-30)*math.Pi/180) + 0.24*math.Cos(2*hpmean*math.Pi/180) + 0.32*math.Cos((3*hpmean+6)*math.Pi/180) - 0.2*math.Cos((4*hpmean-63)*math.Pi/180)
723 deltaTheta := 30 * math.Exp(-sq((hpmean-275)/25))
724 rc := 2 * math.Sqrt(math.Pow(cpmean, 7)/(math.Pow(cpmean, 7)+math.Pow(25, 7)))
725 sl := 1 + (0.015*sq(lpmean-50))/math.Sqrt(20+sq(lpmean-50))
726 sc := 1 + 0.045*cpmean
727 sh := 1 + 0.015*cpmean*t
728 rt := -math.Sin(2*deltaTheta*math.Pi/180) * rc
729
730 return math.Sqrt(sq(deltaLp/(kl*sl))+sq(deltaCp/(kc*sc))+sq(deltaHp/(kh*sh))+rt*(deltaCp/(kc*sc))*(deltaHp/(kh*sh))) * 0.01
731 }
732
733
734
735 func (c1 Color) BlendLab(c2 Color, t float64) Color {
736 l1, a1, b1 := c1.Lab()
737 l2, a2, b2 := c2.Lab()
738 return Lab(l1+t*(l2-l1),
739 a1+t*(a2-a1),
740 b1+t*(b2-b1))
741 }
742
743
744
745
746
747
748 func XyzToLuv(x, y, z float64) (l, a, b float64) {
749
750
751
752 return XyzToLuvWhiteRef(x, y, z, D65)
753 }
754
755 func XyzToLuvWhiteRef(x, y, z float64, wref [3]float64) (l, u, v float64) {
756 if y/wref[1] <= 6.0/29.0*6.0/29.0*6.0/29.0 {
757 l = y / wref[1] * (29.0 / 3.0 * 29.0 / 3.0 * 29.0 / 3.0) / 100.0
758 } else {
759 l = 1.16*math.Cbrt(y/wref[1]) - 0.16
760 }
761 ubis, vbis := xyz_to_uv(x, y, z)
762 un, vn := xyz_to_uv(wref[0], wref[1], wref[2])
763 u = 13.0 * l * (ubis - un)
764 v = 13.0 * l * (vbis - vn)
765 return
766 }
767
768
769
770 func xyz_to_uv(x, y, z float64) (u, v float64) {
771 denom := x + 15.0*y + 3.0*z
772 if denom == 0.0 {
773 u, v = 0.0, 0.0
774 } else {
775 u = 4.0 * x / denom
776 v = 9.0 * y / denom
777 }
778 return
779 }
780
781 func LuvToXyz(l, u, v float64) (x, y, z float64) {
782
783 return LuvToXyzWhiteRef(l, u, v, D65)
784 }
785
786 func LuvToXyzWhiteRef(l, u, v float64, wref [3]float64) (x, y, z float64) {
787
788 if l <= 0.08 {
789 y = wref[1] * l * 100.0 * 3.0 / 29.0 * 3.0 / 29.0 * 3.0 / 29.0
790 } else {
791 y = wref[1] * cub((l+0.16)/1.16)
792 }
793 un, vn := xyz_to_uv(wref[0], wref[1], wref[2])
794 if l != 0.0 {
795 ubis := u/(13.0*l) + un
796 vbis := v/(13.0*l) + vn
797 x = y * 9.0 * ubis / (4.0 * vbis)
798 z = y * (12.0 - 3.0*ubis - 20.0*vbis) / (4.0 * vbis)
799 } else {
800 x, y = 0.0, 0.0
801 }
802 return
803 }
804
805
806
807 func (col Color) Luv() (l, u, v float64) {
808 return XyzToLuv(col.Xyz())
809 }
810
811
812
813
814 func (col Color) LuvWhiteRef(wref [3]float64) (l, u, v float64) {
815 x, y, z := col.Xyz()
816 return XyzToLuvWhiteRef(x, y, z, wref)
817 }
818
819
820
821
822
823 func Luv(l, u, v float64) Color {
824 return Xyz(LuvToXyz(l, u, v))
825 }
826
827
828
829
830 func LuvWhiteRef(l, u, v float64, wref [3]float64) Color {
831 return Xyz(LuvToXyzWhiteRef(l, u, v, wref))
832 }
833
834
835
836
837 func (c1 Color) DistanceLuv(c2 Color) float64 {
838 l1, u1, v1 := c1.Luv()
839 l2, u2, v2 := c2.Luv()
840 return math.Sqrt(sq(l1-l2) + sq(u1-u2) + sq(v1-v2))
841 }
842
843
844
845 func (c1 Color) BlendLuv(c2 Color, t float64) Color {
846 l1, u1, v1 := c1.Luv()
847 l2, u2, v2 := c2.Luv()
848 return Luv(l1+t*(l2-l1),
849 u1+t*(u2-u1),
850 v1+t*(v2-v1))
851 }
852
853
854
855
856
857
858
859
860
861
862 func (col Color) Hcl() (h, c, l float64) {
863 return col.HclWhiteRef(D65)
864 }
865
866 func LabToHcl(L, a, b float64) (h, c, l float64) {
867
868 if math.Abs(b-a) > 1e-4 && math.Abs(a) > 1e-4 {
869 h = math.Mod(57.29577951308232087721*math.Atan2(b, a)+360.0, 360.0)
870 } else {
871 h = 0.0
872 }
873 c = math.Sqrt(sq(a) + sq(b))
874 l = L
875 return
876 }
877
878
879
880
881 func (col Color) HclWhiteRef(wref [3]float64) (h, c, l float64) {
882 L, a, b := col.LabWhiteRef(wref)
883 return LabToHcl(L, a, b)
884 }
885
886
887
888
889
890 func Hcl(h, c, l float64) Color {
891 return HclWhiteRef(h, c, l, D65)
892 }
893
894 func HclToLab(h, c, l float64) (L, a, b float64) {
895 H := 0.01745329251994329576 * h
896 a = c * math.Cos(H)
897 b = c * math.Sin(H)
898 L = l
899 return
900 }
901
902
903
904
905 func HclWhiteRef(h, c, l float64, wref [3]float64) Color {
906 L, a, b := HclToLab(h, c, l)
907 return LabWhiteRef(L, a, b, wref)
908 }
909
910
911
912 func (col1 Color) BlendHcl(col2 Color, t float64) Color {
913 h1, c1, l1 := col1.Hcl()
914 h2, c2, l2 := col2.Hcl()
915
916
917 return Hcl(interp_angle(h1, h2, t), c1+t*(c2-c1), l1+t*(l2-l1)).Clamped()
918 }
919
920
921
922
923
924 func (col Color) LuvLCh() (l, c, h float64) {
925 return col.LuvLChWhiteRef(D65)
926 }
927
928 func LuvToLuvLCh(L, u, v float64) (l, c, h float64) {
929
930 if math.Abs(v-u) > 1e-4 && math.Abs(u) > 1e-4 {
931 h = math.Mod(57.29577951308232087721*math.Atan2(v, u)+360.0, 360.0)
932 } else {
933 h = 0.0
934 }
935 l = L
936 c = math.Sqrt(sq(u) + sq(v))
937 return
938 }
939
940
941
942
943 func (col Color) LuvLChWhiteRef(wref [3]float64) (l, c, h float64) {
944 return LuvToLuvLCh(col.LuvWhiteRef(wref))
945 }
946
947
948
949
950
951 func LuvLCh(l, c, h float64) Color {
952 return LuvLChWhiteRef(l, c, h, D65)
953 }
954
955 func LuvLChToLuv(l, c, h float64) (L, u, v float64) {
956 H := 0.01745329251994329576 * h
957 u = c * math.Cos(H)
958 v = c * math.Sin(H)
959 L = l
960 return
961 }
962
963
964
965
966 func LuvLChWhiteRef(l, c, h float64, wref [3]float64) Color {
967 L, u, v := LuvLChToLuv(l, c, h)
968 return LuvWhiteRef(L, u, v, wref)
969 }
970
971
972
973 func (col1 Color) BlendLuvLCh(col2 Color, t float64) Color {
974 l1, c1, h1 := col1.LuvLCh()
975 l2, c2, h2 := col2.LuvLCh()
976
977
978 return LuvLCh(l1+t*(l2-l1), c1+t*(c2-c1), interp_angle(h1, h2, t))
979 }
980
View as plain text