1 // Written in the D programming language.
2 
3 /**
4     This package contains implementations of various common _color types.
5     Types are supplied for various _color spaces, along with comprehensive
6     _color space conversion functionality.
7 
8     Authors:    Manu Evans
9     Copyright:  Copyright (c) 2015, Manu Evans.
10     License:    $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0)
11     Source:     $(PHOBOSSRC ggplotd/color/package.d)
12 */
13 module ggplotd.color;
14 
15 public import ggplotd.color.rgb;
16 import ggplotd.color.xyz : isXYZ, isxyY;
17 import ggplotd.color.hsx : isHSx;
18 
19 @safe pure nothrow @nogc:
20 
21 
22 /**
23 Detect whether $(D T) is a color type defined under std.color.
24 */
25 enum isColor(T) = isRGB!T || isHSx!T || isXYZ!T || isxyY!T;
26 
27 ///
28 unittest
29 {
30     static assert(isColor!RGB8 == true);
31     static assert(isColor!(XYZ!float) == true);
32     static assert(isColor!float == false);
33 }
34 
35 
36 // declare some common color types
37 /** 24 bit RGB color type with 8 bits per channel. */
38 alias RGB8 =    RGB!("rgb", ubyte);
39 /** 32 bit RGB color type with 8 bits per channel. */
40 alias RGBX8 =   RGB!("rgbx", ubyte);
41 /** 32 bit RGB + alpha color type with 8 bits per channel. */
42 alias RGBA8 =   RGB!("rgba", ubyte);
43 
44 /** Floating point RGB color type. */
45 alias RGBf32 =  RGB!("rgb", float);
46 /** Floating point RGB + alpha color type. */
47 alias RGBAf32 = RGB!("rgba", float);
48 
49 /** 24 bit BGR color type with 8 bits per channel. */
50 alias BGR8 =    RGB!("bgr", ubyte);
51 /** 32 bit BGR color type with 8 bits per channel. */
52 alias BGRX8 =   RGB!("bgrx", ubyte);
53 /** 32 bit BGR + alpha color type with 8 bits per channel. */
54 alias BGRA8 =   RGB!("bgra", ubyte);
55 
56 /** 8 bit luminance-only color type. */
57 alias L8 =      RGB!("l", ubyte);
58 /** 8 bit alpha-only color type. */
59 alias A8 =      RGB!("a", ubyte);
60 /** 16 bit luminance + alpha color type with 8 bits per channel. */
61 alias LA8 =     RGB!("la", ubyte);
62 
63 /** 16 bit signed UV color type with 8 bits per channel. */
64 alias UV8 =     RGB!("rg", byte);
65 /** Floating point UV color type. */
66 alias UVf32 =   RGB!("rg", float);
67 
68 
69 /** Set of colors defined by X11, adopted by the W3C, SVG, and other popular libraries. */
70 enum Colors
71 {
72     aliceBlue            = RGB8(240,248,255),
73     antiqueWhite         = RGB8(250,235,215),
74     aqua                 = RGB8(0,255,255),
75     aquamarine           = RGB8(127,255,212),
76     azure                = RGB8(240,255,255),
77     beige                = RGB8(245,245,220),
78     bisque               = RGB8(255,228,196),
79     black                = RGB8(0,0,0),
80     blanchedAlmond       = RGB8(255,235,205),
81     blue                 = RGB8(0,0,255),
82     blueViolet           = RGB8(138,43,226),
83     brown                = RGB8(165,42,42),
84     burlyWood            = RGB8(222,184,135),
85     cadetBlue            = RGB8(95,158,160),
86     chartreuse           = RGB8(127,255,0),
87     chocolate            = RGB8(210,105,30),
88     coral                = RGB8(255,127,80),
89     cornflowerBlue       = RGB8(100,149,237),
90     cornsilk             = RGB8(255,248,220),
91     crimson              = RGB8(220,20,60),
92     cyan                 = RGB8(0,255,255),
93     darkBlue             = RGB8(0,0,139),
94     darkCyan             = RGB8(0,139,139),
95     darkGoldenrod        = RGB8(184,134,11),
96     darkGray             = RGB8(169,169,169),
97     darkGrey             = RGB8(169,169,169),
98     darkGreen            = RGB8(0,100,0),
99     darkKhaki            = RGB8(189,183,107),
100     darkMagenta          = RGB8(139,0,139),
101     darkOliveGreen       = RGB8(85,107,47),
102     darkOrange           = RGB8(255,140,0),
103     darkOrchid           = RGB8(153,50,204),
104     darkRed              = RGB8(139,0,0),
105     darkSalmon           = RGB8(233,150,122),
106     darkSeaGreen         = RGB8(143,188,143),
107     darkSlateBlue        = RGB8(72,61,139),
108     darkSlateGray        = RGB8(47,79,79),
109     darkSlateGrey        = RGB8(47,79,79),
110     darkTurquoise        = RGB8(0,206,209),
111     darkViolet           = RGB8(148,0,211),
112     deepPink             = RGB8(255,20,147),
113     deepSkyBlue          = RGB8(0,191,255),
114     dimGray              = RGB8(105,105,105),
115     dimGrey              = RGB8(105,105,105),
116     dodgerBlue           = RGB8(30,144,255),
117     fireBrick            = RGB8(178,34,34),
118     floralWhite          = RGB8(255,250,240),
119     forestGreen          = RGB8(34,139,34),
120     fuchsia              = RGB8(255,0,255),
121     gainsboro            = RGB8(220,220,220),
122     ghostWhite           = RGB8(248,248,255),
123     gold                 = RGB8(255,215,0),
124     goldenrod            = RGB8(218,165,32),
125     gray                 = RGB8(128,128,128),
126     grey                 = RGB8(128,128,128),
127     green                = RGB8(0,128,0),
128     greenYellow          = RGB8(173,255,47),
129     honeydew             = RGB8(240,255,240),
130     hotPink              = RGB8(255,105,180),
131     indianRed            = RGB8(205,92,92),
132     indigo               = RGB8(75,0,130),
133     ivory                = RGB8(255,255,240),
134     khaki                = RGB8(240,230,140),
135     lavender             = RGB8(230,230,250),
136     lavenderBlush        = RGB8(255,240,245),
137     lawnGreen            = RGB8(124,252,0),
138     lemonChiffon         = RGB8(255,250,205),
139     lightBlue            = RGB8(173,216,230),
140     lightCoral           = RGB8(240,128,128),
141     lightCyan            = RGB8(224,255,255),
142     lightGoldenrodYellow = RGB8(250,250,210),
143     lightGray            = RGB8(211,211,211),
144     lightGrey            = RGB8(211,211,211),
145     lightGreen           = RGB8(144,238,144),
146     lightPink            = RGB8(255,182,193),
147     lightSalmon          = RGB8(255,160,122),
148     lightSeaGreen        = RGB8(32,178,170),
149     lightSkyBlue         = RGB8(135,206,250),
150     lightSlateGray       = RGB8(119,136,153),
151     lightSlateGrey       = RGB8(119,136,153),
152     lightSteelBlue       = RGB8(176,196,222),
153     lightYellow          = RGB8(255,255,224),
154     lime                 = RGB8(0,255,0),
155     limeGreen            = RGB8(50,205,50),
156     linen                = RGB8(250,240,230),
157     magenta              = RGB8(255,0,255),
158     maroon               = RGB8(128,0,0),
159     mediumAquamarine     = RGB8(102,205,170),
160     mediumBlue           = RGB8(0,0,205),
161     mediumOrchid         = RGB8(186,85,211),
162     mediumPurple         = RGB8(147,112,219),
163     mediumSeaGreen       = RGB8(60,179,113),
164     mediumSlateBlue      = RGB8(123,104,238),
165     mediumSpringGreen    = RGB8(0,250,154),
166     mediumTurquoise      = RGB8(72,209,204),
167     mediumVioletRed      = RGB8(199,21,133),
168     midnightBlue         = RGB8(25,25,112),
169     mintCream            = RGB8(245,255,250),
170     mistyRose            = RGB8(255,228,225),
171     moccasin             = RGB8(255,228,181),
172     navajoWhite          = RGB8(255,222,173),
173     navy                 = RGB8(0,0,128),
174     oldLace              = RGB8(253,245,230),
175     olive                = RGB8(128,128,0),
176     oliveDrab            = RGB8(107,142,35),
177     orange               = RGB8(255,165,0),
178     orangeRed            = RGB8(255,69,0),
179     orchid               = RGB8(218,112,214),
180     paleGoldenrod        = RGB8(238,232,170),
181     paleGreen            = RGB8(152,251,152),
182     paleTurquoise        = RGB8(175,238,238),
183     paleVioletRed        = RGB8(219,112,147),
184     papayaWhip           = RGB8(255,239,213),
185     peachPuff            = RGB8(255,218,185),
186     peru                 = RGB8(205,133,63),
187     pink                 = RGB8(255,192,203),
188     plum                 = RGB8(221,160,221),
189     powderBlue           = RGB8(176,224,230),
190     purple               = RGB8(128,0,128),
191     red                  = RGB8(255,0,0),
192     rosyBrown            = RGB8(188,143,143),
193     royalBlue            = RGB8(65,105,225),
194     saddleBrown          = RGB8(139,69,19),
195     salmon               = RGB8(250,128,114),
196     sandyBrown           = RGB8(244,164,96),
197     seaGreen             = RGB8(46,139,87),
198     seashell             = RGB8(255,245,238),
199     sienna               = RGB8(160,82,45),
200     silver               = RGB8(192,192,192),
201     skyBlue              = RGB8(135,206,235),
202     slateBlue            = RGB8(106,90,205),
203     slateGray            = RGB8(112,128,144),
204     slateGrey            = RGB8(112,128,144),
205     snow                 = RGB8(255,250,250),
206     springGreen          = RGB8(0,255,127),
207     steelBlue            = RGB8(70,130,180),
208     tan                  = RGB8(210,180,140),
209     teal                 = RGB8(0,128,128),
210     thistle              = RGB8(216,191,216),
211     tomato               = RGB8(255,99,71),
212     turquoise            = RGB8(64,224,208),
213     violet               = RGB8(238,130,238),
214     wheat                = RGB8(245,222,179),
215     white                = RGB8(255,255,255),
216     whiteSmoke           = RGB8(245,245,245),
217     yellow               = RGB8(255,255,0),
218     yellowGreen          = RGB8(154,205,50)
219 }
220 
221 
222 // some common color operations
223 C lerp(C, F)(C a, C b, F t)
224 {
225     return blend(a, b, 1-t, t, 1-t, t);
226 }
227 
228 C blend(C, F)(C src, C dest, F srcFactor, F destFactor)
229 {
230     return blend(src, dest, srcFactor, destFactor, srcFactor, destFactor);
231 }
232 
233 C blend(C, F)(C src, C dest, F srcFactor, F destFactor, F srcAlphaFactor, F destAlphaFactor) if(isRGB!C && isFloatingPoint!F)
234 {
235     C r;
236     static if(C.CanFind!(components, 'r'))
237         r.r = cast(typeof(r.r))(src.r*srcFactor + dest.r*destFactor);
238     static if(C.CanFind!(components, 'g'))
239         r.g = cast(typeof(r.g))(src.g*srcFactor + dest.g*destFactor);
240     static if(C.CanFind!(components, 'b'))
241         r.b = cast(typeof(r.b))(src.b*srcFactor + dest.b*destFactor);
242     static if(C.CanFind!(components, 'l'))
243         r.l = cast(typeof(r.l))(src.l*srcFactor + dest.l*destFactor);
244     static if(C.CanFind!(components, 'a'))
245         r.a = cast(typeof(r.a))(src.a*srcAlphaFactor + dest.a*destAlphaFactor);
246     return r;
247 }
248 
249 
250 package:
251 
252 // build mixin code to perform expresions per-element
253 template ComponentExpression(string expression, string component, string op)
254 {
255     template BuildExpression(string e, string c, string op)
256     {
257         static if(e.length == 0)
258             enum BuildExpression = "";
259         else static if(e[0] == '_')
260             enum BuildExpression = c ~ BuildExpression!(e[1..$], c, op);
261         else static if(e[0] == '#')
262             enum BuildExpression = op ~ BuildExpression!(e[1..$], c, op);
263         else
264             enum BuildExpression = e[0] ~ BuildExpression!(e[1..$], c, op);
265     }
266     enum ComponentExpression =
267         "static if(is(typeof(this." ~ component ~ ")))" ~ "\n\t" ~
268             BuildExpression!(expression, component, op);
269 }
270 
271 mixin template ColorOperators(Components...)
272 {
273     typeof(this) opUnary(string op)() const if(op == "+" || op == "-" || (op == "~" && isIntegral!ComponentType))
274     {
275         Unqual!(typeof(this)) res = this;
276         foreach(c; Components)
277             mixin(ComponentExpression!("res._ = #_;", c, op));
278         return res;
279     }
280     typeof(this) opBinary(string op)(typeof(this) rh) const if(op == "+" || op == "-" || op == "*" || op == "/")
281     {
282         Unqual!(typeof(this)) res = this;
283         foreach(c; Components)
284             mixin(ComponentExpression!("res._ #= rh._;", c, op));
285         return res;
286     }
287     typeof(this) opBinary(string op, S)(S rh) const if(isNumeric!S && (op == "*" || op == "/" || op == "^^"))
288     {
289         Unqual!(typeof(this)) res = this;
290         foreach(c; Components)
291             mixin(ComponentExpression!("res._ #= rh;", c, op));
292         return res;
293     }
294     ref typeof(this) opOpAssign(string op)(typeof(this) rh) if(op == "+" || op == "-" || op == "*" || op == "/")
295     {
296         foreach(c; Components)
297             mixin(ComponentExpression!("_ #= rh._;", c, op));
298         return this;
299     }
300     ref typeof(this) opOpAssign(string op, S)(S rh) if(isNumeric!S && (op == "*" || op == "/" || op == "^^"))
301     {
302         foreach(c; Components)
303             mixin(ComponentExpression!("_ #= rh;", c, op));
304         return this;
305     }
306 }