1 // Written in the D programming language. 2 3 /** 4 This package defines human-visible colors in various formats. 5 6 RGB _color formats are particularly flexible to express typical RGB image data 7 in a wide variety of common formats without having to write adapters. 8 9 It is intended that this library facilitate a common API that allows a variety 10 of image and multimedia libraries to interact more seamlessly, without need 11 for constant conversions between custom, library-defined _color data types. 12 13 This package pays very careful attention to correctness with respect to 14 _color space definitions, and correct handling of _color space conversions. 15 For best results, users should also pay careful attention to _color space 16 selection when working with _color data, and the rest will follow. 17 A crash course on understanding _color space can be found at 18 $(LINK2 https://en.wikipedia.org/wiki/Color_space, wikipedia). 19 20 More information regarding specific _color spaces can be found in their 21 respective modules. 22 23 All types and functions offered in this package are $(D_INLINECODE pure), 24 $(D_INLINECODE nothrow), $(D_INLINECODE @safe) and $(D_INLINECODE @nogc). 25 It is intended to be useful by realtime or memory-contrained systems such as 26 video games, games consoles or mobile devices. 27 28 29 Expressing images: 30 31 Images may be expressed in a variety of ways, but a simple way may be to use 32 std.experimental.ndslice to produce simple n-dimensional images. 33 34 ------- 35 import std.experimental.color; 36 import std.experimental.ndslice; 37 38 auto imageBuffer = new RGB8[height*width]; 39 auto image = imageBuffer.sliced(height, width); 40 41 foreach(ref row; image) 42 { 43 foreach(ref pixel; row) 44 { 45 pixel = Colors.white; 46 } 47 } 48 ------- 49 50 Use of ndslice this way allows the use of n-dimentional slices to produce 51 sub-images. 52 53 54 Implement custom _color type: 55 56 The library is extensible such that users or libraries can easily supply 57 their own custom _color formats and expect comprehensive conversion and 58 interaction with any other libraries or code that makes use of 59 std.experimental._color. 60 61 The requirement for a user _color type is to specify a 'parent' _color space, 62 and expose at least a set of conversion functions to/from that parent. 63 64 For instance, HSV is a cylindrical representation of RGB colors, so the 65 'parent' _color type in this case is said to be RGB. 66 If your custom _color space is not derivative of an existing _color space, 67 then you should provide conversion between CIE XYZ, which can most simply 68 express all of human-visible _color. 69 70 ------- 71 struct HueOnlyColor 72 { 73 alias ParentColor = HSV!float; 74 75 static To convertColorImpl(To, From)(From color) if (is(From == HueOnlyColor) && isHSx!To) 76 { 77 return To(color.hue, 1.0, 1.0); // assume maximum saturation, maximum lightness 78 } 79 80 static To convertColorImpl(To, From)(From color) if (isHSx!From && is(To == HueOnlyColor)) 81 { 82 return HueOnlyColor(color.h); // just keep the hue 83 } 84 85 private: 86 float hue; 87 } 88 89 static assert(isColor!HueOnlyColor == true, "This is all that is required to create a valid color type"); 90 ------- 91 92 If your _color type has template args, it may also be necessary to produce a 93 third convertColorImpl function that converts between instantiations with 94 different template args. 95 96 97 Authors: Manu Evans 98 Copyright: Copyright (c) 2015, Manu Evans. 99 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0) 100 Source: $(PHOBOSSRC std/experimental/color/_package.d) 101 */ 102 module std.experimental.color; 103 104 import std.experimental.color.rgb; 105 import std.experimental.color.xyz : XYZ; 106 import std.experimental.normint; 107 108 import std.traits : isNumeric, isFloatingPoint, isSomeChar, Unqual; 109 110 111 /** 112 Detect whether $(D_INLINECODE T) is a color type compatible with std.experimental.color. 113 */ 114 enum isColor(T) = __traits(compiles, convertColor!(XYZ!float)(T.init)); 115 116 /// 117 unittest 118 { 119 import std.experimental.color.rgb; 120 import std.experimental.color.hsx; 121 import std.experimental.color.xyz; 122 123 static assert(isColor!RGB8 == true); 124 static assert(isColor!(XYZ!float) == true); 125 static assert(isColor!(HSL!float) == true); 126 static assert(isColor!float == false); 127 } 128 129 /** 130 Detect whether $(D_INLINECODE T) is a valid color component type. 131 */ 132 enum isColorComponentType(T) = isFloatingPoint!T || is(T == NormalizedInt!U, U); 133 134 /** 135 Detect whether $(D_INLINECODE T) can represent a color component. 136 */ 137 enum isColorScalarType(T) = isNumeric!T || is(T == NormalizedInt!U, U); 138 139 140 // declare some common color types 141 142 /** 24 bit RGB color type with 8 bits per channel. */ 143 alias RGB8 = RGB!("rgb", ubyte); 144 /** 32 bit RGB color type with 8 bits per channel. */ 145 alias RGBX8 = RGB!("rgbx", ubyte); 146 /** 32 bit RGB + alpha color type with 8 bits per channel. */ 147 alias RGBA8 = RGB!("rgba", ubyte); 148 149 /** Floating point RGB color type. */ 150 alias RGBf32 = RGB!("rgb", float); 151 /** Floating point RGB + alpha color type. */ 152 alias RGBAf32 = RGB!("rgba", float); 153 154 /** 24 bit BGR color type with 8 bits per channel. */ 155 alias BGR8 = RGB!("bgr", ubyte); 156 /** 32 bit BGR color type with 8 bits per channel. */ 157 alias BGRX8 = RGB!("bgrx", ubyte); 158 /** 32 bit BGR + alpha color type with 8 bits per channel. */ 159 alias BGRA8 = RGB!("bgra", ubyte); 160 161 /** 8 bit luminance-only color type. */ 162 alias L8 = RGB!("l", ubyte); 163 /** 8 bit alpha-only color type. */ 164 alias A8 = RGB!("a", ubyte); 165 /** 16 bit luminance + alpha color type with 8 bits per channel. */ 166 alias LA8 = RGB!("la", ubyte); 167 168 /** 16 bit signed UV color type with 8 bits per channel. */ 169 alias UV8 = RGB!("rg", byte); 170 /** 24 bit signed UVW color type with 8 bits per channel. */ 171 alias UVW8 = RGB!("rgb", byte); 172 173 174 /** Set of colors defined by X11, adopted by the W3C, SVG, and other popular libraries. */ 175 enum Colors 176 { 177 aliceBlue = RGB8(240,248,255), /// <font color=aliceBlue>◼</font> 178 antiqueWhite = RGB8(250,235,215), /// <font color=antiqueWhite>◼</font> 179 aqua = RGB8(0,255,255), /// <font color=aqua>◼</font> 180 aquamarine = RGB8(127,255,212), /// <font color=aquamarine>◼</font> 181 azure = RGB8(240,255,255), /// <font color=azure>◼</font> 182 beige = RGB8(245,245,220), /// <font color=beige>◼</font> 183 bisque = RGB8(255,228,196), /// <font color=bisque>◼</font> 184 black = RGB8(0,0,0), /// <font color=black>◼</font> 185 blanchedAlmond = RGB8(255,235,205), /// <font color=blanchedAlmond>◼</font> 186 blue = RGB8(0,0,255), /// <font color=blue>◼</font> 187 blueViolet = RGB8(138,43,226), /// <font color=blueViolet>◼</font> 188 brown = RGB8(165,42,42), /// <font color=brown>◼</font> 189 burlyWood = RGB8(222,184,135), /// <font color=burlyWood>◼</font> 190 cadetBlue = RGB8(95,158,160), /// <font color=cadetBlue>◼</font> 191 chartreuse = RGB8(127,255,0), /// <font color=chartreuse>◼</font> 192 chocolate = RGB8(210,105,30), /// <font color=chocolate>◼</font> 193 coral = RGB8(255,127,80), /// <font color=coral>◼</font> 194 cornflowerBlue = RGB8(100,149,237), /// <font color=cornflowerBlue>◼</font> 195 cornsilk = RGB8(255,248,220), /// <font color=cornsilk>◼</font> 196 crimson = RGB8(220,20,60), /// <font color=crimson>◼</font> 197 cyan = RGB8(0,255,255), /// <font color=cyan>◼</font> 198 darkBlue = RGB8(0,0,139), /// <font color=darkBlue>◼</font> 199 darkCyan = RGB8(0,139,139), /// <font color=darkCyan>◼</font> 200 darkGoldenrod = RGB8(184,134,11), /// <font color=darkGoldenrod>◼</font> 201 darkGray = RGB8(169,169,169), /// <font color=darkGray>◼</font> 202 darkGrey = RGB8(169,169,169), /// <font color=darkGrey>◼</font> 203 darkGreen = RGB8(0,100,0), /// <font color=darkGreen>◼</font> 204 darkKhaki = RGB8(189,183,107), /// <font color=darkKhaki>◼</font> 205 darkMagenta = RGB8(139,0,139), /// <font color=darkMagenta>◼</font> 206 darkOliveGreen = RGB8(85,107,47), /// <font color=darkOliveGreen>◼</font> 207 darkOrange = RGB8(255,140,0), /// <font color=darkOrange>◼</font> 208 darkOrchid = RGB8(153,50,204), /// <font color=darkOrchid>◼</font> 209 darkRed = RGB8(139,0,0), /// <font color=darkRed>◼</font> 210 darkSalmon = RGB8(233,150,122), /// <font color=darkSalmon>◼</font> 211 darkSeaGreen = RGB8(143,188,143), /// <font color=darkSeaGreen>◼</font> 212 darkSlateBlue = RGB8(72,61,139), /// <font color=darkSlateBlue>◼</font> 213 darkSlateGray = RGB8(47,79,79), /// <font color=darkSlateGray>◼</font> 214 darkSlateGrey = RGB8(47,79,79), /// <font color=darkSlateGrey>◼</font> 215 darkTurquoise = RGB8(0,206,209), /// <font color=darkTurquoise>◼</font> 216 darkViolet = RGB8(148,0,211), /// <font color=darkViolet>◼</font> 217 deepPink = RGB8(255,20,147), /// <font color=deepPink>◼</font> 218 deepSkyBlue = RGB8(0,191,255), /// <font color=deepSkyBlue>◼</font> 219 dimGray = RGB8(105,105,105), /// <font color=dimGray>◼</font> 220 dimGrey = RGB8(105,105,105), /// <font color=dimGrey>◼</font> 221 dodgerBlue = RGB8(30,144,255), /// <font color=dodgerBlue>◼</font> 222 fireBrick = RGB8(178,34,34), /// <font color=fireBrick>◼</font> 223 floralWhite = RGB8(255,250,240), /// <font color=floralWhite>◼</font> 224 forestGreen = RGB8(34,139,34), /// <font color=forestGreen>◼</font> 225 fuchsia = RGB8(255,0,255), /// <font color=fuchsia>◼</font> 226 gainsboro = RGB8(220,220,220), /// <font color=gainsboro>◼</font> 227 ghostWhite = RGB8(248,248,255), /// <font color=ghostWhite>◼</font> 228 gold = RGB8(255,215,0), /// <font color=gold>◼</font> 229 goldenrod = RGB8(218,165,32), /// <font color=goldenrod>◼</font> 230 gray = RGB8(128,128,128), /// <font color=gray>◼</font> 231 grey = RGB8(128,128,128), /// <font color=grey>◼</font> 232 green = RGB8(0,128,0), /// <font color=green>◼</font> 233 greenYellow = RGB8(173,255,47), /// <font color=greenYellow>◼</font> 234 honeydew = RGB8(240,255,240), /// <font color=honeydew>◼</font> 235 hotPink = RGB8(255,105,180), /// <font color=hotPink>◼</font> 236 indianRed = RGB8(205,92,92), /// <font color=indianRed>◼</font> 237 indigo = RGB8(75,0,130), /// <font color=indigo>◼</font> 238 ivory = RGB8(255,255,240), /// <font color=ivory>◼</font> 239 khaki = RGB8(240,230,140), /// <font color=khaki>◼</font> 240 lavender = RGB8(230,230,250), /// <font color=lavender>◼</font> 241 lavenderBlush = RGB8(255,240,245), /// <font color=lavenderBlush>◼</font> 242 lawnGreen = RGB8(124,252,0), /// <font color=lawnGreen>◼</font> 243 lemonChiffon = RGB8(255,250,205), /// <font color=lemonChiffon>◼</font> 244 lightBlue = RGB8(173,216,230), /// <font color=lightBlue>◼</font> 245 lightCoral = RGB8(240,128,128), /// <font color=lightCoral>◼</font> 246 lightCyan = RGB8(224,255,255), /// <font color=lightCyan>◼</font> 247 lightGoldenrodYellow = RGB8(250,250,210), /// <font color=lightGoldenrodYellow>◼</font> 248 lightGray = RGB8(211,211,211), /// <font color=lightGray>◼</font> 249 lightGrey = RGB8(211,211,211), /// <font color=lightGrey>◼</font> 250 lightGreen = RGB8(144,238,144), /// <font color=lightGreen>◼</font> 251 lightPink = RGB8(255,182,193), /// <font color=lightPink>◼</font> 252 lightSalmon = RGB8(255,160,122), /// <font color=lightSalmon>◼</font> 253 lightSeaGreen = RGB8(32,178,170), /// <font color=lightSeaGreen>◼</font> 254 lightSkyBlue = RGB8(135,206,250), /// <font color=lightSkyBlue>◼</font> 255 lightSlateGray = RGB8(119,136,153), /// <font color=lightSlateGray>◼</font> 256 lightSlateGrey = RGB8(119,136,153), /// <font color=lightSlateGrey>◼</font> 257 lightSteelBlue = RGB8(176,196,222), /// <font color=lightSteelBlue>◼</font> 258 lightYellow = RGB8(255,255,224), /// <font color=lightYellow>◼</font> 259 lime = RGB8(0,255,0), /// <font color=lime>◼</font> 260 limeGreen = RGB8(50,205,50), /// <font color=limeGreen>◼</font> 261 linen = RGB8(250,240,230), /// <font color=linen>◼</font> 262 magenta = RGB8(255,0,255), /// <font color=magenta>◼</font> 263 maroon = RGB8(128,0,0), /// <font color=maroon>◼</font> 264 mediumAquamarine = RGB8(102,205,170), /// <font color=mediumAquamarine>◼</font> 265 mediumBlue = RGB8(0,0,205), /// <font color=mediumBlue>◼</font> 266 mediumOrchid = RGB8(186,85,211), /// <font color=mediumOrchid>◼</font> 267 mediumPurple = RGB8(147,112,219), /// <font color=mediumPurple>◼</font> 268 mediumSeaGreen = RGB8(60,179,113), /// <font color=mediumSeaGreen>◼</font> 269 mediumSlateBlue = RGB8(123,104,238), /// <font color=mediumSlateBlue>◼</font> 270 mediumSpringGreen = RGB8(0,250,154), /// <font color=mediumSpringGreen>◼</font> 271 mediumTurquoise = RGB8(72,209,204), /// <font color=mediumTurquoise>◼</font> 272 mediumVioletRed = RGB8(199,21,133), /// <font color=mediumVioletRed>◼</font> 273 midnightBlue = RGB8(25,25,112), /// <font color=midnightBlue>◼</font> 274 mintCream = RGB8(245,255,250), /// <font color=mintCream>◼</font> 275 mistyRose = RGB8(255,228,225), /// <font color=mistyRose>◼</font> 276 moccasin = RGB8(255,228,181), /// <font color=moccasin>◼</font> 277 navajoWhite = RGB8(255,222,173), /// <font color=navajoWhite>◼</font> 278 navy = RGB8(0,0,128), /// <font color=navy>◼</font> 279 oldLace = RGB8(253,245,230), /// <font color=oldLace>◼</font> 280 olive = RGB8(128,128,0), /// <font color=olive>◼</font> 281 oliveDrab = RGB8(107,142,35), /// <font color=oliveDrab>◼</font> 282 orange = RGB8(255,165,0), /// <font color=orange>◼</font> 283 orangeRed = RGB8(255,69,0), /// <font color=orangeRed>◼</font> 284 orchid = RGB8(218,112,214), /// <font color=orchid>◼</font> 285 paleGoldenrod = RGB8(238,232,170), /// <font color=paleGoldenrod>◼</font> 286 paleGreen = RGB8(152,251,152), /// <font color=paleGreen>◼</font> 287 paleTurquoise = RGB8(175,238,238), /// <font color=paleTurquoise>◼</font> 288 paleVioletRed = RGB8(219,112,147), /// <font color=paleVioletRed>◼</font> 289 papayaWhip = RGB8(255,239,213), /// <font color=papayaWhip>◼</font> 290 peachPuff = RGB8(255,218,185), /// <font color=peachPuff>◼</font> 291 peru = RGB8(205,133,63), /// <font color=peru>◼</font> 292 pink = RGB8(255,192,203), /// <font color=pink>◼</font> 293 plum = RGB8(221,160,221), /// <font color=plum>◼</font> 294 powderBlue = RGB8(176,224,230), /// <font color=powderBlue>◼</font> 295 purple = RGB8(128,0,128), /// <font color=purple>◼</font> 296 red = RGB8(255,0,0), /// <font color=red>◼</font> 297 rosyBrown = RGB8(188,143,143), /// <font color=rosyBrown>◼</font> 298 royalBlue = RGB8(65,105,225), /// <font color=royalBlue>◼</font> 299 saddleBrown = RGB8(139,69,19), /// <font color=saddleBrown>◼</font> 300 salmon = RGB8(250,128,114), /// <font color=salmon>◼</font> 301 sandyBrown = RGB8(244,164,96), /// <font color=sandyBrown>◼</font> 302 seaGreen = RGB8(46,139,87), /// <font color=seaGreen>◼</font> 303 seashell = RGB8(255,245,238), /// <font color=seashell>◼</font> 304 sienna = RGB8(160,82,45), /// <font color=sienna>◼</font> 305 silver = RGB8(192,192,192), /// <font color=silver>◼</font> 306 skyBlue = RGB8(135,206,235), /// <font color=skyBlue>◼</font> 307 slateBlue = RGB8(106,90,205), /// <font color=slateBlue>◼</font> 308 slateGray = RGB8(112,128,144), /// <font color=slateGray>◼</font> 309 slateGrey = RGB8(112,128,144), /// <font color=slateGrey>◼</font> 310 snow = RGB8(255,250,250), /// <font color=snow>◼</font> 311 springGreen = RGB8(0,255,127), /// <font color=springGreen>◼</font> 312 steelBlue = RGB8(70,130,180), /// <font color=steelBlue>◼</font> 313 tan = RGB8(210,180,140), /// <font color=tan>◼</font> 314 teal = RGB8(0,128,128), /// <font color=teal>◼</font> 315 thistle = RGB8(216,191,216), /// <font color=thistle>◼</font> 316 tomato = RGB8(255,99,71), /// <font color=tomato>◼</font> 317 turquoise = RGB8(64,224,208), /// <font color=turquoise>◼</font> 318 violet = RGB8(238,130,238), /// <font color=violet>◼</font> 319 wheat = RGB8(245,222,179), /// <font color=wheat>◼</font> 320 white = RGB8(255,255,255), /// <font color=white>◼</font> 321 whiteSmoke = RGB8(245,245,245), /// <font color=whiteSmoke>◼</font> 322 yellow = RGB8(255,255,0), /// <font color=yellow>◼</font> 323 yellowGreen = RGB8(154,205,50) /// <font color=yellowGreen>◼</font> 324 } 325 326 327 /** 328 Convert between _color types. 329 330 Conversion is always supported between any pair of valid _color types. 331 Colour types usually implement only direct conversion between their immediate 'parent' _color type. 332 In the case of distantly related colors, convertColor will follow a conversion path via 333 intermediate representations such that it is able to perform the conversion. 334 335 For instance, a conversion from HSV to Lab necessary follows the conversion path: HSV -> RGB -> XYZ -> Lab. 336 337 Params: color = A _color in some source format. 338 Returns: $(D_INLINECODE color) converted to the target format. 339 */ 340 To convertColor(To, From)(From color) @safe pure nothrow @nogc 341 { 342 // cast along a conversion path to reach our target conversion 343 alias Path = ConversionPath!(From, To); 344 345 // no conversion is necessary 346 static if (Path.length == 0) 347 return color; 348 else static if (Path.length > 1) 349 { 350 // we need to recurse to trace a path via the first common ancestor 351 static if (__traits(compiles, From.convertColorImpl!(Path[0])(color))) 352 return convertColor!To(From.convertColorImpl!(Path[0])(color)); 353 else 354 return convertColor!To(To.convertColorImpl!(Path[0])(color)); 355 } 356 else 357 { 358 static if (__traits(compiles, From.convertColorImpl!(Path[0])(color))) 359 return From.convertColorImpl!(Path[0])(color); 360 else 361 return To.convertColorImpl!(Path[0])(color); 362 } 363 } 364 /// 365 unittest 366 { 367 assert(convertColor!(RGBA8)(convertColor!(XYZ!float)(RGBA8(0xFF, 0xFF, 0xFF, 0xFF))) == RGBA8(0xFF, 0xFF, 0xFF, 0)); 368 } 369 370 371 /** 372 Create a color from a string. 373 374 Params: str = A string representation of a _color.$(BR) 375 May be a hex _color in the standard forms: (#/$)rgb/argb/rrggbb/aarrggbb$(BR) 376 May also be the name of any _color from the $(D_INLINECODE Colors) enum. 377 Returns: The _color expressed by the string. 378 Throws: Throws $(D_INLINECODE std.conv.ConvException) if the string is invalid. 379 */ 380 Color colorFromString(Color = RGB8)(scope const(char)[] str) pure @safe 381 { 382 import std.conv : ConvException; 383 384 uint error; 385 auto r = colorFromStringImpl(str, error); 386 387 if (error > 0) 388 { 389 if (error == 1) 390 throw new ConvException("Hex string has invalid length"); 391 throw new ConvException("String is not a valid color"); 392 } 393 394 return cast(Color)r; 395 } 396 397 /** 398 Create a color from a string. 399 400 This version of the function is $(D_INLINECODE nothrow), $(D_INLINECODE @nogc). 401 402 Params: str = A string representation of a _color.$(BR) 403 May be a hex _color in the standard forms: (#/$)rgb/argb/rrggbb/aarrggbb$(BR) 404 May also be the name of any _color from the $(D_INLINECODE Colors) enum. 405 color = Receives the _color expressed by the string. 406 Returns: $(D_INLINECODE true) if a _color was successfully parsed from the string, $(D_INLINECODE false) otherwise. 407 */ 408 bool colorFromString(Color = RGB8)(scope const(char)[] str, out Color color) pure nothrow @safe @nogc 409 { 410 uint error; 411 auto r = colorFromStringImpl(str, error); 412 if (!error) 413 { 414 color = cast(Color)r; 415 return true; 416 } 417 return false; 418 } 419 420 /// 421 unittest 422 { 423 // common hex formats supported: 424 425 // 3 digits 426 assert(colorFromString("F80") == RGB8(0xFF, 0x88, 0x00)); 427 assert(colorFromString("#F80") == RGB8(0xFF, 0x88, 0x00)); 428 assert(colorFromString("$F80") == RGB8(0xFF, 0x88, 0x00)); 429 430 // 6 digits 431 assert(colorFromString("FF8000") == RGB8(0xFF, 0x80, 0x00)); 432 assert(colorFromString("#FF8000") == RGB8(0xFF, 0x80, 0x00)); 433 assert(colorFromString("$FF8000") == RGB8(0xFF, 0x80, 0x00)); 434 435 // 4/8 digita (/w alpha) 436 assert(colorFromString!RGBA8("#8C41") == RGBA8(0xCC, 0x44, 0x11, 0x88)); 437 assert(colorFromString!RGBA8("#80CC4401") == RGBA8(0xCC, 0x44, 0x01, 0x80)); 438 439 // named colors (case-insensitive) 440 assert(colorFromString("red") == RGB8(0xFF, 0x0, 0x0)); 441 assert(colorFromString("WHITE") == RGB8(0xFF, 0xFF, 0xFF)); 442 assert(colorFromString("LightGoldenrodYellow") == RGB8(250,250,210)); 443 444 // parse failure 445 RGB8 c; 446 assert(colorFromString("Ultraviolet", c) == false); 447 } 448 449 450 package: 451 452 import std.traits : isInstanceOf, TemplateOf; 453 import std.typetuple : TypeTuple; 454 455 RGBA8 colorFromStringImpl(scope const(char)[] str, out uint error) pure nothrow @safe @nogc 456 { 457 static const(char)[] getHex(const(char)[] hex) pure nothrow @nogc @safe 458 { 459 if (hex.length > 0 && (hex[0] == '#' || hex[0] == '$')) 460 hex = hex[1..$]; 461 foreach (i; 0 .. hex.length) 462 { 463 if (!(hex[i] >= '0' && hex[i] <= '9' || hex[i] >= 'a' && hex[i] <= 'f' || hex[i] >= 'A' && hex[i] <= 'F')) 464 return null; 465 } 466 return hex; 467 } 468 469 const(char)[] hex = getHex(str); 470 if (hex) 471 { 472 static ubyte val(char c) pure nothrow @nogc @safe 473 { 474 if (c >= '0' && c <= '9') 475 return cast(ubyte)(c - '0'); 476 else if (c >= 'a' && c <= 'f') 477 return cast(ubyte)(c - 'a' + 10); 478 else 479 return cast(ubyte)(c - 'A' + 10); 480 } 481 482 if (hex.length == 3) 483 { 484 ubyte r = val(hex[0]); 485 ubyte g = val(hex[1]); 486 ubyte b = val(hex[2]); 487 return RGBA8(cast(ubyte)(r | (r << 4)), cast(ubyte)(g | (g << 4)), cast(ubyte)(b | (b << 4)), 0); 488 } 489 if (hex.length == 4) 490 { 491 ubyte a = val(hex[0]); 492 ubyte r = val(hex[1]); 493 ubyte g = val(hex[2]); 494 ubyte b = val(hex[3]); 495 return RGBA8(cast(ubyte)(r | (r << 4)), cast(ubyte)(g | (g << 4)), cast(ubyte)(b | (b << 4)), cast(ubyte)(a | (a << 4))); 496 } 497 if (hex.length == 6) 498 { 499 ubyte r = cast(ubyte)(val(hex[0]) << 4) | val(hex[1]); 500 ubyte g = cast(ubyte)(val(hex[2]) << 4) | val(hex[3]); 501 ubyte b = cast(ubyte)(val(hex[4]) << 4) | val(hex[5]); 502 return RGBA8(r, g, b, 0); 503 } 504 if (hex.length == 8) 505 { 506 ubyte a = cast(ubyte)(val(hex[0]) << 4) | val(hex[1]); 507 ubyte r = cast(ubyte)(val(hex[2]) << 4) | val(hex[3]); 508 ubyte g = cast(ubyte)(val(hex[4]) << 4) | val(hex[5]); 509 ubyte b = cast(ubyte)(val(hex[6]) << 4) | val(hex[7]); 510 return RGBA8(r, g, b, a); 511 } 512 513 error = 1; 514 return RGBA8(); 515 } 516 517 // need to write a string compare, since phobos is not nothrow @nogc, etc... 518 static bool streqi(const(char)[] a, const(char)[] b) 519 { 520 if (a.length != b.length) 521 return false; 522 foreach(i; 0 .. a.length) 523 { 524 auto c1 = (a[i] >= 'A' && a[i] <= 'Z') ? a[i] | 0x20 : a[i]; 525 auto c2 = (b[i] >= 'A' && b[i] <= 'Z') ? b[i] | 0x20 : b[i]; 526 if(c1 != c2) 527 return false; 528 } 529 return true; 530 } 531 532 foreach (k; __traits(allMembers, Colors)) 533 { 534 if (streqi(str, k)) 535 mixin("return cast(RGBA8)Colors." ~ k ~ ";"); 536 } 537 538 error = 2; 539 return RGBA8(); 540 } 541 542 // find the fastest type to do format conversion without losing precision 543 template WorkingType(From, To) 544 { 545 static if (isFloatingPoint!From && isFloatingPoint!To) 546 { 547 static if (From.sizeof > To.sizeof) 548 alias WorkingType = From; 549 else 550 alias WorkingType = To; 551 } 552 else static if (isFloatingPoint!To) 553 alias WorkingType = To; 554 else static if (isFloatingPoint!From) 555 alias WorkingType = FloatTypeFor!To; 556 else 557 { 558 // small integer types can use float and not lose precision 559 static if (From.sizeof <= 2 && To.sizeof <= 2) 560 alias WorkingType = float; 561 else 562 alias WorkingType = double; 563 } 564 } 565 566 private template isParentType(Parent, Of) 567 { 568 static if (!is(Of.ParentColor)) 569 enum isParentType = false; 570 else static if (isInstanceOf!(TemplateOf!Parent, Of.ParentColor)) 571 enum isParentType = true; 572 else 573 enum isParentType = isParentType!(Parent, Of.ParentColor); 574 } 575 576 private template FindPath(From, To) 577 { 578 static if (isInstanceOf!(TemplateOf!To, From)) 579 alias FindPath = TypeTuple!(To); 580 else static if (isParentType!(From, To)) 581 alias FindPath = TypeTuple!(FindPath!(From, To.ParentColor), To); 582 else static if (is(From.ParentColor)) 583 alias FindPath = TypeTuple!(From, FindPath!(From.ParentColor, To)); 584 else 585 static assert(false, "Shouldn't be here!"); 586 } 587 588 // find the conversion path from one distant type to another 589 template ConversionPath(From, To) 590 { 591 static if (is(Unqual!From == Unqual!To)) 592 alias ConversionPath = TypeTuple!(); 593 else 594 { 595 alias Path = FindPath!(Unqual!From, Unqual!To); 596 static if (Path.length == 1 && !is(Path[0] == From)) 597 alias ConversionPath = Path; 598 else 599 alias ConversionPath = Path[1..$]; 600 } 601 } 602 unittest 603 { 604 import std.experimental.color.hsx; 605 import std.experimental.color.lab; 606 import std.experimental.color.xyz; 607 608 // dest indirect conversion paths 609 static assert(is(ConversionPath!(XYZ!float, const XYZ!float) == TypeTuple!())); 610 static assert(is(ConversionPath!(RGB8, RGB8) == TypeTuple!())); 611 612 static assert(is(ConversionPath!(XYZ!float, XYZ!double) == TypeTuple!(XYZ!double))); 613 static assert(is(ConversionPath!(xyY!float, XYZ!float) == TypeTuple!(XYZ!float))); 614 static assert(is(ConversionPath!(xyY!float, XYZ!double) == TypeTuple!(XYZ!double))); 615 static assert(is(ConversionPath!(XYZ!float, xyY!float) == TypeTuple!(xyY!float))); 616 static assert(is(ConversionPath!(XYZ!float, xyY!double) == TypeTuple!(xyY!double))); 617 618 static assert(is(ConversionPath!(HSL!float, XYZ!float) == TypeTuple!(RGB!("rgb", float, false), XYZ!float))); 619 static assert(is(ConversionPath!(LCh!float, HSI!double) == TypeTuple!(Lab!float, XYZ!double, RGB!("rgb", double), HSI!double))); 620 static assert(is(ConversionPath!(shared HSI!double, immutable LCh!float) == TypeTuple!(RGB!("rgb", double), XYZ!float, Lab!float, LCh!float))); 621 } 622 623 // build mixin code to perform expresions per-element 624 template ComponentExpression(string expression, string component, string op) 625 { 626 template BuildExpression(string e, string c, string op) 627 { 628 static if (e.length == 0) 629 enum BuildExpression = ""; 630 else static if (e[0] == '_') 631 enum BuildExpression = c ~ BuildExpression!(e[1..$], c, op); 632 else static if (e[0] == '#') 633 enum BuildExpression = op ~ BuildExpression!(e[1..$], c, op); 634 else 635 enum BuildExpression = e[0] ~ BuildExpression!(e[1..$], c, op); 636 } 637 enum ComponentExpression = 638 "static if (is(typeof(this." ~ component ~ ")))" ~ "\n\t" ~ 639 BuildExpression!(expression, component, op); 640 }