54af89ae5d5c92665b458a5cf5666d11179a7d5b
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-color.h"
22 #include "svg/css-ostringstream.h"
24 using Inkscape::CSSOStringStream;
25 using std::vector;
27 static bool profileMatches( SVGICCColor const* first, SVGICCColor const* second );
29 #define PROFILE_EPSILON 0.00000001
31 SPColor::SPColor() :
32 icc(0)
33 {
34 v.c[0] = 0;
35 v.c[1] = 0;
36 v.c[2] = 0;
37 }
39 SPColor::SPColor( SPColor const& other ) :
40 icc(0)
41 {
42 *this = other;
43 }
45 SPColor::SPColor( float r, float g, float b ) :
46 icc(0)
47 {
48 set( r, g, b );
49 }
51 SPColor::SPColor( guint32 value ) :
52 icc(0)
53 {
54 set( value );
55 }
57 SPColor::~SPColor()
58 {
59 delete icc;
60 icc = 0;
61 }
64 SPColor& SPColor::operator= (SPColor const& other)
65 {
66 SVGICCColor* tmp_icc = other.icc ? new SVGICCColor(*other.icc) : 0;
68 v.c[0] = other.v.c[0];
69 v.c[1] = other.v.c[1];
70 v.c[2] = other.v.c[2];
71 if ( icc ) {
72 delete icc;
73 }
74 icc = tmp_icc;
76 return *this;
77 }
80 /**
81 * Returns true if colors match.
82 */
83 bool SPColor::operator == (SPColor const& other) const
84 {
85 bool match = (v.c[0] != other.v.c[0])
86 && (v.c[1] != other.v.c[1])
87 && (v.c[2] != other.v.c[2]);
89 match &= profileMatches( icc, other.icc );
91 return match;
92 }
94 /**
95 * Returns true if no RGB value differs epsilon or more in both colors,
96 * false otherwise.
97 */
98 bool SPColor::isClose( SPColor const& other, float epsilon ) const
99 {
100 bool match = false;
102 match = (fabs((v.c[0]) - (other.v.c[0])) < epsilon)
103 && (fabs((v.c[1]) - (other.v.c[1])) < epsilon)
104 && (fabs((v.c[2]) - (other.v.c[2])) < epsilon);
106 match &= profileMatches( icc, other.icc );
108 return match;
109 }
111 static bool profileMatches( SVGICCColor const* first, SVGICCColor const* second ) {
112 bool match = false;
113 if ( !first && !second ) {
114 match = true;
115 } else {
116 match = first && second
117 && (first->colorProfile == second->colorProfile)
118 && (first->colors.size() == second->colors.size());
119 if ( match ) {
120 for ( guint i = 0; i < first->colors.size(); i++ ) {
121 match &= (fabs(first->colors[i] - second->colors[i]) < PROFILE_EPSILON);
122 }
123 }
124 }
125 return match;
126 }
128 /**
129 * Sets RGB values and colorspace in color.
130 * \pre 0 <={r,g,b}<=1
131 */
132 void SPColor::set( float r, float g, float b )
133 {
134 g_return_if_fail(r >= 0.0);
135 g_return_if_fail(r <= 1.0);
136 g_return_if_fail(g >= 0.0);
137 g_return_if_fail(g <= 1.0);
138 g_return_if_fail(b >= 0.0);
139 g_return_if_fail(b <= 1.0);
141 // TODO clear icc if set?
142 v.c[0] = r;
143 v.c[1] = g;
144 v.c[2] = b;
145 }
147 /**
148 * Converts 32bit value to RGB floats and sets color.
149 */
150 void SPColor::set( guint32 value )
151 {
152 // TODO clear icc if set?
153 v.c[0] = (value >> 24) / 255.0F;
154 v.c[1] = ((value >> 16) & 0xff) / 255.0F;
155 v.c[2] = ((value >> 8) & 0xff) / 255.0F;
156 }
158 /**
159 * Convert SPColor with integer alpha value to 32bit RGBA value.
160 * \pre alpha < 256
161 */
162 guint32 SPColor::toRGBA32( gint alpha ) const
163 {
164 g_return_val_if_fail (alpha <= 0xff, 0x0);
166 guint32 rgba = SP_RGBA32_U_COMPOSE( SP_COLOR_F_TO_U(v.c[0]),
167 SP_COLOR_F_TO_U(v.c[1]),
168 SP_COLOR_F_TO_U(v.c[2]),
169 alpha );
170 return rgba;
171 }
173 /**
174 * Convert SPColor with float alpha value to 32bit RGBA value.
175 * \pre color != NULL && 0 <= alpha <= 1
176 */
177 guint32 SPColor::toRGBA32( gdouble alpha ) const
178 {
179 g_return_val_if_fail(alpha >= 0.0, 0x0);
180 g_return_val_if_fail(alpha <= 1.0, 0x0);
182 return toRGBA32( static_cast<gint>(SP_COLOR_F_TO_U(alpha)) );
183 }
185 std::string SPColor::toString() const
186 {
187 CSSOStringStream css;
189 std::string result;
190 char tmp[64] = {0};
192 sp_svg_write_color(tmp, sizeof(tmp), toRGBA32(0x0ff));
193 css << tmp;
195 if ( icc ) {
196 if ( !css.str().empty() ) {
197 css << " ";
198 }
199 css << "icc-color(" << icc->colorProfile;
200 for (vector<double>::const_iterator i(icc->colors.begin()),
201 iEnd(icc->colors.end());
202 i != iEnd; ++i) {
203 css << ", " << *i;
204 }
205 css << ')';
206 }
208 return css.str();
209 }
212 /**
213 * Fill rgb float array with values from SPColor.
214 * \pre color != NULL && rgb != NULL && rgb[0-2] is meaningful
215 */
216 void
217 sp_color_get_rgb_floatv(SPColor const *color, float *rgb)
218 {
219 g_return_if_fail (color != NULL);
220 g_return_if_fail (rgb != NULL);
222 rgb[0] = color->v.c[0];
223 rgb[1] = color->v.c[1];
224 rgb[2] = color->v.c[2];
225 }
227 /**
228 * Fill cmyk float array with values from SPColor.
229 * \pre color != NULL && cmyk != NULL && cmyk[0-3] is meaningful
230 */
231 void
232 sp_color_get_cmyk_floatv(SPColor const *color, float *cmyk)
233 {
234 g_return_if_fail (color != NULL);
235 g_return_if_fail (cmyk != NULL);
237 sp_color_rgb_to_cmyk_floatv( cmyk,
238 color->v.c[0],
239 color->v.c[1],
240 color->v.c[2] );
241 }
243 /* Plain mode helpers */
245 /**
246 * Fill hsv float array from r,g,b float values.
247 */
248 void
249 sp_color_rgb_to_hsv_floatv (float *hsv, float r, float g, float b)
250 {
251 float max, min, delta;
253 max = MAX (MAX (r, g), b);
254 min = MIN (MIN (r, g), b);
255 delta = max - min;
257 hsv[2] = max;
259 if (max > 0) {
260 hsv[1] = delta / max;
261 } else {
262 hsv[1] = 0.0;
263 }
265 if (hsv[1] != 0.0) {
266 if (r == max) {
267 hsv[0] = (g - b) / delta;
268 } else if (g == max) {
269 hsv[0] = 2.0 + (b - r) / delta;
270 } else {
271 hsv[0] = 4.0 + (r - g) / delta;
272 }
274 hsv[0] = hsv[0] / 6.0;
276 if (hsv[0] < 0) hsv[0] += 1.0;
277 }
278 else
279 hsv[0] = 0.0;
280 }
282 /**
283 * Fill rgb float array from h,s,v float values.
284 */
285 void
286 sp_color_hsv_to_rgb_floatv (float *rgb, float h, float s, float v)
287 {
288 gdouble f, w, q, t, d;
290 d = h * 5.99999999;
291 f = d - floor (d);
292 w = v * (1.0 - s);
293 q = v * (1.0 - (s * f));
294 t = v * (1.0 - (s * (1.0 - f)));
296 if (d < 1.0) {
297 *rgb++ = v;
298 *rgb++ = t;
299 *rgb++ = w;
300 } else if (d < 2.0) {
301 *rgb++ = q;
302 *rgb++ = v;
303 *rgb++ = w;
304 } else if (d < 3.0) {
305 *rgb++ = w;
306 *rgb++ = v;
307 *rgb++ = t;
308 } else if (d < 4.0) {
309 *rgb++ = w;
310 *rgb++ = q;
311 *rgb++ = v;
312 } else if (d < 5.0) {
313 *rgb++ = t;
314 *rgb++ = w;
315 *rgb++ = v;
316 } else {
317 *rgb++ = v;
318 *rgb++ = w;
319 *rgb++ = q;
320 }
321 }
323 /**
324 * Fill hsl float array from r,g,b float values.
325 */
326 void
327 sp_color_rgb_to_hsl_floatv (float *hsl, float r, float g, float b)
328 {
329 float max = MAX (MAX (r, g), b);
330 float min = MIN (MIN (r, g), b);
331 float delta = max - min;
333 hsl[2] = (max + min)/2.0;
335 if (delta == 0) {
336 hsl[0] = 0;
337 hsl[1] = 0;
338 } else {
339 if (hsl[2] <= 0.5)
340 hsl[1] = delta / (max + min);
341 else
342 hsl[1] = delta / (2 - max - min);
344 if (r == max) hsl[0] = (g - b) / delta;
345 else if (g == max) hsl[0] = 2.0 + (b - r) / delta;
346 else if (b == max) hsl[0] = 4.0 + (r - g) / delta;
348 hsl[0] = hsl[0] / 6.0;
350 if (hsl[0] < 0) hsl[0] += 1;
351 if (hsl[0] > 1) hsl[0] -= 1;
352 }
353 }
355 float
356 hue_2_rgb (float v1, float v2, float h)
357 {
358 if (h < 0) h += 6.0;
359 if (h > 6) h -= 6.0;
361 if (h < 1) return v1 + (v2 - v1) * h;
362 if (h < 3) return v2;
363 if (h < 4) return v1 + (v2 - v1) * (4 - h);
364 return v1;
365 }
367 /**
368 * Fill rgb float array from h,s,l float values.
369 */
370 void
371 sp_color_hsl_to_rgb_floatv (float *rgb, float h, float s, float l)
372 {
373 if (s == 0) {
374 rgb[0] = l;
375 rgb[1] = l;
376 rgb[2] = l;
377 } else {
378 float v2;
379 if (l < 0.5) {
380 v2 = l * (1 + s);
381 } else {
382 v2 = l + s - l*s;
383 }
384 float v1 = 2*l - v2;
386 rgb[0] = hue_2_rgb (v1, v2, h*6 + 2.0);
387 rgb[1] = hue_2_rgb (v1, v2, h*6);
388 rgb[2] = hue_2_rgb (v1, v2, h*6 - 2.0);
389 }
390 }
392 /**
393 * Fill cmyk float array from r,g,b float values.
394 */
395 void
396 sp_color_rgb_to_cmyk_floatv (float *cmyk, float r, float g, float b)
397 {
398 float c, m, y, k, kd;
400 c = 1.0 - r;
401 m = 1.0 - g;
402 y = 1.0 - b;
403 k = MIN (MIN (c, m), y);
405 c = c - k;
406 m = m - k;
407 y = y - k;
409 kd = 1.0 - k;
411 if (kd > 1e-9) {
412 c = c / kd;
413 m = m / kd;
414 y = y / kd;
415 }
417 cmyk[0] = c;
418 cmyk[1] = m;
419 cmyk[2] = y;
420 cmyk[3] = k;
421 }
423 /**
424 * Fill rgb float array from c,m,y,k float values.
425 */
426 void
427 sp_color_cmyk_to_rgb_floatv (float *rgb, float c, float m, float y, float k)
428 {
429 float kd;
431 kd = 1.0 - k;
433 c = c * kd;
434 m = m * kd;
435 y = y * kd;
437 c = c + k;
438 m = m + k;
439 y = y + k;
441 rgb[0] = 1.0 - c;
442 rgb[1] = 1.0 - m;
443 rgb[2] = 1.0 - y;
444 }
446 /*
447 Local Variables:
448 mode:c++
449 c-file-style:"stroustrup"
450 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
451 indent-tabs-mode:nil
452 fill-column:99
453 End:
454 */
455 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :