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 }