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