1 #define __SP_COLOR_C__
3 /** \file
4 * Colors.
5 *
6 * Author:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 * Jon A. Cruz <jon@joncruz.org>
10 *
11 * Copyright (C) 2001-2002 Lauris Kaplinski
12 * Copyright (C) 2001 Ximian, Inc.
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
17 #include <math.h>
18 #include "color.h"
19 #include "svg/svg-icc-color.h"
20 #include "svg/svg-device-color.h"
21 #include "svg/svg-color.h"
23 #include "svg/css-ostringstream.h"
25 using Inkscape::CSSOStringStream;
26 using std::vector;
28 static bool profileMatches( SVGICCColor const* first, SVGICCColor const* second );
30 #define PROFILE_EPSILON 0.00000001
32 SPColor::SPColor() :
33 icc(0),
34 device(0)
35 {
36 v.c[0] = 0;
37 v.c[1] = 0;
38 v.c[2] = 0;
39 }
41 SPColor::SPColor( SPColor const& other ) :
42 icc(0),
43 device(0)
44 {
45 *this = other;
46 }
48 SPColor::SPColor( float r, float g, float b ) :
49 icc(0),
50 device(0)
51 {
52 set( r, g, b );
53 }
55 SPColor::SPColor( guint32 value ) :
56 icc(0),
57 device(0)
58 {
59 set( value );
60 }
62 SPColor::~SPColor()
63 {
64 delete icc;
65 delete device;
66 icc = 0;
67 device = 0;
68 }
71 SPColor& SPColor::operator= (SPColor const& other)
72 {
73 SVGICCColor* tmp_icc = other.icc ? new SVGICCColor(*other.icc) : 0;
74 SVGDeviceColor* tmp_device = other.device ? new SVGDeviceColor(*other.device) : 0;
76 v.c[0] = other.v.c[0];
77 v.c[1] = other.v.c[1];
78 v.c[2] = other.v.c[2];
79 if ( icc ) {
80 delete icc;
81 }
82 icc = tmp_icc;
84 if ( device ) {
85 delete device;
86 }
87 device = tmp_device;
89 return *this;
90 }
93 /**
94 * Returns true if colors match.
95 */
96 bool SPColor::operator == (SPColor const& other) const
97 {
98 bool match = (v.c[0] != other.v.c[0])
99 && (v.c[1] != other.v.c[1])
100 && (v.c[2] != other.v.c[2]);
102 match &= profileMatches( icc, other.icc );
103 //TODO?: match &= devicecolorMatches( device, other.device );
105 return match;
106 }
108 /**
109 * Returns true if no RGB value differs epsilon or more in both colors,
110 * false otherwise.
111 */
112 bool SPColor::isClose( SPColor const& other, float epsilon ) const
113 {
114 bool match = false;
116 match = (fabs((v.c[0]) - (other.v.c[0])) < epsilon)
117 && (fabs((v.c[1]) - (other.v.c[1])) < epsilon)
118 && (fabs((v.c[2]) - (other.v.c[2])) < epsilon);
120 match &= profileMatches( icc, other.icc );
122 return match;
123 }
125 static bool profileMatches( SVGICCColor const* first, SVGICCColor const* second ) {
126 bool match = false;
127 if ( !first && !second ) {
128 match = true;
129 } else {
130 match = first && second
131 && (first->colorProfile == second->colorProfile)
132 && (first->colors.size() == second->colors.size());
133 if ( match ) {
134 for ( guint i = 0; i < first->colors.size(); i++ ) {
135 match &= (fabs(first->colors[i] - second->colors[i]) < PROFILE_EPSILON);
136 }
137 }
138 }
139 return match;
140 }
142 /**
143 * Sets RGB values and colorspace in color.
144 * \pre 0 <={r,g,b}<=1
145 */
146 void SPColor::set( float r, float g, float b )
147 {
148 g_return_if_fail(r >= 0.0);
149 g_return_if_fail(r <= 1.0);
150 g_return_if_fail(g >= 0.0);
151 g_return_if_fail(g <= 1.0);
152 g_return_if_fail(b >= 0.0);
153 g_return_if_fail(b <= 1.0);
155 // TODO clear icc if set?
156 v.c[0] = r;
157 v.c[1] = g;
158 v.c[2] = b;
159 }
161 /**
162 * Converts 32bit value to RGB floats and sets color.
163 */
164 void SPColor::set( guint32 value )
165 {
166 // TODO clear icc if set?
167 v.c[0] = (value >> 24) / 255.0F;
168 v.c[1] = ((value >> 16) & 0xff) / 255.0F;
169 v.c[2] = ((value >> 8) & 0xff) / 255.0F;
170 }
172 /**
173 * Convert SPColor with integer alpha value to 32bit RGBA value.
174 * \pre alpha < 256
175 */
176 guint32 SPColor::toRGBA32( gint alpha ) const
177 {
178 g_return_val_if_fail (alpha <= 0xff, 0x0);
180 guint32 rgba = SP_RGBA32_U_COMPOSE( SP_COLOR_F_TO_U(v.c[0]),
181 SP_COLOR_F_TO_U(v.c[1]),
182 SP_COLOR_F_TO_U(v.c[2]),
183 alpha );
184 return rgba;
185 }
187 /**
188 * Convert SPColor with float alpha value to 32bit RGBA value.
189 * \pre color != NULL && 0 <= alpha <= 1
190 */
191 guint32 SPColor::toRGBA32( gdouble alpha ) const
192 {
193 g_return_val_if_fail(alpha >= 0.0, 0x0);
194 g_return_val_if_fail(alpha <= 1.0, 0x0);
196 return toRGBA32( static_cast<gint>(SP_COLOR_F_TO_U(alpha)) );
197 }
199 std::string SPColor::toString() const
200 {
201 CSSOStringStream css;
203 std::string result;
204 char tmp[64] = {0};
206 sp_svg_write_color(tmp, sizeof(tmp), toRGBA32(0x0ff));
207 css << tmp;
209 if ( icc ) {
210 if ( !css.str().empty() ) {
211 css << " ";
212 }
213 css << "icc-color(" << icc->colorProfile;
214 for (vector<double>::const_iterator i(icc->colors.begin()),
215 iEnd(icc->colors.end());
216 i != iEnd; ++i) {
217 css << ", " << *i;
218 }
219 css << ')';
220 }
222 if ( device && device->type != DEVICE_COLOR_INVALID) {
223 if ( !css.str().empty() ) {
224 css << " ";
225 }
227 switch(device->type){
228 case DEVICE_GRAY:
229 css << "device-gray(";
230 break;
231 case DEVICE_RGB:
232 css << "device-rgb(";
233 break;
234 case DEVICE_CMYK:
235 css << "device-cmyk(";
236 break;
237 case DEVICE_NCHANNEL:
238 css << "device-nchannel(";
239 break;
240 case DEVICE_COLOR_INVALID:
241 //should not be reached
242 break;
243 }
245 for (vector<double>::const_iterator i(device->colors.begin()),
246 iEnd(device->colors.end());
247 i != iEnd; ++i) {
248 if (i!=device->colors.begin()) css << ", ";
249 css << *i;
250 }
251 css << ')';
252 }
254 return css.str();
255 }
258 /**
259 * Fill rgb float array with values from SPColor.
260 * \pre color != NULL && rgb != NULL && rgb[0-2] is meaningful
261 */
262 void
263 sp_color_get_rgb_floatv(SPColor const *color, float *rgb)
264 {
265 g_return_if_fail (color != NULL);
266 g_return_if_fail (rgb != NULL);
268 rgb[0] = color->v.c[0];
269 rgb[1] = color->v.c[1];
270 rgb[2] = color->v.c[2];
271 }
273 /**
274 * Fill cmyk float array with values from SPColor.
275 * \pre color != NULL && cmyk != NULL && cmyk[0-3] is meaningful
276 */
277 void
278 sp_color_get_cmyk_floatv(SPColor const *color, float *cmyk)
279 {
280 g_return_if_fail (color != NULL);
281 g_return_if_fail (cmyk != NULL);
283 sp_color_rgb_to_cmyk_floatv( cmyk,
284 color->v.c[0],
285 color->v.c[1],
286 color->v.c[2] );
287 }
289 /* Plain mode helpers */
291 /**
292 * Fill hsv float array from r,g,b float values.
293 */
294 void
295 sp_color_rgb_to_hsv_floatv (float *hsv, float r, float g, float b)
296 {
297 float max, min, delta;
299 max = MAX (MAX (r, g), b);
300 min = MIN (MIN (r, g), b);
301 delta = max - min;
303 hsv[2] = max;
305 if (max > 0) {
306 hsv[1] = delta / max;
307 } else {
308 hsv[1] = 0.0;
309 }
311 if (hsv[1] != 0.0) {
312 if (r == max) {
313 hsv[0] = (g - b) / delta;
314 } else if (g == max) {
315 hsv[0] = 2.0 + (b - r) / delta;
316 } else {
317 hsv[0] = 4.0 + (r - g) / delta;
318 }
320 hsv[0] = hsv[0] / 6.0;
322 if (hsv[0] < 0) hsv[0] += 1.0;
323 }
324 else
325 hsv[0] = 0.0;
326 }
328 /**
329 * Fill rgb float array from h,s,v float values.
330 */
331 void
332 sp_color_hsv_to_rgb_floatv (float *rgb, float h, float s, float v)
333 {
334 gdouble f, w, q, t, d;
336 d = h * 5.99999999;
337 f = d - floor (d);
338 w = v * (1.0 - s);
339 q = v * (1.0 - (s * f));
340 t = v * (1.0 - (s * (1.0 - f)));
342 if (d < 1.0) {
343 *rgb++ = v;
344 *rgb++ = t;
345 *rgb++ = w;
346 } else if (d < 2.0) {
347 *rgb++ = q;
348 *rgb++ = v;
349 *rgb++ = w;
350 } else if (d < 3.0) {
351 *rgb++ = w;
352 *rgb++ = v;
353 *rgb++ = t;
354 } else if (d < 4.0) {
355 *rgb++ = w;
356 *rgb++ = q;
357 *rgb++ = v;
358 } else if (d < 5.0) {
359 *rgb++ = t;
360 *rgb++ = w;
361 *rgb++ = v;
362 } else {
363 *rgb++ = v;
364 *rgb++ = w;
365 *rgb++ = q;
366 }
367 }
369 /**
370 * Fill hsl float array from r,g,b float values.
371 */
372 void
373 sp_color_rgb_to_hsl_floatv (float *hsl, float r, float g, float b)
374 {
375 float max = MAX (MAX (r, g), b);
376 float min = MIN (MIN (r, g), b);
377 float delta = max - min;
379 hsl[2] = (max + min)/2.0;
381 if (delta == 0) {
382 hsl[0] = 0;
383 hsl[1] = 0;
384 } else {
385 if (hsl[2] <= 0.5)
386 hsl[1] = delta / (max + min);
387 else
388 hsl[1] = delta / (2 - max - min);
390 if (r == max) hsl[0] = (g - b) / delta;
391 else if (g == max) hsl[0] = 2.0 + (b - r) / delta;
392 else if (b == max) hsl[0] = 4.0 + (r - g) / delta;
394 hsl[0] = hsl[0] / 6.0;
396 if (hsl[0] < 0) hsl[0] += 1;
397 if (hsl[0] > 1) hsl[0] -= 1;
398 }
399 }
401 float
402 hue_2_rgb (float v1, float v2, float h)
403 {
404 if (h < 0) h += 6.0;
405 if (h > 6) h -= 6.0;
407 if (h < 1) return v1 + (v2 - v1) * h;
408 if (h < 3) return v2;
409 if (h < 4) return v1 + (v2 - v1) * (4 - h);
410 return v1;
411 }
413 /**
414 * Fill rgb float array from h,s,l float values.
415 */
416 void
417 sp_color_hsl_to_rgb_floatv (float *rgb, float h, float s, float l)
418 {
419 if (s == 0) {
420 rgb[0] = l;
421 rgb[1] = l;
422 rgb[2] = l;
423 } else {
424 float v2;
425 if (l < 0.5) {
426 v2 = l * (1 + s);
427 } else {
428 v2 = l + s - l*s;
429 }
430 float v1 = 2*l - v2;
432 rgb[0] = hue_2_rgb (v1, v2, h*6 + 2.0);
433 rgb[1] = hue_2_rgb (v1, v2, h*6);
434 rgb[2] = hue_2_rgb (v1, v2, h*6 - 2.0);
435 }
436 }
438 /**
439 * Fill cmyk float array from r,g,b float values.
440 */
441 void
442 sp_color_rgb_to_cmyk_floatv (float *cmyk, float r, float g, float b)
443 {
444 float c, m, y, k, kd;
446 c = 1.0 - r;
447 m = 1.0 - g;
448 y = 1.0 - b;
449 k = MIN (MIN (c, m), y);
451 c = c - k;
452 m = m - k;
453 y = y - k;
455 kd = 1.0 - k;
457 if (kd > 1e-9) {
458 c = c / kd;
459 m = m / kd;
460 y = y / kd;
461 }
463 cmyk[0] = c;
464 cmyk[1] = m;
465 cmyk[2] = y;
466 cmyk[3] = k;
467 }
469 /**
470 * Fill rgb float array from c,m,y,k float values.
471 */
472 void
473 sp_color_cmyk_to_rgb_floatv (float *rgb, float c, float m, float y, float k)
474 {
475 float kd;
477 kd = 1.0 - k;
479 c = c * kd;
480 m = m * kd;
481 y = y * kd;
483 c = c + k;
484 m = m + k;
485 y = y + k;
487 rgb[0] = 1.0 - c;
488 rgb[1] = 1.0 - m;
489 rgb[2] = 1.0 - y;
490 }
492 /*
493 Local Variables:
494 mode:c++
495 c-file-style:"stroustrup"
496 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
497 indent-tabs-mode:nil
498 fill-column:99
499 End:
500 */
501 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :