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 }