1 /** 2 * Copyright: Mike Wey 2011 3 * License: zlib (See accompanying LICENSE file) 4 * Authors: Mike Wey 5 */ 6 7 module dmagick.Color; 8 9 import std.conv; 10 import std.math; 11 import std..string; 12 import std.traits; 13 14 import dmagick.Exception; 15 import dmagick.Utils; 16 17 import dmagick.c.color; 18 import dmagick.c.magickType; 19 import dmagick.c.pixel; 20 import dmagick.c.quantum; 21 22 /** 23 * A container for the pixel values: red, green, blue and opacity. 24 */ 25 class Color 26 { 27 PixelPacket* packet; 28 29 /** */ 30 this() 31 { 32 packet = new PixelPacket; 33 34 packet.opacity = OpaqueOpacity; 35 } 36 37 /** 38 * Create a Color from the specified Quantums. 39 */ 40 this(Quantum red, Quantum green, Quantum blue, Quantum opacity = OpaqueOpacity) 41 { 42 this(); 43 44 packet.red = red; 45 packet.green = green; 46 packet.blue = blue; 47 packet.opacity = opacity; 48 } 49 50 /** 51 * Create a Color from a X11 color specification string 52 */ 53 this(string color) 54 { 55 this(); 56 57 const(char)* name = toStringz(color); 58 59 QueryColorDatabase(name, packet, DMagickExceptionInfo()); 60 } 61 62 /** 63 * Create a Color from this PixelPacket. 64 */ 65 this(PixelPacket packet) 66 { 67 this(); 68 69 this.packet.red = packet.red; 70 this.packet.green = packet.green; 71 this.packet.blue = packet.blue; 72 this.packet.opacity = packet.opacity; 73 } 74 75 /** 76 * Create a Color and set the internal pointer to this PixelPacket. 77 * We can use this to change pixels in an image through Color. 78 */ 79 package this(PixelPacket* packet) 80 { 81 this.packet = packet; 82 } 83 84 package PixelPacket pixelPacket() const 85 { 86 return *packet; 87 } 88 89 package void pixelPacket(PixelPacket packet) 90 { 91 this.packet.red = packet.red; 92 this.packet.green = packet.green; 93 this.packet.blue = packet.blue; 94 this.packet.opacity = packet.opacity; 95 } 96 97 /** */ 98 override bool opEquals(Object obj) 99 { 100 Color color = cast(Color)obj; 101 102 if ( color is null ) 103 return false; 104 105 return pixelPacket == color.pixelPacket; 106 } 107 108 /** 109 * Returns the value as a hex string. 110 */ 111 override string toString() const 112 { 113 static if ( MagickQuantumDepth == 8 ) 114 string frm = "%02X"; 115 else static if ( MagickQuantumDepth == 16 ) 116 string frm = "%04X"; 117 else static if ( MagickQuantumDepth == 32 ) 118 string frm = "%08X"; 119 else 120 string frm = "%016X"; 121 122 123 static if ( isFloatingPoint!Quantum ) 124 { 125 if ( packet.opacity == OpaqueOpacity ) 126 return format("#"~frm~frm~frm, rndtol(packet.red), rndtol(packet.green), rndtol(packet.blue)); 127 else 128 return format("#"~frm~frm~frm~frm, rndtol(packet.red), rndtol(packet.green), rndtol(packet.blue), rndtol(QuantumRange-packet.opacity)); 129 } 130 else 131 { 132 if ( packet.opacity == OpaqueOpacity ) 133 return format("#"~frm~frm~frm, packet.red, packet.green, packet.blue); 134 else 135 return format("#"~frm~frm~frm~frm, packet.red, packet.green, packet.blue, QuantumRange-packet.opacity); 136 } 137 } 138 139 unittest 140 { 141 Color color = new Color("blue"); 142 143 static if ( MagickQuantumDepth == 8 ) 144 assert(color.toString() == "#0000FF"); 145 else static if ( MagickQuantumDepth == 16 ) 146 assert(color.toString() == "#00000000FFFF"); 147 else static if ( MagickQuantumDepth == 16 ) 148 assert(color.toString() == "#0000000000000000FFFFFFFF"); 149 else 150 assert(color.toString() == "#00000000000000000000000000000000FFFFFFFFFFFFFFFF"); 151 } 152 153 /* 154 * Needed when comparing colors with dmd 2.058. 155 */ 156 Object opCast(T)() 157 if ( is(T == Object) ) 158 { 159 return this; 160 } 161 162 /** 163 * Support casting between different colors. 164 * You can also use std.conv.to 165 */ 166 T opCast(T : Color)() 167 { 168 T color = new T(); 169 color.packet = packet; 170 171 return color; 172 } 173 174 /** 175 * The value for red in the range [0 .. QuantumRange] 176 */ 177 void redQuantum(Quantum red) 178 { 179 packet.red = red; 180 } 181 ///ditto 182 Quantum redQuantum() 183 { 184 return packet.red; 185 } 186 187 /** 188 * The value for green in the range [0 .. QuantumRange] 189 */ 190 void greenQuantum(Quantum green) 191 { 192 packet.green = green; 193 } 194 ///ditto 195 Quantum greenQuantum() 196 { 197 return packet.green; 198 } 199 200 /** 201 * The value for blue in the range [0 .. QuantumRange] 202 */ 203 void blueQuantum(Quantum blue) 204 { 205 packet.blue = blue; 206 } 207 ///ditto 208 Quantum blueQuantum() 209 { 210 return packet.blue; 211 } 212 213 /** 214 * The opacity as a byte. [0 .. 255] 215 */ 216 void opacityByte(ubyte opacity) 217 { 218 packet.opacity = ScaleCharToQuantum(opacity); 219 } 220 ///ditto 221 ubyte opacityByte() 222 { 223 return ScaleQuantumToChar(packet.opacity); 224 } 225 226 /** 227 * The value for opacity in the range [0 .. QuantumRange] 228 */ 229 void opacityQuantum(Quantum opacity) 230 { 231 packet.opacity = opacity; 232 } 233 ///ditto 234 Quantum opacityQuantum() 235 { 236 return packet.opacity; 237 } 238 239 /** 240 * The value for opacity as a double in the range [0.0 .. 1.0] 241 */ 242 void opacity(double opacity) 243 { 244 packet.opacity = scaleDoubleToQuantum(opacity); 245 } 246 ///ditto 247 double opacity() 248 { 249 return scaleQuantumToDouble(packet.opacity); 250 } 251 252 /** 253 * The intensity of this color. 254 */ 255 double intensity() 256 { 257 //The Constants used here are derived from BT.709 Which standardizes HDTV 258 259 return scaleQuantumToDouble(cast(Quantum)( 260 0.2126*packet.red+0.7152*packet.green+0.0722*packet.blue)); 261 } 262 263 /** 264 * Create a copy of this Color. 265 */ 266 Color clone() 267 { 268 return new Color(pixelPacket); 269 } 270 271 /** 272 * Returns the name of the color or the value as a hex string. 273 */ 274 string name() 275 { 276 size_t numberOfColors; 277 const(ColorInfo)** colorList; 278 const(char)* pattern = toStringz("*"); 279 280 colorList = GetColorInfoList(pattern, &numberOfColors, DMagickExceptionInfo()); 281 282 for ( int i = 0; i < numberOfColors; i++ ) 283 { 284 if ( colorList[i].compliance == ComplianceType.UndefinedCompliance ) 285 continue; 286 287 MagickPixelPacket color = colorList[i].color; 288 289 if ( packet.red == color.red && packet.green == color.green 290 && packet.blue == color.blue && packet.opacity == color.opacity ) 291 return to!(string)(colorList[i].name); 292 } 293 294 return toString(); 295 } 296 297 unittest 298 { 299 Color color = new Color("red"); 300 assert( color.name == "red" ); 301 } 302 303 static pure nothrow Quantum scaleDoubleToQuantum(double value) 304 { 305 return cast(Quantum)(value*QuantumRange); 306 } 307 308 static pure nothrow double scaleQuantumToDouble(Quantum value) 309 { 310 return (cast(double)value)/QuantumRange; 311 } 312 }