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