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 }