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 }