1 #define __SP_COLOR_C__
3 /** \file
4 * Colors and colorspaces.
5 *
6 * Author:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 *
10 * Copyright (C) 2001-2002 Lauris Kaplinski
11 * Copyright (C) 2001 Ximian, Inc.
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 #include <math.h>
17 #include "color.h"
19 /// A color space is just a name.
20 struct SPColorSpace {
21 gchar const *name;
22 };
24 static SPColorSpace const RGB = {"RGB"};
25 static SPColorSpace const CMYK = {"CMYK"};
27 /**
28 * Returns one of three values depending on if color is valid and if it
29 * has a valid color space. Likely redundant as soon as this is C++.
30 */
31 SPColorSpaceClass
32 sp_color_get_colorspace_class(SPColor const *color)
33 {
34 g_return_val_if_fail (color != NULL, SP_COLORSPACE_CLASS_INVALID);
36 if (color->colorspace == &RGB) return SP_COLORSPACE_CLASS_PROCESS;
37 if (color->colorspace == &CMYK) return SP_COLORSPACE_CLASS_PROCESS;
39 return SP_COLORSPACE_CLASS_UNKNOWN;
40 }
42 /**
43 * Returns the SPColorSpaceType corresponding to color's color space.
44 * \todo color->colorspace should simply be a named enum.
45 */
46 SPColorSpaceType
47 sp_color_get_colorspace_type(SPColor const *color)
48 {
49 g_return_val_if_fail (color != NULL, SP_COLORSPACE_TYPE_INVALID);
51 if (color->colorspace == &RGB) return SP_COLORSPACE_TYPE_RGB;
52 if (color->colorspace == &CMYK) return SP_COLORSPACE_TYPE_CMYK;
54 return SP_COLORSPACE_TYPE_UNKNOWN;
55 }
57 /**
58 * Shallow copy of color struct with NULL check.
59 */
60 void
61 sp_color_copy(SPColor *dst, SPColor const *src)
62 {
63 g_return_if_fail (dst != NULL);
64 g_return_if_fail (src != NULL);
66 *dst = *src;
67 }
69 /**
70 * Returns TRUE if c0 or c1 is NULL, or if colors/opacities differ.
71 */
72 gboolean
73 sp_color_is_equal (SPColor const *c0, SPColor const *c1)
74 {
75 g_return_val_if_fail (c0 != NULL, TRUE);
76 g_return_val_if_fail (c1 != NULL, TRUE);
78 if (c0->colorspace != c1->colorspace) return FALSE;
79 if (c0->v.c[0] != c1->v.c[0]) return FALSE;
80 if (c0->v.c[1] != c1->v.c[1]) return FALSE;
81 if (c0->v.c[2] != c1->v.c[2]) return FALSE;
82 if ((c0->colorspace == &CMYK) && (c0->v.c[3] != c1->v.c[3])) return FALSE;
84 return TRUE;
85 }
87 /**
88 * Returns TRUE if no RGB value differs epsilon or more in both colors,
89 * with CMYK aditionally comparing opacity, or if c0 or c1 is NULL.
90 * \note Do we want the latter?
91 */
92 gboolean
93 sp_color_is_close (SPColor const *c0, SPColor const *c1, float epsilon)
94 {
95 g_return_val_if_fail (c0 != NULL, TRUE);
96 g_return_val_if_fail (c1 != NULL, TRUE);
98 if (c0->colorspace != c1->colorspace) return FALSE;
99 if (fabs ((c0->v.c[0]) - (c1->v.c[0])) >= epsilon) return FALSE;
100 if (fabs ((c0->v.c[1]) - (c1->v.c[1])) >= epsilon) return FALSE;
101 if (fabs ((c0->v.c[2]) - (c1->v.c[2])) >= epsilon) return FALSE;
102 if ((c0->colorspace == &CMYK) && (fabs ((c0->v.c[3]) - (c1->v.c[3])) >= epsilon)) return FALSE;
104 return TRUE;
105 }
107 /**
108 * Sets RGB values and colorspace in color.
109 * \pre color != NULL && 0 <={r,g,b}<=1
110 */
111 void
112 sp_color_set_rgb_float(SPColor *color, float r, float g, float b)
113 {
114 g_return_if_fail(color != NULL);
115 g_return_if_fail(r >= 0.0);
116 g_return_if_fail(r <= 1.0);
117 g_return_if_fail(g >= 0.0);
118 g_return_if_fail(g <= 1.0);
119 g_return_if_fail(b >= 0.0);
120 g_return_if_fail(b <= 1.0);
122 color->colorspace = &RGB;
123 color->v.c[0] = r;
124 color->v.c[1] = g;
125 color->v.c[2] = b;
126 color->v.c[3] = 0;
127 }
129 /**
130 * Converts 32bit value to RGB floats and sets color.
131 * \pre color != NULL
132 */
133 void
134 sp_color_set_rgb_rgba32(SPColor *color, guint32 value)
135 {
136 g_return_if_fail (color != NULL);
138 color->colorspace = &RGB;
139 color->v.c[0] = (value >> 24) / 255.0F;
140 color->v.c[1] = ((value >> 16) & 0xff) / 255.0F;
141 color->v.c[2] = ((value >> 8) & 0xff) / 255.0F;
142 color->v.c[3] = 0;
143 }
145 /**
146 * Sets CMYK values and colorspace in color.
147 * \pre color != NULL && 0 <={c,m,y,k}<=1
148 */
149 void
150 sp_color_set_cmyk_float(SPColor *color, float c, float m, float y, float k)
151 {
152 g_return_if_fail(color != NULL);
153 g_return_if_fail(c >= 0.0);
154 g_return_if_fail(c <= 1.0);
155 g_return_if_fail(m >= 0.0);
156 g_return_if_fail(m <= 1.0);
157 g_return_if_fail(y >= 0.0);
158 g_return_if_fail(y <= 1.0);
159 g_return_if_fail(k >= 0.0);
160 g_return_if_fail(k <= 1.0);
162 color->colorspace = &CMYK;
163 color->v.c[0] = c;
164 color->v.c[1] = m;
165 color->v.c[2] = y;
166 color->v.c[3] = k;
167 }
169 /**
170 * Convert SPColor with integer alpha value to 32bit RGBA value.
171 * \pre color != NULL && alpha < 256
172 */
173 guint32
174 sp_color_get_rgba32_ualpha(SPColor const *color, guint32 alpha)
175 {
176 guint32 rgba;
178 g_return_val_if_fail (color != NULL, 0x0);
179 g_return_val_if_fail (alpha <= 0xff, 0x0);
181 if (color->colorspace == &RGB) {
182 rgba = SP_RGBA32_U_COMPOSE(SP_COLOR_F_TO_U(color->v.c[0]),
183 SP_COLOR_F_TO_U(color->v.c[1]),
184 SP_COLOR_F_TO_U(color->v.c[2]),
185 alpha);
186 } else {
187 float rgb[3];
188 rgb[0] = rgb[1] = rgb[2] = 0.0;
189 sp_color_get_rgb_floatv (color, rgb);
190 rgba = SP_RGBA32_U_COMPOSE(SP_COLOR_F_TO_U(rgb[0]),
191 SP_COLOR_F_TO_U(rgb[1]),
192 SP_COLOR_F_TO_U(rgb[2]),
193 alpha);
194 }
196 return rgba;
197 }
199 /**
200 * Convert SPColor with float alpha value to 32bit RGBA value.
201 * \pre color != NULL && 0 <= alpha <= 1
202 */
203 guint32
204 sp_color_get_rgba32_falpha(SPColor const *color, float alpha)
205 {
206 g_return_val_if_fail(color != NULL, 0x0);
207 g_return_val_if_fail(alpha >= 0.0, 0x0);
208 g_return_val_if_fail(alpha <= 1.0, 0x0);
210 return sp_color_get_rgba32_ualpha(color, SP_COLOR_F_TO_U(alpha));
211 }
213 /**
214 * Fill rgb float array with values from SPColor.
215 * \pre color != NULL && rgb != NULL && rgb[0-2] is meaningful
216 */
217 void
218 sp_color_get_rgb_floatv(SPColor const *color, float *rgb)
219 {
220 g_return_if_fail (color != NULL);
221 g_return_if_fail (rgb != NULL);
223 if (color->colorspace == &RGB) {
224 rgb[0] = color->v.c[0];
225 rgb[1] = color->v.c[1];
226 rgb[2] = color->v.c[2];
227 } else if (color->colorspace == &CMYK) {
228 sp_color_cmyk_to_rgb_floatv(rgb,
229 color->v.c[0],
230 color->v.c[1],
231 color->v.c[2],
232 color->v.c[3]);
233 }
234 }
236 /**
237 * Fill cmyk float array with values from SPColor.
238 * \pre color != NULL && cmyk != NULL && cmyk[0-3] is meaningful
239 */
240 void
241 sp_color_get_cmyk_floatv(SPColor const *color, float *cmyk)
242 {
243 g_return_if_fail (color != NULL);
244 g_return_if_fail (cmyk != NULL);
246 if (color->colorspace == &CMYK) {
247 cmyk[0] = color->v.c[0];
248 cmyk[1] = color->v.c[1];
249 cmyk[2] = color->v.c[2];
250 cmyk[3] = color->v.c[3];
251 } else if (color->colorspace == &RGB) {
252 sp_color_rgb_to_cmyk_floatv(cmyk,
253 color->v.c[0],
254 color->v.c[1],
255 color->v.c[2]);
256 }
257 }
259 /* Plain mode helpers */
261 /**
262 * Fill hsv float array from r,g,b float values.
263 */
264 void
265 sp_color_rgb_to_hsv_floatv (float *hsv, float r, float g, float b)
266 {
267 float max, min, delta;
269 max = MAX (MAX (r, g), b);
270 min = MIN (MIN (r, g), b);
271 delta = max - min;
273 hsv[2] = max;
275 if (max > 0) {
276 hsv[1] = delta / max;
277 } else {
278 hsv[1] = 0.0;
279 }
281 if (hsv[1] != 0.0) {
282 if (r == max) {
283 hsv[0] = (g - b) / delta;
284 } else if (g == max) {
285 hsv[0] = 2.0 + (b - r) / delta;
286 } else {
287 hsv[0] = 4.0 + (r - g) / delta;
288 }
290 hsv[0] = hsv[0] / 6.0;
292 if (hsv[0] < 0) hsv[0] += 1.0;
293 }
294 else
295 hsv[0] = 0.0;
296 }
298 /**
299 * Fill rgb float array from h,s,v float values.
300 */
301 void
302 sp_color_hsv_to_rgb_floatv (float *rgb, float h, float s, float v)
303 {
304 gdouble f, w, q, t, d;
306 d = h * 5.99999999;
307 f = d - floor (d);
308 w = v * (1.0 - s);
309 q = v * (1.0 - (s * f));
310 t = v * (1.0 - (s * (1.0 - f)));
312 if (d < 1.0) {
313 *rgb++ = v;
314 *rgb++ = t;
315 *rgb++ = w;
316 } else if (d < 2.0) {
317 *rgb++ = q;
318 *rgb++ = v;
319 *rgb++ = w;
320 } else if (d < 3.0) {
321 *rgb++ = w;
322 *rgb++ = v;
323 *rgb++ = t;
324 } else if (d < 4.0) {
325 *rgb++ = w;
326 *rgb++ = q;
327 *rgb++ = v;
328 } else if (d < 5.0) {
329 *rgb++ = t;
330 *rgb++ = w;
331 *rgb++ = v;
332 } else {
333 *rgb++ = v;
334 *rgb++ = w;
335 *rgb++ = q;
336 }
337 }
339 /**
340 * Fill hsl float array from r,g,b float values.
341 */
342 void
343 sp_color_rgb_to_hsl_floatv (float *hsl, float r, float g, float b)
344 {
345 float max = MAX (MAX (r, g), b);
346 float min = MIN (MIN (r, g), b);
347 float delta = max - min;
349 hsl[2] = (max + min)/2.0;
351 if (delta == 0) {
352 hsl[0] = 0;
353 hsl[1] = 0;
354 } else {
355 if (hsl[2] <= 0.5)
356 hsl[1] = delta / (max + min);
357 else
358 hsl[1] = delta / (2 - max - min);
360 if (r == max) hsl[0] = (g - b) / delta;
361 else if (g == max) hsl[0] = 2.0 + (b - r) / delta;
362 else if (b == max) hsl[0] = 4.0 + (r - g) / delta;
364 hsl[0] = hsl[0] / 6.0;
366 if (hsl[0] < 0) hsl[0] += 1;
367 if (hsl[0] > 1) hsl[0] -= 1;
368 }
369 }
371 float
372 hue_2_rgb (float v1, float v2, float h)
373 {
374 if (h < 0) h += 6.0;
375 if (h > 6) h -= 6.0;
377 if (h < 1) return v1 + (v2 - v1) * h;
378 if (h < 3) return v2;
379 if (h < 4) return v1 + (v2 - v1) * (4 - h);
380 return v1;
381 }
383 /**
384 * Fill rgb float array from h,s,l float values.
385 */
386 void
387 sp_color_hsl_to_rgb_floatv (float *rgb, float h, float s, float l)
388 {
389 if (s == 0) {
390 rgb[0] = l;
391 rgb[1] = l;
392 rgb[2] = l;
393 } else {
394 float v2;
395 if (l < 0.5) {
396 v2 = l * (1 + s);
397 } else {
398 v2 = l + s - l*s;
399 }
400 float v1 = 2*l - v2;
402 rgb[0] = hue_2_rgb (v1, v2, h*6 + 2.0);
403 rgb[1] = hue_2_rgb (v1, v2, h*6);
404 rgb[2] = hue_2_rgb (v1, v2, h*6 - 2.0);
405 }
406 }
408 /**
409 * Fill cmyk float array from r,g,b float values.
410 */
411 void
412 sp_color_rgb_to_cmyk_floatv (float *cmyk, float r, float g, float b)
413 {
414 float c, m, y, k, kd;
416 c = 1.0 - r;
417 m = 1.0 - g;
418 y = 1.0 - b;
419 k = MIN (MIN (c, m), y);
421 c = c - k;
422 m = m - k;
423 y = y - k;
425 kd = 1.0 - k;
427 if (kd > 1e-9) {
428 c = c / kd;
429 m = m / kd;
430 y = y / kd;
431 }
433 cmyk[0] = c;
434 cmyk[1] = m;
435 cmyk[2] = y;
436 cmyk[3] = k;
437 }
439 /**
440 * Fill rgb float array from c,m,y,k float values.
441 */
442 void
443 sp_color_cmyk_to_rgb_floatv (float *rgb, float c, float m, float y, float k)
444 {
445 float kd;
447 kd = 1.0 - k;
449 c = c * kd;
450 m = m * kd;
451 y = y * kd;
453 c = c + k;
454 m = m + k;
455 y = y + k;
457 rgb[0] = 1.0 - c;
458 rgb[1] = 1.0 - m;
459 rgb[2] = 1.0 - y;
460 }
462 /*
463 Local Variables:
464 mode:c++
465 c-file-style:"stroustrup"
466 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
467 indent-tabs-mode:nil
468 fill-column:99
469 End:
470 */
471 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :