Code

* infrastructure to store device colors as described in http://www.w3.org/TR/2009...
[inkscape.git] / src / color.cpp
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;
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
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;
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;
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 )
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;
161 /**
162  * Converts 32bit value to RGB floats and sets color.
163  */
164 void SPColor::set( guint32 value )
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;
172 /**
173  * Convert SPColor with integer alpha value to 32bit RGBA value.
174  * \pre alpha < 256
175  */
176 guint32 SPColor::toRGBA32( gint alpha ) const
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;
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
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)) );
199 std::string SPColor::toString() const
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             css << ", " << *i;
249         }
250         css << ')';
251     }
253     return css.str();
257 /**
258  * Fill rgb float array with values from SPColor.
259  * \pre color != NULL && rgb != NULL && rgb[0-2] is meaningful
260  */
261 void
262 sp_color_get_rgb_floatv(SPColor const *color, float *rgb)
264     g_return_if_fail (color != NULL);
265     g_return_if_fail (rgb != NULL);
267     rgb[0] = color->v.c[0];
268     rgb[1] = color->v.c[1];
269     rgb[2] = color->v.c[2];
272 /**
273  * Fill cmyk float array with values from SPColor.
274  * \pre color != NULL && cmyk != NULL && cmyk[0-3] is meaningful
275  */
276 void
277 sp_color_get_cmyk_floatv(SPColor const *color, float *cmyk)
279     g_return_if_fail (color != NULL);
280     g_return_if_fail (cmyk != NULL);
282     sp_color_rgb_to_cmyk_floatv( cmyk,
283                                  color->v.c[0],
284                                  color->v.c[1],
285                                  color->v.c[2] );
288 /* Plain mode helpers */
290 /**
291  * Fill hsv float array from r,g,b float values.
292  */
293 void
294 sp_color_rgb_to_hsv_floatv (float *hsv, float r, float g, float b)
296     float max, min, delta;
298     max = MAX (MAX (r, g), b);
299     min = MIN (MIN (r, g), b);
300     delta = max - min;
302     hsv[2] = max;
304     if (max > 0) {
305         hsv[1] = delta / max;
306     } else {
307         hsv[1] = 0.0;
308     }
310     if (hsv[1] != 0.0) {
311         if (r == max) {
312             hsv[0] = (g - b) / delta;
313         } else if (g == max) {
314             hsv[0] = 2.0 + (b - r) / delta;
315         } else {
316             hsv[0] = 4.0 + (r - g) / delta;
317         }
319         hsv[0] = hsv[0] / 6.0;
321         if (hsv[0] < 0) hsv[0] += 1.0;
322     }
323     else
324         hsv[0] = 0.0;
327 /**
328  * Fill rgb float array from h,s,v float values.
329  */
330 void
331 sp_color_hsv_to_rgb_floatv (float *rgb, float h, float s, float v)
333     gdouble f, w, q, t, d;
335     d = h * 5.99999999;
336     f = d - floor (d);
337     w = v * (1.0 - s);
338     q = v * (1.0 - (s * f));
339     t = v * (1.0 - (s * (1.0 - f)));
341     if (d < 1.0) {
342         *rgb++ = v;
343         *rgb++ = t;
344         *rgb++ = w;
345     } else if (d < 2.0) {
346         *rgb++ = q;
347         *rgb++ = v;
348         *rgb++ = w;
349     } else if (d < 3.0) {
350         *rgb++ = w;
351         *rgb++ = v;
352         *rgb++ = t;
353     } else if (d < 4.0) {
354         *rgb++ = w;
355         *rgb++ = q;
356         *rgb++ = v;
357     } else if (d < 5.0) {
358         *rgb++ = t;
359         *rgb++ = w;
360         *rgb++ = v;
361     } else {
362         *rgb++ = v;
363         *rgb++ = w;
364         *rgb++ = q;
365     }
368 /**
369  * Fill hsl float array from r,g,b float values.
370  */
371 void
372 sp_color_rgb_to_hsl_floatv (float *hsl, float r, float g, float b)
374     float max = MAX (MAX (r, g), b);
375     float min = MIN (MIN (r, g), b);
376     float delta = max - min;
378     hsl[2] = (max + min)/2.0;
380     if (delta == 0) {
381         hsl[0] = 0;
382         hsl[1] = 0;
383     } else {
384         if (hsl[2] <= 0.5)
385             hsl[1] = delta / (max + min);
386         else
387             hsl[1] = delta / (2 - max - min);
389         if (r == max) hsl[0] = (g - b) / delta;
390         else if (g == max) hsl[0] = 2.0 + (b - r) / delta;
391         else if (b == max) hsl[0] = 4.0 + (r - g) / delta;
393         hsl[0] = hsl[0] / 6.0;
395         if (hsl[0] < 0) hsl[0] += 1;
396         if (hsl[0] > 1) hsl[0] -= 1;
397     }
400 float
401 hue_2_rgb (float v1, float v2, float h)
403     if (h < 0) h += 6.0;
404     if (h > 6) h -= 6.0;
406     if (h < 1) return v1 + (v2 - v1) * h;
407     if (h < 3) return v2;
408     if (h < 4) return v1 + (v2 - v1) * (4 - h);
409     return v1;
412 /**
413  * Fill rgb float array from h,s,l float values.
414  */
415 void
416 sp_color_hsl_to_rgb_floatv (float *rgb, float h, float s, float l)
418     if (s == 0) {
419         rgb[0] = l;
420         rgb[1] = l;
421         rgb[2] = l;
422     } else {
423         float v2;
424         if (l < 0.5) {
425             v2 = l * (1 + s);
426         } else {
427             v2 = l + s - l*s;
428         }
429         float v1 = 2*l - v2;
431         rgb[0] = hue_2_rgb (v1, v2, h*6 + 2.0);
432         rgb[1] = hue_2_rgb (v1, v2, h*6);
433         rgb[2] = hue_2_rgb (v1, v2, h*6 - 2.0);
434     }
437 /**
438  * Fill cmyk float array from r,g,b float values.
439  */
440 void
441 sp_color_rgb_to_cmyk_floatv (float *cmyk, float r, float g, float b)
443     float c, m, y, k, kd;
445     c = 1.0 - r;
446     m = 1.0 - g;
447     y = 1.0 - b;
448     k = MIN (MIN (c, m), y);
450     c = c - k;
451     m = m - k;
452     y = y - k;
454     kd = 1.0 - k;
456     if (kd > 1e-9) {
457         c = c / kd;
458         m = m / kd;
459         y = y / kd;
460     }
462     cmyk[0] = c;
463     cmyk[1] = m;
464     cmyk[2] = y;
465     cmyk[3] = k;
468 /**
469  * Fill rgb float array from c,m,y,k float values.
470  */
471 void
472 sp_color_cmyk_to_rgb_floatv (float *rgb, float c, float m, float y, float k)
474     float kd;
476     kd = 1.0 - k;
478     c = c * kd;
479     m = m * kd;
480     y = y * kd;
482     c = c + k;
483     m = m + k;
484     y = y + k;
486     rgb[0] = 1.0 - c;
487     rgb[1] = 1.0 - m;
488     rgb[2] = 1.0 - y;
491 /*
492   Local Variables:
493   mode:c++
494   c-file-style:"stroustrup"
495   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
496   indent-tabs-mode:nil
497   fill-column:99
498   End:
499 */
500 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :