limage - lcolor/hsl.go
1 package lcolor
2
3 import (
4 "image/color"
5
6 "vimagination.zapto.org/limage/internal"
7 )
8
9 // HSLA represents the Hue, Saturation, Lightness and Alpha of a pixel
10 type HSLA struct {
11 H, S, L, A uint16
12 }
13
14 // RGBToHSL converts
15 func RGBToHSL(cl color.Color) HSLA {
16 var mn, mx uint32
17 clN := internal.ColourToNRGBA(cl)
18 mn = uint32(internal.Min(clN.R, clN.G, clN.B))
19 mx = uint32(internal.Max(clN.R, clN.G, clN.B))
20 l := mx + mn
21 hsl := HSLA{
22 L: uint16(l >> 1),
23 A: clN.A,
24 }
25 c := mx - mn
26 if c == 0 {
27 return hsl
28 }
29 if l <= 0xffff {
30 hsl.S = uint16(0xffff * c / l)
31 } else {
32 hsl.S = uint16(0xffff * c / (0x1fffe - l))
33 }
34 hsl.H = colourToHue(clN, uint16(mx), c)
35 return hsl
36 }
37
38 // RGBA implements the color.Color interface
39 func (h HSLA) RGBA() (uint32, uint32, uint32, uint32) {
40 return h.ToNRGBA().RGBA()
41 }
42
43 // ToNRGBA converts the HSL color into the RGB colorspace
44 func (h HSLA) ToNRGBA() color.NRGBA64 {
45 if h.S == 0 {
46 return color.NRGBA64{
47 R: h.L,
48 G: h.L,
49 B: h.L,
50 A: h.A,
51 }
52 }
53 c := uint32(h.L) << 1
54 if c < 0x7fff {
55 c = 0x1fffe - c
56 }
57 c = c * uint32(h.S) / 0xffff
58 return hcmaToColour(uint32(h.H), c, h.L-uint16(c>>2), h.A)
59 }
60
61 // HSVA represents the Hue, Saturation, Value and Alpha of a pixel
62 type HSVA struct {
63 H, S, V, A uint16
64 }
65
66 // RGBToHSV converts a color to the HSV color space
67 func RGBToHSV(cl color.Color) HSVA {
68 var mn, mx uint16
69 clN := internal.ColourToNRGBA(cl)
70 mn = internal.Min(clN.R, clN.G, clN.B)
71 mx = internal.Max(clN.R, clN.G, clN.B)
72 hsv := HSVA{
73 V: mx,
74 A: clN.A,
75 }
76 c := uint32(mx - mn)
77 if c == 0 {
78 return hsv
79 }
80 hsv.S = uint16(0xffff * c / uint32(mx))
81 hsv.H = colourToHue(clN, mx, c)
82 return hsv
83 }
84
85 // RGBA implements the color.Color interface
86 func (h HSVA) RGBA() (uint32, uint32, uint32, uint32) {
87 return h.ToNRGBA().RGBA()
88 }
89
90 // ToNRGBA converts the HSV color into the RGB colorspace
91 func (h HSVA) ToNRGBA() color.NRGBA64 {
92 if h.S == 0 {
93 return color.NRGBA64{
94 R: h.V,
95 G: h.V,
96 B: h.V,
97 A: h.A,
98 }
99 }
100 c := uint16(uint32(h.V) * uint32(h.S) / 0xffff)
101 return hcmaToColour(uint32(h.H), uint32(c), h.V-c, h.A)
102 }
103
104 func colourToHue(cl color.NRGBA64, mx uint16, c uint32) uint16 {
105 var h uint32
106 switch mx {
107 case cl.R:
108 if cl.G >= cl.B {
109 h = 0x00000 + 0xffff*uint32(cl.G-cl.B)/c
110 } else {
111 h = 0x5fffa - 0xffff*uint32(cl.B-cl.G)/c
112 }
113 case cl.G:
114 if cl.B >= cl.R {
115 h = 0x1fffe + 0xffff*uint32(cl.B-cl.R)/c
116 } else {
117 h = 0x1fffe - 0xffff*uint32(cl.R-cl.B)/c
118 }
119 case cl.B:
120 if cl.R >= cl.G {
121 h = 0x3fffc + 0xffff*uint32(cl.R-cl.G)/c
122 } else {
123 h = 0x3fffc - 0xffff*uint32(cl.G-cl.R)/c
124 }
125 }
126
127 return uint16(h / 6)
128 }
129
130 func hcmaToColour(hue, c uint32, m, a uint16) color.NRGBA64 {
131 var h uint32
132 if hue != 0xffff {
133 h = hue * 6
134 }
135 x := h % 0x1fffe
136 if x >= 0xffff {
137 x = 0x1fffe - x
138 }
139 x = x * c / 0xffff
140 cl := color.NRGBA64{
141 A: a,
142 }
143 switch h / 0xffff {
144 case 0:
145 cl.R = uint16(c)
146 cl.G = uint16(x)
147 case 1:
148 cl.R = uint16(x)
149 cl.G = uint16(c)
150 case 2:
151 cl.G = uint16(c)
152 cl.B = uint16(x)
153 case 3:
154 cl.G = uint16(x)
155 cl.B = uint16(c)
156 case 4:
157 cl.R = uint16(x)
158 cl.B = uint16(c)
159 case 5:
160 cl.R = uint16(c)
161 cl.B = uint16(x)
162 }
163 cl.R += m
164 cl.G += m
165 cl.B += m
166 return cl
167 }
168