1 module skia.SKColor;
2 
3 import skia.SKColorF;
4 import skia.Exceptions;
5 
6 import std.experimental.logger;
7 
8 import std.conv;
9 import std.format;
10 import std.range;
11 import std..string;
12 
13 struct SKColor {
14 	enum SKColor Empty = SKColor(0);
15 
16 	private uint color;
17 
18 	this(uint value) {
19 		color = value;
20 	}
21 
22 	this(ubyte red, ubyte green, ubyte blue, ubyte alpha) {
23 		color = cast(uint)((alpha << 24) | (red << 16) | (green << 8) | blue);
24 	}
25 
26 	this(ubyte red, ubyte green, ubyte blue) {
27 		color = (0xff000000u | cast(uint)(red << 16) | cast(uint)(green << 8) | blue);
28 	}
29 
30 	SKColor WithRed(ubyte red) {
31 		return SKColor(red, Green, Blue, Alpha);
32 	}
33 
34 	SKColor WithGreen(ubyte green) {
35 		return SKColor(Red, green, Blue, Alpha);
36 	}
37 
38 	SKColor WithBlue(ubyte blue) {
39 		return SKColor(Red, Green, blue, Alpha);
40 	}
41 
42 	SKColor WithAlpha(ubyte alpha) {
43 		return SKColor(Red, Green, Blue, alpha);
44 	}
45 
46 	ubyte Alpha() {
47 		return cast(ubyte)((color >> 24) & 0xff);
48 	}
49 
50 	ubyte Red() {
51 		return cast(ubyte)((color >> 16) & 0xff);
52 	}
53 
54 	ubyte Green() {
55 		return cast(ubyte)((color >> 8) & 0xff);
56 	}
57 
58 	ubyte Blue() {
59 		return cast(ubyte)((color) & 0xff);
60 	}
61 
62 	float Hue() {
63 		float h;
64 		float s;
65 		float v;
66 		ToHsv(h, s, v);
67 		return h;
68 	}
69 
70 	static SKColor FromHsl(float h, float s, float l, ubyte a = cast(ubyte) 255) {
71 		auto colorf = SKColorF.FromHsl(h, s, l);
72 
73 		// RGB results from 0 to 255
74 		auto r = colorf.Red * 255f;
75 		auto g = colorf.Green * 255f;
76 		auto b = colorf.Blue * 255f;
77 
78 		return SKColor(cast(ubyte) r, cast(ubyte) g, cast(ubyte) b, a);
79 	}
80 
81 	static SKColor FromHsv(float h, float s, float v, ubyte a = cast(ubyte) 255) {
82 		auto colorf = SKColorF.FromHsv(h, s, v);
83 
84 		// RGB results from 0 to 255
85 		auto r = colorf.Red * 255f;
86 		auto g = colorf.Green * 255f;
87 		auto b = colorf.Blue * 255f;
88 
89 		return SKColor(cast(ubyte) r, cast(ubyte) g, cast(ubyte) b, a);
90 	}
91 
92 	void ToHsl(ref float h, ref float s, ref float l) {
93 		// RGB from 0 to 255
94 		auto r = Red / 255f;
95 		auto g = Green / 255f;
96 		auto b = Blue / 255f;
97 
98 		auto colorf = SKColorF(r, g, b);
99 		colorf.ToHsl(h, s, l);
100 	}
101 
102 	void ToHsv(ref float h, ref float s, ref float v) {
103 		// RGB from 0 to 255
104 		auto r = Red / 255f;
105 		auto g = Green / 255f;
106 		auto b = Blue / 255f;
107 
108 		auto colorf = SKColorF(r, g, b);
109 		colorf.ToHsv(h, s, v);
110 	}
111 
112 	string ToString() {
113 		// return "#{Alpha:x2}{Red:x2}{Green:x2}{Blue:x2}";
114 		return format("#%02x%02x%02x%02x", Alpha, Red, Green, Blue);
115 	}
116 
117 	// bool Equals(SKColor obj)
118 	// {
119 	// 	return obj.color == color;
120 	// }
121 
122 	// override bool Equals(object other)
123 	// {
124 	// 	return other is SKColor f && Equals(f);
125 	// }
126 
127 	// static bool operator == (SKColor left, SKColor right)
128 	// {
129 	// 	return left.Equals(right);
130 	// }
131 
132 	// static bool operator != (SKColor left, SKColor right)
133 	// {
134 	// 	return !left.Equals(right);
135 	// }
136 
137 	// override int GetHashCode()
138 	// {
139 	// 	return color.GetHashCode();
140 	// }
141 
142 	uint opCast(T)() if (is(T == uint)) {
143 		return color;
144 	}
145 
146 	static SKColor Parse(string hexString) {
147 		SKColor color;
148 		if (!TryParse(hexString, color))
149 			throw new ArgumentException("Invalid hexadecimal color string.", hexString.stringof);
150 		return color;
151 	}
152 
153 	static bool TryParse(string hexString, ref SKColor color) {
154 		if (hexString.empty()) {
155 			// error
156 			color = SKColor.Empty;
157 			return false;
158 		}
159 
160 		// clean up string
161 		hexString = hexString.strip().toUpper();
162 		if (hexString[0] == '#')
163 			hexString = hexString[1 .. $];
164 
165 		size_t len = hexString.length;
166 		if (len == 3 || len == 4) {
167 			ubyte a;
168 			// parse [A]
169 			if (len == 4) {
170 				if (!tryParse(format("%c%c", hexString[len - 4], hexString[len - 4]), a)) {
171 					// error
172 					color = SKColor.Empty;
173 					return false;
174 				}
175 			} else {
176 				a = cast(ubyte) 255;
177 			}
178 
179 			// parse RGB
180 			ubyte r, g, b;
181 			if (!tryParse(format("%c%c", hexString[len - 3],
182 					hexString[len - 3]), r) || !tryParse(format("%c%c", hexString[len - 2],
183 					hexString[len - 2]), g) || !tryParse(format("%c%c",
184 					hexString[len - 1], hexString[len - 1]), b)) {
185 				// error
186 				color = SKColor.Empty;
187 				return false;
188 			}
189 
190 			// success
191 			color = SKColor(r, g, b, a);
192 			return true;
193 		}
194 
195 		if (len == 6 || len == 8) {
196 			// parse [AA]RRGGBB
197 			uint number = 0;
198 
199 			if (!tryParse(hexString, number)) {
200 				// error
201 				color = SKColor.Empty;
202 				return false;
203 			}
204 
205 			// success
206 			color = SKColor(number);
207 
208 			// alpha was not provided, so use 255
209 			if (len == 6) {
210 				color = color.WithAlpha(cast(ubyte) 255);
211 			}
212 			return true;
213 		}
214 
215 		// error
216 		color = SKColor.Empty;
217 		return false;
218 	}
219 
220 	// parse [AA]RRGGBB
221 	static bool tryParse(string hexString, out uint color) {
222 		try {
223 			color = to!uint(hexString, 16);
224 			return true;
225 		} catch (Exception ex) {
226 			warningf("Hex: %s, Error: %s", hexString, ex.msg);
227 			return false;
228 		}
229 	}
230 
231 	// parse CC
232 	static bool tryParse(string hexString, out ubyte data) {
233 		try {
234 			data = to!ubyte(hexString, 16);
235 			return true;
236 		} catch (Exception ex) {
237 			warningf("Hex: %s, Error: %s", hexString, ex.msg);
238 			return false;
239 		}
240 	}
241 
242 	SKColorF opCast(T)() if (is(T == SKColorF)) {
243 		SKColorF colorF;
244 		SkiaApi.sk_color4f_from_color(color, &colorF);
245 		return colorF;
246 	}
247 }