1 // Written in the D programming language.
2
3 /**
4 This module contains declaration of useful color related operations.
5
6 In dlangui, colors are represented as 32 bit uint AARRGGBB values.
7
8 Synopsis:
9
10 ----
11 import dlangui.graphics.colors;
12
13 ----
14
15 Copyright: Vadim Lopatin, 2015
16 License: Boost License 1.0
17 Authors: Vadim Lopatin, coolreader.org@gmail.com
18 */
19 module dlangui.graphics.colors;
20
21 import dlangui.core.types;
22
23 import std.string;
24 import std.algorithm;
25 import std.traits;
26 import std.conv;
27 import std.range;
28
29 /// special color constant to identify value as not a color (to use default/parent value instead)
30 immutable uint COLOR_UNSPECIFIED = 0xFFDEADFF;
31 /// transparent color constant
32 immutable uint COLOR_TRANSPARENT = 0xFFFFFFFF;
33
34 immutable string COLOR_DRAWABLE = "#color";
35
36 /// Color constants enum, contributed by zhaopuming
37 /// refer to http://rapidtables.com/web/color/RGB_Color.htm#color%20table
38 /// #275
39 enum Color {
40 maroon = 0x800000,
41 dark_red = 0x8B0000,
42 brown = 0xA52A2A,
43 firebrick = 0xB22222,
44 crimson = 0xDC143C,
45 red = 0xFF0000,
46 tomato = 0xFF6347,
47 coral = 0xFF7F50,
48 indian_red = 0xCD5C5C,
49 light_coral = 0xF08080,
50 dark_salmon = 0xE9967A,
51 salmon = 0xFA8072,
52 light_salmon = 0xFFA07A,
53 orange_red = 0xFF4500,
54 dark_orange = 0xFF8C00,
55 orange = 0xFFA500,
56 gold = 0xFFD700,
57 dark_golden_rod = 0xB8860B,
58 golden_rod = 0xDAA520,
59 pale_golden_rod = 0xEEE8AA,
60 dark_khaki = 0xBDB76B,
61 khaki = 0xF0E68C,
62 olive = 0x808000,
63 yellow = 0xFFFF00,
64 yellow_green = 0x9ACD32,
65 dark_olive_green = 0x556B2F,
66 olive_drab = 0x6B8E23,
67 lawn_green = 0x7CFC00,
68 chart_reuse = 0x7FFF00,
69 green_yellow = 0xADFF2F,
70 dark_green = 0x006400,
71 green = 0x008000,
72 forest_green = 0x228B22,
73 lime = 0x00FF00,
74 lime_green = 0x32CD32,
75 light_green = 0x90EE90,
76 pale_green = 0x98FB98,
77 dark_sea_green = 0x8FBC8F,
78 medium_spring_green = 0x00FA9A,
79 spring_green = 0x00FF7F,
80 sea_green = 0x2E8B57,
81 medium_aqua_marine = 0x66CDAA,
82 medium_sea_green = 0x3CB371,
83 light_sea_green = 0x20B2AA,
84 dark_slate_gray = 0x2F4F4F,
85 teal = 0x008080,
86 dark_cyan = 0x008B8B,
87 aqua = 0x00FFFF,
88 cyan = 0x00FFFF,
89 light_cyan = 0xE0FFFF,
90 dark_turquoise = 0x00CED1,
91 turquoise = 0x40E0D0,
92 medium_turquoise = 0x48D1CC,
93 pale_turquoise = 0xAFEEEE,
94 aqua_marine = 0x7FFFD4,
95 powder_blue = 0xB0E0E6,
96 cadet_blue = 0x5F9EA0,
97 steel_blue = 0x4682B4,
98 corn_flower_blue = 0x6495ED,
99 deep_sky_blue = 0x00BFFF,
100 dodger_blue = 0x1E90FF,
101 light_blue = 0xADD8E6,
102 sky_blue = 0x87CEEB,
103 light_sky_blue = 0x87CEFA,
104 midnight_blue = 0x191970,
105 navy = 0x000080,
106 dark_blue = 0x00008B,
107 medium_blue = 0x0000CD,
108 blue = 0x0000FF,
109 royal_blue = 0x4169E1,
110 blue_violet = 0x8A2BE2,
111 indigo = 0x4B0082,
112 dark_slate_blue = 0x483D8B,
113 slate_blue = 0x6A5ACD,
114 medium_slate_blue = 0x7B68EE,
115 medium_purple = 0x9370DB,
116 dark_magenta = 0x8B008B,
117 dark_violet = 0x9400D3,
118 dark_orchid = 0x9932CC,
119 medium_orchid = 0xBA55D3,
120 purple = 0x800080,
121 thistle = 0xD8BFD8,
122 plum = 0xDDA0DD,
123 violet = 0xEE82EE,
124 magenta = 0xFF00FF,
125 fuchsia = 0xFF00FF,
126 orchid = 0xDA70D6,
127 medium_violet_red = 0xC71585,
128 pale_violet_red = 0xDB7093,
129 deep_pink = 0xFF1493,
130 hot_pink = 0xFF69B4,
131 light_pink = 0xFFB6C1,
132 pink = 0xFFC0CB,
133 antique_white = 0xFAEBD7,
134 beige = 0xF5F5DC,
135 bisque = 0xFFE4C4,
136 blanched_almond = 0xFFEBCD,
137 wheat = 0xF5DEB3,
138 corn_silk = 0xFFF8DC,
139 lemon_chiffon = 0xFFFACD,
140 light_golden_rod_yellow = 0xFAFAD2,
141 light_yellow = 0xFFFFE0,
142 saddle_brown = 0x8B4513,
143 sienna = 0xA0522D,
144 chocolate = 0xD2691E,
145 peru = 0xCD853F,
146 sandy_brown = 0xF4A460,
147 burly_wood = 0xDEB887,
148 tan = 0xD2B48C,
149 rosy_brown = 0xBC8F8F,
150 moccasin = 0xFFE4B5,
151 navajo_white = 0xFFDEAD,
152 peach_puff = 0xFFDAB9,
153 misty_rose = 0xFFE4E1,
154 lavender_blush = 0xFFF0F5,
155 linen = 0xFAF0E6,
156 old_lace = 0xFDF5E6,
157 papaya_whip = 0xFFEFD5,
158 sea_shell = 0xFFF5EE,
159 mint_cream = 0xF5FFFA,
160 slate_gray = 0x708090,
161 light_slate_gray = 0x778899,
162 light_steel_blue = 0xB0C4DE,
163 lavender = 0xE6E6FA,
164 floral_white = 0xFFFAF0,
165 alice_blue = 0xF0F8FF,
166 ghost_white = 0xF8F8FF,
167 honeydew = 0xF0FFF0,
168 ivory = 0xFFFFF0,
169 azure = 0xF0FFFF,
170 snow = 0xFFFAFA,
171 black = 0x000000,
172 dim_gray = 0x696969,
173 gray = 0x808080,
174 dark_gray = 0xA9A9A9,
175 silver = 0xC0C0C0,
176 light_gray = 0xD3D3D3,
177 gainsboro = 0xDCDCDC,
178 white_smoke = 0xF5F5F5,
179 white = 0xFFFFFF,
180
181 }
182
183 immutable uint COLOR_TRANSFORM_OFFSET_NONE = 0x80808080;
184 immutable uint COLOR_TRANSFORM_MULTIPLY_NONE = 0x40404040;
185
186
187 uint makeRGBA(T)(T r, T g, T b, T a) pure nothrow {
188 return (cast(uint)a << 24)|(cast(uint)r << 16)|(cast(uint)g << 8)|(cast(uint)b);
189 }
190
191 /// blend two RGB pixels using alpha
192 uint blendARGB(uint dst, uint src, uint alpha) pure nothrow {
193 uint srca = (src >> 24) & 0xFF;
194 uint dsta = (dst >> 24) & 0xFF;
195
196 uint srcr = (src >> 16) & 0xFF;
197 uint srcg = (src >> 8) & 0xFF;
198 uint srcb = (src >> 0) & 0xFF;
199
200 uint dstr = (dst >> 16) & 0xFF;
201 uint dstg = (dst >> 8) & 0xFF;
202 uint dstb = (dst >> 0) & 0xFF;
203
204 uint ialpha = 255 - alpha;
205
206 uint r = ((srcr * ialpha + dstr * alpha) >> 8) & 0xFF;
207 uint g = ((srcg * ialpha + dstg * alpha) >> 8) & 0xFF;
208 uint b = ((srcb * ialpha + dstb * alpha) >> 8) & 0xFF;
209
210 uint a = ((srca * ialpha + dsta * alpha) >> 8) & 0xFF;
211
212 return (a << 24) | (r << 16) | (g << 8) | b;
213 }
214
215 //immutable int[3] COMPONENT_OFFSET_BGR = [2, 1, 0];
216 immutable int[3] COMPONENT_OFFSET_BGR = [2, 1, 0];
217 //immutable int[3] COMPONENT_OFFSET_BGR = [1, 2, 0];
218 immutable int[3] COMPONENT_OFFSET_RGB = [0, 1, 2];
219 immutable int COMPONENT_OFFSET_ALPHA = 3;
220 int subpixelComponentIndex(int x0, SubpixelRenderingMode mode) pure nothrow {
221 switch (mode) with(SubpixelRenderingMode) {
222 case RGB:
223 return COMPONENT_OFFSET_BGR[x0];
224 case BGR:
225 default:
226 return COMPONENT_OFFSET_BGR[x0];
227 }
228 }
229
230 /// blend subpixel using alpha
231 void blendSubpixel(ubyte * dst, ubyte * src, uint alpha, int x0, SubpixelRenderingMode mode) {
232 uint dstalpha = dst[COMPONENT_OFFSET_ALPHA];
233 int offset = subpixelComponentIndex(x0, mode);
234 uint srcr = src[offset];
235 dst[COMPONENT_OFFSET_ALPHA] = 0;
236 if (dstalpha > 0x80) {
237 dst[offset] = cast(ubyte)srcr;
238 return;
239 }
240 uint dstr = dst[offset];
241 uint ialpha = 256 - alpha;
242 uint r = ((srcr * ialpha + dstr * alpha) >> 8) & 0xFF;
243 dst[offset] = cast(ubyte)r;
244 }
245
246 /// blend two alpha values 0..255 (255 is fully transparent, 0 is opaque)
247 uint blendAlpha(uint a1, uint a2) pure nothrow {
248 if (!a1)
249 return a2;
250 if (!a2)
251 return a1;
252 return (((a1 ^ 0xFF) * (a2 ^ 0xFF)) >> 8) ^ 0xFF;
253 }
254
255 /// applies additional alpha to color
256 uint addAlpha(uint color, uint alpha) pure nothrow {
257 alpha = blendAlpha(color >> 24, alpha);
258 return (color & 0xFFFFFF) | (alpha << 24);
259 }
260
261 ubyte rgbToGray(uint color) pure nothrow {
262 uint srcr = (color >> 16) & 0xFF;
263 uint srcg = (color >> 8) & 0xFF;
264 uint srcb = (color >> 0) & 0xFF;
265 return cast(uint)(((srcr + srcg + srcg + srcb) >> 2) & 0xFF);
266 }
267
268
269 // todo
270 struct ColorTransformHandler {
271 void initialize(ref ColorTransform transform) {
272
273 }
274 uint transform(uint color) {
275 return color;
276 }
277 }
278
279 uint transformComponent(int src, int addBefore, int multiply, int addAfter) pure nothrow {
280 int add1 = (cast(int)(addBefore << 1)) - 0x100;
281 int add2 = (cast(int)(addAfter << 1)) - 0x100;
282 int mul = cast(int)(multiply << 2);
283 int res = (((src + add1) * mul) >> 8) + add2;
284 if (res < 0)
285 res = 0;
286 else if (res > 255)
287 res = 255;
288 return cast(uint)res;
289 }
290
291 uint transformRGBA(uint src, uint addBefore, uint multiply, uint addAfter) pure nothrow {
292 uint a = transformComponent(src >> 24, addBefore >> 24, multiply >> 24, addAfter >> 24);
293 uint r = transformComponent((src >> 16) & 0xFF, (addBefore >> 16) & 0xFF, (multiply >> 16) & 0xFF, (addAfter >> 16) & 0xFF);
294 uint g = transformComponent((src >> 8) & 0xFF, (addBefore >> 8) & 0xFF, (multiply >> 8) & 0xFF, (addAfter >> 8) & 0xFF);
295 uint b = transformComponent(src & 0xFF, addBefore & 0xFF, multiply & 0xFF, addAfter & 0xFF);
296 return (a << 24) | (r << 16) | (g << 8) | b;
297 }
298
299 struct ColorTransform {
300 uint addBefore = COLOR_TRANSFORM_OFFSET_NONE;
301 uint multiply = COLOR_TRANSFORM_MULTIPLY_NONE;
302 uint addAfter = COLOR_TRANSFORM_OFFSET_NONE;
303 @property bool empty() const {
304 return addBefore == COLOR_TRANSFORM_OFFSET_NONE
305 && multiply == COLOR_TRANSFORM_MULTIPLY_NONE
306 && addAfter == COLOR_TRANSFORM_OFFSET_NONE;
307 }
308 uint transform(uint color) {
309 return transformRGBA(color, addBefore, multiply, addAfter);
310 }
311 }
312
313
314 /// blend two RGB pixels using alpha
315 ubyte blendGray(ubyte dst, ubyte src, uint alpha) pure nothrow {
316 uint ialpha = 256 - alpha;
317 return cast(ubyte)(((src * ialpha + dst * alpha) >> 8) & 0xFF);
318 }
319
320 /// returns true if color is #FFxxxxxx (color alpha is 255)
321 bool isFullyTransparentColor(uint color) pure nothrow {
322 return (color >> 24) == 0xFF;
323 }
324
325 /// decode color string supported formats: #RGB, #ARGB, #RRGGBB, #AARRGGBB, rgb(r,g,b), rgba(r,g,b,a), rgba(r,g,b,a%)
326 //TODO: the name doesn't match function
327 uint decodeHexColor(string s, uint defValue = 0) @safe
328 {
329 s = s.strip.toLower;
330 return decodeColorInternal(s, defValue);
331 }
332 ///
333 @safe unittest
334 {
335 assert(decodeHexColor("") == 0);
336 assert(decodeHexColor("@null") == COLOR_TRANSPARENT);
337 assert(decodeHexColor("trAnsParent") == COLOR_TRANSPARENT);
338 assert(decodeHexColor("grAy") == 0x808080);
339 assert(decodeHexColor("#8B008B") == 0x8B008B);
340 assert(decodeHexColor("#fFf") == 0xfff);
341 assert(decodeHexColor("#f0F0") == 0xf0f0);
342 assert(decodeHexColor("#80ff0000") == 0x80ff0000);
343 assert(decodeHexColor("rgba(255, 0, 0,.5 )") == 0x80ff0000);
344 assert(decodeHexColor("rgba(255,0, 0, 50%)") == 0x80ff0000);
345 assert(decodeHexColor("rgba(255,0, 0, 100%)") == 0xff0000);
346 assert(decodeHexColor("rgba(255,0, 0, 0%)") == 0x00000000);
347 assert(decodeHexColor("rgb(255,255, 255)") == 0xffffff);
348 assert(decodeHexColor("rgba(255,0, 0, 150%)") == 0xff0000); // invalid input
349 assert(decodeHexColor("rgba(255,0, 0, -34%)") == 0x00000000); // invalid input
350 assert(decodeHexColor("rgb(321,321,321)") == 0xffffff); // invalid input
351 assert(decodeHexColor("not_valid_color_name") == 0x00000000); // invalid input, return def value
352 assert(decodeHexColor("#80ff00000") == 0x000000000); // invalid input, return def value
353 assert(decodeHexColor("#f0") == 0x00000000); // invalid input, return def value
354 assert(decodeHexColor("rgba(255,255, 255, 10)") == 0xffffff); // invalid input
355 assert(decodeHexColor("rgba(444,0, 0, -5)") == 0x00000000); // invalid input
356 }
357
358 uint decodeCSSColor(string s, uint defValue = 0) @safe
359 {
360 s = s.strip.toLower;
361 if (s.startsWith("#")) {
362 if (s.length.among(7, 9)) { //#RRGGBB #AARRGGBB
363 //s = s[1 .. $];
364 import std.stdio : writeln;
365 auto d = s[1 .. $];
366 auto color = parse!uint(d, 16); //RGB(A) by default
367 if (s.length == 5)
368 { //RGBA
369 color = ((color & 0xF00) >> 4) | ((color & 0xF0) << 8) | ((color & 0xF) << 20);
370 }
371 else if (s.length == 9)
372 { //RRGGBBAA
373 color = ((color & 0xFF) << 24) | (color >> 8);
374 }
375 return color;
376 }
377 return defValue;
378 }
379 return decodeColorInternal(s, defValue);
380 }
381 ///
382 @safe unittest
383 {
384 assert(decodeCSSColor("gray") == Color.gray);
385 assert(decodeCSSColor("#AABBCC80") == 0x80AABBCC);
386 }
387
388 @safe unittest
389 {
390 assert(decodeCSSColor("#AABBCC80") == decodeColorInternal("#80AABBCC"));
391 assert(decodeCSSColor("#FF000080") == decodeColorInternal("#80FF0000"));
392 assert(decodeCSSColor("#0000FF80") == decodeColorInternal("#800000FF"));
393 }
394
395
396 private uint decodeColorInternal(string s, uint defValue = 0) @safe
397 {
398 if (s.empty)
399 return defValue;
400 if (s == "@null" || s == "transparent")
401 return COLOR_TRANSPARENT;
402 if (s.startsWith("#")) {
403 if (s.length.among(4, 5, 7, 9)) { //#RGB #ARGB #RRGGBB #AARRGGBB
404 s = s[1 .. $];
405 auto color = parse!uint(s, 16); //RGB(A) by default
406 return color;
407 }
408 return defValue;
409 }
410 else if (s.startsWith("rgba(") && s.endsWith(")"))
411 {
412 s = s[5 .. $ - 1];
413 auto parts = s.split(",");
414 if (parts.length != 4)
415 return defValue;
416 uint r = to!uint(parts[0].strip).clamp(0, 255);
417 uint g = to!uint(parts[1].strip).clamp(0, 255);
418 uint b = to!uint(parts[2].strip).clamp(0, 255);
419 uint a = 255;
420 auto ap = parts[3].strip;
421 if (ap.endsWith("%")) { //rgba(r,g,b,a%)
422 auto alpha = to!float(ap[0 .. $ - 1].strip);
423 a = cast(uint)((alpha * 255.0 / 100.0) + 0.5).clamp(0, 255);
424 }
425 else { //rgba(r,g,b,a)
426 auto alpha = to!float(parts[3].strip);
427 a = cast(uint)(alpha * 255.0 + 0.5).clamp(0, 255);
428 }
429 if(a == 255)
430 return (r << 16) | (g << 8) | b;
431 return (a == 0) ? 0x00000000 : (a << 24) | (r << 16) | (g << 8) | b;
432 }
433 else if (s.startsWith("rgb(") && s.endsWith(")"))
434 {
435 s = s[4 .. $ - 1];
436 auto parts = s.split(",");
437 if (parts.length != 3)
438 return defValue;
439 uint r = to!uint(parts[0].strip).clamp(0, 255);
440 uint g = to!uint(parts[1].strip).clamp(0, 255);
441 uint b = to!uint(parts[2].strip).clamp(0, 255);
442 return (r << 16) | (g << 8) | b;
443 }
444 foreach (color; __traits(allMembers, Color))
445 {
446 if (color == s)
447 return color.to!Color;
448 }
449 return defValue;
450 }