1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2012-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-it8
24 * @short_description: Read and write IT8 color sample exchange files
25 *
26 * This object represents .ti1 and .ti3 files which can contain raw
27 * or normalized sample data.
28 */
29
30 #include "config.h"
31
32 #include <glib.h>
33 #include <lcms2.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "cd-it8.h"
38 #include "cd-color.h"
39 #include "cd-context-lcms.h"
40
41 static void cd_it8_class_init (CdIt8Class *klass);
42 static void cd_it8_init (CdIt8 *it8);
43 static void cd_it8_finalize (GObject *object);
44
45 #define GET_PRIVATE(o) (cd_it8_get_instance_private (o))
46
47 /**
48 * CdIt8Private:
49 *
50 * Private #CdIt8 data
51 **/
52 typedef struct
53 {
54 CdIt8Kind kind;
55 cmsContext context_lcms;
56 CdMat3x3 matrix;
57 gboolean normalized;
58 gboolean spectral;
59 gboolean enable_created;
60 gchar *instrument;
61 gchar *reference;
62 gchar *originator;
63 gchar *title;
64 GPtrArray *array_spectra;
65 GPtrArray *array_rgb;
66 GPtrArray *array_xyz;
67 GPtrArray *options;
68 } CdIt8Private;
69
70 enum {
71 PROP_0,
72 PROP_KIND,
73 PROP_INSTRUMENT,
74 PROP_REFERENCE,
75 PROP_NORMALIZED,
76 PROP_ORIGINATOR,
77 PROP_TITLE,
78 PROP_SPECTRAL,
79 PROP_LAST
80 };
81
G_DEFINE_TYPE_WITH_PRIVATE(CdIt8,cd_it8,G_TYPE_OBJECT)82 G_DEFINE_TYPE_WITH_PRIVATE (CdIt8, cd_it8, G_TYPE_OBJECT)
83
84 /**
85 * cd_it8_error_quark:
86 *
87 * Return value: An error quark.
88 *
89 * Since: 0.1.0
90 **/
91 GQuark
92 cd_it8_error_quark (void)
93 {
94 static GQuark quark = 0;
95 if (!quark) {
96 quark = g_quark_from_static_string ("cd_it8_error");
97 }
98 return quark;
99 }
100
101 /**
102 * _cmsIT8GetPropertyDbl:
103 *
104 * This gets a property ensuring the decimal point is '.' rather than what is
105 * specified in LC_NUMERIC
106 **/
107 static gdouble
_cmsIT8GetPropertyDbl(cmsHANDLE it8_lcms,const gchar * key)108 _cmsIT8GetPropertyDbl (cmsHANDLE it8_lcms, const gchar *key)
109 {
110 const gchar *value;
111 value = cmsIT8GetProperty (it8_lcms, key);
112 if (value == NULL)
113 return -1;
114 return g_ascii_strtod (value, NULL);
115 }
116
117 /**
118 * _cmsIT8GetPropertyInt:
119 **/
120 static guint
_cmsIT8GetPropertyInt(cmsHANDLE it8_lcms,const gchar * key)121 _cmsIT8GetPropertyInt (cmsHANDLE it8_lcms, const gchar *key)
122 {
123 const gchar *value;
124 guint64 tmp;
125
126 value = cmsIT8GetProperty (it8_lcms, key);
127 if (value == NULL)
128 return 0;
129 tmp = g_ascii_strtoull (value, NULL, 10);
130 if (tmp > G_MAXUINT)
131 return 0;
132
133 return tmp;
134 }
135
136 /**
137 * _cmsIT8GetDataRowColDbl:
138 *
139 * This gets a data value ensuring the decimal point is '.' rather than what is
140 * specified in LC_NUMERIC
141 **/
142 static gdouble
_cmsIT8GetDataRowColDbl(cmsHANDLE it8_lcms,gint row,gint col)143 _cmsIT8GetDataRowColDbl (cmsHANDLE it8_lcms, gint row, gint col)
144 {
145 const char *value;
146 value = cmsIT8GetDataRowCol (it8_lcms, row, col);
147 if (value == NULL)
148 return -1;
149 return g_ascii_strtod (value, NULL);
150 }
151
152 /**
153 * _cmsIT8WriteFloat:
154 *
155 * We can't use g_ascii_dtostr() as this produces very inefficient
156 * strings for storage. Instead write the string with 13 decimal places
157 * and then manually remove trailing zeros more than one as ArgyllCMS
158 * doesn't support integers as floats (!)
159 **/
160 static void
_cmsIT8WriteFloat(gchar * buffer,gsize buffer_size,gdouble value)161 _cmsIT8WriteFloat (gchar *buffer, gsize buffer_size, gdouble value)
162 {
163 guint i;
164 memset (buffer, '\0', buffer_size);
165 g_ascii_formatd (buffer, buffer_size, "%.12f", value);
166 for (i = G_ASCII_DTOSTR_BUF_SIZE - 1; i > 2; i--) {
167 if (buffer[i] == '\0')
168 continue;
169 if (buffer[i] != '0')
170 break;
171 if (buffer[i-1] == '.')
172 break;
173 buffer[i] = '\0';
174 }
175 }
176
177 /**
178 * _cmsIT8SetPropertyDbl:
179 *
180 * This sets a property ensuring the decimal point is '.' rather than what is
181 * specified in LC_NUMERIC
182 **/
183 static void
_cmsIT8SetPropertyDbl(cmsHANDLE it8_lcms,const gchar * key,gdouble value)184 _cmsIT8SetPropertyDbl (cmsHANDLE it8_lcms, const gchar *key, gdouble value)
185 {
186 gchar buffer[G_ASCII_DTOSTR_BUF_SIZE];
187 _cmsIT8WriteFloat (buffer, G_ASCII_DTOSTR_BUF_SIZE, value);
188 cmsIT8SetPropertyUncooked (it8_lcms, key, buffer);
189 }
190
191 /**
192 * _cmsIT8SetPropertyInt:
193 **/
194 static void
_cmsIT8SetPropertyInt(cmsHANDLE it8_lcms,const gchar * key,gint value)195 _cmsIT8SetPropertyInt (cmsHANDLE it8_lcms, const gchar *key, gint value)
196 {
197 gchar buffer[G_ASCII_DTOSTR_BUF_SIZE];
198 g_ascii_formatd (buffer, G_ASCII_DTOSTR_BUF_SIZE, "%.0f", value);
199 cmsIT8SetPropertyUncooked (it8_lcms, key, buffer);
200 }
201
202 /**
203 * _cmsIT8SetDataRowColDbl:
204 *
205 * This sets a data value ensuring the decimal point is '.' rather than what is
206 * specified in LC_NUMERIC
207 **/
208 static void
_cmsIT8SetDataRowColDbl(cmsHANDLE it8_lcms,gint row,gint col,gdouble value)209 _cmsIT8SetDataRowColDbl (cmsHANDLE it8_lcms, gint row, gint col, gdouble value)
210 {
211 gchar buffer[G_ASCII_DTOSTR_BUF_SIZE];
212 _cmsIT8WriteFloat (buffer, G_ASCII_DTOSTR_BUF_SIZE, value);
213 cmsIT8SetDataRowCol (it8_lcms, row, col, buffer);
214 }
215
216 /**
217 * cd_it8_set_matrix:
218 * @it8: a #CdIt8 instance.
219 * @matrix: a #CdMat3x3.
220 *
221 * Set the calibration matrix in the it8 file.
222 *
223 * Since: 0.1.20
224 **/
225 void
cd_it8_set_matrix(CdIt8 * it8,const CdMat3x3 * matrix)226 cd_it8_set_matrix (CdIt8 *it8, const CdMat3x3 *matrix)
227 {
228 CdIt8Private *priv = GET_PRIVATE (it8);
229 g_return_if_fail (CD_IS_IT8 (it8));
230 cd_mat33_copy (matrix, &priv->matrix);
231 }
232
233 /**
234 * cd_it8_get_matrix:
235 * @it8: a #CdIt8 instance.
236 *
237 * Gets the calibration matrix in the it8 file.
238 *
239 * Return value: a #CdMat3x3.
240 *
241 * Since: 0.1.20
242 **/
243 const CdMat3x3 *
cd_it8_get_matrix(CdIt8 * it8)244 cd_it8_get_matrix (CdIt8 *it8)
245 {
246 CdIt8Private *priv = GET_PRIVATE (it8);
247 g_return_val_if_fail (CD_IS_IT8 (it8), NULL);
248 return &priv->matrix;
249 }
250
251 /**
252 * cd_it8_set_kind:
253 * @it8: a #CdIt8 instance.
254 * @kind: a #CdIt8Kind, e.g %CD_IT8_KIND_TI3.
255 *
256 * Set the kind of IT8 file.
257 *
258 * Since: 0.1.20
259 **/
260 void
cd_it8_set_kind(CdIt8 * it8,CdIt8Kind kind)261 cd_it8_set_kind (CdIt8 *it8, CdIt8Kind kind)
262 {
263 CdIt8Private *priv = GET_PRIVATE (it8);
264 g_return_if_fail (CD_IS_IT8 (it8));
265 priv->kind = kind;
266 }
267
268 /**
269 * cd_it8_get_kind:
270 * @it8: a #CdIt8 instance.
271 *
272 * Gets the kind of IT8 file.
273 *
274 * Return value: a #CdIt8Kind, e.g %CD_IT8_KIND_TI3.
275 *
276 * Since: 0.1.20
277 **/
278 CdIt8Kind
cd_it8_get_kind(CdIt8 * it8)279 cd_it8_get_kind (CdIt8 *it8)
280 {
281 CdIt8Private *priv = GET_PRIVATE (it8);
282 g_return_val_if_fail (CD_IS_IT8 (it8), 0);
283 return priv->kind;
284 }
285
286 /**
287 * cd_it8_parse_luminance:
288 **/
289 static gboolean
cd_it8_parse_luminance(const gchar * text,CdColorXYZ * xyz,GError ** error)290 cd_it8_parse_luminance (const gchar *text, CdColorXYZ *xyz, GError **error)
291 {
292 g_auto(GStrv) split = NULL;
293
294 split = g_strsplit (text, " ", -1);
295 if (g_strv_length (split) != 3) {
296 g_set_error (error,
297 CD_IT8_ERROR,
298 CD_IT8_ERROR_INVALID_FORMAT,
299 "LUMINANCE_XYZ_CDM2 format invalid: %s",
300 text);
301 return FALSE;
302 }
303
304 xyz->X = g_ascii_strtod (split[0], NULL);
305 xyz->Y = g_ascii_strtod (split[1], NULL);
306 xyz->Z = g_ascii_strtod (split[2], NULL);
307 return TRUE;
308 }
309
310 /**
311 * cd_it8_get_originator:
312 * @it8: a #CdIt8 instance.
313 *
314 * Gets the file orginator.
315 *
316 * Return value: The originator, or %NULL if unset
317 *
318 * Since: 0.1.20
319 **/
320 const gchar *
cd_it8_get_originator(CdIt8 * it8)321 cd_it8_get_originator (CdIt8 *it8)
322 {
323 CdIt8Private *priv = GET_PRIVATE (it8);
324 g_return_val_if_fail (CD_IS_IT8 (it8), NULL);
325 return priv->originator;
326 }
327
328 /**
329 * cd_it8_get_title:
330 * @it8: a #CdIt8 instance.
331 *
332 * Gets the file title.
333 *
334 * Return value: The title, or %NULL if unset
335 *
336 * Since: 0.1.20
337 **/
338 const gchar *
cd_it8_get_title(CdIt8 * it8)339 cd_it8_get_title (CdIt8 *it8)
340 {
341 CdIt8Private *priv = GET_PRIVATE (it8);
342 g_return_val_if_fail (CD_IS_IT8 (it8), NULL);
343 return priv->title;
344 }
345
346 /**
347 * cd_it8_get_instrument:
348 * @it8: a #CdIt8 instance.
349 *
350 * Gets the instrument the file was created by.
351 *
352 * Return value: The instrument, or %NULL if unset
353 *
354 * Since: 0.1.20
355 **/
356 const gchar *
cd_it8_get_instrument(CdIt8 * it8)357 cd_it8_get_instrument (CdIt8 *it8)
358 {
359 CdIt8Private *priv = GET_PRIVATE (it8);
360 g_return_val_if_fail (CD_IS_IT8 (it8), NULL);
361 return priv->instrument;
362 }
363
364 /**
365 * cd_it8_get_reference:
366 * @it8: a #CdIt8 instance.
367 *
368 * Gets the reference the file was created against.
369 *
370 * Return value: The reference, or %NULL if unset
371 *
372 * Since: 0.1.20
373 **/
374 const gchar *
cd_it8_get_reference(CdIt8 * it8)375 cd_it8_get_reference (CdIt8 *it8)
376 {
377 CdIt8Private *priv = GET_PRIVATE (it8);
378 g_return_val_if_fail (CD_IS_IT8 (it8), NULL);
379 return priv->reference;
380 }
381
382 /**
383 * cd_it8_get_enable_created:
384 * @it8: a #CdIt8 instance.
385 *
386 * Gets if the 'CREATED' attribute will be written. This is typically only
387 * set in the self test programs.
388 *
389 * Return value: The reference, or %NULL if unset
390 *
391 * Since: 0.1.33
392 **/
393 gboolean
cd_it8_get_enable_created(CdIt8 * it8)394 cd_it8_get_enable_created (CdIt8 *it8)
395 {
396 CdIt8Private *priv = GET_PRIVATE (it8);
397 g_return_val_if_fail (CD_IS_IT8 (it8), FALSE);
398 return priv->enable_created;
399 }
400
401 /**
402 * cd_it8_get_normalized:
403 * @it8: a #CdIt8 instance.
404 *
405 * Gets if the data should be written normlaised to y=100.
406 *
407 * Return value: %TRUE if the data should be normalised.
408 *
409 * Since: 0.1.20
410 **/
411 gboolean
cd_it8_get_normalized(CdIt8 * it8)412 cd_it8_get_normalized (CdIt8 *it8)
413 {
414 CdIt8Private *priv = GET_PRIVATE (it8);
415 g_return_val_if_fail (CD_IS_IT8 (it8), FALSE);
416 return priv->normalized;
417 }
418
419 /**
420 * cd_it8_get_spectral:
421 * @it8: a #CdIt8 instance.
422 *
423 * Gets if the data is spectral or XYZ.
424 *
425 * Return value: %TRUE if the data is in spectral bands.
426 *
427 * Since: 0.1.20
428 **/
429 gboolean
cd_it8_get_spectral(CdIt8 * it8)430 cd_it8_get_spectral (CdIt8 *it8)
431 {
432 CdIt8Private *priv = GET_PRIVATE (it8);
433 g_return_val_if_fail (CD_IS_IT8 (it8), FALSE);
434 return priv->spectral;
435 }
436
437 /**
438 * cd_it8_load_ti1_cal:
439 **/
440 static gboolean
cd_it8_load_ti1_cal(CdIt8 * it8,cmsHANDLE it8_lcms,GError ** error)441 cd_it8_load_ti1_cal (CdIt8 *it8, cmsHANDLE it8_lcms, GError **error)
442 {
443 CdIt8Private *priv = GET_PRIVATE (it8);
444 CdColorRGB *rgb;
445 CdColorXYZ *xyz;
446 const gchar *tmp;
447 guint i;
448 guint number_of_sets = 0;
449
450 tmp = cmsIT8GetProperty (it8_lcms, "COLOR_REP");
451 if (g_strcmp0 (tmp, "RGB") != 0) {
452 g_set_error (error,
453 CD_IT8_ERROR,
454 CD_IT8_ERROR_INVALID_FORMAT,
455 "Invalid data format: %s", tmp);
456 return FALSE;
457 }
458
459 /* copy out data entries */
460 number_of_sets = _cmsIT8GetPropertyInt (it8_lcms, "NUMBER_OF_SETS");
461 if (number_of_sets == 0) {
462 g_set_error_literal (error,
463 CD_IT8_ERROR,
464 CD_IT8_ERROR_INVALID_FORMAT,
465 "Invalid format, NUMBER_OF_SETS required");
466 return FALSE;
467 }
468
469 for (i = 0; i < number_of_sets; i++) {
470 rgb = cd_color_rgb_new ();
471 rgb->R = _cmsIT8GetDataRowColDbl (it8_lcms, i, 1);
472 rgb->G = _cmsIT8GetDataRowColDbl (it8_lcms, i, 2);
473 rgb->B = _cmsIT8GetDataRowColDbl (it8_lcms, i, 3);
474
475 /* ti1 files don't have NORMALIZED_TO_Y_100 so guess on
476 * the asumption the first patch isn't black */
477 if (rgb->R > 1.0 || rgb->G > 1.0 || rgb->B > 1.0)
478 priv->normalized = TRUE;
479 if (priv->normalized) {
480 rgb->R /= 100.0f;
481 rgb->G /= 100.0f;
482 rgb->B /= 100.0f;
483 }
484 g_ptr_array_add (priv->array_rgb, rgb);
485 xyz = cd_color_xyz_new ();
486 cd_color_xyz_set (xyz, 0.0, 0.0, 0.0);
487 g_ptr_array_add (priv->array_xyz, xyz);
488 }
489 return TRUE;
490 }
491
492 /**
493 * cd_it8_load_ti3:
494 **/
495 static gboolean
cd_it8_load_ti3(CdIt8 * it8,cmsHANDLE it8_lcms,GError ** error)496 cd_it8_load_ti3 (CdIt8 *it8, cmsHANDLE it8_lcms, GError **error)
497 {
498 CdIt8Private *priv = GET_PRIVATE (it8);
499 CdColorRGB *rgb;
500 CdColorXYZ luminance;
501 CdColorXYZ *xyz;
502 const gchar *tmp;
503 gboolean scaled_to_y100 = FALSE;
504 guint i;
505 guint number_of_sets = 0;
506
507 tmp = cmsIT8GetProperty (it8_lcms, "COLOR_REP");
508 if (g_strcmp0 (tmp, "RGB_XYZ") != 0) {
509 g_set_error (error,
510 CD_IT8_ERROR,
511 CD_IT8_ERROR_INVALID_FORMAT,
512 "Invalid data format: %s", tmp);
513 return FALSE;
514 }
515
516 /* if normalized, then scale back up */
517 tmp = cmsIT8GetProperty (it8_lcms, "NORMALIZED_TO_Y_100");
518 if (g_strcmp0 (tmp, "YES") == 0) {
519 scaled_to_y100 = TRUE;
520 tmp = cmsIT8GetProperty (it8_lcms, "LUMINANCE_XYZ_CDM2");
521 if (!cd_it8_parse_luminance (tmp, &luminance, error))
522 return FALSE;
523 } else {
524 cd_color_xyz_set (&luminance, 1.f, 1.f, 1.f);
525 }
526
527 /* set spectral flag */
528 tmp = cmsIT8GetProperty (it8_lcms, "INSTRUMENT_TYPE_SPECTRAL");
529 cd_it8_set_spectral (it8, g_strcmp0 (tmp, "YES") == 0);
530
531 /* set instrument */
532 cd_it8_set_instrument (it8, cmsIT8GetProperty (it8_lcms, "TARGET_INSTRUMENT"));
533
534 /* copy out data entries */
535 number_of_sets = _cmsIT8GetPropertyInt (it8_lcms, "NUMBER_OF_SETS");
536 if (number_of_sets == 0) {
537 g_set_error_literal (error,
538 CD_IT8_ERROR,
539 CD_IT8_ERROR_INVALID_FORMAT,
540 "Invalid format, NUMBER_OF_SETS required");
541 return FALSE;
542 }
543 for (i = 0; i < number_of_sets; i++) {
544 rgb = cd_color_rgb_new ();
545 rgb->R = _cmsIT8GetDataRowColDbl (it8_lcms, i, 1);
546 rgb->G = _cmsIT8GetDataRowColDbl (it8_lcms, i, 2);
547 rgb->B = _cmsIT8GetDataRowColDbl (it8_lcms, i, 3);
548 if (scaled_to_y100) {
549 rgb->R /= 100.0f;
550 rgb->G /= 100.0f;
551 rgb->B /= 100.0f;
552 }
553 g_ptr_array_add (priv->array_rgb, rgb);
554 xyz = cd_color_xyz_new ();
555 xyz->X = _cmsIT8GetDataRowColDbl (it8_lcms, i, 4);
556 xyz->Y = _cmsIT8GetDataRowColDbl (it8_lcms, i, 5);
557 xyz->Z = _cmsIT8GetDataRowColDbl (it8_lcms, i, 6);
558 if (scaled_to_y100) {
559 xyz->X /= 100.0f;
560 xyz->Y /= 100.0f;
561 xyz->Z /= 100.0f;
562 xyz->X *= luminance.X;
563 xyz->Y *= luminance.Y;
564 xyz->Z *= luminance.Z;
565 }
566 g_ptr_array_add (priv->array_xyz, xyz);
567 }
568 return TRUE;
569 }
570
571 /**
572 * cd_it8_load_ccmx:
573 **/
574 static gboolean
cd_it8_load_ccmx(CdIt8 * it8,cmsHANDLE it8_lcms,GError ** error)575 cd_it8_load_ccmx (CdIt8 *it8, cmsHANDLE it8_lcms, GError **error)
576 {
577 CdIt8Private *priv = GET_PRIVATE (it8);
578 const gchar *tmp;
579
580 /* check color format */
581 tmp = cmsIT8GetProperty (it8_lcms, "COLOR_REP");
582 if (g_strcmp0 (tmp, "XYZ") != 0) {
583 g_set_error (error,
584 CD_IT8_ERROR,
585 CD_IT8_ERROR_INVALID_FORMAT,
586 "Invalid CCMX data format: %s", tmp);
587 return FALSE;
588 }
589
590 /* set instrument */
591 cd_it8_set_instrument (it8, cmsIT8GetProperty (it8_lcms, "INSTRUMENT"));
592
593 /* just load the matrix */
594 priv->matrix.m00 = _cmsIT8GetDataRowColDbl (it8_lcms, 0, 0);
595 priv->matrix.m01 = _cmsIT8GetDataRowColDbl (it8_lcms, 0, 1);
596 priv->matrix.m02 = _cmsIT8GetDataRowColDbl (it8_lcms, 0, 2);
597 priv->matrix.m10 = _cmsIT8GetDataRowColDbl (it8_lcms, 1, 0);
598 priv->matrix.m11 = _cmsIT8GetDataRowColDbl (it8_lcms, 1, 1);
599 priv->matrix.m12 = _cmsIT8GetDataRowColDbl (it8_lcms, 1, 2);
600 priv->matrix.m20 = _cmsIT8GetDataRowColDbl (it8_lcms, 2, 0);
601 priv->matrix.m21 = _cmsIT8GetDataRowColDbl (it8_lcms, 2, 1);
602 priv->matrix.m22 = _cmsIT8GetDataRowColDbl (it8_lcms, 2, 2);
603 return TRUE;
604 }
605
606 /**
607 * cd_it8_load_ccss_spect:
608 **/
609 static gboolean
cd_it8_load_ccss_spect(CdIt8 * it8,cmsHANDLE it8_lcms,GError ** error)610 cd_it8_load_ccss_spect (CdIt8 *it8, cmsHANDLE it8_lcms, GError **error)
611 {
612 const gchar *tmp;
613 gboolean has_index;
614 gdouble spectral_end;
615 gdouble spectral_norm;
616 gdouble spectral_start;
617 guint i;
618 guint j;
619 guint number_of_fields;
620 guint number_of_sets;
621 guint spectral_bands;
622
623 /* get spectra endpoints */
624 tmp = cmsIT8GetProperty (it8_lcms, "SPECTRAL_START_NM");
625 if (tmp == NULL) {
626 g_set_error_literal (error,
627 CD_IT8_ERROR,
628 CD_IT8_ERROR_INVALID_FORMAT,
629 "Invalid format, SPECTRAL_START_NM required");
630 return FALSE;
631 }
632 spectral_start = _cmsIT8GetPropertyDbl (it8_lcms, "SPECTRAL_START_NM");
633 spectral_end = _cmsIT8GetPropertyDbl (it8_lcms, "SPECTRAL_END_NM");
634 if (spectral_end == 0) {
635 g_set_error_literal (error,
636 CD_IT8_ERROR,
637 CD_IT8_ERROR_INVALID_FORMAT,
638 "Invalid format, SPECTRAL_END_NM required");
639 return FALSE;
640 }
641
642 /* get number of bands */
643 spectral_bands = _cmsIT8GetPropertyInt (it8_lcms, "SPECTRAL_BANDS");
644 if (spectral_bands == 0) {
645 g_set_error_literal (error,
646 CD_IT8_ERROR,
647 CD_IT8_ERROR_INVALID_FORMAT,
648 "Invalid format: SPECTRAL_BANDS required");
649 return FALSE;
650 }
651 if (spectral_bands < 2 || spectral_bands > 16384) {
652 g_set_error (error,
653 CD_IT8_ERROR,
654 CD_IT8_ERROR_INVALID_FORMAT,
655 "Invalid CCSS spectral size: %u", spectral_bands);
656 return FALSE;
657 }
658
659 /* get spectral norm */
660 spectral_norm = _cmsIT8GetPropertyDbl (it8_lcms, "SPECTRAL_NORM");
661 if (spectral_norm < 0.f)
662 spectral_norm = 1.f;
663
664 /* ArgyllCMS seems to support an index in the CCSS file, and not in the
665 * SPECT or CMF but like any good library support each mode */
666 number_of_fields = _cmsIT8GetPropertyInt (it8_lcms, "NUMBER_OF_FIELDS");
667 if (number_of_fields == 0) {
668 g_set_error_literal (error,
669 CD_IT8_ERROR,
670 CD_IT8_ERROR_INVALID_FORMAT,
671 "Invalid format, NUMBER_OF_FIELDS required");
672 return FALSE;
673 }
674 if (spectral_bands == number_of_fields) {
675 has_index = FALSE;
676 } else if (spectral_bands + 1 == number_of_fields) {
677 has_index = TRUE;
678 } else {
679 g_set_error (error,
680 CD_IT8_ERROR,
681 CD_IT8_ERROR_INVALID_FORMAT,
682 "Invalid CCSS: bands = %u, fields = %u",
683 spectral_bands, number_of_fields);
684 return FALSE;
685 }
686
687 /* read out the arrays of data */
688 number_of_sets = _cmsIT8GetPropertyInt (it8_lcms, "NUMBER_OF_SETS");
689 if (number_of_sets == 0) {
690 g_set_error_literal (error,
691 CD_IT8_ERROR,
692 CD_IT8_ERROR_INVALID_FORMAT,
693 "Invalid format, NUMBER_OF_SETS required");
694 return FALSE;
695 }
696 for (j = 0; j < (guint) number_of_sets; j++) {
697 g_autoptr(CdSpectrum) spectrum = NULL;
698 spectrum = cd_spectrum_sized_new (spectral_bands);
699 if (has_index) {
700 cd_spectrum_set_id (spectrum,
701 cmsIT8GetDataRowCol(it8_lcms, j, 0));
702 } else {
703 g_autofree gchar *label = NULL;
704 label = g_strdup_printf ("%u", j + 1);
705 cd_spectrum_set_id (spectrum, label);
706 }
707 for (i = has_index; i < number_of_fields; i++) {
708 cd_spectrum_add_value (spectrum,
709 _cmsIT8GetDataRowColDbl (it8_lcms, j, i));
710 }
711 cd_spectrum_set_start (spectrum, spectral_start);
712 cd_spectrum_set_end (spectrum, spectral_end);
713 cd_spectrum_set_norm (spectrum, spectral_norm);
714 cd_it8_add_spectrum (it8, spectrum);
715 }
716 return TRUE;
717 }
718
719 /**
720 * cd_it8_load_cmf:
721 **/
722 static gboolean
cd_it8_load_cmf(CdIt8 * it8,cmsHANDLE it8_lcms,GError ** error)723 cd_it8_load_cmf (CdIt8 *it8, cmsHANDLE it8_lcms, GError **error)
724 {
725 gdouble spectral_end;
726 gdouble spectral_norm;
727 gdouble spectral_start;
728 guint i;
729 guint j;
730 guint number_of_fields;
731 guint number_of_sets;
732 guint spectral_bands;
733
734 /* get spectra endpoints */
735 spectral_start = _cmsIT8GetPropertyDbl (it8_lcms, "SPECTRAL_START_NM");
736 if (spectral_start == 0) {
737 g_set_error_literal (error,
738 CD_IT8_ERROR,
739 CD_IT8_ERROR_INVALID_FORMAT,
740 "Invalid format, SPECTRAL_START_NM required");
741 return FALSE;
742 }
743 spectral_end = _cmsIT8GetPropertyDbl (it8_lcms, "SPECTRAL_END_NM");
744 if (spectral_end == 0) {
745 g_set_error_literal (error,
746 CD_IT8_ERROR,
747 CD_IT8_ERROR_INVALID_FORMAT,
748 "Invalid format, SPECTRAL_END_NM required");
749 return FALSE;
750 }
751
752 /* get number of bands */
753 spectral_bands = _cmsIT8GetPropertyInt (it8_lcms, "SPECTRAL_BANDS");
754 if (spectral_bands == 0) {
755 g_set_error_literal (error,
756 CD_IT8_ERROR,
757 CD_IT8_ERROR_INVALID_FORMAT,
758 "Invalid format, SPECTRAL_BANDS required");
759 return FALSE;
760 }
761 if (spectral_bands < 2 || spectral_bands > 16384) {
762 g_set_error (error,
763 CD_IT8_ERROR,
764 CD_IT8_ERROR_INVALID_FORMAT,
765 "Invalid CCSS spectral size: %u", spectral_bands);
766 return FALSE;
767 }
768
769 /* get spectral norm */
770 spectral_norm = _cmsIT8GetPropertyDbl (it8_lcms, "SPECTRAL_NORM");
771 if (spectral_norm < 0.f)
772 spectral_norm = 1.f;
773
774 /* CMF files are un-indexed and implicitly XYZ */
775 number_of_fields = _cmsIT8GetPropertyInt (it8_lcms, "NUMBER_OF_FIELDS");
776 if (number_of_fields == 0) {
777 g_set_error_literal (error,
778 CD_IT8_ERROR,
779 CD_IT8_ERROR_INVALID_FORMAT,
780 "Invalid format, NUMBER_OF_FIELDS required");
781 return FALSE;
782 }
783 if (spectral_bands != number_of_fields) {
784 g_set_error (error,
785 CD_IT8_ERROR,
786 CD_IT8_ERROR_INVALID_FORMAT,
787 "Invalid CMF: bands != fields (%u, %u)",
788 spectral_bands, number_of_fields);
789 return FALSE;
790 }
791
792 /* read out the arrays of data */
793 number_of_sets = _cmsIT8GetPropertyInt (it8_lcms, "NUMBER_OF_SETS");
794 if (number_of_sets != 3) {
795 g_set_error (error,
796 CD_IT8_ERROR,
797 CD_IT8_ERROR_INVALID_FORMAT,
798 "Invalid CMF: sets != 3 (%u)",
799 number_of_sets);
800 return FALSE;
801 }
802 for (j = 0; j < (guint) number_of_sets; j++) {
803 g_autoptr(CdSpectrum) spectrum = NULL;
804 spectrum = cd_spectrum_sized_new (spectral_bands);
805 if (j == 0)
806 cd_spectrum_set_id (spectrum, "X");
807 else if (j == 1)
808 cd_spectrum_set_id (spectrum, "Y");
809 else
810 cd_spectrum_set_id (spectrum, "Z");
811 for (i = 0; i < number_of_fields; i++) {
812 cd_spectrum_add_value (spectrum,
813 _cmsIT8GetDataRowColDbl (it8_lcms, j, i));
814 }
815 cd_spectrum_set_start (spectrum, spectral_start);
816 cd_spectrum_set_end (spectrum, spectral_end);
817 cd_spectrum_set_norm (spectrum, spectral_norm);
818 cd_it8_add_spectrum (it8, spectrum);
819 }
820 return TRUE;
821 }
822
823 /**
824 * cd_it8_has_option:
825 * @it8: a #CdIt8 instance.
826 * @option: a option, e.g. "TYPE_CRT"
827 *
828 * Finds an option in the file.
829 *
830 * Return value: %TRUE if the option is set
831 *
832 * Since: 0.1.20
833 **/
834 gboolean
cd_it8_has_option(CdIt8 * it8,const gchar * option)835 cd_it8_has_option (CdIt8 *it8, const gchar *option)
836 {
837 CdIt8Private *priv = GET_PRIVATE (it8);
838 const gchar *tmp;
839 guint i;
840
841 g_return_val_if_fail (CD_IS_IT8 (it8), FALSE);
842 g_return_val_if_fail (option != NULL, FALSE);
843
844 for (i = 0; i < priv->options->len; i++) {
845 tmp = g_ptr_array_index (priv->options, i);
846 if (g_strcmp0 (tmp, option) == 0)
847 return TRUE;
848 }
849 return FALSE;
850 }
851
852 /**
853 * cd_it8_load_from_data:
854 * @it8: a #CdIt8 instance.
855 * @data: text data
856 * @size: the size of text data
857 * @error: a #GError, or %NULL
858 *
859 * Loads a it8 file from data.
860 *
861 * Return value: %TRUE if a valid it8 file was read.
862 *
863 * Since: 0.1.20
864 **/
865 gboolean
cd_it8_load_from_data(CdIt8 * it8,const gchar * data,gsize size,GError ** error)866 cd_it8_load_from_data (CdIt8 *it8,
867 const gchar *data,
868 gsize size,
869 GError **error)
870 {
871 CdIt8Private *priv = GET_PRIVATE (it8);
872 cmsHANDLE it8_lcms = NULL;
873 const gchar *tmp;
874 gboolean ret = TRUE;
875 gchar **props = NULL;
876 guint i;
877 g_autoptr(GError) error_local = NULL;
878
879 g_return_val_if_fail (CD_IS_IT8 (it8), FALSE);
880 g_return_val_if_fail (data != NULL, FALSE);
881 g_return_val_if_fail (size > 0, FALSE);
882
883 /* clear old data */
884 g_ptr_array_set_size (priv->array_rgb, 0);
885 g_ptr_array_set_size (priv->array_xyz, 0);
886 g_ptr_array_set_size (priv->options, 0);
887 cd_mat33_clear (&priv->matrix);
888
889 /* load the it8 data */
890 it8_lcms = cmsIT8LoadFromMem (priv->context_lcms, (void *) data, size);
891 if (it8_lcms == NULL) {
892 ret = cd_context_lcms_error_check (priv->context_lcms, &error_local);
893 if (!ret) {
894 g_set_error_literal (error,
895 CD_IT8_ERROR,
896 CD_IT8_ERROR_FAILED,
897 error_local->message);
898 goto out;
899 }
900 ret = FALSE;
901 g_set_error_literal (error,
902 CD_IT8_ERROR,
903 CD_IT8_ERROR_FAILED,
904 "Failed to load but no error set");
905 goto out;
906 }
907
908 /* add options */
909 cmsIT8EnumProperties (it8_lcms, &props);
910 for (i = 0; props[i] != NULL; i++) {
911 if (g_str_has_prefix (props[i], "TYPE_"))
912 cd_it8_add_option (it8, props[i]);
913 }
914
915 /* get sheet type */
916 tmp = cmsIT8GetSheetType (it8_lcms);
917 if (g_str_has_prefix (tmp, "CTI1")) {
918 cd_it8_set_kind (it8, CD_IT8_KIND_TI1);
919 } else if (g_str_has_prefix (tmp, "CTI3")) {
920 cd_it8_set_kind (it8, CD_IT8_KIND_TI3);
921 } else if (g_str_has_prefix (tmp, "CCMX")) {
922 cd_it8_set_kind (it8, CD_IT8_KIND_CCMX);
923 } else if (g_str_has_prefix (tmp, "CCSS")) {
924 cd_it8_set_kind (it8, CD_IT8_KIND_CCSS);
925 } else if (g_str_has_prefix (tmp, "CMF")) {
926 cd_it8_set_kind (it8, CD_IT8_KIND_CMF);
927 } else if (g_str_has_prefix (tmp, "SPECT")) {
928 cd_it8_set_kind (it8, CD_IT8_KIND_SPECT);
929 } else if (g_str_has_prefix (tmp, "CAL")) {
930 cd_it8_set_kind (it8, CD_IT8_KIND_CAL);
931 } else {
932 ret = FALSE;
933 g_set_error (error,
934 CD_IT8_ERROR,
935 CD_IT8_ERROR_UNKNOWN_KIND,
936 "Unknown sheet type: %s", tmp);
937 goto out;
938 }
939
940 /* get ti1 and ti3 specific data */
941 switch (priv->kind) {
942 case CD_IT8_KIND_TI1:
943 case CD_IT8_KIND_CAL:
944 ret = cd_it8_load_ti1_cal (it8, it8_lcms, error);
945 if (!ret)
946 goto out;
947 break;
948 case CD_IT8_KIND_TI3:
949 ret = cd_it8_load_ti3 (it8, it8_lcms, error);
950 if (!ret)
951 goto out;
952 break;
953 case CD_IT8_KIND_CCMX:
954 ret = cd_it8_load_ccmx (it8, it8_lcms, error);
955 if (!ret)
956 goto out;
957 break;
958 case CD_IT8_KIND_CCSS:
959 case CD_IT8_KIND_SPECT:
960 ret = cd_it8_load_ccss_spect (it8, it8_lcms, error);
961 if (!ret)
962 goto out;
963 break;
964 case CD_IT8_KIND_CMF:
965 ret = cd_it8_load_cmf (it8, it8_lcms, error);
966 if (!ret)
967 goto out;
968 break;
969 default:
970 break;
971 }
972
973 /* set common bits */
974 cd_it8_set_title (it8, cmsIT8GetProperty (it8_lcms, "DISPLAY"));
975 cd_it8_set_originator (it8, cmsIT8GetProperty (it8_lcms, "ORIGINATOR"));
976 cd_it8_set_reference (it8, cmsIT8GetProperty (it8_lcms, "REFERENCE"));
977 out:
978 if (it8_lcms != NULL)
979 cmsIT8Free (it8_lcms);
980 return ret;
981 }
982
983 /**
984 * cd_it8_load_from_file:
985 * @it8: a #CdIt8 instance.
986 * @file: a #GFile
987 * @error: a #GError, or %NULL
988 *
989 * Loads a it8 file from disk.
990 *
991 * Return value: %TRUE if a valid it8 file was read.
992 *
993 * Since: 0.1.20
994 **/
995 gboolean
cd_it8_load_from_file(CdIt8 * it8,GFile * file,GError ** error)996 cd_it8_load_from_file (CdIt8 *it8, GFile *file, GError **error)
997 {
998 gsize size = 0;
999 g_autofree gchar *data = NULL;
1000
1001 g_return_val_if_fail (CD_IS_IT8 (it8), FALSE);
1002 g_return_val_if_fail (G_IS_FILE (file), FALSE);
1003
1004 /* load file */
1005 if (!g_file_load_contents (file, NULL, &data, &size, NULL, error))
1006 return FALSE;
1007
1008 /* load data */
1009 if (!cd_it8_load_from_data (it8, data, size, error))
1010 return FALSE;
1011 return TRUE;
1012 }
1013
1014 /**
1015 * cd_it8_color_match:
1016 **/
1017 static gboolean
cd_it8_color_match(CdColorRGB * rgb,gdouble r,gdouble g,gdouble b)1018 cd_it8_color_match (CdColorRGB *rgb, gdouble r, gdouble g, gdouble b)
1019 {
1020 if (ABS (rgb->R - r) > 0.01f)
1021 return FALSE;
1022 if (ABS (rgb->G - g) > 0.01f)
1023 return FALSE;
1024 if (ABS (rgb->B - b) > 0.01f)
1025 return FALSE;
1026 return TRUE;
1027 }
1028
1029 /**
1030 * cd_it8_convert_xyz_to_string:
1031 **/
1032 static gchar *
cd_it8_convert_xyz_to_string(CdColorXYZ * src)1033 cd_it8_convert_xyz_to_string (CdColorXYZ *src)
1034 {
1035 gchar buffer[3][G_ASCII_DTOSTR_BUF_SIZE];
1036 g_ascii_dtostr (buffer[0], G_ASCII_DTOSTR_BUF_SIZE, src->X);
1037 g_ascii_dtostr (buffer[1], G_ASCII_DTOSTR_BUF_SIZE, src->Y);
1038 g_ascii_dtostr (buffer[2], G_ASCII_DTOSTR_BUF_SIZE, src->Z);
1039 return g_strdup_printf ("%s %s %s", buffer[0], buffer[1], buffer[2]);
1040 }
1041
1042 /**
1043 * cd_it8_save_to_file_ti1_ti3:
1044 **/
1045 static gboolean
cd_it8_save_to_file_ti1_ti3(CdIt8 * it8,cmsHANDLE it8_lcms,GError ** error)1046 cd_it8_save_to_file_ti1_ti3 (CdIt8 *it8, cmsHANDLE it8_lcms, GError **error)
1047 {
1048 CdIt8Private *priv = GET_PRIVATE (it8);
1049 CdColorRGB *rgb_tmp;
1050 CdColorXYZ lumi_xyz;
1051 CdColorXYZ *xyz_tmp;
1052 gboolean is_white;
1053 gdouble normalize = 0.0f;
1054 guint i;
1055 guint luminance_samples = 0;
1056 g_autofree gchar *lumi_str = NULL;
1057
1058 /* calculate the absolute XYZ in candelas per meter squared */
1059 cd_color_xyz_clear (&lumi_xyz);
1060 if (priv->normalized) {
1061 for (i = 0; i < priv->array_rgb->len; i++) {
1062 rgb_tmp = g_ptr_array_index (priv->array_rgb, i);
1063
1064 /* is this 100% white? */
1065 is_white = cd_it8_color_match (rgb_tmp, 1.0f, 1.0f, 1.0f);
1066 if (!is_white)
1067 continue;
1068 luminance_samples++;
1069 xyz_tmp = g_ptr_array_index (priv->array_xyz, i);
1070 lumi_xyz.X += xyz_tmp->X;
1071 lumi_xyz.Y += xyz_tmp->Y;
1072 lumi_xyz.Z += xyz_tmp->Z;
1073 if (xyz_tmp->Y > normalize)
1074 normalize = xyz_tmp->Y;
1075 }
1076 if (luminance_samples == 0) {
1077 g_set_error_literal (error,
1078 CD_IT8_ERROR,
1079 CD_IT8_ERROR_FAILED,
1080 "Failed to find any white samples");
1081 return FALSE;
1082 }
1083 lumi_xyz.X /= luminance_samples;
1084 lumi_xyz.Y /= luminance_samples;
1085 lumi_xyz.Z /= luminance_samples;
1086
1087 /* scale all the readings to 100 */
1088 normalize = 100.0f / normalize;
1089 }
1090 lumi_str = cd_it8_convert_xyz_to_string (&lumi_xyz);
1091
1092 /* write data */
1093 if (priv->kind == CD_IT8_KIND_TI1) {
1094 cmsIT8SetSheetType (it8_lcms, "CTI1 ");
1095 cmsIT8SetPropertyStr (it8_lcms, "DESCRIPTOR",
1096 "Calibration Target chart information 1");
1097 } else if (priv->kind == CD_IT8_KIND_TI3) {
1098 cmsIT8SetSheetType (it8_lcms, "CTI3 ");
1099 cmsIT8SetPropertyStr (it8_lcms, "DESCRIPTOR",
1100 "Calibration Target chart information 3");
1101 }
1102 if (priv->kind == CD_IT8_KIND_TI3) {
1103 cmsIT8SetPropertyStr (it8_lcms, "DEVICE_CLASS",
1104 "DISPLAY");
1105 }
1106 cmsIT8SetPropertyStr (it8_lcms, "COLOR_REP", "RGB_XYZ");
1107 if (priv->instrument != NULL) {
1108 cmsIT8SetPropertyStr (it8_lcms, "TARGET_INSTRUMENT",
1109 priv->instrument);
1110 }
1111 cmsIT8SetPropertyStr (it8_lcms, "INSTRUMENT_TYPE_SPECTRAL",
1112 priv->spectral ? "YES" : "NO");
1113 if (priv->normalized) {
1114 cmsIT8SetPropertyStr (it8_lcms, "NORMALIZED_TO_Y_100", "YES");
1115 cmsIT8SetPropertyStr (it8_lcms, "LUMINANCE_XYZ_CDM2", lumi_str);
1116 } else {
1117 cmsIT8SetPropertyStr (it8_lcms, "NORMALIZED_TO_Y_100", "NO");
1118 }
1119 _cmsIT8SetPropertyInt (it8_lcms, "NUMBER_OF_FIELDS", 7);
1120 _cmsIT8SetPropertyInt (it8_lcms, "NUMBER_OF_SETS", priv->array_rgb->len);
1121 cmsIT8SetDataFormat (it8_lcms, 0, "SAMPLE_ID");
1122 cmsIT8SetDataFormat (it8_lcms, 1, "RGB_R");
1123 cmsIT8SetDataFormat (it8_lcms, 2, "RGB_G");
1124 cmsIT8SetDataFormat (it8_lcms, 3, "RGB_B");
1125 cmsIT8SetDataFormat (it8_lcms, 4, "XYZ_X");
1126 cmsIT8SetDataFormat (it8_lcms, 5, "XYZ_Y");
1127 cmsIT8SetDataFormat (it8_lcms, 6, "XYZ_Z");
1128
1129 /* write to the it8 file */
1130 for (i = 0; i < priv->array_rgb->len; i++) {
1131 rgb_tmp = g_ptr_array_index (priv->array_rgb, i);
1132 xyz_tmp = g_ptr_array_index (priv->array_xyz, i);
1133
1134 _cmsIT8SetDataRowColDbl(it8_lcms, i, 0, i + 1);
1135 if (priv->normalized) {
1136 _cmsIT8SetDataRowColDbl(it8_lcms, i, 1, rgb_tmp->R * 100.0f);
1137 _cmsIT8SetDataRowColDbl(it8_lcms, i, 2, rgb_tmp->G * 100.0f);
1138 _cmsIT8SetDataRowColDbl(it8_lcms, i, 3, rgb_tmp->B * 100.0f);
1139 _cmsIT8SetDataRowColDbl(it8_lcms, i, 4, xyz_tmp->X * normalize);
1140 _cmsIT8SetDataRowColDbl(it8_lcms, i, 5, xyz_tmp->Y * normalize);
1141 _cmsIT8SetDataRowColDbl(it8_lcms, i, 6, xyz_tmp->Z * normalize);
1142 } else {
1143 _cmsIT8SetDataRowColDbl(it8_lcms, i, 1, rgb_tmp->R);
1144 _cmsIT8SetDataRowColDbl(it8_lcms, i, 2, rgb_tmp->G);
1145 _cmsIT8SetDataRowColDbl(it8_lcms, i, 3, rgb_tmp->B);
1146 _cmsIT8SetDataRowColDbl(it8_lcms, i, 4, xyz_tmp->X);
1147 _cmsIT8SetDataRowColDbl(it8_lcms, i, 5, xyz_tmp->Y);
1148 _cmsIT8SetDataRowColDbl(it8_lcms, i, 6, xyz_tmp->Z);
1149 }
1150 }
1151 return TRUE;
1152 }
1153
1154 /**
1155 * cd_it8_save_to_file_cal:
1156 **/
1157 static gboolean
cd_it8_save_to_file_cal(CdIt8 * it8,cmsHANDLE it8_lcms,GError ** error)1158 cd_it8_save_to_file_cal (CdIt8 *it8, cmsHANDLE it8_lcms, GError **error)
1159 {
1160 CdIt8Private *priv = GET_PRIVATE (it8);
1161 CdColorRGB *rgb_tmp;
1162 gboolean ret = TRUE;
1163 guint i;
1164
1165 /* write data */
1166 cmsIT8SetSheetType (it8_lcms, "CAL ");
1167 cmsIT8SetPropertyStr (it8_lcms, "DESCRIPTOR",
1168 "Device Calibration Curves");
1169 cmsIT8SetPropertyStr (it8_lcms, "DEVICE_CLASS", "DISPLAY");
1170 cmsIT8SetPropertyStr (it8_lcms, "COLOR_REP", "RGB");
1171 if (priv->instrument != NULL) {
1172 cmsIT8SetPropertyStr (it8_lcms, "TARGET_INSTRUMENT",
1173 priv->instrument);
1174 }
1175 _cmsIT8SetPropertyInt (it8_lcms, "NUMBER_OF_FIELDS", 4);
1176 _cmsIT8SetPropertyInt (it8_lcms, "NUMBER_OF_SETS", priv->array_rgb->len);
1177 cmsIT8SetDataFormat (it8_lcms, 0, "RGB_I");
1178 cmsIT8SetDataFormat (it8_lcms, 1, "RGB_R");
1179 cmsIT8SetDataFormat (it8_lcms, 2, "RGB_G");
1180 cmsIT8SetDataFormat (it8_lcms, 3, "RGB_B");
1181
1182 /* write to the it8 file */
1183 for (i = 0; i < priv->array_rgb->len; i++) {
1184 rgb_tmp = g_ptr_array_index (priv->array_rgb, i);
1185 _cmsIT8SetDataRowColDbl(it8_lcms, i, 0, 1.0f / (gdouble) (priv->array_rgb->len - 1) * (gdouble) i);
1186 _cmsIT8SetDataRowColDbl(it8_lcms, i, 1, rgb_tmp->R);
1187 _cmsIT8SetDataRowColDbl(it8_lcms, i, 2, rgb_tmp->G);
1188 _cmsIT8SetDataRowColDbl(it8_lcms, i, 3, rgb_tmp->B);
1189 }
1190
1191 return ret;
1192 }
1193
1194 /**
1195 * cd_it8_save_to_file_ccmx:
1196 **/
1197 static gboolean
cd_it8_save_to_file_ccmx(CdIt8 * it8,cmsHANDLE it8_lcms,GError ** error)1198 cd_it8_save_to_file_ccmx (CdIt8 *it8, cmsHANDLE it8_lcms, GError **error)
1199 {
1200 CdIt8Private *priv = GET_PRIVATE (it8);
1201 gboolean ret = TRUE;
1202
1203 cmsIT8SetSheetType (it8_lcms, "CCMX ");
1204 cmsIT8SetPropertyStr (it8_lcms, "DESCRIPTOR",
1205 "Device Correction Matrix");
1206
1207 cmsIT8SetPropertyStr (it8_lcms, "COLOR_REP", "XYZ");
1208 _cmsIT8SetPropertyInt (it8_lcms, "NUMBER_OF_FIELDS", 3);
1209 _cmsIT8SetPropertyInt (it8_lcms, "NUMBER_OF_SETS", 3);
1210 cmsIT8SetDataFormat (it8_lcms, 0, "XYZ_X");
1211 cmsIT8SetDataFormat (it8_lcms, 1, "XYZ_Y");
1212 cmsIT8SetDataFormat (it8_lcms, 2, "XYZ_Z");
1213
1214 /* save instrument */
1215 if (priv->instrument != NULL) {
1216 cmsIT8SetPropertyStr (it8_lcms, "INSTRUMENT",
1217 priv->instrument);
1218 }
1219
1220 /* just save the matrix */
1221 _cmsIT8SetDataRowColDbl (it8_lcms, 0, 0, priv->matrix.m00);
1222 _cmsIT8SetDataRowColDbl (it8_lcms, 0, 1, priv->matrix.m01);
1223 _cmsIT8SetDataRowColDbl (it8_lcms, 0, 2, priv->matrix.m02);
1224 _cmsIT8SetDataRowColDbl (it8_lcms, 1, 0, priv->matrix.m10);
1225 _cmsIT8SetDataRowColDbl (it8_lcms, 1, 1, priv->matrix.m11);
1226 _cmsIT8SetDataRowColDbl (it8_lcms, 1, 2, priv->matrix.m12);
1227 _cmsIT8SetDataRowColDbl (it8_lcms, 2, 0, priv->matrix.m20);
1228 _cmsIT8SetDataRowColDbl (it8_lcms, 2, 1, priv->matrix.m21);
1229 _cmsIT8SetDataRowColDbl (it8_lcms, 2, 2, priv->matrix.m22);
1230
1231 return ret;
1232 }
1233
1234 /**
1235 * cd_it8_save_to_file_cmf:
1236 **/
1237 static gboolean
cd_it8_save_to_file_cmf(CdIt8 * it8,cmsHANDLE it8_lcms,GError ** error)1238 cd_it8_save_to_file_cmf (CdIt8 *it8, cmsHANDLE it8_lcms, GError **error)
1239 {
1240 CdIt8Private *priv = GET_PRIVATE (it8);
1241 CdSpectrum *spectrum;
1242 guint i;
1243 guint j;
1244 guint number_of_sets;
1245 guint spectral_bands;
1246
1247 cmsIT8SetSheetType (it8_lcms, "CMF ");
1248 cmsIT8SetPropertyStr (it8_lcms, "DESCRIPTOR",
1249 "Color Match Function");
1250
1251 /* check data is valid */
1252 number_of_sets = priv->array_spectra->len;
1253 if (number_of_sets != 3) {
1254 g_set_error_literal (error,
1255 CD_IT8_ERROR,
1256 CD_IT8_ERROR_INVALID_FORMAT,
1257 "Cannot write CMF: XYZ data required");
1258 return FALSE;
1259 }
1260
1261 /* all the arrays have to have the same length */
1262 spectrum = g_ptr_array_index (priv->array_spectra, 0);
1263 spectral_bands = cd_spectrum_get_size (spectrum);
1264 _cmsIT8SetPropertyDbl (it8_lcms, "SPECTRAL_START_NM", cd_spectrum_get_start (spectrum));
1265 _cmsIT8SetPropertyDbl (it8_lcms, "SPECTRAL_END_NM", cd_spectrum_get_end (spectrum));
1266 _cmsIT8SetPropertyInt (it8_lcms, "SPECTRAL_BANDS", spectral_bands);
1267 _cmsIT8SetPropertyDbl (it8_lcms, "SPECTRAL_NORM", cd_spectrum_get_norm (spectrum));
1268 _cmsIT8SetPropertyInt (it8_lcms, "NUMBER_OF_FIELDS", spectral_bands);
1269
1270 /* set DATA_FORMAT (using an ID if there are more than one spectra */
1271 spectrum = g_ptr_array_index (priv->array_spectra, 0);
1272 for (i = 0; i < spectral_bands; i++) {
1273 g_autofree gchar *label = NULL;
1274 label = g_strdup_printf ("SPEC_%.0f",
1275 cd_spectrum_get_wavelength (spectrum, i));
1276 cmsIT8SetDataFormat (it8_lcms, i, label);
1277 }
1278
1279 /* set DATA */
1280 _cmsIT8SetPropertyInt (it8_lcms, "NUMBER_OF_SETS", number_of_sets);
1281 for (j = 0; j < number_of_sets; j++) {
1282 spectrum = g_ptr_array_index (priv->array_spectra, j);
1283 for (i = 0; i < spectral_bands; i++) {
1284 if (!priv->normalized) {
1285 _cmsIT8SetDataRowColDbl (it8_lcms, j, i,
1286 cd_spectrum_get_value (spectrum, i));
1287 } else {
1288 _cmsIT8SetDataRowColDbl (it8_lcms, j, i,
1289 cd_spectrum_get_value_raw (spectrum, i));
1290 }
1291 }
1292 }
1293 return TRUE;
1294 }
1295
1296 /**
1297 * cd_it8_save_to_file_ccss_sp:
1298 **/
1299 static gboolean
cd_it8_save_to_file_ccss_sp(CdIt8 * it8,cmsHANDLE it8_lcms,GError ** error)1300 cd_it8_save_to_file_ccss_sp (CdIt8 *it8, cmsHANDLE it8_lcms, GError **error)
1301 {
1302 CdIt8Private *priv = GET_PRIVATE (it8);
1303 CdSpectrum *spectrum;
1304 gboolean has_index = FALSE;
1305 guint i;
1306 guint j;
1307 guint number_of_sets;
1308 guint spectral_bands;
1309
1310 switch (priv->kind) {
1311 case CD_IT8_KIND_CCSS:
1312 cmsIT8SetSheetType (it8_lcms, "CCSS ");
1313 cmsIT8SetPropertyStr (it8_lcms, "DESCRIPTOR",
1314 "Colorimeter Calibration Spectral Set");
1315 break;
1316 case CD_IT8_KIND_CMF:
1317 cmsIT8SetSheetType (it8_lcms, "CMF ");
1318 cmsIT8SetPropertyStr (it8_lcms, "DESCRIPTOR",
1319 "Color Match Function");
1320 break;
1321 case CD_IT8_KIND_SPECT:
1322 cmsIT8SetSheetType (it8_lcms, "SPECT ");
1323 cmsIT8SetPropertyStr (it8_lcms, "DESCRIPTOR",
1324 "Spectral Power");
1325 break;
1326 default:
1327 break;
1328 }
1329
1330 /* check data is valid */
1331 number_of_sets = priv->array_spectra->len;
1332 if (number_of_sets == 0) {
1333 g_set_error_literal (error,
1334 CD_IT8_ERROR,
1335 CD_IT8_ERROR_INVALID_FORMAT,
1336 "Cannot write CCSS: spectral data required");
1337 return FALSE;
1338 }
1339 if (number_of_sets > 1)
1340 has_index = TRUE;
1341
1342 /* all the arrays have to have the same length */
1343 spectrum = g_ptr_array_index (priv->array_spectra, 0);
1344 spectral_bands = cd_spectrum_get_size (spectrum);
1345 _cmsIT8SetPropertyDbl (it8_lcms, "SPECTRAL_START_NM", cd_spectrum_get_start (spectrum));
1346 _cmsIT8SetPropertyDbl (it8_lcms, "SPECTRAL_END_NM", cd_spectrum_get_end (spectrum));
1347 _cmsIT8SetPropertyInt (it8_lcms, "SPECTRAL_BANDS", spectral_bands);
1348 _cmsIT8SetPropertyInt (it8_lcms, "NUMBER_OF_FIELDS", spectral_bands + has_index);
1349 if (priv->normalized)
1350 _cmsIT8SetPropertyDbl (it8_lcms, "SPECTRAL_NORM", cd_spectrum_get_norm (spectrum));
1351
1352 /* set DATA_FORMAT (using an ID if there are more than one spectra */
1353 if (has_index)
1354 cmsIT8SetDataFormat (it8_lcms, 0, "SAMPLE_ID");
1355 spectrum = g_ptr_array_index (priv->array_spectra, 0);
1356 for (i = 0; i < spectral_bands; i++) {
1357 g_autofree gchar *label = NULL;
1358 /* there are more spectral bands than integers between the
1359 * start and stop wavelengths */
1360 if ((cd_spectrum_get_end (spectrum) -
1361 cd_spectrum_get_start (spectrum)) < spectral_bands) {
1362 label = g_strdup_printf ("SPEC_%.0f",
1363 cd_spectrum_get_wavelength (spectrum, i) * 1000.f);
1364 } else {
1365 label = g_strdup_printf ("SPEC_%.0f",
1366 cd_spectrum_get_wavelength (spectrum, i));
1367 }
1368 cmsIT8SetDataFormat (it8_lcms, i + has_index, label);
1369 }
1370
1371 /* set DATA */
1372 _cmsIT8SetPropertyInt (it8_lcms, "NUMBER_OF_SETS", number_of_sets);
1373 for (j = 0; j < number_of_sets; j++) {
1374 spectrum = g_ptr_array_index (priv->array_spectra, j);
1375 if (has_index) {
1376 cmsIT8SetDataRowCol (it8_lcms, j, 0,
1377 cd_spectrum_get_id (spectrum));
1378 }
1379 for (i = 0; i < spectral_bands; i++) {
1380 if (!priv->normalized) {
1381 _cmsIT8SetDataRowColDbl (it8_lcms, j, i + has_index,
1382 cd_spectrum_get_value (spectrum, i));
1383 } else {
1384 _cmsIT8SetDataRowColDbl (it8_lcms, j, i + has_index,
1385 cd_spectrum_get_value_raw (spectrum, i));
1386 }
1387 }
1388 }
1389 return TRUE;
1390 }
1391
1392 /**
1393 * cd_it8_save_to_data:
1394 * @it8: a #CdIt8 instance.
1395 * @data: a pointer to returned data
1396 * @size: size of @data
1397 * @error: a #GError, or %NULL
1398 *
1399 * Saves a it8 file to an area of memory.
1400 *
1401 * Return value: %TRUE if it8 file was saved.
1402 *
1403 * Since: 0.1.26
1404 **/
1405 gboolean
cd_it8_save_to_data(CdIt8 * it8,gchar ** data,gsize * size,GError ** error)1406 cd_it8_save_to_data (CdIt8 *it8,
1407 gchar **data,
1408 gsize *size,
1409 GError **error)
1410 {
1411 CdIt8Private *priv = GET_PRIVATE (it8);
1412 cmsHANDLE it8_lcms = NULL;
1413 const gchar *tmp;
1414 gboolean ret;
1415 GDateTime *datetime = NULL;
1416 cmsUInt32Number size_tmp = 0;
1417 guint i;
1418 g_autofree gchar *data_tmp = NULL;
1419 g_autofree gchar *date_str = NULL;
1420
1421 g_return_val_if_fail (CD_IS_IT8 (it8), FALSE);
1422
1423 /* set common data */
1424 it8_lcms = cmsIT8Alloc (priv->context_lcms);
1425 if (priv->title != NULL) {
1426 cmsIT8SetPropertyStr (it8_lcms, "DISPLAY",
1427 priv->title);
1428 }
1429 if (priv->originator != NULL) {
1430 cmsIT8SetPropertyStr (it8_lcms, "ORIGINATOR",
1431 priv->originator);
1432 }
1433 if (priv->reference != NULL) {
1434 cmsIT8SetPropertyStr (it8_lcms, "REFERENCE",
1435 priv->reference);
1436 }
1437
1438 /* set time and date in crazy ArgllCMS format, e.g.
1439 * 'Wed Dec 19 18:47:57 2012' */
1440 if (priv->enable_created) {
1441 datetime = g_date_time_new_now_local ();
1442 date_str = g_date_time_format (datetime, "%a %b %d %H:%M:%S %Y");
1443 cmsIT8SetPropertyStr (it8_lcms, "CREATED", date_str);
1444 }
1445
1446 /* set ti1 and ti3 specific data */
1447 switch (priv->kind) {
1448 case CD_IT8_KIND_TI1:
1449 case CD_IT8_KIND_TI3:
1450 ret = cd_it8_save_to_file_ti1_ti3 (it8, it8_lcms, error);
1451 if (!ret)
1452 goto out;
1453 break;
1454 case CD_IT8_KIND_CAL:
1455 ret = cd_it8_save_to_file_cal (it8, it8_lcms, error);
1456 if (!ret)
1457 goto out;
1458 break;
1459 case CD_IT8_KIND_CCMX:
1460 ret = cd_it8_save_to_file_ccmx (it8, it8_lcms, error);
1461 if (!ret)
1462 goto out;
1463 break;
1464 case CD_IT8_KIND_CMF:
1465 ret = cd_it8_save_to_file_cmf (it8, it8_lcms, error);
1466 if (!ret)
1467 goto out;
1468 break;
1469 case CD_IT8_KIND_CCSS:
1470 case CD_IT8_KIND_SPECT:
1471 ret = cd_it8_save_to_file_ccss_sp (it8, it8_lcms, error);
1472 if (!ret)
1473 goto out;
1474 break;
1475 default:
1476 break;
1477 }
1478
1479 /* save any options */
1480 for (i = 0; i < priv->options->len; i++) {
1481 tmp = g_ptr_array_index (priv->options, i);
1482 cmsIT8SetPropertyStr (it8_lcms, tmp, "YES");
1483 }
1484
1485 /* write the file */
1486 ret = cmsIT8SaveToMem (it8_lcms, NULL, &size_tmp);
1487 g_assert (ret);
1488 data_tmp = g_malloc (size_tmp);
1489 ret = cmsIT8SaveToMem (it8_lcms, data_tmp, &size_tmp);
1490 g_assert (ret);
1491
1492 /* save for caller */
1493 if (data != NULL)
1494 *data = g_strdup (data_tmp);
1495
1496 /* LCMS alocates an extra byte for the '\0' byte */
1497 if (size != NULL)
1498 *size = size_tmp - 1;
1499 out:
1500 if (it8_lcms != NULL)
1501 cmsIT8Free (it8_lcms);
1502 if (datetime != NULL)
1503 g_date_time_unref (datetime);
1504 return ret;
1505 }
1506
1507 /**
1508 * cd_it8_save_to_file:
1509 * @it8: a #CdIt8 instance.
1510 * @file: a #GFile
1511 * @error: a #GError, or %NULL
1512 *
1513 * Saves a it8 file to disk
1514 *
1515 * Return value: %TRUE if it8 file was saved.
1516 *
1517 * Since: 0.1.20
1518 **/
1519 gboolean
cd_it8_save_to_file(CdIt8 * it8,GFile * file,GError ** error)1520 cd_it8_save_to_file (CdIt8 *it8, GFile *file, GError **error)
1521 {
1522 gsize size = 0;
1523 g_autofree gchar *data = NULL;
1524
1525 g_return_val_if_fail (CD_IS_IT8 (it8), FALSE);
1526 g_return_val_if_fail (G_IS_FILE (file), FALSE);
1527
1528 /* get data */
1529 if (!cd_it8_save_to_data (it8, &data, &size, error))
1530 return FALSE;
1531
1532 /* save file */
1533 return g_file_replace_contents (file, data, size, NULL,
1534 FALSE, G_FILE_CREATE_NONE,
1535 NULL, NULL, error);
1536 }
1537
1538 /**
1539 * cd_it8_add_option:
1540 * @it8: a #CdIt8 instance.
1541 * @option: A IT8 option, e.g. "TYPE_LCD"
1542 *
1543 * Sets any extra options that have to be set in the CCMX file
1544 *
1545 * Since: 0.1.20
1546 **/
1547 void
cd_it8_add_option(CdIt8 * it8,const gchar * option)1548 cd_it8_add_option (CdIt8 *it8, const gchar *option)
1549 {
1550 CdIt8Private *priv = GET_PRIVATE (it8);
1551 g_return_if_fail (CD_IS_IT8 (it8));
1552 g_ptr_array_add (priv->options, g_strdup (option));
1553 }
1554
1555 /**
1556 * cd_it8_set_normalized:
1557 * @it8: a #CdIt8 instance.
1558 * @normalized: If the data is normalized
1559 *
1560 * Sets if normalized data should be written to the .it8 file.
1561 *
1562 * Since: 0.1.20
1563 **/
1564 void
cd_it8_set_normalized(CdIt8 * it8,gboolean normalized)1565 cd_it8_set_normalized (CdIt8 *it8, gboolean normalized)
1566 {
1567 CdIt8Private *priv = GET_PRIVATE (it8);
1568 g_return_if_fail (CD_IS_IT8 (it8));
1569 priv->normalized = normalized;
1570 }
1571
1572 /**
1573 * cd_it8_set_spectral:
1574 * @it8: a #CdIt8 instance.
1575 * @spectral: If the data is spectral
1576 *
1577 * Sets if spectral data should be written to the .it8 file.
1578 *
1579 * Since: 0.1.20
1580 **/
1581 void
cd_it8_set_spectral(CdIt8 * it8,gboolean spectral)1582 cd_it8_set_spectral (CdIt8 *it8, gboolean spectral)
1583 {
1584 CdIt8Private *priv = GET_PRIVATE (it8);
1585 g_return_if_fail (CD_IS_IT8 (it8));
1586 priv->spectral = spectral;
1587 }
1588
1589 /**
1590 * cd_it8_set_originator:
1591 * @it8: a #CdIt8 instance.
1592 * @originator: the program name, e.g. "gcm-calibrate"
1593 *
1594 * Sets the program name that created the .it8 file
1595 *
1596 * Since: 0.1.20
1597 **/
1598 void
cd_it8_set_originator(CdIt8 * it8,const gchar * originator)1599 cd_it8_set_originator (CdIt8 *it8, const gchar *originator)
1600 {
1601 CdIt8Private *priv = GET_PRIVATE (it8);
1602 g_return_if_fail (CD_IS_IT8 (it8));
1603
1604 g_free (priv->originator);
1605 priv->originator = g_strdup (originator);
1606 }
1607
1608 /**
1609 * cd_it8_set_title:
1610 * @it8: a #CdIt8 instance.
1611 * @title: the title name, e.g. "Factory calibration"
1612 *
1613 * Sets the display name for the file.
1614 *
1615 * Since: 0.1.20
1616 **/
1617 void
cd_it8_set_title(CdIt8 * it8,const gchar * title)1618 cd_it8_set_title (CdIt8 *it8, const gchar *title)
1619 {
1620 CdIt8Private *priv = GET_PRIVATE (it8);
1621 g_return_if_fail (CD_IS_IT8 (it8));
1622
1623 g_free (priv->title);
1624 priv->title = g_strdup (title);
1625 }
1626
1627 /**
1628 * cd_it8_set_instrument:
1629 * @it8: a #CdIt8 instance.
1630 * @instrument: the instruemnt name, e.g. "huey"
1631 *
1632 * Sets the measuring instrument that created the .it8 file
1633 *
1634 * Since: 0.1.20
1635 **/
1636 void
cd_it8_set_instrument(CdIt8 * it8,const gchar * instrument)1637 cd_it8_set_instrument (CdIt8 *it8, const gchar *instrument)
1638 {
1639 CdIt8Private *priv = GET_PRIVATE (it8);
1640 g_return_if_fail (CD_IS_IT8 (it8));
1641
1642 g_free (priv->instrument);
1643 priv->instrument = g_strdup (instrument);
1644 }
1645
1646 /**
1647 * cd_it8_set_reference:
1648 * @it8: a #CdIt8 instance.
1649 * @reference: the instruemnt name, e.g. "colormunki"
1650 *
1651 * Sets the reference that as used to create the .it8 reference
1652 *
1653 * Since: 0.1.20
1654 **/
1655 void
cd_it8_set_reference(CdIt8 * it8,const gchar * reference)1656 cd_it8_set_reference (CdIt8 *it8, const gchar *reference)
1657 {
1658 CdIt8Private *priv = GET_PRIVATE (it8);
1659 g_return_if_fail (CD_IS_IT8 (it8));
1660
1661 g_free (priv->reference);
1662 priv->reference = g_strdup (reference);
1663 }
1664
1665 /**
1666 * cd_it8_set_enable_created:
1667 * @it8: a #CdIt8 instance.
1668 * @enable_created: Is 'CREATED' should be written
1669 *
1670 * Sets if the 'CREATED' attribute should be written. This is mainly useful
1671 * in the self test programs where we want to string compare the output data
1672 * with a known reference.
1673 *
1674 * Since: 0.1.33
1675 **/
1676 void
cd_it8_set_enable_created(CdIt8 * it8,gboolean enable_created)1677 cd_it8_set_enable_created (CdIt8 *it8, gboolean enable_created)
1678 {
1679 CdIt8Private *priv = GET_PRIVATE (it8);
1680 g_return_if_fail (CD_IS_IT8 (it8));
1681 priv->enable_created = enable_created;
1682 }
1683
1684 /**
1685 * cd_it8_add_data:
1686 * @it8: a #CdIt8 instance.
1687 * @rgb: a #CdColorRGB, or %NULL
1688 * @xyz: a #CdColorXYZ, or %NULL
1689 *
1690 * Adds a reading to this object. If either of @rgb or @xyz is NULL then
1691 * a black reading (0.0, 0.0, 0.0) is added instead.
1692 *
1693 * Since: 0.1.20
1694 **/
1695 void
cd_it8_add_data(CdIt8 * it8,const CdColorRGB * rgb,const CdColorXYZ * xyz)1696 cd_it8_add_data (CdIt8 *it8, const CdColorRGB *rgb, const CdColorXYZ *xyz)
1697 {
1698 CdIt8Private *priv = GET_PRIVATE (it8);
1699 CdColorRGB *rgb_tmp;
1700 CdColorXYZ *xyz_tmp;
1701
1702 g_return_if_fail (CD_IS_IT8 (it8));
1703
1704 /* add RGB */
1705 if (rgb != NULL) {
1706 rgb_tmp = cd_color_rgb_dup (rgb);
1707 } else {
1708 rgb_tmp = cd_color_rgb_new ();
1709 cd_color_rgb_set (rgb_tmp, 0.0f, 0.0f, 0.0f);
1710 }
1711 g_ptr_array_add (priv->array_rgb, rgb_tmp);
1712
1713 /* add XYZ */
1714 if (xyz != NULL) {
1715 xyz_tmp = cd_color_xyz_dup (xyz);
1716 } else {
1717 xyz_tmp = cd_color_xyz_new ();
1718 cd_color_xyz_set (xyz_tmp, 0.0f, 0.0f, 0.0f);
1719 }
1720 g_ptr_array_add (priv->array_xyz, xyz_tmp);
1721 }
1722
1723 /**
1724 * cd_it8_get_data_size:
1725 * @it8: a #CdIt8 instance.
1726 *
1727 * Gets the data size.
1728 *
1729 * Return value: The number of RGB-XYZ readings in this object.
1730 *
1731 * Since: 0.1.20
1732 **/
1733 guint
cd_it8_get_data_size(CdIt8 * it8)1734 cd_it8_get_data_size (CdIt8 *it8)
1735 {
1736 CdIt8Private *priv = GET_PRIVATE (it8);
1737 g_return_val_if_fail (CD_IS_IT8 (it8), G_MAXUINT);
1738 return priv->array_xyz->len;
1739 }
1740
1741 /**
1742 * cd_it8_get_data_item:
1743 * @it8: a #CdIt8 instance.
1744 * @idx: the item index
1745 * @rgb: the returned RGB value
1746 * @xyz: the returned XYZ value
1747 *
1748 * Gets a specific bit of data from this object.
1749 * The returned data are absolute readings and are not normalised.
1750 *
1751 * Return value: %TRUE if the index existed.
1752 *
1753 * Since: 0.1.20
1754 **/
1755 gboolean
cd_it8_get_data_item(CdIt8 * it8,guint idx,CdColorRGB * rgb,CdColorXYZ * xyz)1756 cd_it8_get_data_item (CdIt8 *it8, guint idx, CdColorRGB *rgb, CdColorXYZ *xyz)
1757 {
1758 CdIt8Private *priv = GET_PRIVATE (it8);
1759 const CdColorRGB *rgb_tmp;
1760 const CdColorXYZ *xyz_tmp;
1761
1762 g_return_val_if_fail (CD_IS_IT8 (it8), FALSE);
1763
1764 if (idx > priv->array_xyz->len)
1765 return FALSE;
1766 if (rgb != NULL) {
1767 rgb_tmp = g_ptr_array_index (priv->array_rgb, idx);
1768 cd_color_rgb_copy (rgb_tmp, rgb);
1769 }
1770 if (xyz != NULL) {
1771 xyz_tmp = g_ptr_array_index (priv->array_xyz, idx);
1772 cd_color_xyz_copy (xyz_tmp, xyz);
1773 }
1774 return TRUE;
1775 }
1776
1777 /**
1778 * cd_it8_get_xyz_for_rgb:
1779 * @it8: a #CdIt8 instance.
1780 * @R: the red value
1781 * @G: the green value
1782 * @B: the blue value
1783 * @delta: the smallest difference between colors, e.g. 0.01f
1784 *
1785 * Gets the XYZ value for a specific RGB value.
1786 *
1787 * Return value: (transfer none): A CdColorXYZ, or %NULL if the sample does not exist.
1788 *
1789 * Since: 1.2.6
1790 **/
1791 CdColorXYZ *
cd_it8_get_xyz_for_rgb(CdIt8 * it8,gdouble R,gdouble G,gdouble B,gdouble delta)1792 cd_it8_get_xyz_for_rgb (CdIt8 *it8, gdouble R, gdouble G, gdouble B, gdouble delta)
1793 {
1794 CdIt8Private *priv = GET_PRIVATE (it8);
1795 CdColorXYZ *xyz_tmp;
1796 guint i;
1797 const CdColorRGB *rgb_tmp;
1798
1799 g_return_val_if_fail (CD_IS_IT8 (it8), NULL);
1800
1801 for (i = 0; i < priv->array_xyz->len; i++) {
1802 rgb_tmp = g_ptr_array_index (priv->array_rgb, i);
1803 if (ABS (rgb_tmp->R - R) > delta)
1804 continue;
1805 if (ABS (rgb_tmp->G - G) > delta)
1806 continue;
1807 if (ABS (rgb_tmp->B - B) > delta)
1808 continue;
1809 xyz_tmp = g_ptr_array_index (priv->array_xyz, i);
1810 return xyz_tmp;
1811 }
1812 return NULL;
1813 }
1814
1815 /**
1816 * cd_it8_set_spectrum_array:
1817 * @it8: a #CdIt8 instance.
1818 * @data: (transfer container) (element-type CdSpectrum): the spectral data
1819 *
1820 * Set the spectral data
1821 *
1822 * Since: 1.1.6
1823 **/
1824 void
cd_it8_set_spectrum_array(CdIt8 * it8,GPtrArray * data)1825 cd_it8_set_spectrum_array (CdIt8 *it8, GPtrArray *data)
1826 {
1827 CdIt8Private *priv = GET_PRIVATE (it8);
1828 g_return_if_fail (CD_IS_IT8 (it8));
1829 g_ptr_array_unref (priv->array_spectra);
1830 priv->array_spectra = g_ptr_array_ref (data);
1831 }
1832
1833 /**
1834 * cd_it8_add_spectrum:
1835 * @it8: a #CdIt8 instance.
1836 * @spectrum: the spectral data
1837 *
1838 * Adds a spectrum to the spectral array.
1839 *
1840 * Since: 1.1.6
1841 **/
1842 void
cd_it8_add_spectrum(CdIt8 * it8,CdSpectrum * spectrum)1843 cd_it8_add_spectrum (CdIt8 *it8, CdSpectrum *spectrum)
1844 {
1845 CdIt8Private *priv = GET_PRIVATE (it8);
1846 const gchar *id;
1847 CdSpectrum *tmp;
1848
1849 g_return_if_fail (CD_IS_IT8 (it8));
1850
1851 /* remove any existing spectra with this same ID */
1852 id = cd_spectrum_get_id (spectrum);
1853 if (id != NULL) {
1854 tmp = cd_it8_get_spectrum_by_id (it8, id);
1855 if (tmp != NULL)
1856 g_ptr_array_remove (priv->array_spectra, tmp);
1857 }
1858
1859 /* add this */
1860 g_ptr_array_add (priv->array_spectra, cd_spectrum_dup (spectrum));
1861 }
1862
1863 /**
1864 * cd_it8_get_spectrum_array:
1865 * @it8: a #CdIt8 instance.
1866 *
1867 * Gets the spectral data of IT8 file.
1868 *
1869 * Return value: (transfer container) (element-type CdSpectrum): spectral data
1870 *
1871 * Since: 1.1.6
1872 **/
1873 GPtrArray *
cd_it8_get_spectrum_array(CdIt8 * it8)1874 cd_it8_get_spectrum_array (CdIt8 *it8)
1875 {
1876 CdIt8Private *priv = GET_PRIVATE (it8);
1877 g_return_val_if_fail (CD_IS_IT8 (it8), NULL);
1878 return g_ptr_array_ref (priv->array_spectra);
1879 }
1880
1881 /**
1882 * cd_it8_get_spectrum_by_id:
1883 * @it8: a #CdIt8 instance.
1884 * @id: the spectrum ID value
1885 *
1886 * Gets a specific spectrum in an IT8 file.
1887 *
1888 * Return value: (transfer none): spectrum, or %NULL
1889 *
1890 * Since: 1.1.6
1891 **/
1892 CdSpectrum *
cd_it8_get_spectrum_by_id(CdIt8 * it8,const gchar * id)1893 cd_it8_get_spectrum_by_id (CdIt8 *it8, const gchar *id)
1894 {
1895 CdIt8Private *priv = GET_PRIVATE (it8);
1896 CdSpectrum *tmp;
1897 guint i;
1898
1899 g_return_val_if_fail (CD_IS_IT8 (it8), NULL);
1900 g_return_val_if_fail (id != NULL, NULL);
1901
1902 for (i = 0; i < priv->array_spectra->len; i++) {
1903 tmp = g_ptr_array_index (priv->array_spectra, i);
1904 if (g_strcmp0 (cd_spectrum_get_id (tmp), id) == 0)
1905 return tmp;
1906 }
1907 return NULL;
1908 }
1909
1910 /**********************************************************************/
1911
1912 /*
1913 * cd_it8_get_property:
1914 */
1915 static void
cd_it8_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1916 cd_it8_get_property (GObject *object,
1917 guint prop_id,
1918 GValue *value,
1919 GParamSpec *pspec)
1920 {
1921 CdIt8 *it8 = CD_IT8 (object);
1922 CdIt8Private *priv = GET_PRIVATE (it8);
1923
1924 switch (prop_id) {
1925 case PROP_KIND:
1926 g_value_set_uint (value, priv->kind);
1927 break;
1928 case PROP_NORMALIZED:
1929 g_value_set_boolean (value, priv->normalized);
1930 break;
1931 case PROP_ORIGINATOR:
1932 g_value_set_string (value, priv->originator);
1933 break;
1934 case PROP_TITLE:
1935 g_value_set_string (value, priv->title);
1936 break;
1937 case PROP_INSTRUMENT:
1938 g_value_set_string (value, priv->instrument);
1939 break;
1940 case PROP_REFERENCE:
1941 g_value_set_string (value, priv->reference);
1942 break;
1943 case PROP_SPECTRAL:
1944 g_value_set_boolean (value, priv->spectral);
1945 break;
1946 default:
1947 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1948 break;
1949 }
1950 }
1951
1952 /**
1953 * cd_it8_set_property:
1954 **/
1955 static void
cd_it8_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1956 cd_it8_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1957 {
1958 CdIt8 *it8 = CD_IT8 (object);
1959 CdIt8Private *priv = GET_PRIVATE (it8);
1960
1961 switch (prop_id) {
1962 case PROP_KIND:
1963 priv->kind = g_value_get_uint (value);
1964 break;
1965 default:
1966 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1967 break;
1968 }
1969 }
1970
1971 /*
1972 * cd_it8_class_init:
1973 */
1974 static void
cd_it8_class_init(CdIt8Class * klass)1975 cd_it8_class_init (CdIt8Class *klass)
1976 {
1977 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1978
1979 object_class->get_property = cd_it8_get_property;
1980 object_class->set_property = cd_it8_set_property;
1981 object_class->finalize = cd_it8_finalize;
1982
1983 /**
1984 * CdIt8:kind:
1985 *
1986 * The kind of IT8 file.
1987 *
1988 * Since: 0.1.20
1989 **/
1990 g_object_class_install_property (object_class,
1991 PROP_KIND,
1992 g_param_spec_uint ("kind",
1993 NULL, NULL,
1994 0, G_MAXUINT, 0,
1995 G_PARAM_READWRITE));
1996
1997 /**
1998 * CdIt8:normalized:
1999 *
2000 * If the results file is normalized.
2001 *
2002 * Since: 0.1.20
2003 **/
2004 g_object_class_install_property (object_class,
2005 PROP_NORMALIZED,
2006 g_param_spec_boolean ("normalized",
2007 NULL, NULL,
2008 FALSE,
2009 G_PARAM_READABLE));
2010
2011 /**
2012 * CdIt8:originator:
2013 *
2014 * The framework that created the results, e.g. "cd-self-test"
2015 *
2016 * Since: 0.1.20
2017 **/
2018 g_object_class_install_property (object_class,
2019 PROP_ORIGINATOR,
2020 g_param_spec_string ("originator",
2021 NULL, NULL,
2022 NULL,
2023 G_PARAM_READABLE));
2024
2025 /**
2026 * CdIt8:title:
2027 *
2028 * The file title, e.g. "Factor calibration".
2029 *
2030 * Since: 0.1.20
2031 **/
2032 g_object_class_install_property (object_class,
2033 PROP_TITLE,
2034 g_param_spec_string ("title",
2035 NULL, NULL,
2036 NULL,
2037 G_PARAM_READABLE));
2038
2039 /**
2040 * CdIt8:instrument:
2041 *
2042 * The instrument that created the results, e.g. "huey"
2043 *
2044 * Since: 0.1.20
2045 **/
2046 g_object_class_install_property (object_class,
2047 PROP_INSTRUMENT,
2048 g_param_spec_string ("instrument",
2049 NULL, NULL,
2050 NULL,
2051 G_PARAM_READABLE));
2052
2053 /**
2054 * CdIt8:reference:
2055 *
2056 * The reference that created the results, e.g. "colormunki"
2057 *
2058 * Since: 0.1.20
2059 **/
2060 g_object_class_install_property (object_class,
2061 PROP_REFERENCE,
2062 g_param_spec_string ("reference",
2063 NULL, NULL,
2064 NULL,
2065 G_PARAM_READABLE));
2066
2067 /**
2068 * CdIt8:spectral:
2069 *
2070 * If the results file is spectral.
2071 *
2072 * Since: 0.1.20
2073 **/
2074 g_object_class_install_property (object_class,
2075 PROP_SPECTRAL,
2076 g_param_spec_boolean ("spectral",
2077 NULL, NULL,
2078 FALSE,
2079 G_PARAM_READABLE));
2080 }
2081
2082 /*
2083 * cd_it8_init:
2084 */
2085 static void
cd_it8_init(CdIt8 * it8)2086 cd_it8_init (CdIt8 *it8)
2087 {
2088 CdIt8Private *priv = GET_PRIVATE (it8);
2089 priv->context_lcms = cd_context_lcms_new ();
2090
2091 cd_mat33_clear (&priv->matrix);
2092 priv->array_rgb = g_ptr_array_new_with_free_func ((GDestroyNotify) cd_color_rgb_free);
2093 priv->array_xyz = g_ptr_array_new_with_free_func ((GDestroyNotify) cd_color_xyz_free);
2094 priv->array_spectra = g_ptr_array_new_with_free_func ((GDestroyNotify) cd_spectrum_free);
2095 priv->options = g_ptr_array_new_with_free_func (g_free);
2096 priv->enable_created = TRUE;
2097
2098 /* ensure the remote errors are registered */
2099 cd_it8_error_quark ();
2100 }
2101
2102 /*
2103 * cd_it8_finalize:
2104 */
2105 static void
cd_it8_finalize(GObject * object)2106 cd_it8_finalize (GObject *object)
2107 {
2108 CdIt8 *it8 = CD_IT8 (object);
2109 CdIt8Private *priv = GET_PRIVATE (it8);
2110
2111 g_return_if_fail (CD_IS_IT8 (object));
2112
2113 cd_context_lcms_free (priv->context_lcms);
2114 g_ptr_array_unref (priv->array_spectra);
2115 g_ptr_array_unref (priv->array_rgb);
2116 g_ptr_array_unref (priv->array_xyz);
2117 g_ptr_array_unref (priv->options);
2118 g_free (priv->originator);
2119 g_free (priv->title);
2120 g_free (priv->instrument);
2121 g_free (priv->reference);
2122
2123 G_OBJECT_CLASS (cd_it8_parent_class)->finalize (object);
2124 }
2125
2126 /**
2127 * cd_it8_new:
2128 *
2129 * Creates a new #CdIt8 object.
2130 *
2131 * Return value: a new CdIt8 object.
2132 *
2133 * Since: 0.1.20
2134 **/
2135 CdIt8 *
cd_it8_new(void)2136 cd_it8_new (void)
2137 {
2138 CdIt8 *it8;
2139 it8 = g_object_new (CD_TYPE_IT8, NULL);
2140 return CD_IT8 (it8);
2141 }
2142
2143 /**
2144 * cd_it8_new_with_kind:
2145 * @kind: a #CdIt8Kind, e.g %CD_IT8_KIND_TI3.
2146 *
2147 * Creates a new #CdIt8 object.
2148 *
2149 * Return value: a new CdIt8 object.
2150 *
2151 * Since: 0.1.20
2152 **/
2153 CdIt8 *
cd_it8_new_with_kind(CdIt8Kind kind)2154 cd_it8_new_with_kind (CdIt8Kind kind)
2155 {
2156 CdIt8 *it8;
2157 it8 = g_object_new (CD_TYPE_IT8,
2158 "kind", kind,
2159 NULL);
2160 return CD_IT8 (it8);
2161 }
2162
2163