1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2010-2014 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU Lesser General Public License Version 2.1
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * SECTION:cd-color
24 * @short_description: Color object data functionality
25 *
26 * Functions to manipulate color values.
27 */
28
29 #include "config.h"
30
31 #include <math.h>
32 #include <glib-object.h>
33 #include <lcms2.h>
34
35 #include "cd-color.h"
36 #include "cd-interp.h"
37 #include "cd-interp-akima.h"
38 #include "cd-interp-linear.h"
39
40 /* this is private */
41 struct _CdColorSwatch {
42 gchar *name;
43 CdColorLab value;
44 };
45
46 /**
47 * cd_color_xyz_dup:
48 *
49 * Since: 0.1.27
50 **/
51 CdColorXYZ *
cd_color_xyz_dup(const CdColorXYZ * src)52 cd_color_xyz_dup (const CdColorXYZ *src)
53 {
54 CdColorXYZ *dest;
55 g_return_val_if_fail (src != NULL, NULL);
56 dest = cd_color_xyz_new ();
57 dest->X = src->X;
58 dest->Y = src->Y;
59 dest->Z = src->Z;
60 return dest;
61 }
62
63 /**
64 * cd_color_rgb_dup:
65 *
66 * Since: 0.1.27
67 **/
68 CdColorRGB *
cd_color_rgb_dup(const CdColorRGB * src)69 cd_color_rgb_dup (const CdColorRGB *src)
70 {
71 CdColorRGB *dest;
72 g_return_val_if_fail (src != NULL, NULL);
73 dest = cd_color_rgb_new ();
74 dest->R = src->R;
75 dest->G = src->G;
76 dest->B = src->B;
77 return dest;
78 }
79
80 /**
81 * cd_color_lab_dup:
82 *
83 * Since: 0.1.32
84 **/
85 CdColorLab *
cd_color_lab_dup(const CdColorLab * src)86 cd_color_lab_dup (const CdColorLab *src)
87 {
88 CdColorLab *dest;
89 g_return_val_if_fail (src != NULL, NULL);
90 dest = cd_color_lab_new ();
91 dest->L = src->L;
92 dest->a = src->a;
93 dest->b = src->b;
94 return dest;
95 }
96
97 /**
98 * cd_color_yxy_dup:
99 *
100 * Since: 0.1.27
101 **/
102 CdColorYxy *
cd_color_yxy_dup(const CdColorYxy * src)103 cd_color_yxy_dup (const CdColorYxy *src)
104 {
105 CdColorYxy *dest;
106 g_return_val_if_fail (src != NULL, NULL);
107 dest = cd_color_yxy_new ();
108 dest->Y = src->Y;
109 dest->x = src->x;
110 dest->y = src->y;
111 return dest;
112 }
113
114 /**
115 * cd_color_uvw_dup:
116 *
117 * Since: 1.1.6
118 **/
119 CdColorUVW *
cd_color_uvw_dup(const CdColorUVW * src)120 cd_color_uvw_dup (const CdColorUVW *src)
121 {
122 CdColorUVW *dest;
123 g_return_val_if_fail (src != NULL, NULL);
124 dest = cd_color_uvw_new ();
125 dest->U = src->U;
126 dest->V = src->V;
127 dest->W = src->W;
128 return dest;
129 }
130
131 /**
132 * cd_color_swatch_dup:
133 *
134 * Since: 0.1.32
135 **/
136 CdColorSwatch *
cd_color_swatch_dup(const CdColorSwatch * src)137 cd_color_swatch_dup (const CdColorSwatch *src)
138 {
139 CdColorSwatch *dest;
140 g_return_val_if_fail (src != NULL, NULL);
141 dest = cd_color_swatch_new ();
142 dest->name = g_strdup (src->name);
143 cd_color_lab_copy (&src->value, &dest->value);
144 return dest;
145 }
146
147 /**
148 * cd_color_swatch_get_name:
149 *
150 * Since: 0.1.32
151 **/
152 const gchar *
cd_color_swatch_get_name(const CdColorSwatch * swatch)153 cd_color_swatch_get_name (const CdColorSwatch *swatch)
154 {
155 g_return_val_if_fail (swatch != NULL, NULL);
156 return swatch->name;
157 }
158
159 /**
160 * cd_color_swatch_get_value:
161 *
162 * Since: 0.1.32
163 **/
164 const CdColorLab *
cd_color_swatch_get_value(const CdColorSwatch * swatch)165 cd_color_swatch_get_value (const CdColorSwatch *swatch)
166 {
167 g_return_val_if_fail (swatch != NULL, NULL);
168 return &swatch->value;
169 }
170
171 /**
172 * cd_color_xyz_get_type:
173 *
174 * Gets a specific type.
175 *
176 * Return value: a #GType
177 *
178 * Since: 0.1.6
179 **/
180 GType
cd_color_xyz_get_type(void)181 cd_color_xyz_get_type (void)
182 {
183 static GType type_id = 0;
184 if (!type_id)
185 type_id = g_boxed_type_register_static ("CdColorXYZ",
186 (GBoxedCopyFunc) cd_color_xyz_dup,
187 (GBoxedFreeFunc) cd_color_xyz_free);
188 return type_id;
189 }
190
191 /**
192 * cd_color_rgb_get_type:
193 *
194 * Gets a specific type.
195 *
196 * Return value: a #GType
197 *
198 * Since: 0.1.6
199 **/
200 GType
cd_color_rgb_get_type(void)201 cd_color_rgb_get_type (void)
202 {
203 static GType type_id = 0;
204 if (!type_id)
205 type_id = g_boxed_type_register_static ("CdColorRGB",
206 (GBoxedCopyFunc) cd_color_rgb_dup,
207 (GBoxedFreeFunc) cd_color_rgb_free);
208 return type_id;
209 }
210
211 /**
212 * cd_color_lab_get_type:
213 *
214 * Gets a specific type.
215 *
216 * Return value: a #GType
217 *
218 * Since: 0.1.32
219 **/
220 GType
cd_color_lab_get_type(void)221 cd_color_lab_get_type (void)
222 {
223 static GType type_id = 0;
224 if (!type_id)
225 type_id = g_boxed_type_register_static ("CdColorLab",
226 (GBoxedCopyFunc) cd_color_lab_dup,
227 (GBoxedFreeFunc) cd_color_lab_free);
228 return type_id;
229 }
230
231 /**
232 * cd_color_yxy_get_type:
233 *
234 * Gets a specific type.
235 *
236 * Return value: a #GType
237 *
238 * Since: 0.1.6
239 **/
240 GType
cd_color_yxy_get_type(void)241 cd_color_yxy_get_type (void)
242 {
243 static GType type_id = 0;
244 if (!type_id)
245 type_id = g_boxed_type_register_static ("CdColorYxy",
246 (GBoxedCopyFunc) cd_color_yxy_dup,
247 (GBoxedFreeFunc) cd_color_yxy_free);
248 return type_id;
249 }
250
251 /**
252 * cd_color_uvw_get_type:
253 *
254 * Gets a specific type.
255 *
256 * Return value: a #GType
257 *
258 * Since: 1.1.6
259 **/
260 GType
cd_color_uvw_get_type(void)261 cd_color_uvw_get_type (void)
262 {
263 static GType type_id = 0;
264 if (!type_id)
265 type_id = g_boxed_type_register_static ("CdColorUVW",
266 (GBoxedCopyFunc) cd_color_uvw_dup,
267 (GBoxedFreeFunc) cd_color_uvw_free);
268 return type_id;
269 }
270
271 /**
272 * cd_color_swatch_get_type:
273 *
274 * Gets a specific type.
275 *
276 * Return value: a #GType
277 *
278 * Since: 0.1.32
279 **/
280 GType
cd_color_swatch_get_type(void)281 cd_color_swatch_get_type (void)
282 {
283 static GType type_id = 0;
284 if (!type_id)
285 type_id = g_boxed_type_register_static ("CdColorSwatch",
286 (GBoxedCopyFunc) cd_color_swatch_dup,
287 (GBoxedFreeFunc) cd_color_swatch_free);
288 return type_id;
289 }
290
291 /**
292 * cd_color_xyz_new:
293 *
294 * Allocates a color value.
295 *
296 * Return value: A newly allocated #CdColorXYZ object
297 *
298 * Since: 0.1.0
299 **/
300 CdColorXYZ *
cd_color_xyz_new(void)301 cd_color_xyz_new (void)
302 {
303 return g_slice_new0 (CdColorXYZ);
304 }
305
306 /**
307 * cd_color_rgb_new:
308 *
309 * Allocates a color value.
310 *
311 * Return value: A newly allocated #CdColorRGB object
312 *
313 * Since: 0.1.0
314 **/
315 CdColorRGB *
cd_color_rgb_new(void)316 cd_color_rgb_new (void)
317 {
318 return g_slice_new0 (CdColorRGB);
319 }
320
321 /**
322 * cd_color_lab_new:
323 *
324 * Allocates a color value.
325 *
326 * Return value: A newly allocated #CdColorLab object
327 *
328 * Since: 0.1.32
329 **/
330 CdColorLab *
cd_color_lab_new(void)331 cd_color_lab_new (void)
332 {
333 return g_slice_new0 (CdColorLab);
334 }
335
336 /**
337 * cd_color_yxy_new:
338 *
339 * Allocates a color value.
340 *
341 * Return value: A newly allocated #CdColorYxy object
342 *
343 * Since: 0.1.0
344 **/
345 CdColorYxy *
cd_color_yxy_new(void)346 cd_color_yxy_new (void)
347 {
348 return g_slice_new0 (CdColorYxy);
349 }
350
351 /**
352 * cd_color_uvw_new:
353 *
354 * Allocates a color value.
355 *
356 * Return value: A newly allocated #CdColorUVW object
357 *
358 * Since: 1.1.6
359 **/
360 CdColorUVW *
cd_color_uvw_new(void)361 cd_color_uvw_new (void)
362 {
363 return g_slice_new0 (CdColorUVW);
364 }
365
366 /**
367 * cd_color_swatch_new:
368 *
369 * Allocates a color value.
370 *
371 * Return value: A newly allocated #CdColorSwatch object
372 *
373 * Since: 0.1.32
374 **/
375 CdColorSwatch *
cd_color_swatch_new(void)376 cd_color_swatch_new (void)
377 {
378 return g_slice_new0 (CdColorSwatch);
379 }
380
381 /**
382 * cd_color_xyz_free:
383 * @src: the color object
384 *
385 * Deallocates a color value.
386 *
387 * Since: 0.1.0
388 **/
389 void
cd_color_xyz_free(CdColorXYZ * src)390 cd_color_xyz_free (CdColorXYZ *src)
391 {
392 g_slice_free (CdColorXYZ, src);
393 }
394
395 /**
396 * cd_color_rgb_free:
397 * @src: the color object
398 *
399 * Deallocates a color value.
400 *
401 * Since: 0.1.0
402 **/
403 void
cd_color_rgb_free(CdColorRGB * src)404 cd_color_rgb_free (CdColorRGB *src)
405 {
406 g_slice_free (CdColorRGB, src);
407 }
408
409 /**
410 * cd_color_lab_free:
411 * @src: the color object
412 *
413 * Deallocates a color value.
414 *
415 * Since: 0.1.32
416 **/
417 void
cd_color_lab_free(CdColorLab * src)418 cd_color_lab_free (CdColorLab *src)
419 {
420 g_slice_free (CdColorLab, src);
421 }
422
423 /**
424 * cd_color_yxy_free:
425 * @src: the color object
426 *
427 * Deallocates a color value.
428 *
429 * Since: 0.1.0
430 **/
431 void
cd_color_yxy_free(CdColorYxy * src)432 cd_color_yxy_free (CdColorYxy *src)
433 {
434 g_slice_free (CdColorYxy, src);
435 }
436
437 /**
438 * cd_color_uvw_free:
439 * @src: the color object
440 *
441 * Deallocates a color value.
442 *
443 * Since: 1.1.6
444 **/
445 void
cd_color_uvw_free(CdColorUVW * src)446 cd_color_uvw_free (CdColorUVW *src)
447 {
448 g_slice_free (CdColorUVW, src);
449 }
450
451 /**
452 * cd_color_swatch_free:
453 * @src: the color object
454 *
455 * Deallocates a color swatch.
456 *
457 * Since: 0.1.32
458 **/
459 void
cd_color_swatch_free(CdColorSwatch * src)460 cd_color_swatch_free (CdColorSwatch *src)
461 {
462 g_free (src->name);
463 g_slice_free (CdColorSwatch, src);
464 }
465
466 /**
467 * cd_color_xyz_set:
468 * @dest: the destination color
469 * @X: component value
470 * @Y: component value
471 * @Z: component value
472 *
473 * Initialises a color value.
474 *
475 * Since: 0.1.27
476 **/
477 void
cd_color_xyz_set(CdColorXYZ * dest,gdouble X,gdouble Y,gdouble Z)478 cd_color_xyz_set (CdColorXYZ *dest, gdouble X, gdouble Y, gdouble Z)
479 {
480 g_return_if_fail (dest != NULL);
481
482 dest->X = X;
483 dest->Y = Y;
484 dest->Z = Z;
485 }
486
487 /**
488 * cd_color_xyz_clear:
489 * @dest: the destination color
490 *
491 * Initialises a color value.
492 *
493 * Since: 0.1.27
494 **/
495 void
cd_color_xyz_clear(CdColorXYZ * dest)496 cd_color_xyz_clear (CdColorXYZ *dest)
497 {
498 g_return_if_fail (dest != NULL);
499
500 dest->X = 0.0f;
501 dest->Y = 0.0f;
502 dest->Z = 0.0f;
503 }
504
505 /**
506 * cd_color_rgb_set:
507 * @dest: the destination color
508 * @R: component value
509 * @G: component value
510 * @B: component value
511 *
512 * Initialises a color value.
513 *
514 * Since: 0.1.27
515 **/
516 void
cd_color_rgb_set(CdColorRGB * dest,gdouble R,gdouble G,gdouble B)517 cd_color_rgb_set (CdColorRGB *dest, gdouble R, gdouble G, gdouble B)
518 {
519 g_return_if_fail (dest != NULL);
520
521 dest->R = R;
522 dest->G = G;
523 dest->B = B;
524 }
525
526 /**
527 * cd_color_lab_set:
528 * @dest: the destination color
529 * @L: component value
530 * @a: component value
531 * @b: component value
532 *
533 * Initialises a color value.
534 *
535 * Since: 0.1.32
536 **/
537 void
cd_color_lab_set(CdColorLab * dest,gdouble L,gdouble a,gdouble b)538 cd_color_lab_set (CdColorLab *dest, gdouble L, gdouble a, gdouble b)
539 {
540 g_return_if_fail (dest != NULL);
541
542 dest->L = L;
543 dest->a = a;
544 dest->b = b;
545 }
546
547 /**
548 * cd_color_lab_delta_e76:
549 * @p1: Lab value 1
550 * @p2: Lab value 2
551 *
552 * Calculates the ΔE of two colors using the 1976 formula.
553 *
554 * Return value: distance metric, where JND ΔE ≈ 2.3
555 *
556 * Since: 0.1.32
557 **/
558 gdouble
cd_color_lab_delta_e76(const CdColorLab * p1,const CdColorLab * p2)559 cd_color_lab_delta_e76 (const CdColorLab *p1, const CdColorLab *p2)
560 {
561 return sqrt (pow (p2->L - p1->L, 2) +
562 pow (p2->a - p1->a, 2) +
563 pow (p2->b - p1->b, 2));
564 }
565
566 /**
567 * cd_color_yxy_set:
568 * @dest: the destination color
569 * @Y: component value
570 * @x: component value
571 * @y: component value
572 *
573 * Initialises a color value.
574 *
575 * Since: 0.1.27
576 **/
577 void
cd_color_yxy_set(CdColorYxy * dest,gdouble Y,gdouble x,gdouble y)578 cd_color_yxy_set (CdColorYxy *dest, gdouble Y, gdouble x, gdouble y)
579 {
580 g_return_if_fail (dest != NULL);
581
582 dest->Y = Y;
583 dest->x = x;
584 dest->y = y;
585 }
586
587 /**
588 * cd_color_uvw_set:
589 * @dest: the destination color
590 * @U: component value
591 * @V: component value
592 * @W: component value
593 *
594 * Initialises a color value.
595 *
596 * Since: 1.1.6
597 **/
598 void
cd_color_uvw_set(CdColorUVW * dest,gdouble U,gdouble V,gdouble W)599 cd_color_uvw_set (CdColorUVW *dest, gdouble U, gdouble V, gdouble W)
600 {
601 g_return_if_fail (dest != NULL);
602
603 dest->U = U;
604 dest->V = V;
605 dest->W = W;
606 }
607
608 /**
609 * cd_color_swatch_set_name:
610 * @dest: the destination swatch
611 * @name: component name
612 *
613 * Initialises a swatch name.
614 *
615 * Since: 0.1.32
616 **/
617 void
cd_color_swatch_set_name(CdColorSwatch * dest,const gchar * name)618 cd_color_swatch_set_name (CdColorSwatch *dest, const gchar *name)
619 {
620 g_return_if_fail (dest != NULL);
621 g_return_if_fail (name != NULL);
622 g_free (dest->name);
623 dest->name = g_strdup (name);
624 }
625
626 /**
627 * cd_color_swatch_set_value:
628 * @dest: the destination swatch
629 * @value: component value
630 *
631 * Initialises a swatch value.
632 *
633 * Since: 0.1.32
634 **/
635 void
cd_color_swatch_set_value(CdColorSwatch * dest,const CdColorLab * value)636 cd_color_swatch_set_value (CdColorSwatch *dest, const CdColorLab *value)
637 {
638 g_return_if_fail (dest != NULL);
639 g_return_if_fail (value != NULL);
640 cd_color_lab_copy (value, &dest->value);
641 }
642
643 /**
644 * cd_color_xyz_copy:
645 * @src: the source color
646 * @dest: the destination color
647 *
648 * Deep copies a color value.
649 *
650 * Since: 0.1.27
651 **/
652 void
cd_color_xyz_copy(const CdColorXYZ * src,CdColorXYZ * dest)653 cd_color_xyz_copy (const CdColorXYZ *src, CdColorXYZ *dest)
654 {
655 g_return_if_fail (src != NULL);
656 g_return_if_fail (dest != NULL);
657
658 dest->X = src->X;
659 dest->Y = src->Y;
660 dest->Z = src->Z;
661 }
662
663 /**
664 * cd_color_yxy_copy:
665 * @src: the source color
666 * @dest: the destination color
667 *
668 * Deep copies a color value.
669 *
670 * Since: 0.1.27
671 **/
672 void
cd_color_yxy_copy(const CdColorYxy * src,CdColorYxy * dest)673 cd_color_yxy_copy (const CdColorYxy *src, CdColorYxy *dest)
674 {
675 g_return_if_fail (src != NULL);
676 g_return_if_fail (dest != NULL);
677
678 dest->Y = src->Y;
679 dest->x = src->x;
680 dest->y = src->y;
681 }
682
683 /**
684 * cd_color_uvw_copy:
685 * @src: the source color
686 * @dest: the destination color
687 *
688 * Deep copies a color value.
689 *
690 * Since: 1.1.6
691 **/
692 void
cd_color_uvw_copy(const CdColorUVW * src,CdColorUVW * dest)693 cd_color_uvw_copy (const CdColorUVW *src, CdColorUVW *dest)
694 {
695 g_return_if_fail (src != NULL);
696 g_return_if_fail (dest != NULL);
697
698 dest->U = src->U;
699 dest->V = src->V;
700 dest->W = src->W;
701 }
702
703 /**
704 * cd_color_lab_copy:
705 * @src: the source color
706 * @dest: the destination color
707 *
708 * Deep copies a color value.
709 *
710 * Since: 0.1.32
711 **/
712 void
cd_color_lab_copy(const CdColorLab * src,CdColorLab * dest)713 cd_color_lab_copy (const CdColorLab *src, CdColorLab *dest)
714 {
715 g_return_if_fail (src != NULL);
716 g_return_if_fail (dest != NULL);
717
718 dest->L = src->L;
719 dest->a = src->a;
720 dest->b = src->b;
721 }
722
723 /**
724 * cd_color_rgb_copy:
725 * @src: the source color
726 * @dest: the destination color
727 *
728 * Deep copies a color value.
729 *
730 * Since: 0.1.27
731 **/
732 void
cd_color_rgb_copy(const CdColorRGB * src,CdColorRGB * dest)733 cd_color_rgb_copy (const CdColorRGB *src, CdColorRGB *dest)
734 {
735 g_return_if_fail (src != NULL);
736 g_return_if_fail (dest != NULL);
737
738 dest->R = src->R;
739 dest->G = src->G;
740 dest->B = src->B;
741 }
742
743 /**
744 * cd_color_rgb8_to_rgb:
745 * @src: the source color
746 * @dest: the destination color
747 *
748 * Convert from one color format to another.
749 *
750 * Since: 0.1.27
751 **/
752 void
cd_color_rgb8_to_rgb(const CdColorRGB8 * src,CdColorRGB * dest)753 cd_color_rgb8_to_rgb (const CdColorRGB8 *src, CdColorRGB *dest)
754 {
755 g_return_if_fail (src != NULL);
756 g_return_if_fail (dest != NULL);
757
758 dest->R = (gdouble) src->R / 255.0f;
759 dest->G = (gdouble) src->G / 255.0f;
760 dest->B = (gdouble) src->B / 255.0f;
761 }
762
763 /**
764 * cd_color_value_double_to_uint8:
765 **/
766 static guint8
cd_color_value_double_to_uint8(gdouble value)767 cd_color_value_double_to_uint8 (gdouble value)
768 {
769 if (value < 0)
770 return 0;
771 if (value > 1.0f)
772 return 255;
773 return value * 255.0f;
774 }
775
776 /**
777 * cd_color_rgb_to_rgb8:
778 * @src: the source color
779 * @dest: the destination color
780 *
781 * Convert from one color format to another.
782 *
783 * Since: 0.1.27
784 **/
785 void
cd_color_rgb_to_rgb8(const CdColorRGB * src,CdColorRGB8 * dest)786 cd_color_rgb_to_rgb8 (const CdColorRGB *src, CdColorRGB8 *dest)
787 {
788 g_return_if_fail (src != NULL);
789 g_return_if_fail (dest != NULL);
790
791 /* also deal with overflow and underflow */
792 dest->R = cd_color_value_double_to_uint8 (src->R);
793 dest->G = cd_color_value_double_to_uint8 (src->G);
794 dest->B = cd_color_value_double_to_uint8 (src->B);
795 }
796
797 /**
798 * cd_color_yxy_to_xyz:
799 * @src: the source color
800 * @dest: the destination color
801 *
802 * Convert from one color format to another.
803 *
804 * Since: 0.1.27
805 **/
806 void
cd_color_yxy_to_xyz(const CdColorYxy * src,CdColorXYZ * dest)807 cd_color_yxy_to_xyz (const CdColorYxy *src, CdColorXYZ *dest)
808 {
809 g_return_if_fail (src != NULL);
810 g_return_if_fail (dest != NULL);
811
812 g_assert (src->Y >= 0.0f);
813 g_assert (src->x >= 0.0f);
814 g_assert (src->y >= 0.0f);
815 g_assert (src->Y <= 100.0f);
816 g_assert (src->x <= 1.0f);
817 g_assert (src->y <= 1.0f);
818
819 /* very small luminance */
820 if (src->Y < 1e-6) {
821 dest->X = 0.0f;
822 dest->Y = 0.0f;
823 dest->Z = 0.0f;
824 return;
825 }
826
827 dest->X = (src->x * src->Y) / src->y;
828 dest->Y = src->Y;
829 dest->Z = (1.0f - src->x - src->y) * src->Y / src->y;
830 }
831
832 /**
833 * cd_color_xyz_normalize:
834 * @src: the source color
835 * @dest: the destination color
836 *
837 * Normalizes @src to y=1.0
838 *
839 * Since: 1.1.6
840 **/
841 void
cd_color_xyz_normalize(const CdColorXYZ * src,gdouble max,CdColorXYZ * dest)842 cd_color_xyz_normalize (const CdColorXYZ *src, gdouble max, CdColorXYZ *dest)
843 {
844 dest->X = max * src->X / src->Y;
845 dest->Z = max * src->Z / src->Y;
846 dest->Y = max;
847 }
848
849 /**
850 * cd_color_xyz_to_cct:
851 * @src: the source color
852 *
853 * Gets the correlated color temperature for the XYZ value.
854 *
855 * Since: 1.1.6
856 **/
857 gdouble
cd_color_xyz_to_cct(const CdColorXYZ * src)858 cd_color_xyz_to_cct (const CdColorXYZ *src)
859 {
860 cmsCIExyY tmp;
861 cmsCIEXYZ src_lcms;
862 gboolean ret;
863 gdouble value;
864
865 /* in case cmsFloat64Number != gdouble */
866 src_lcms.X = src->X;
867 src_lcms.Y = src->Y;
868 src_lcms.Z = src->Z;
869 cmsXYZ2xyY (&tmp, &src_lcms);
870 ret = cmsTempFromWhitePoint (&value, &tmp);
871 if (!ret)
872 return -1.f;
873 return value;
874 }
875
876 /**
877 * cd_color_uvw_get_chroma_difference:
878 * @p1: color
879 * @p2: color
880 *
881 * Gets the chromaticity distance in the CIE 1960 UCS.
882 *
883 * Return value: The Euclidean distance
884 *
885 * Since: 1.1.6
886 **/
887 gdouble
cd_color_uvw_get_chroma_difference(const CdColorUVW * p1,const CdColorUVW * p2)888 cd_color_uvw_get_chroma_difference (const CdColorUVW *p1, const CdColorUVW *p2)
889 {
890 return sqrt (pow ((p1->U - p2->U), 2) + pow ((p1->V - p2->V), 2));
891 }
892
893 /**
894 * cd_color_uvw_set_planckian_locus:
895 * @dest: destination color
896 * @temp: temperature in Kelvin
897 *
898 * Sets the CIEUVW color from a Planckian locus of specific temperature.
899 *
900 * Since: 1.1.6
901 **/
902 void
cd_color_uvw_set_planckian_locus(CdColorUVW * dest,gdouble temp)903 cd_color_uvw_set_planckian_locus (CdColorUVW *dest, gdouble temp)
904 {
905 dest->W = 1.0;
906 dest->U = (0.860117757 +
907 (1.54118254 * temp * 1e-4) +
908 (1.28641212 * pow (temp, 2) * 1e-7)) /
909 (1.0 +
910 (8.42420235 * temp * 1e-4) +
911 (7.08145163 * pow (temp, 2) * 1e-7));
912 dest->V = (0.317398726 +
913 (4.22806245 * temp * 1e-5) +
914 (4.20481691 * pow (temp, 2) * 1e-8)) /
915 (1.0 -
916 (2.89741816 * temp * 1e-5) +
917 (1.61456053 * pow (temp, 2) * 1e-7));
918 }
919
920 /**
921 * cd_color_xyz_to_yxy:
922 * @src: the source color
923 * @dest: the destination color
924 *
925 * Convert from one color format to another.
926 *
927 * Since: 0.1.27
928 **/
929 void
cd_color_xyz_to_yxy(const CdColorXYZ * src,CdColorYxy * dest)930 cd_color_xyz_to_yxy (const CdColorXYZ *src, CdColorYxy *dest)
931 {
932 gdouble sum;
933
934 g_return_if_fail (src != NULL);
935 g_return_if_fail (dest != NULL);
936
937 /* prevent division by zero */
938 sum = src->X + src->Y + src->Z;
939 if (fabs (sum) < 1e-6) {
940 cd_color_yxy_set (dest, 0.f, 0.f, 0.f);
941 return;
942 }
943
944 dest->Y = src->Y;
945 dest->x = src->X / sum;
946 dest->y = src->Y / sum;
947 }
948
949 typedef struct {
950 gdouble Y;
951 gdouble u;
952 gdouble v;
953 } CdColorYuv;
954
955 static void
cd_color_xyz_to_yuv(const CdColorXYZ * src,CdColorYuv * dest)956 cd_color_xyz_to_yuv (const CdColorXYZ *src, CdColorYuv *dest)
957 {
958 gdouble sum = src->X + 15 * src->Y + 3 * src->Z;
959 dest->Y = src->Y;
960 dest->u = 4 * src->X / sum;
961 dest->v = 6 * src->Y / sum;
962 }
963
964 /**
965 * cd_color_xyz_to_uvw:
966 * @src: the source color
967 * @whitepoint: the whitepoint
968 * @dest: the destination color
969 *
970 * Convert from one color format to another.
971 *
972 * Since: 1.1.6
973 **/
974 void
cd_color_xyz_to_uvw(const CdColorXYZ * src,const CdColorXYZ * whitepoint,CdColorUVW * dest)975 cd_color_xyz_to_uvw (const CdColorXYZ *src,
976 const CdColorXYZ *whitepoint,
977 CdColorUVW *dest)
978 {
979 CdColorYuv wp;
980 CdColorYuv tmp;
981
982 cd_color_xyz_to_yuv (whitepoint, &wp);
983 cd_color_xyz_to_yuv (src, &tmp);
984
985 dest->W = 25 * pow (src->Y * 100.f / wp.Y, 1.f/3.f) - 17.f;
986 dest->U = 13 * dest->W * (tmp.u - wp.u);
987 dest->V = 13 * dest->W * (tmp.v - wp.v);
988 }
989
990 /**
991 * cd_color_yxy_to_uvw:
992 * @src: the source color
993 * @dest: the destination color
994 *
995 * Convert from one color format to another.
996 *
997 * Since: 1.1.6
998 **/
999 void
cd_color_yxy_to_uvw(const CdColorYxy * src,CdColorUVW * dest)1000 cd_color_yxy_to_uvw (const CdColorYxy *src, CdColorUVW *dest)
1001 {
1002 gdouble sum = (-2 * src->x) + (12 * src->y) + (3 * src->Y);
1003 dest->U = (4 * src->x) / sum;
1004 dest->V = (6 * src->y) / sum;
1005 dest->W = src->Y;
1006 }
1007
1008 /* source: https://github.com/jonls/redshift/blob/master/README-colorramp
1009 * use a Planckian curve below 5000K */
1010 static const CdColorRGB blackbody_data_d65plankian[] = {
1011 { 1.0000, 0.1817, 0.0000 }, /* 1000K */
1012 { 1.0000, 0.2550, 0.0000 }, /* 1100K */
1013 { 1.0000, 0.3094, 0.0000 }, /* 1200K */
1014 { 1.0000, 0.3536, 0.0000 }, /* ... */
1015 { 1.0000, 0.3909, 0.0000 },
1016 { 1.0000, 0.4232, 0.0000 },
1017 { 1.0000, 0.4516, 0.0000 },
1018 { 1.0000, 0.4768, 0.0000 },
1019 { 1.0000, 0.4992, 0.0000 },
1020 { 1.0000, 0.5194, 0.0000 },
1021 { 1.0000, 0.5436, 0.0868 },
1022 { 1.0000, 0.5662, 0.1407 },
1023 { 1.0000, 0.5873, 0.1836 },
1024 { 1.0000, 0.6072, 0.2214 },
1025 { 1.0000, 0.6260, 0.2559 },
1026 { 1.0000, 0.6437, 0.2882 },
1027 { 1.0000, 0.6605, 0.3187 },
1028 { 1.0000, 0.6765, 0.3479 },
1029 { 1.0000, 0.6916, 0.3758 },
1030 { 1.0000, 0.7060, 0.4027 },
1031 { 1.0000, 0.7198, 0.4286 },
1032 { 1.0000, 0.7329, 0.4537 },
1033 { 1.0000, 0.7454, 0.4779 },
1034 { 1.0000, 0.7574, 0.5015 },
1035 { 1.0000, 0.7689, 0.5243 },
1036 { 1.0000, 0.7799, 0.5464 },
1037 { 1.0000, 0.7904, 0.5679 },
1038 { 1.0000, 0.8005, 0.5888 },
1039 { 1.0000, 0.8102, 0.6092 },
1040 { 1.0000, 0.8196, 0.6289 },
1041 { 1.0000, 0.8285, 0.6482 },
1042 { 1.0000, 0.8372, 0.6669 },
1043 { 1.0000, 0.8455, 0.6851 },
1044 { 1.0000, 0.8535, 0.7028 },
1045 { 1.0000, 0.8612, 0.7201 },
1046 { 1.0000, 0.8686, 0.7369 },
1047 { 1.0000, 0.8758, 0.7533 },
1048 { 1.0000, 0.8827, 0.7692 },
1049 { 1.0000, 0.8893, 0.7848 },
1050 { 1.0000, 0.8958, 0.7999 },
1051 { 1.0000, 0.9020, 0.8147 },
1052 { 1.0000, 0.9096, 0.8284 },
1053 { 1.0000, 0.9171, 0.8419 },
1054 { 1.0000, 0.9244, 0.8552 },
1055 { 1.0000, 0.9316, 0.8684 },
1056 { 1.0000, 0.9385, 0.8813 },
1057 { 1.0000, 0.9454, 0.8940 },
1058 { 1.0000, 0.9520, 0.9066 },
1059 { 1.0000, 0.9585, 0.9189 },
1060 { 1.0000, 0.9649, 0.9311 },
1061 { 1.0000, 0.9711, 0.9431 },
1062 { 1.0000, 0.9771, 0.9548 },
1063 { 1.0000, 0.9831, 0.9664 },
1064 { 1.0000, 0.9888, 0.9778 },
1065 { 1.0000, 0.9945, 0.9890 },
1066 { 1.0000, 1.0000, 1.0000 }, /* 6500K */
1067 { 0.9895, 0.9935, 1.0000 },
1068 { 0.9794, 0.9872, 1.0000 },
1069 { 0.9698, 0.9812, 1.0000 },
1070 { 0.9605, 0.9754, 1.0000 },
1071 { 0.9516, 0.9698, 1.0000 },
1072 { 0.9430, 0.9644, 1.0000 },
1073 { 0.9348, 0.9592, 1.0000 },
1074 { 0.9269, 0.9542, 1.0000 },
1075 { 0.9193, 0.9494, 1.0000 },
1076 { 0.9119, 0.9447, 1.0000 },
1077 { 0.9049, 0.9402, 1.0000 },
1078 { 0.8981, 0.9358, 1.0000 },
1079 { 0.8915, 0.9316, 1.0000 },
1080 { 0.8852, 0.9275, 1.0000 },
1081 { 0.8791, 0.9236, 1.0000 },
1082 { 0.8732, 0.9197, 1.0000 },
1083 { 0.8674, 0.9160, 1.0000 },
1084 { 0.8619, 0.9125, 1.0000 },
1085 { 0.8566, 0.9090, 1.0000 },
1086 { 0.8514, 0.9056, 1.0000 },
1087 { 0.8464, 0.9023, 1.0000 },
1088 { 0.8415, 0.8991, 1.0000 },
1089 { 0.8368, 0.8960, 1.0000 },
1090 { 0.8323, 0.8930, 1.0000 },
1091 { 0.8278, 0.8901, 1.0000 },
1092 { 0.8235, 0.8873, 1.0000 },
1093 { 0.8194, 0.8845, 1.0000 },
1094 { 0.8153, 0.8818, 1.0000 },
1095 { 0.8114, 0.8792, 1.0000 },
1096 { 0.8075, 0.8767, 1.0000 },
1097 { 0.8038, 0.8742, 1.0000 },
1098 { 0.8002, 0.8718, 1.0000 },
1099 { 0.7967, 0.8694, 1.0000 },
1100 { 0.7932, 0.8671, 1.0000 },
1101 { 0.7898, 0.8649, 1.0000 } /* 10000K */
1102 };
1103
1104 /* source: http://www.vendian.org/mncharity/dir3/blackbody/
1105 * rescaled to make exactly 6500K equal to full intensity in all
1106 * channels */
1107 static const CdColorRGB blackbody_data_d65modified[] = {
1108 { 1.0000, 0.0425, 0.0000 }, /* 1000K */
1109 { 1.0000, 0.0668, 0.0000 }, /* 1100K */
1110 { 1.0000, 0.0911, 0.0000 }, /* 1200K */
1111 { 1.0000, 0.1149, 0.0000 }, /* ... */
1112 { 1.0000, 0.1380, 0.0000 },
1113 { 1.0000, 0.1604, 0.0000 },
1114 { 1.0000, 0.1819, 0.0000 },
1115 { 1.0000, 0.2024, 0.0000 },
1116 { 1.0000, 0.2220, 0.0000 },
1117 { 1.0000, 0.2406, 0.0000 },
1118 { 1.0000, 0.2630, 0.0062 },
1119 { 1.0000, 0.2868, 0.0155 },
1120 { 1.0000, 0.3102, 0.0261 },
1121 { 1.0000, 0.3334, 0.0379 },
1122 { 1.0000, 0.3562, 0.0508 },
1123 { 1.0000, 0.3787, 0.0650 },
1124 { 1.0000, 0.4008, 0.0802 },
1125 { 1.0000, 0.4227, 0.0964 },
1126 { 1.0000, 0.4442, 0.1136 },
1127 { 1.0000, 0.4652, 0.1316 },
1128 { 1.0000, 0.4859, 0.1505 },
1129 { 1.0000, 0.5062, 0.1702 },
1130 { 1.0000, 0.5262, 0.1907 },
1131 { 1.0000, 0.5458, 0.2118 },
1132 { 1.0000, 0.5650, 0.2335 },
1133 { 1.0000, 0.5839, 0.2558 },
1134 { 1.0000, 0.6023, 0.2786 },
1135 { 1.0000, 0.6204, 0.3018 },
1136 { 1.0000, 0.6382, 0.3255 },
1137 { 1.0000, 0.6557, 0.3495 },
1138 { 1.0000, 0.6727, 0.3739 },
1139 { 1.0000, 0.6894, 0.3986 },
1140 { 1.0000, 0.7058, 0.4234 },
1141 { 1.0000, 0.7218, 0.4485 },
1142 { 1.0000, 0.7375, 0.4738 },
1143 { 1.0000, 0.7529, 0.4992 },
1144 { 1.0000, 0.7679, 0.5247 },
1145 { 1.0000, 0.7826, 0.5503 },
1146 { 1.0000, 0.7970, 0.5760 },
1147 { 1.0000, 0.8111, 0.6016 },
1148 { 1.0000, 0.8250, 0.6272 },
1149 { 1.0000, 0.8384, 0.6529 },
1150 { 1.0000, 0.8517, 0.6785 },
1151 { 1.0000, 0.8647, 0.7040 },
1152 { 1.0000, 0.8773, 0.7294 },
1153 { 1.0000, 0.8897, 0.7548 },
1154 { 1.0000, 0.9019, 0.7801 },
1155 { 1.0000, 0.9137, 0.8051 },
1156 { 1.0000, 0.9254, 0.8301 },
1157 { 1.0000, 0.9367, 0.8550 },
1158 { 1.0000, 0.9478, 0.8795 },
1159 { 1.0000, 0.9587, 0.9040 },
1160 { 1.0000, 0.9694, 0.9283 },
1161 { 1.0000, 0.9798, 0.9524 },
1162 { 1.0000, 0.9900, 0.9763 },
1163 { 1.0000, 1.0000, 1.0000 }, /* 6500K */
1164 { 0.9771, 0.9867, 1.0000 },
1165 { 0.9554, 0.9740, 1.0000 },
1166 { 0.9349, 0.9618, 1.0000 },
1167 { 0.9154, 0.9500, 1.0000 },
1168 { 0.8968, 0.9389, 1.0000 },
1169 { 0.8792, 0.9282, 1.0000 },
1170 { 0.8624, 0.9179, 1.0000 },
1171 { 0.8465, 0.9080, 1.0000 },
1172 { 0.8313, 0.8986, 1.0000 },
1173 { 0.8167, 0.8895, 1.0000 },
1174 { 0.8029, 0.8808, 1.0000 },
1175 { 0.7896, 0.8724, 1.0000 },
1176 { 0.7769, 0.8643, 1.0000 },
1177 { 0.7648, 0.8565, 1.0000 },
1178 { 0.7532, 0.8490, 1.0000 },
1179 { 0.7420, 0.8418, 1.0000 },
1180 { 0.7314, 0.8348, 1.0000 },
1181 { 0.7212, 0.8281, 1.0000 },
1182 { 0.7113, 0.8216, 1.0000 },
1183 { 0.7018, 0.8153, 1.0000 },
1184 { 0.6927, 0.8092, 1.0000 },
1185 { 0.6839, 0.8032, 1.0000 },
1186 { 0.6755, 0.7975, 1.0000 },
1187 { 0.6674, 0.7921, 1.0000 },
1188 { 0.6595, 0.7867, 1.0000 },
1189 { 0.6520, 0.7816, 1.0000 },
1190 { 0.6447, 0.7765, 1.0000 },
1191 { 0.6376, 0.7717, 1.0000 },
1192 { 0.6308, 0.7670, 1.0000 },
1193 { 0.6242, 0.7623, 1.0000 },
1194 { 0.6179, 0.7579, 1.0000 },
1195 { 0.6117, 0.7536, 1.0000 },
1196 { 0.6058, 0.7493, 1.0000 },
1197 { 0.6000, 0.7453, 1.0000 },
1198 { 0.5944, 0.7414, 1.0000 } /* 10000K */
1199 };
1200
1201 /**
1202 * cd_color_rgb_interpolate:
1203 *
1204 * Since: 0.1.26
1205 **/
1206 void
cd_color_rgb_interpolate(const CdColorRGB * p1,const CdColorRGB * p2,gdouble index,CdColorRGB * result)1207 cd_color_rgb_interpolate (const CdColorRGB *p1,
1208 const CdColorRGB *p2,
1209 gdouble index,
1210 CdColorRGB *result)
1211 {
1212 g_return_if_fail (p1 != NULL);
1213 g_return_if_fail (p2 != NULL);
1214 g_return_if_fail (index >= 0.0f);
1215 g_return_if_fail (index <= 1.0f);
1216 g_return_if_fail (result != NULL);
1217 result->R = (1.0 - index) * p1->R + index * p2->R;
1218 result->G = (1.0 - index) * p1->G + index * p2->G;
1219 result->B = (1.0 - index) * p1->B + index * p2->B;
1220 }
1221
1222 /**
1223 * cd_color_rgb_from_wavelength:
1224 * @dest: a #CdColorRGB for the RGB result
1225 * @wavelength: the wavelength roughly between 380nm and 780nm
1226 *
1227 * Set an RGB color which is roughly representative to the wavelength.
1228 *
1229 * Since: 1.3.4
1230 **/
1231 void
cd_color_rgb_from_wavelength(CdColorRGB * dest,gdouble wavelength)1232 cd_color_rgb_from_wavelength (CdColorRGB *dest, gdouble wavelength)
1233 {
1234 const gdouble gamma = 0.80;
1235 gdouble factor;
1236
1237 /* use the colors specified in
1238 * http://www.efg2.com/Lab/ScienceAndEngineering/Spectra.htm */
1239 if (wavelength < 380) {
1240 dest->R = 0.0;
1241 dest->G = 0.0;
1242 dest->B = 0.0;
1243 } else if (wavelength < 440) {
1244 dest->R = -(wavelength - 440.f) / (440.f - 380.f);
1245 dest->G = 0.0;
1246 dest->B = 1.0;
1247 } else if (wavelength < 490) {
1248 dest->R = 0.0;
1249 dest->G = (wavelength - 440) / (490 - 440);
1250 dest->B = 1.0;
1251 } else if (wavelength < 510) {
1252 dest->R = 0.0;
1253 dest->G = 1.0;
1254 dest->B = -(wavelength - 510) / (510 - 490);
1255 } else if (wavelength < 580) {
1256 dest->R = (wavelength - 510) / (580 - 510);
1257 dest->G = 1.0;
1258 dest->B = 0.0;
1259 } else if (wavelength < 645) {
1260 dest->R = 1.0;
1261 dest->G = -(wavelength - 645) / (645 - 580);
1262 dest->B = 0.0;
1263 } else if (wavelength < 781) {
1264 dest->R = 1.0;
1265 dest->G = 0.0;
1266 dest->B = 0.0;
1267 } else {
1268 dest->R = 0.0;
1269 dest->G = 0.0;
1270 dest->B = 0.0;
1271 }
1272
1273 /* intensity should fall off near the vision limits */
1274 if (wavelength >= 380 && wavelength < 420) {
1275 factor = 0.3 + 0.7 * (wavelength - 380) / (420 - 380);
1276 } else if (wavelength >= 420 && wavelength < 701) {
1277 factor = 1.0;
1278 } else if (wavelength >= 701 && wavelength < 781) {
1279 factor = 0.3 + 0.7 * (780 - wavelength) / (780 - 700);
1280 } else {
1281 factor = 0.0;
1282 };
1283
1284 /* scale by factor and then apply gamma */
1285 if (dest->R > 0.f)
1286 dest->R = pow (dest->R * factor, gamma);
1287 if (dest->G > 0.f)
1288 dest->G = pow (dest->G * factor, gamma);
1289 if (dest->B > 0.f)
1290 dest->B = pow (dest->B * factor, gamma);
1291 }
1292
1293 /**
1294 * cd_color_rgb_array_is_monotonic:
1295 * @array: (element-type CdColorRGB): Input array
1296 *
1297 * Checks the array for monotonicity.
1298 *
1299 * Return value: %TRUE if the array is monotonic
1300 *
1301 * Since: 0.1.31
1302 **/
1303 gboolean
cd_color_rgb_array_is_monotonic(const GPtrArray * array)1304 cd_color_rgb_array_is_monotonic (const GPtrArray *array)
1305 {
1306 CdColorRGB last_rgb;
1307 CdColorRGB *rgb;
1308 guint i;
1309
1310 g_return_val_if_fail (array != NULL, FALSE);
1311
1312 /* check if monotonic */
1313 cd_color_rgb_set (&last_rgb, 0.0, 0.0, 0.0);
1314 for (i = 0; i < array->len; i++) {
1315 rgb = g_ptr_array_index (array, i);
1316 if (rgb->R < last_rgb.R)
1317 return FALSE;
1318 if (rgb->G < last_rgb.G)
1319 return FALSE;
1320 if (rgb->B < last_rgb.B)
1321 return FALSE;
1322 cd_color_rgb_copy (rgb, &last_rgb);
1323 }
1324 return TRUE;
1325 }
1326
1327 /**
1328 * cd_color_rgb_array_new:
1329 *
1330 * Creates a new RGB array.
1331 *
1332 * Return value: (element-type CdColorRGB) (transfer full): New array
1333 *
1334 * Since: 0.1.31
1335 **/
1336 GPtrArray *
cd_color_rgb_array_new(void)1337 cd_color_rgb_array_new (void)
1338 {
1339 return g_ptr_array_new_with_free_func ((GDestroyNotify) cd_color_rgb_free);
1340 }
1341
1342 /**
1343 * cd_color_rgb_array_interpolate:
1344 * @array: (element-type CdColorRGB): Input array
1345 * @new_length: the target length of the return array
1346 *
1347 * Interpolate the RGB array to a different size.
1348 * This uses the Akima interpolation algorithm unless the array would become
1349 * non-monotonic, in which case it falls back to linear interpolation.
1350 *
1351 * Return value: (element-type CdColorRGB) (transfer full): An array of size @new_length or %NULL
1352 *
1353 * Since: 0.1.31
1354 **/
1355 GPtrArray *
cd_color_rgb_array_interpolate(const GPtrArray * array,guint new_length)1356 cd_color_rgb_array_interpolate (const GPtrArray *array, guint new_length)
1357 {
1358 CdColorRGB *rgb;
1359 CdInterp *interp[3];
1360 gboolean ret;
1361 gdouble tmp;
1362 GPtrArray *result = NULL;
1363 guint i;
1364 guint j;
1365 guint m;
1366
1367 g_return_val_if_fail (array != NULL, NULL);
1368 g_return_val_if_fail (new_length > 0, NULL);
1369
1370 /* check if monotonic */
1371 ret = cd_color_rgb_array_is_monotonic (array);
1372 if (!ret)
1373 goto out;
1374
1375 /* create new array */
1376 result = cd_color_rgb_array_new ();
1377 for (i = 0; i < new_length; i++) {
1378 rgb = cd_color_rgb_new ();
1379 g_ptr_array_add (result, rgb);
1380 }
1381
1382 /* try each interpolation method in turn */
1383 for (m = 0; m < 2; m++) {
1384
1385 /* setup interpolation */
1386 for (j = 0; j < 3; j++) {
1387 if (m == 0)
1388 interp[j] = cd_interp_akima_new ();
1389 else if (m == 1)
1390 interp[j] = cd_interp_linear_new ();
1391 }
1392
1393 /* add data */
1394 for (i = 0; i < array->len; i++) {
1395 rgb = g_ptr_array_index (array, i);
1396 tmp = (gdouble) i / (gdouble) (array->len - 1);
1397 cd_interp_insert (interp[0], tmp, rgb->R);
1398 cd_interp_insert (interp[1], tmp, rgb->G);
1399 cd_interp_insert (interp[2], tmp, rgb->B);
1400 }
1401
1402 /* do interpolation of array */
1403 for (j = 0; j < 3; j++) {
1404 ret = cd_interp_prepare (interp[j], NULL);
1405 if (!ret)
1406 break;
1407 }
1408 for (i = 0; i < new_length; i++) {
1409 tmp = (gdouble) i / (gdouble) (new_length - 1);
1410 rgb = g_ptr_array_index (result, i);
1411 rgb->R = cd_interp_eval (interp[0], tmp, NULL);
1412 rgb->G = cd_interp_eval (interp[1], tmp, NULL);
1413 rgb->B = cd_interp_eval (interp[2], tmp, NULL);
1414 }
1415
1416 /* tear down the interpolation */
1417 for (j = 0; j < 3; j++)
1418 g_object_unref (interp[j]);
1419
1420 /* check if monotonic */
1421 ret = cd_color_rgb_array_is_monotonic (result);
1422 if (ret)
1423 break;
1424
1425 /* try harder */
1426 }
1427 out:
1428 return result;
1429 }
1430
1431 /**
1432 * cd_color_get_blackbody_rgb_full:
1433 * @temp: the temperature in Kelvin
1434 * @result: the destination color
1435 * @flags: some #CdColorBlackbodyFlags, e.g. %CD_COLOR_BLACKBODY_FLAG_USE_PLANCKIAN
1436 *
1437 * Get the blackbody color for a specific temperature. If the temperature
1438 * range is outside 1000K to 10000K then the result is clipped.
1439 *
1440 * Return value: TRUE if @temp was in range and the result accurate
1441 *
1442 * Since: 1.3.5
1443 **/
1444 gboolean
cd_color_get_blackbody_rgb_full(gdouble temp,CdColorRGB * result,CdColorBlackbodyFlags flags)1445 cd_color_get_blackbody_rgb_full (gdouble temp,
1446 CdColorRGB *result,
1447 CdColorBlackbodyFlags flags)
1448 {
1449 gboolean ret = TRUE;
1450 gdouble alpha;
1451 gint temp_index;
1452 const CdColorRGB *blackbody_func = blackbody_data_d65modified;
1453
1454 /* use modified curve */
1455 if (flags & CD_COLOR_BLACKBODY_FLAG_USE_PLANCKIAN)
1456 blackbody_func = blackbody_data_d65plankian;
1457
1458 /* check lower bound */
1459 if (temp < 1000) {
1460 ret = FALSE;
1461 temp = 1000;
1462 }
1463
1464 /* check upper bound */
1465 if (temp > 10000) {
1466 ret = FALSE;
1467 temp = 10000;
1468 }
1469
1470 /* bilinear interpolate the blackbody data */
1471 alpha = ((guint) temp % 100) / 100.0;
1472 temp_index = ((guint) temp - 1000) / 100;
1473 cd_color_rgb_interpolate (&blackbody_func[temp_index],
1474 &blackbody_func[temp_index + 1],
1475 alpha,
1476 result);
1477 return ret;
1478 }
1479
1480 /**
1481 * cd_color_get_blackbody_rgb:
1482 * @temp: the temperature in Kelvin
1483 * @result: the destination color
1484 *
1485 * Get the blackbody color for a specific temperature. If the temperature
1486 * range is outside 1000K to 10000K then the result is clipped.
1487 *
1488 * Return value: TRUE if @temp was in range and the result accurate
1489 *
1490 * Since: 0.1.26
1491 **/
1492 gboolean
cd_color_get_blackbody_rgb(guint temp,CdColorRGB * result)1493 cd_color_get_blackbody_rgb (guint temp, CdColorRGB *result)
1494 {
1495 return cd_color_get_blackbody_rgb_full (temp, result,
1496 CD_COLOR_BLACKBODY_FLAG_NONE);
1497 }
1498