1 /*
2  * DSFML - The Simple and Fast Multimedia Library for D
3  *
4  * Copyright (c) 2013 - 2017 Jeremy DeHaan (dehaan.jeremiah@gmail.com)
5  *
6  * This software is provided 'as-is', without any express or implied warranty.
7  * In no event will the authors be held liable for any damages arising from the
8  * use of this software.
9  *
10  * Permission is granted to anyone to use this software for any purpose,
11  * including commercial applications, and to alter it and redistribute it
12  * freely, subject to the following restrictions:
13  *
14  * 1. The origin of this software must not be misrepresented; you must not claim
15  * that you wrote the original software. If you use this software in a product,
16  * an acknowledgment in the product documentation would be appreciated but is
17  * not required.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  * misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source distribution
23  */
24 
25 /**
26  * $(U Color) is a simple color class composed of 4 components:
27  * $(UL
28  * $(LI Red)
29  * $(LI Green)
30  * $(LI Blue)
31  * $(LI Alpha (opacity)))
32  *
33  * Each component is a public member, an unsigned integer in the range [0, 255].
34  * Thus, colors can be constructed and manipulated very easily:
35  *
36  * ---
37  * auto color = Color(255, 0, 0); // red
38  * color.r = 0;                // make it black
39  * color.b = 128;              // make it dark blue
40  * ---
41  *
42  * $(PARA The fourth component of colors, named "alpha", represents the opacity
43  * of the color. A color with an alpha value of 255 will be fully opaque, while
44  * an alpha value of 0 will make a color fully transparent, whatever the value
45  * of the other components is.
46  *
47  * The most common colors are already defined as static variables:)
48  * ---
49  * auto black       = Color.Black;
50  * auto white       = Color.White;
51  * auto red         = Color.Red;
52  * auto green       = Color.Green;
53  * auto blue        = Color.Blue;
54  * auto yellow      = Color.Yellow;
55  * auto magenta     = Color.Magenta;
56  * auto cyan        = Color.Cyan;
57  * auto transparent = Color.Transparent;
58  * ---
59  *
60  * $(PARA Colors can also be added and modulated (multiplied) using the
61  * overloaded operators `+` and `*`.)
62  */
63 module dsfml.graphics.color;
64 
65 import std.math, std.traits;
66 
67 import std.algorithm;
68 
69 /**
70  * Color is a utility struct for manipulating 32-bits RGBA colors.
71  */
72 struct Color
73 {
74     /// Red component
75     ubyte r;
76     /// Green component
77     ubyte g;
78     /// Blue component
79     ubyte b;
80     /// Alpha component
81     ubyte a = 255;
82 
83     static immutable Black = Color(0, 0, 0, 255);
84     static immutable White = Color(255, 255, 255, 255);
85     static immutable Red = Color(255, 0, 0, 255);
86     static immutable Green = Color(0, 255, 0,255);
87     static immutable Blue = Color(0, 0, 255,255);
88     static immutable Yellow = Color(255, 255, 0, 255);
89     static immutable Magenta = Color(255, 0, 255, 255);
90     static immutable Cyan = Color(0, 255, 255, 255);
91     static immutable Transparent = Color(0, 0, 0, 0);
92 
93     /// Get the string representation of the Color.
94     string toString() const
95     {
96         import std.conv;
97         return "R: " ~ text(r) ~ " G: " ~ text(g) ~ " B: " ~ text(b) ~ " A: " ~ text(a);
98     }
99 
100     /**
101      * Overlolad of the `+`, `-`, and `*` operators.
102      *
103      * This operator returns the component-wise sum, subtraction, or
104      * multiplication (also called"modulation") of two colors.
105      *
106      * For addition and subtraction, components that exceed 255 are clamped to
107      * 255 and those below 0 are clamped to 0. For multiplication, are divided
108      * by 255 so that the result is still in the range [0, 255].
109      *
110      * Params:
111      * otherColor = The Color to be added to/subtracted from/bultiplied by this
112      *              one
113      *
114      * Returns:
115      * The addition, subtraction, or multiplication between this Color and the
116      * other.
117      */
118     Color opBinary(string op)(Color otherColor) const
119         if((op == "+") || (op == "-") || (op == "*"))
120     {
121         static if(op == "+")
122         {
123             return Color(cast(ubyte)min(r+otherColor.r, 255),
124                          cast(ubyte)min(g+otherColor.g, 255),
125                          cast(ubyte)min(b+otherColor.b, 255),
126                          cast(ubyte)min(a+otherColor.a, 255));
127         }
128         static if(op == "-")
129         {
130             return Color(cast(ubyte)max(r-otherColor.r, 0),
131                          cast(ubyte)max(g-otherColor.g, 0),
132                          cast(ubyte)max(b-otherColor.b, 0),
133                          cast(ubyte)max(a-otherColor.a, 0));
134         }
135         static if(op == "*")
136         {
137             return Color(cast(ubyte)(r*otherColor.r / 255),
138                          cast(ubyte)(g*otherColor.g / 255),
139                          cast(ubyte)(b*otherColor.b / 255),
140                          cast(ubyte)(a*otherColor.a / 255));
141         }
142     }
143 
144     /**
145      * Overlolad of the `*` and `/` operators.
146      *
147      * This operator returns the component-wise multiplicaton or division of a
148      * color and a scalar.
149      * Components that exceed 255 are clamped to 255 and those below 0 are
150      * clamped to 0.
151      *
152      * Params:
153      * num = the scalar to multiply/divide the Color.
154      *
155      * Returns:
156      * The multiplication or division of this Color by the scalar.
157      */
158     Color opBinary(string op, E)(E num) const
159         if(isNumeric!(E) && ((op == "*") || (op == "/")))
160     {
161         static if(op == "*")
162         {
163             //actually dividing or multiplying by a negative
164             if(num < 1)
165             {
166                 return Color(cast(ubyte)max(r*num, 0),
167                              cast(ubyte)max(g*num, 0),
168                              cast(ubyte)max(b*num, 0),
169                              cast(ubyte)max(a*num, 0));
170             }
171             else
172             {
173                 return Color(cast(ubyte)min(r*num, 255),
174                              cast(ubyte)min(g*num, 255),
175                              cast(ubyte)min(b*num, 255),
176                              cast(ubyte)min(a*num, 255));
177             }
178         }
179         static if(op == "/")
180         {
181             //actually multiplying or dividing by a negative
182             if(num < 1)
183             {
184                 return Color(cast(ubyte)min(r/num, 255),
185                              cast(ubyte)min(g/num, 255),
186                              cast(ubyte)min(b/num, 255),
187                              cast(ubyte)min(a/num, 255));
188             }
189             else
190             {
191                 return Color(cast(ubyte)max(r/num, 0),
192                              cast(ubyte)max(g/num, 0),
193                              cast(ubyte)max(b/num, 0),
194                              cast(ubyte)max(a/num, 0));
195             }
196         }
197     }
198 
199     /**
200      * Overlolad of the `+=, `-=`, and `*=` operators.
201      *
202      * This operation computes the component-wise sum, subtraction, or
203      * multiplication (also called"modulation") of two colors and assigns it to
204      * the left operand.
205      * Components that exceed 255 are clamped to 255 and those below 0 are
206      * clamped to 0. For multiplication, are divided
207      * by 255 so that the result is still in the range [0, 255].
208      *
209      * Params:
210      * otherColor = The Color to be added to/subtracted from/bultiplied by this
211      *              one
212      *
213      * Returns:
214      * A reference to this color after performing the addition, subtraction, or
215      * multiplication.
216      */
217     ref Color opOpAssign(string op)(Color otherColor)
218         if((op == "+") || (op == "-") || (op == "*"))
219     {
220         static if(op == "+")
221         {
222             r = cast(ubyte)min(r+otherColor.r, 255);
223             g = cast(ubyte)min(g+otherColor.g, 255);
224             b = cast(ubyte)min(b+otherColor.b, 255);
225             a = cast(ubyte)min(a+otherColor.a, 255);
226         }
227         static if(op == "-")
228         {
229             r = cast(ubyte)max(r-otherColor.r, 0);
230             g = cast(ubyte)max(g-otherColor.g, 0);
231             b = cast(ubyte)max(b-otherColor.b, 0);
232             a = cast(ubyte)max(a-otherColor.a, 0);
233         }
234         static if(op == "*")
235         {
236             r = cast(ubyte)(r*otherColor.r / 255);
237             g = cast(ubyte)(g*otherColor.g / 255);
238             b = cast(ubyte)(b*otherColor.b / 255);
239             a = cast(ubyte)(a*otherColor.a / 255);
240         }
241 
242         return this;
243     }
244 
245     /**
246      * Overlolad of the `*=` and `/=` operators.
247      *
248      * This operation computers the component-wise multiplicaton or division of
249      * a color and a scalar, then assignes it to the color.
250      * Components that exceed 255 are clamped to 255 and those below 0 are
251      * clamped to 0.
252      *
253      * Params:
254      * num = the scalar to multiply/divide the Color
255      *
256      * Returns:
257      * A reference to this color after performing the multiplication or
258      * division.
259      */
260     ref Color opOpAssign(string op, E)(E num)
261         if(isNumeric!(E) && ((op == "*") || (op == "/")))
262     {
263         static if(op == "*")
264         {
265             //actually dividing or multiplying by a negative
266             if(num < 1)
267             {
268                 r = cast(ubyte)max(r*num, 0);
269                 g = cast(ubyte)max(g*num, 0);
270                 b = cast(ubyte)max(b*num, 0);
271                 a = cast(ubyte)max(a*num, 0);
272             }
273             else
274             {
275                 r = cast(ubyte)min(r*num, 255);
276                 g = cast(ubyte)min(g*num, 255);
277                 b = cast(ubyte)min(b*num, 255);
278                 a = cast(ubyte)min(a*num, 255);
279             }
280 
281             return this;
282         }
283         static if(op == "/")
284         {
285             //actually multiplying or dividing by a negative
286             if( num < 1)
287             {
288                 r = cast(ubyte)min(r/num, 255);
289                 g = cast(ubyte)min(g/num, 255);
290                 b = cast(ubyte)min(b/num, 255);
291                 a = cast(ubyte)min(a/num, 255);
292             }
293             else
294             {
295                 r = cast(ubyte)max(r/num, 0);
296                 g = cast(ubyte)max(g/num, 0);
297                 b = cast(ubyte)max(b/num, 0);
298                 a = cast(ubyte)max(a/num, 0);
299             }
300 
301             return this;
302         }
303     }
304     /**
305      * Overload of the `==` and `!=` operators.
306      *
307      * This operator compares two colors and check if they are equal.
308      *
309      * Params:
310      * otherColor = the Color to be compared with
311      *
312      * Returns: true if colors are equal, false if they are different.
313      */
314     bool opEquals(Color otherColor) const
315     {
316         return ((r == otherColor.r) && (g == otherColor.g) && (b == otherColor.b) && (a == otherColor.a));
317     }
318 }
319 
320 unittest
321 {
322     version(DSFML_Unittest_Graphics)
323     {
324         import std.stdio;
325 
326         writeln("Unit test for Color");
327 
328         //will perform arithmatic on Color to make sure everything works right.
329 
330         Color color = Color(100,100,100, 100);
331 
332         color*= 2;//(200, 200, 200, 200)
333 
334         color = color *.5;//(100, 100, 100, 100)
335 
336         color = color / 2;//(50, 50, 50, 50)
337 
338         color/= 2;//(25, 25, 25, 25)
339 
340         color+= Color(40,20,10,5);//(65,45, 35, 30)
341 
342 
343         color-= Color(5,10,20,40);//(60, 35, 15, 0)
344 
345         color = color + Color(40, 20, 10, 5);//(100, 55, 25, 5)
346 
347         color = color - Color(5, 10, 20, 40);//(95, 45, 5, 0)
348 
349         assert(color == Color(95, 45, 5, 0));
350 
351         writeln();
352     }
353 }