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 }