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 }