1
2 /*
3 * Argyll Color Correction System
4 * Named Color Access Library
5 *
6 * Author: Graeme W. Gill
7 * Date: 3/12/2013
8 *
9 * Copyright 2013 Graeme W. Gill
10 * All rights reserved.
11 *
12 * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
13 * see the License.txt file for licencing details.
14 */
15
16 /*
17 * Currently supports CxF2, CxF3 & ICC names color profiles.
18 */
19
20 /*
21 * TTBD:
22 *
23 * Probably want to add some other formst:
24 * (see <http://www.selapa.net/swatchbooker/>
25 * and <http://www.selapa.net/swatches/colors/fileformats.php> )
26 *
27 * Adobe .aco
28 * .acb
29 * .acbl
30 * .ase
31 * .bcf
32 * corel .cpl
33 * corel .xml
34 * autocad .acb RGB only
35 *
36 * and lower quality RGB formats:
37 *
38 * Adobe .acf ?? May support Lab
39 * corel .acb
40 *
41 * Paint Shop Pro .pal ???
42 *
43 * RAL ?
44 */
45
46 #undef DEBUG
47
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <math.h>
51 #include <sys/types.h>
52 #include <time.h>
53 #include "copyright.h"
54 #include "aconfig.h"
55 #ifndef SALONEINSTLIB
56 #include "numlib.h"
57 #include "icc.h"
58 #else
59 #include "numsup.h"
60 #endif
61 #include "conv.h"
62 #include "cgats.h"
63 #include "xspect.h"
64 #include "mxml.h"
65 #include "namedc.h"
66 #ifdef STANDALONE_TEST
67 #include "ui.h"
68 #endif /* STANDALONE_TEST */
69
70 #ifndef STANDALONE_TEST
71
72 # define DEB6 6
73 # define DEB4 4
74
75 #ifdef NT /* You'd think there might be some standards.... */
76 # ifndef __BORLANDC__
77 # define stricmp _stricmp
78 # endif
79 #else
80 # define stricmp strcasecmp
81 #endif
82
83 /* Forward declarations */
84 static void clear_namedc(namedc *p);
85 static void clear_nce(nce *p);
86
do_hash(ORD32 hash,const char * s)87 static ORD32 do_hash(ORD32 hash, const char *s) {
88 int i;
89 for (i = 0; s[i] != '\000'; i++) {
90 hash += s[i];
91 hash += (hash << 10);
92 hash ^= (hash >> 6);
93 }
94 return hash;
95 }
96
do_hash2(const char * s1,const char * s2)97 static int do_hash2(const char *s1, const char *s2) {
98 ORD32 hash;
99
100 hash = do_hash(0, s1);
101 hash = do_hash(hash, s2);
102
103 return (int)hash;
104 }
105
106 /* Compare while ignoring possible prefix on s1 */
pfxcmp(const char * s1,const char * s2)107 static int pfxcmp(const char *s1, const char *s2) {
108 const char *cp;
109
110 if ((cp = strchr(s1, ':')) == NULL)
111 cp = s1;
112 else
113 cp++;
114
115 return strcmp(cp, s2);
116 }
117
118 /* XML data type callback for mxmlLoadFile() */
119 mxml_type_t
type_cb(mxml_node_t * node)120 type_cb(mxml_node_t *node) {
121 mxml_node_t *parent = mxmlGetParent(node);
122 const char *pname;
123 const char *name = node->value.element.name;
124
125 if (parent == NULL)
126 return MXML_TEXT;
127
128 pname = parent->value.element.name;
129
130 // printf("~1 type_cb got node named '%s'\n",name);
131
132 if ((pfxcmp(pname, "ColorCIELab") == 0
133 || pfxcmp(pname, "ColorSpaceCIELab") == 0)
134 && (pfxcmp(name, "L") == 0
135 || pfxcmp(name, "A") == 0
136 || pfxcmp(name, "B") == 0))
137 return MXML_REAL;
138
139 if ((pfxcmp(pname, "ColorCIEXYZ") == 0
140 || pfxcmp(pname, "ColorSpaceCIEXYZ") == 0)
141 && (pfxcmp(name, "X") == 0
142 || pfxcmp(name, "Y") == 0
143 || pfxcmp(name, "Z") == 0))
144 return MXML_REAL;
145
146 // ReflectanceSpectrum
147 // Example doesn't have NumPoints, Increment
148 // <ReflectanceSpectrum MeasureDate="2003-09-28T12:15:33-05:00"_ColorSpecification="CSD65-2" Name="45/0 Spectral" StartWL="400" NumPoints="31" Increment="10">
149 // 0.0580 0.0594 0.0594 0.0584 0.0581 0.0591 0.0599 0.0601 0.0603 0.0610 0.0634 0.0695 0.0760 0.0786 0.0798 0.0826 0.0897 0.1024 0.1197 0.1350 0.1434 0.1455 0.1499 0.1594 0.1721 0.1842 0.1913 0.1928 0.1878 0.1734 0.1704
150
151 if ((pfxcmp(pname, "ColorSRGB") == 0
152 || pfxcmp(pname, "ColorSpaceSRGB") == 0)
153 && (pfxcmp(name, "R") == 0
154 || pfxcmp(name, "G") == 0
155 || pfxcmp(name, "B") == 0))
156 return MXML_REAL;
157
158 if ((pfxcmp(pname, "ColorCMYK") == 0
159 || pfxcmp(pname, "ColorSpaceCMYK") == 0)
160 && (pfxcmp(name, "Cyan") == 0
161 || pfxcmp(name, "Magenta") == 0
162 || pfxcmp(name, "Yellow") == 0
163 || pfxcmp(name, "Black") == 0))
164 return MXML_REAL;
165
166 if ((pfxcmp(pname, "FileInformation") == 0
167 || pfxcmp(pname, "Header") == 0)
168 && (pfxcmp(name, "Creator") == 0
169 || pfxcmp(name, "CreationDate") == 0
170 || pfxcmp(name, "Description") == 0))
171 return MXML_OPAQUE; /* Don't split strings up */
172
173 if ((pfxcmp(pname, "IlluminationOptions") == 0
174 || pfxcmp(pname, "TristimulusSpec") == 0
175 || pfxcmp(pname, "ColorSpaceSpecificationSpectrumTristimulus") == 0)
176 && (pfxcmp(name, "Illuminant") == 0
177 || pfxcmp(name, "Observer") == 0
178 || pfxcmp(name, "FieldOfView") == 0))
179 return MXML_OPAQUE; /* Don't split strings up */
180
181
182 return MXML_TEXT;
183 }
184
185
sax_cb(mxml_node_t * node,mxml_sax_event_t event,void * data)186 void sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *data) {
187 namedc *p = (namedc *)data;
188
189 if (event == MXML_SAX_ELEMENT_OPEN) {
190 const char *pname = mxmlGetElement(node);
191
192 if (pfxcmp(pname, "Resources") == 0) {
193 //printf("~1 open of Resources\n");
194 p->indata = 1;
195 }
196
197 if ((p->options & NAMEDC_OP_NODATA) == 0
198 || !p->indata) {
199 //printf("~1 options 0x%x, indata %d, retaining '%s'\n",p->options,p->indata,pname);
200 //a1logd(p->log, 4, "sax_cb: retaining %s\n",pname);
201 mxmlRetain(node);
202 }
203
204 } else if (event == MXML_SAX_DIRECTIVE) {
205 mxmlRetain(node);
206 } else if (event == MXML_SAX_DATA) {
207 /* If the parent was retained, then retain this data node as well. */
208 if (mxmlGetRefCount(mxmlGetParent(node)) > 1) {
209 mxmlRetain(node);
210 }
211 } else if (event == MXML_SAX_ELEMENT_CLOSE) {
212 const char *pname = mxmlGetElement(node);
213 if(pfxcmp(pname, "Resources") == 0) {
214 //printf("~1 close of Resources\n");
215 p->indata = 0;
216 }
217 }
218 }
219
220
221 /* Return a temporary key with prefix */
pfx(namedc * p,char * key)222 char *pfx(namedc *p, char *key) {
223 if (p->pfx[0] == '\000')
224 return key;
225
226 snprintf(p->prefix, 200, "%s:%s", p->pfx, key);
227
228 return p->prefix;
229 }
230
231 /* Return a temporary key with prefix at front and after each '/' */
pfxp(namedc * p,char * path)232 char *pfxp(namedc *p, char *path) {
233 char *cp = p->prefix;
234 char *sp, *ep;
235 int plen, slen;
236
237 if (p->pfx[0] == '\000')
238 return path;
239
240 plen = strlen(p->pfx);
241
242 /* Figure out the first source key */
243 sp = path;
244 if ((ep = strchr(sp, '/')) != NULL) {
245 slen = ep - sp;
246 ep++; /* Point beyond '/' */
247 } else {
248 slen = strlen(sp);
249 ep = sp + slen; /* point at '\000' */
250 }
251
252 for (;;) {
253 if ((NAMEDC_PLEN -1 -(cp - p->prefix)) < (plen + 1 + slen))
254 break; /* No room for prefix + path element */
255
256 strcpy(cp, p->pfx);
257 cp += plen;
258 *cp++ = ':';
259 strncpy(cp, sp, slen);
260 cp += slen;
261
262 /* Figure out the next source key */
263 sp = ep;
264
265 if ((ep = strchr(sp, '/')) != NULL) {
266 slen = ep - sp;
267 ep++; /* Point beyond '/' */
268 } else {
269 slen = strlen(sp);
270 ep = sp + slen; /* point at '\000' */
271 }
272
273 if (slen <= 0) /* No more path elements */
274 break;
275
276 if ((NAMEDC_PLEN -1 -(cp - p->prefix)) < 1)
277 break; /* No room for '/' */
278 *cp++ = '/';
279 }
280 *cp++ = '\000';
281
282 return p->prefix;
283 }
284
285 /* Return a temporary key with suffix */
sfx(namedc * p,char * key)286 char *sfx(namedc *p, char *key) {
287 if (p->pfx[0] == '\000')
288 return key;
289
290 snprintf(p->prefix, 200, "%s:%s", key, p->pfx);
291
292 return p->prefix;
293 }
294
295 /* Read in a namedc from a cxf2 or cxf3 file */
296 /* Return nz on error */
read_cxf(namedc * p,const char * filename,int options)297 static int read_cxf(namedc *p, const char *filename, int options) {
298 FILE *fp;
299 char *pfilename;
300 mxml_node_t *tree, *cxf, *pnode, *node;
301 const char *attr, *name;
302 int cxf2 = 0;
303 int i, j;
304
305 a1logd(p->log, 1, "read_cxf: file '%s' options 0x%x\n",filename,options);
306
307 if (p->filename == NULL) {
308 if ((pfilename = strdup(filename)) == NULL) {
309 snprintf(p->err, NAMEDC_ERRL, "Malloc of filename failed");
310 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
311 return p->errc = 2;
312 }
313 clear_namedc(p);
314 p->filename = pfilename;
315 }
316 p->options = options;
317
318 if ((fp = fopen(p->filename, "r")) == NULL) {
319 snprintf(p->err, NAMEDC_ERRL, "Opening XML file '%s' failed",p->filename);
320 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
321 return p->errc = 1;
322 }
323
324 #ifdef NEVER
325 tree = mxmlLoadFile(NULL, fp, type_cb);
326 #else
327 p->indata = 0;
328 tree = mxmlSAXLoadFd(NULL, fileno(fp), type_cb, sax_cb, (void *)p);
329 #endif
330 fclose(fp);
331
332 if (tree == NULL) {
333 snprintf(p->err, NAMEDC_ERRL, "Parsing XML file '%s' failed",p->filename);
334 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
335 return p->errc = 1;
336 }
337
338 if ((cxf = mxmlFindElement(tree, tree, NULL, NULL, NULL, MXML_DESCEND_FIRST)) == NULL
339 || mxmlGetType(cxf) != MXML_ELEMENT) {
340 snprintf(p->err, NAMEDC_ERRL, "Failed to find top element in '%s'",p->filename);
341 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
342 mxmlDelete(tree);
343 return p->errc = 1;
344 }
345 name = cxf->value.element.name;
346
347 if ((attr = strchr(cxf->value.element.name, ':')) != NULL) {
348 int len = attr - name;
349 if (len > 99)
350 len = 99;
351 strncpy(p->pfx, name, len);
352 p->pfx[len] = '\000';
353 } else {
354 p->pfx[0] = '\000';
355 }
356
357 a1logd(p->log, 4, "read_cxf: prefix '%s'\n",p->pfx);
358
359 if (strcmp(name, pfx(p, "CxF")) != 0) {
360 snprintf(p->err, NAMEDC_ERRL, "Top element not called CxF in '%s'",p->filename);
361 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
362 mxmlDelete(tree);
363 return p->errc = 1;
364 }
365
366 /* Check that its CxF3 */
367 if ((attr = mxmlElementGetAttr(cxf, sfx(p, "xmlns"))) == NULL) {
368 snprintf(p->err, NAMEDC_ERRL, "Failed to find CxF attribute %s in '%s'",sfx(p, "xmlns"), p->filename);
369 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
370 mxmlDelete(tree);
371 return p->errc = 1;
372 }
373
374 if (strcmp(attr, "http://colorexchangeformat.com/CxF3-core") != 0) {
375 if (strcmp(attr, "http://colorexchangeformat.com/v2") != 0) {
376 snprintf(p->err, NAMEDC_ERRL, "File '%s' is not CxF format",p->filename);
377 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
378 mxmlDelete(tree);
379 return p->errc = 1;
380 } else {
381 cxf2 = 1;
382 a1logd(p->log, 1, "read_cxf: This is a CxF2 file\n");
383 }
384 } else {
385 a1logd(p->log, 1, "read_cxf: This is a CxF3 file\n");
386 }
387
388 /* Grab the description */
389 if (cxf2) {
390 if ((node = mxmlFindPathNode(cxf, pfxp(p,"Palette"))) == NULL
391 || (name = mxmlElementGetAttr(node, "PaletteName")) == NULL)
392 name = NULL;
393
394 if (name == NULL)
395 name = p->filename;
396 if ((p->description = strdup(name)) == NULL) {
397 snprintf(p->err, NAMEDC_ERRL, "Malloc of description string failed");
398 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
399 mxmlDelete(tree);
400 return p->errc = 2;
401 }
402 a1logd(p->log, 2, "read_cxf: description '%s'\n",p->description);
403
404 p->hash = do_hash2(p->filename, p->description);
405
406 } else { /* else cxf3 */
407 if ((node = mxmlFindPathNode(cxf, pfxp(p,"FileInformation/Description"))) == NULL)
408 name = NULL;
409 else
410 name = mxmlGetOpaque(node);
411 if (name == NULL)
412 name = p->filename;
413 if ((p->description = strdup(name)) == NULL) {
414 snprintf(p->err, NAMEDC_ERRL, "Malloc of description string failed");
415 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
416 mxmlDelete(tree);
417 return p->errc = 2;
418 }
419 a1logd(p->log, 2, "read_cxf: description '%s'\n",p->description);
420
421 p->hash = do_hash2(p->filename, p->description);
422 }
423
424 if ((p->options & NAMEDC_OP_NODATA) == 0) {
425 char *SampleKey = NULL;
426 char *SampleNameKey = NULL;
427 char *SampleLabKey = NULL;
428 char *SampleXYZKey = NULL;
429 char *SampleCMYKKey = NULL;
430 const char *colorSpecification = NULL; /* cxf3 ColorSpecification of Lab or XYZ */
431
432 if (cxf2) {
433 /* Locate the Palette/ColorSet node */
434 if ((pnode = mxmlFindPathNode(cxf, pfxp(p,"Palette/ColorSet"))) == NULL) {
435 snprintf(p->err, NAMEDC_ERRL, "Failed to find Resources/ObjectCollection in '%s'",p->filename);
436 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
437 mxmlDelete(tree);
438 return p->errc = 1;
439 }
440 SampleKey = "Color";
441 SampleNameKey = "ColorName";
442 SampleLabKey = "ColorSpaceCIELab";
443 SampleXYZKey = "ColorSpaceCIEXYZ";
444 SampleCMYKKey = "ColorSpaceCMYK";
445
446 } else {
447 /* Locate the Resources/ObjectCollection node */
448 if ((pnode = mxmlFindPathNode(cxf, pfxp(p,"Resources/ObjectCollection"))) == NULL) {
449 snprintf(p->err, NAMEDC_ERRL, "Failed to find Resources/ObjectCollection in '%s'",p->filename);
450 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
451 mxmlDelete(tree);
452 return p->errc = 1;
453 }
454 SampleKey = "Object";
455 SampleNameKey = "Name";
456 SampleLabKey = "ColorCIELab";
457 SampleXYZKey = "ColorCIEXYZ";
458 SampleCMYKKey = "ColorCMYK";
459 }
460
461 /* - - - - - - - - - - - - */
462 /* Read all the colors. */
463 /* Start with first node */
464 node = mxmlFindElement(pnode, pnode, pfx(p,SampleKey), NULL, NULL, MXML_DESCEND_FIRST);
465 for (i = 0; node != NULL; i++) {
466 int j;
467 mxml_node_t *ppvals, *pvals, *val;
468 const char *name;
469 double Lab[3];
470 int Lab_v = 0;
471 double dev[MAX_CHAN];
472 int dev_n = 0;
473 icColorSpaceSignature devSig = icMaxEnumData;
474
475 if (mxmlGetType(node) != MXML_ELEMENT) {
476 a1logd(p->log, DEB6, "read_cxf: skipping non element node type %d\n",mxmlGetType(node));
477 goto next;
478 }
479 a1logd(p->log, DEB6, "read_cxf: read node '%s'\n",node->value.element.name);
480
481 if (strcmp(node->value.element.name, pfx(p,SampleKey)) != 0) {
482 a1logd(p->log, DEB6, "read_cxf: skipping non %s node\n",SampleKey);
483 goto next;
484 }
485
486 if (!cxf2) {
487 /* Get the ObjectType attribute of Object */
488 if ((attr = mxmlElementGetAttr(node, "ObjectType")) == NULL) {
489 a1logd(p->log, DEB6, "read_cxf: skipping node without ObjectType\n");
490 goto next; /* Skip this one */
491 }
492
493 #ifdef NEVER
494 /* Check the attribute value (Should we ?) */
495 /* Known values are: Standard, Color */
496 /* May be Trial, Target, Substrate, Colorant, ... ? */
497 if (strcmp(attr, "Standard") != 0
498 && strcmp(attr, "Color") != 0) {
499 a1logd(p->log, DEB6, "read_cxf: skipping node with ObjectType = '%s'\n",attr);
500 goto next; /* Skip this one */
501 }
502 #endif
503 }
504
505 if ((attr = mxmlElementGetAttr(node, SampleNameKey)) == NULL) {
506 a1logd(p->log, DEB6, "read_cxf: skipping without %s\n",SampleNameKey);
507 goto next; /* Skip this one */
508 }
509 name = attr;
510
511 a1logd(p->log, DEB4, "read_cxf: got color %d name '%s'\n",i,name);
512
513 if (!cxf2) {
514 if ((ppvals = mxmlFindElement(node, node, pfx(p,"ColorValues"), NULL, NULL, MXML_DESCEND_FIRST)) == NULL) {
515 a1logd(p->log, DEB4, "read_cxf: no reference ColorValuesname - skipping\n");
516 goto next;
517 }
518 } else {
519 ppvals = node;
520 }
521
522 if ((p->options & NAMEDC_OP_NOSPEC) == 0) {
523 /* ~~~~9999 should look for spectral */
524 }
525
526 /* See if there is ColorCIELab */
527 if ((pvals = mxmlFindElement(ppvals, ppvals, pfx(p,SampleLabKey), NULL, NULL, MXML_DESCEND_FIRST)) != NULL) {
528 char *key[3] = { "L", "A", "B" };
529
530 /* Check which color specification is being used with Lab */
531 if (!cxf2 && colorSpecification == NULL) {
532 colorSpecification = mxmlElementGetAttr(pvals, "ColorSpecification");
533 }
534
535 for (j = 0; j < 3; j++) {
536 if ((val = mxmlFindElement(pvals, pvals, pfx(p, key[j]), NULL, NULL, MXML_DESCEND_FIRST)) == NULL) {
537 a1logd(p->log, DEB6, "read_cxf: failed to find ColorCIELab component %s\n",key[j]);
538 break; /* oops */
539 }
540 Lab[j] = mxmlGetReal(val);
541 a1logd(p->log, DEB6, "read_cxf: got ColorCIELab component %s value %f\n",key[j],Lab[j]);
542 }
543 if (j >= 3)
544 Lab_v = 1;
545 }
546
547 if (!Lab_v) {
548
549 a1logd(p->log, DEB6, "read_cxf: no valid ColorCIELab value, look for XYZ\n");
550
551 /* See if there is ColorCIEXYZ instead */
552 if ((pvals = mxmlFindElement(ppvals, ppvals, pfx(p,SampleXYZKey), NULL, NULL, MXML_DESCEND_FIRST)) != NULL) {
553 char *key[3] = { "X", "Y", "Z" };
554
555 /* Check which color specification is being used with Lab */
556 if (!cxf2 && colorSpecification == NULL) {
557 colorSpecification = mxmlElementGetAttr(pvals, "ColorSpecification");
558 }
559
560 for (j = 0; j < 3; j++) {
561 if ((val = mxmlFindElement(pvals, pvals, pfx(p, key[j]), NULL, NULL, MXML_DESCEND_FIRST)) == NULL) {
562 a1logd(p->log, DEB6, "read_cxf: failed to find ColorCIEXYZ component %s\n",key[j]);
563 break; /* oops */
564 }
565 Lab[j] = mxmlGetReal(val);
566 a1logd(p->log, DEB6, "read_cxf: got ColorCIEXYZ component %s value %f\n",key[j],Lab[j]);
567 }
568 if (j >= 3) {
569 icmXYZ2Lab(&icmD50, Lab, Lab);
570 Lab_v = 1;
571 }
572 }
573 }
574
575 if (!Lab_v) {
576 a1logd(p->log, DEB6, "read_cxf: no CIE value found - skipping color\n");
577 goto next;
578 }
579
580 /* Read any device color values */
581 if (!cxf2) {
582 if ((ppvals = mxmlFindElement(node, node, pfx(p,"DeviceColorValues"), NULL, NULL, MXML_DESCEND_FIRST)) != NULL) {
583
584 a1logd(p->log, DEB4, "read_cxf: got DeviceColorValues\n");
585 }
586 }
587
588 if (ppvals != NULL) {
589
590 // ~~99 could add read of other device values here
591
592 /* See if there is ColorCMYK */
593 if ((pvals = mxmlFindElement(ppvals, ppvals, pfx(p,SampleCMYKKey), NULL, NULL, MXML_DESCEND_FIRST)) != NULL) {
594 char *key[4] = { "Cyan", "Magenta", "Yellow", "Black" };
595
596 a1logd(p->log, DEB4, "read_cxf: got ColorCMYK\n");
597 for (j = 0; j < 4; j++) {
598 if ((val = mxmlFindElement(pvals, pvals, pfx(p, key[j]), NULL, NULL, MXML_DESCEND_FIRST)) == NULL) {
599 a1logd(p->log, DEB6, "read_cxf: failed to find ColorCMYK component %s\n",key[j]);
600 break; /* oops */
601 }
602 dev[j] = mxmlGetReal(val);
603 a1logd(p->log, DEB6, "read_cxf: got ColorCMYK component %s value %f\n",key[j],dev[j]);
604 }
605 if (j >= 4) {
606 dev_n = j;
607 devSig = icSigCmykData;
608 }
609 }
610 }
611
612 /* Add an entry */
613 if (p->count >= p->count_a) {
614 unsigned int count_n;
615 count_n = p->count_a + 4096/sizeof(nce);
616
617 a1logd(p->log, 8, "read_cxf: increasing data array size to %d\n",count_n);
618 if ((p->data = recalloc(p->data, p->count_a, sizeof(nce), count_n, sizeof(nce))) == NULL) {
619 snprintf(p->err, NAMEDC_ERRL, "Malloc of data size %d failed",p->count_a);
620 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
621 mxmlDelete(tree);
622 return p->errc = 2;
623 }
624 p->count_a = count_n;
625 }
626 clear_nce(&p->data[p->count]);
627
628 if ((p->data[p->count].name = strdup(name)) == NULL) {
629 snprintf(p->err, NAMEDC_ERRL, "Malloc of color name string failed");
630 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
631 mxmlDelete(tree);
632 return p->errc = 2;
633 }
634
635 if (Lab_v) {
636 p->data[p->count].Lab[0] = Lab[0];
637 p->data[p->count].Lab[1] = Lab[1];
638 p->data[p->count].Lab[2] = Lab[2];
639 p->data[p->count].Lab_v = 1;
640 }
641
642 if (dev_n > 0 && devSig != icMaxEnumData) {
643 for (j = 0; j < dev_n; j++)
644 p->data[p->count].dev[j] = dev[j];
645 p->data[p->count].dev_n = dev_n;
646 p->data[p->count].devSig = devSig;
647 } else {
648 p->data[p->count].dev_n = 0;
649 p->data[p->count].devSig = icMaxEnumData;
650 }
651
652 a1logd(p->log, 8, "read_cxf: added color %d\n",p->count);
653
654 p->count++;
655
656 next:; /* Next color */
657 node = mxmlGetNextSibling(node);
658 }
659
660 /* - - - - - - - - - - - - */
661 /* Read extra information */
662 if (cxf2) {
663 /* Grab the creator */
664 if ((node = mxmlFindPathNode(cxf, pfxp(p,"Preamble/Header/Creator"))) == NULL)
665 name = NULL;
666 else
667 name = mxmlGetOpaque(node);
668
669 if (name == NULL)
670 name = "[Unknown]";
671 if ((p->creator = strdup(name)) == NULL) {
672 snprintf(p->err, NAMEDC_ERRL, "Malloc of creator string failed");
673 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
674 mxmlDelete(tree);
675 return p->errc = 2;
676 }
677 a1logd(p->log, 2, "read_cxf: creator '%s'\n",p->creator);
678
679 /* Grab the illuminant type */
680 if ((node = mxmlFindPathNode(cxf, pfxp(p,"Palette/ColorSet/CollectionColorSpaceSpecification/ColorSpaceSpecificationSpectrumTristimulus/IlluminationOptions/Illuminant"))) == NULL
681 || (name = mxmlGetOpaque(node)) == NULL) {
682 a1logd(p->log, 2, "read_cxf: failed to locate Illuminant - assuming D50\n");
683 p->ill = icxIT_D50;
684 } else {
685 if (strcmp(name, "Illuminant_A") == 0)
686 p->ill = icxIT_A;
687 else if (strcmp(name, "Illuminant_D50") == 0)
688 p->ill = icxIT_D50;
689 else if (strcmp(name, "Illuminant_D55") == 0)
690 p->ill = icxIT_D55;
691 else if (strcmp(name, "Illuminant_D65") == 0)
692 p->ill = icxIT_D65;
693 else if (strcmp(name, "Illuminant_E") == 0)
694 p->ill = icxIT_E;
695 else {
696 a1logd(p->log, 2, "read_cxf: Illuminant '%s' unrecognised\n",name);
697 p->ill = icxIT_D50;
698 }
699 }
700 a1logd(p->log, 2, "read_cxf: illuminant '%s'\n",icm2str(icmIlluminant, p->ill));
701
702 /* Grab the observer type */
703 if ((node = mxmlFindPathNode(cxf, pfxp(p,"Palette/ColorSet/CollectionColorSpaceSpecification/ColorSpaceSpecificationSpectrumTristimulus/FieldOfView"))) == NULL
704 || (name = mxmlGetOpaque(node)) == NULL) {
705 a1logd(p->log, 2, "read_cxf: failed to locate FieldOfView - assuming 2 degree\n");
706 p->obs = icxOT_CIE_1931_2;
707 } else {
708 if (strcmp(name, "FieldOfView_2_Degree") == 0)
709 p->obs = icxOT_CIE_1931_2;
710 else if (strcmp(name, "FieldOfView_10_Degree") == 0)
711 p->obs = icxOT_CIE_1964_10;
712 else {
713 a1logd(p->log, 2, "read_cxf: Illuminant '%s' unrecognised\n",name);
714 p->obs = icxOT_CIE_1931_2;
715 }
716 }
717 a1logd(p->log, 2, "read_cxf: observer '%s'\n",icm2str(icmStandardObserver, p->obs));
718
719 } else {
720 int found_io = 0;
721
722 /* Grab the creator */
723 if ((node = mxmlFindPathNode(cxf, pfxp(p,"FileInformation/Creator"))) == NULL)
724 name = NULL;
725 else
726 name = mxmlGetOpaque(node);
727
728 if (name == NULL)
729 name = "[Unknown]";
730 if ((p->creator = strdup(name)) == NULL) {
731 snprintf(p->err, NAMEDC_ERRL, "Malloc of creator string failed");
732 a1logd(p->log, 1, "read_cxf: %s\n",p->err);
733 mxmlDelete(tree);
734 return p->errc = 2;
735 }
736 a1logd(p->log, 2, "read_cxf: creator '%s'\n",p->creator);
737
738 if (colorSpecification != NULL) {
739 a1logd(p->log, 2, "read_cxf: colorSpecification '%s'\n",colorSpecification);
740 }
741
742 /* Look through the color specifications and find the one that matches */
743 /* the Lab or XYZ color specification */
744 pnode = mxmlFindPathNode(cxf, pfxp(p,"Resources/ColorSpecificationCollection/ColorSpecification"));
745 while (pnode != NULL) {
746 name = mxmlElementGetAttr(pnode, "Id");
747
748 if (colorSpecification == NULL
749 || (name != NULL && strcmp(name, colorSpecification) == 0)) {
750
751 a1logd(p->log, 2, "read_cxf: found node matching colorSpecification '%s'\n",colorSpecification);
752
753 /* Grab the illuminant type */
754 if ((node = mxmlFindPathNode(pnode, pfxp(p,"TristimulusSpec/Illuminant"))) == NULL
755 || (name = mxmlGetOpaque(node)) == NULL) {
756 a1logd(p->log, 2, "read_cxf: failed to locate Illuminant - assuming D50\n");
757 p->ill = icxIT_D50;
758 } else {
759 if (strcmp(name, "A") == 0)
760 p->ill = icxIT_A;
761 else if (strcmp(name, "D50") == 0)
762 p->ill = icxIT_D50;
763 else if (strcmp(name, "D55") == 0)
764 p->ill = icxIT_D55;
765 else if (strcmp(name, "D65") == 0)
766 p->ill = icxIT_D65;
767 else if (strcmp(name, "E") == 0)
768 p->ill = icxIT_E;
769 else {
770 a1logd(p->log, 2, "read_cxf: Illuminant '%s' unrecognised\n",name);
771 p->ill = icxIT_D50;
772 }
773 }
774 a1logd(p->log, 2, "read_cxf: illuminant '%s'\n",icm2str(icmIlluminant, p->ill));
775
776 /* Grab the first observer type */
777 if ((node = mxmlFindPathNode(cxf, pfxp(p,"Resources/ColorSpecificationCollection/ColorSpecification/TristimulusSpec/Observer"))) == NULL
778 || (name = mxmlGetOpaque(node)) == NULL) {
779 a1logd(p->log, 2, "read_cxf: failed to locate Observer - assuming 2 degree\n");
780 p->obs = icxOT_CIE_1931_2;
781 } else {
782 if (strcmp(name, "2_Degree") == 0)
783 p->obs = icxOT_CIE_1931_2;
784 else if (strcmp(name, "10_Degree") == 0)
785 p->obs = icxOT_CIE_1964_10;
786 else {
787 a1logd(p->log, 2, "read_cxf: Illuminant '%s' unrecognised\n",name);
788 p->obs = icxOT_CIE_1931_2;
789 }
790 }
791 a1logd(p->log, 2, "read_cxf: observer '%s'\n",icm2str(icmStandardObserver, p->obs));
792 found_io = 1;
793 break;
794 }
795 pnode = mxmlGetNextSibling(pnode);
796 }
797
798 if (!found_io) {
799 p->ill = icxIT_D50;
800 p->obs = icxOT_CIE_1931_2;
801 a1logd(p->log, 2, "read_cxf: failed to locate ColorSpecification - assuming D50 2 degree observer\n");
802 }
803
804 /* Other values of interest:
805 Resources/ColorSpecificationCollection/ColorSpecification/ColorSpecification/MeasurementSpec/MeasurementType ie. "Spectrum_Reflectance"
806 Resources/ColorSpecificationCollection/ColorSpecification/ColorSpecification/MeasurementSpec/CalibrationStandard ie. "GMDI" for Gretag Macbeth Calibration, "XRGA" for X-Rite Graphic Arts Standard.
807 Resources/ColorSpecificationCollection/ColorSpecification/ColorSpecification/MeasurementSpec/Device/DeviceFilter ie. "Filter_None" "Filter_UVExcluded",
808 */
809 }
810 }
811
812 mxmlDelete(tree);
813
814 a1logd(p->log, 1, "read_cxf: done - %d colors\n",p->count);
815 return 0;
816 }
817
818 /* Read in a namedc from a ICC file */
819 /* Return nz on error */
read_icc(namedc * p,const char * filename,int options)820 static int read_icc(namedc *p, const char *filename, int options) {
821 char *pfilename;
822 icmFile *fp;
823 icc *icco;
824
825 a1logd(p->log, 1, "read_icc: file '%s' options 0x%x\n",filename,options);
826
827 if (p->filename == NULL) {
828 if ((pfilename = strdup(filename)) == NULL) {
829 snprintf(p->err, NAMEDC_ERRL, "Malloc of filename failed");
830 a1logd(p->log, 1, "read_icc: %s\n",p->err);
831 return p->errc = 2;
832 }
833 clear_namedc(p);
834 p->filename = pfilename;
835 }
836 p->options = options;
837
838 /* Open up the file for reading */
839 if ((fp = new_icmFileStd_name(p->filename,"r")) == NULL) {
840 snprintf(p->err, NAMEDC_ERRL, "Opening ICC file '%s' failed",p->filename);
841 a1logd(p->log, 1, "read_icc: %s\n",p->err);
842 return p->errc = 1;
843 }
844
845 if ((icco = new_icc()) == NULL) {
846 snprintf(p->err, NAMEDC_ERRL, "Creation ICC object failed");
847 a1logd(p->log, 1, "read_icc: %s\n",p->err);
848 fp->del(fp);
849 return p->errc = 1;
850 }
851
852 if (icco->read(icco, fp, 0) != 0) {
853 snprintf(p->err, NAMEDC_ERRL, "Failed to read '%s'\n",p->filename);
854 a1logd(p->log, 1, "read_icc: %s\n",p->err);
855 icco->del(icco);
856 fp->del(fp);
857 return p->errc = 1;
858 }
859
860 if (icco->header->deviceClass != icSigNamedColorClass) {
861 snprintf(p->err, NAMEDC_ERRL, "Not a named color profile");
862 a1logd(p->log, 1, "read_icc: %s\n",p->err);
863 icco->del(icco);
864 fp->del(fp);
865 return p->errc = 1;
866 }
867
868 if (icco->header->pcs != icSigXYZData
869 && icco->header->pcs != icSigLabData) {
870 snprintf(p->err, NAMEDC_ERRL, "Unrecognised PCS");
871 a1logd(p->log, 1, "read_icc: %s\n",p->err);
872 icco->del(icco);
873 fp->del(fp);
874 return p->errc = 1;
875 }
876
877 if (icco->find_tag(icco, icSigNamedColor2Tag) != 0) {
878 snprintf(p->err, NAMEDC_ERRL, "Can't find ncl2 tag");
879 a1logd(p->log, 1, "read_icc: %s\n",p->err);
880 icco->del(icco);
881 fp->del(fp);
882 return p->errc = 1;
883 }
884
885 {
886 icmTextDescription *txd;
887
888 /* Try and read the tag from the file */
889 if ((txd = (icmTextDescription *)icco->read_tag(icco, icSigProfileDescriptionTag)) == NULL) {
890 snprintf(p->err, NAMEDC_ERRL, "No description tag");
891 a1logd(p->log, 1, "read_icc: %s\n",p->err);
892 icco->del(icco);
893 fp->del(fp);
894 return p->errc = 1;
895 }
896
897 if ((p->description = strdup(txd->desc)) == NULL) {
898 snprintf(p->err, NAMEDC_ERRL, "Malloc of description string failed");
899 a1logd(p->log, 1, "read_icc: %s\n",p->err);
900 icco->del(icco);
901 fp->del(fp);
902 return p->errc = 2;
903 }
904 a1logd(p->log, 2, "read_icc: description '%s'\n",p->description);
905 }
906
907 p->hash = do_hash2(p->filename, p->description);
908
909 if ((p->options & NAMEDC_OP_NODATA) == 0) {
910 icmNamedColor *tag;
911 char name[3 * 32];
912 double Lab[3];
913 int Lab_v = 0;
914 double dev[MAX_CHAN];
915 int dev_n = 0;
916 icColorSpaceSignature devSig = icMaxEnumData;
917 int i, j;
918
919 #ifdef NEVER
920 /* See if there is a measurementType tag */
921 icmMeasurement *meastag;
922 if ((meastag = (icmMeasurement *)icco->read_tag(icco, icSigMeasurementTag)) != NULL) {
923 p->ill = meastag->illuminant;
924 p->obs = meastag->observer;
925 a1logd(p->log, 2, "read_cxf: assuming D50 illuminant and 2 degree observer\n");
926 } else {
927 p->ill = icIlluminantD50;
928 p->obs = icStdObs1931TwoDegrees;
929 }
930 a1logd(p->log, 2, "read_cxf: illuminant '%s'\n",icm2str(icmIlluminant, p->ill));
931 a1logd(p->log, 2, "read_cxf: observer '%s'\n",icm2str(icmStandardObserver, p->obs));
932 #endif
933
934 if ((tag = (icmNamedColor *)icco->read_tag(icco, icSigNamedColor2Tag)) == NULL) {
935 snprintf(p->err, NAMEDC_ERRL, "Can't read ncl2 tag");
936 a1logd(p->log, 1, "read_icc: %s\n",p->err);
937 icco->del(icco);
938 fp->del(fp);
939 return p->errc = 1;
940 }
941
942 for (i = 0; i < tag->count; i++) {
943
944 /* Get the name */
945 strcpy(name, tag->prefix);
946 strcat(name, tag->data[i].root);
947 strcat(name, tag->suffix);
948
949 a1logd(p->log, DEB4, "read_icc: got color %d name '%s'\n",i,name);
950
951 if (icco->header->pcs == icSigXYZData) {
952 Lab[0] = tag->data[i].pcsCoords[0];
953 Lab[1] = tag->data[i].pcsCoords[1];
954 Lab[2] = tag->data[i].pcsCoords[2];
955 icmXYZ2Lab(&icmD50, Lab, Lab);
956
957 } else { /* Lab */
958 Lab[0] = tag->data[i].pcsCoords[0];
959 Lab[1] = tag->data[i].pcsCoords[1];
960 Lab[2] = tag->data[i].pcsCoords[2];
961 }
962 Lab_v = 1;
963 a1logd(p->log, DEB6, "read_icc: got ColorCIELab value %s\n",icmPdv(3, Lab));
964
965 /* Add any device space */
966 devSig = icco->header->colorSpace;
967 dev_n = icmCSSig2nchan(devSig);
968 for (j = 0; j < dev_n; j++)
969 dev[j] = tag->data[i].deviceCoords[j];
970
971 a1logd(p->log, DEB6, "read_icc: got %s value %s\n",
972 icm2str(icmColorSpaceSignature, devSig), icmPdv(dev_n, dev));
973
974 /* Add an entry */
975 if (p->count >= p->count_a) {
976 unsigned int count_n;
977 count_n = p->count_a + 4096/sizeof(nce);
978
979 a1logd(p->log, 8, "read_icc: increasing data array size to %d\n",count_n);
980 if ((p->data = recalloc(p->data, p->count_a, sizeof(nce), count_n, sizeof(nce))) == NULL) {
981 snprintf(p->err, NAMEDC_ERRL, "Malloc of data size %d failed",p->count_a);
982 a1logd(p->log, 1, "read_icc: %s\n",p->err);
983 icco->del(icco);
984 fp->del(fp);
985 return p->errc = 2;
986 }
987 p->count_a = count_n;
988 }
989 clear_nce(&p->data[p->count]);
990
991 if ((p->data[p->count].name = strdup(name)) == NULL) {
992 snprintf(p->err, NAMEDC_ERRL, "Malloc of color name string failed");
993 a1logd(p->log, 1, "read_icc: %s\n",p->err);
994 icco->del(icco);
995 fp->del(fp);
996 return p->errc = 2;
997 }
998
999 if (Lab_v) {
1000 p->data[p->count].Lab[0] = Lab[0];
1001 p->data[p->count].Lab[1] = Lab[1];
1002 p->data[p->count].Lab[2] = Lab[2];
1003 p->data[p->count].Lab_v = 1;
1004 }
1005
1006 if (dev_n > 0 && devSig != icMaxEnumData) {
1007 for (j = 0; j < dev_n; j++)
1008 p->data[p->count].dev[j] = dev[j];
1009 p->data[p->count].dev_n = dev_n;
1010 p->data[p->count].devSig = devSig;
1011 } else {
1012 p->data[p->count].dev_n = 0;
1013 p->data[p->count].devSig = icMaxEnumData;
1014 }
1015
1016 a1logd(p->log, 8, "read_icc: added color %d\n",p->count);
1017
1018 p->count++;
1019
1020 next:; /* Next color */
1021 }
1022 }
1023
1024 icco->del(icco);
1025 fp->del(fp);
1026
1027 return 0;
1028 }
1029
1030 /* Read any format named color files */
1031 /* Return nz on error */
read_nc(namedc * p,const char * filename,int options)1032 static int read_nc(namedc *p, const char *filename, int options) {
1033 int rv;
1034
1035 if ((p->format == 0 || p->format == 1)
1036 && (rv = read_cxf(p, filename, options)) == 0) {
1037 p->format = 1;
1038 return rv;
1039 }
1040
1041 if ((p->format == 0 || p->format == 2)
1042 && (rv = read_icc(p, filename, options)) == 0) {
1043 p->format = 2;
1044 return rv;
1045 }
1046
1047 /* Try other file types here */
1048
1049 return rv;
1050 }
1051
1052
1053 /* Return the index of the best mataching color, -1 on error. */
1054 /* Lab[] is assumed to be D50, 2 degree standard observer based CIE value, */
1055 /* and the spec value should only be provided if this is a reflective or */
1056 /* transmissive measurement, NULL if emissive. */
1057 /* If named color library is expects other than D50, 2 degree, then */
1058 /* it will use the spectral value if not NULL, or chromatically */
1059 /* adapt the Lab value. */
1060 /* deType == 0 DE76 */
1061 /* deType == 1 DE94 */
1062 /* deType == 2 DE2000 */
1063 /* if de != NULL, return the delta E */
match(struct _namedc * p,double * de,double * pLab,xspect * rspect,int deType)1064 int match(struct _namedc *p, double *de, double *pLab, xspect *rspect, int deType) {
1065 int i, bix = -1;
1066 double bde = 1e99;
1067 double Lab[3];
1068
1069 if (p->filename == NULL) { /* We haven't been opened */
1070 snprintf(p->err, NAMEDC_ERRL, "We haven't been opened");
1071 a1logd(p->log, 1, "match: %s\n",p->err);
1072 return -1;
1073 }
1074
1075 /* If the colors haven't been read yet, read them now */
1076 if (p->data == NULL || (p->options & NAMEDC_OP_NODATA)) {
1077 if (read_nc(p, NULL, (p->options & ~NAMEDC_OP_NODATA))) {
1078 a1logd(p->log, 1, "match: on demand data load failed with '%s'\n",p->err);
1079 return -1;
1080 }
1081 a1logd(p->log, 1, "match: after loading there are %d colors\n",p->count);
1082 }
1083
1084 icmCpy3(Lab, pLab);
1085
1086 if (p->ill != icIlluminantD50 || p->obs != icStdObs1931TwoDegrees) {
1087 if (rspect != NULL) {
1088
1089 if (p->sp2cie == NULL) {
1090 if ((p->sp2cie = new_xsp2cie(p->ill, NULL, p->obs, NULL, icSigLabData, 0)) == NULL) {
1091 snprintf(p->err, NAMEDC_ERRL, "creating spetral conversion failed");
1092 a1logd(p->log, 1, "match: %s\n",p->err);
1093 return -1;
1094
1095 }
1096 }
1097
1098 /* Convert spectrum to the XYZ we want */
1099 p->sp2cie->convert(p->sp2cie, Lab, rspect);
1100
1101 } else {
1102 if (p->chrom[0][0] <= -1e38) {
1103 double wXYZ[3];
1104
1105 // Special case this for a consistent value with ICC profiles
1106 if (p->obs == icxOT_CIE_1931_2 && p->ill == icxIT_D65) {
1107 icmCpy3(wXYZ, icmD65_ary3);
1108 } else {
1109 xsp2cie *tt;
1110 xspect ts;
1111 // Get the XYZ of the given white point for the illuminant and observer
1112 if ((tt = new_xsp2cie(p->ill, NULL, p->obs, NULL, icSigXYZData, 0)) == NULL) {
1113 snprintf(p->err, NAMEDC_ERRL, "creating spetral conversion failed");
1114 a1logd(p->log, 1, "match: %s\n",p->err);
1115 return -1;
1116 }
1117 if (standardIlluminant(&ts, icxIT_E, 0.0)) {
1118 snprintf(p->err, NAMEDC_ERRL, "match: creating E type spectrum failed");
1119 a1logd(p->log, 1, "match: %s\n",p->err);
1120 return -1;
1121 }
1122 tt->convert(tt, wXYZ, &ts);
1123 tt->del(tt);
1124 }
1125 icmAry2XYZ(p->dXYZ, wXYZ);
1126
1127 /* Chreate chromatic adapation matrix from D50 to named color */
1128 icmChromAdaptMatrix(ICM_CAM_BRADFORD, p->dXYZ, icmD50, p->chrom);
1129 }
1130 icmLab2XYZ(&icmD50, Lab, pLab);
1131 icmMulBy3x3(Lab, p->chrom, Lab);
1132 icmXYZ2Lab(&p->dXYZ, Lab, Lab);
1133 }
1134 }
1135
1136 if (deType == 0) {
1137 for (i = 0; i < p->count; i++) {
1138 double de = icmLabDEsq(Lab, p->data[i].Lab);
1139 if (de < bde) {
1140 bde = de;
1141 bix = i;
1142 }
1143 }
1144
1145 } else if (deType == 1) {
1146 for (i = 0; i < p->count; i++) {
1147 double de = icmCIE94sq(Lab, p->data[i].Lab);
1148 if (de < bde) {
1149 bde = de;
1150 bix = i;
1151 }
1152 }
1153
1154 } else if (deType == 2) {
1155 for (i = 0; i < p->count; i++) {
1156 double de = icmCIE2Ksq(Lab, p->data[i].Lab);
1157 if (de < bde) {
1158 bde = de;
1159 bix = i;
1160 }
1161 }
1162 } else {
1163 snprintf(p->err, NAMEDC_ERRL, "Unnown deType %d",deType);
1164 a1logd(p->log, 1, "match: %s\n",p->err);
1165 return -1;
1166 }
1167
1168 if (bix < 0) {
1169 snprintf(p->err, NAMEDC_ERRL, "No colors to match against");
1170 a1logd(p->log, 1, "match: %s\n",p->err);
1171 return -1;
1172 }
1173 if (de != NULL) {
1174 *de = sqrt(bde);
1175 }
1176 return bix;
1177 }
1178
1179 /* Free an entry */
clear_nce(nce * p)1180 static void clear_nce(nce *p) {
1181 if (p != NULL) {
1182 if (p->name != NULL)
1183 free(p->name);
1184 p->name = NULL;
1185 if (p->sp != NULL)
1186 free(p->sp);
1187 p->sp = NULL;
1188 }
1189 }
1190
1191 /* Free the contents */
clear_namedc(namedc * p)1192 static void clear_namedc(namedc *p) {
1193 if (p != NULL) {
1194 int i;
1195 if (p->filename != NULL)
1196 free(p->filename);
1197 p->filename = NULL;
1198 if (p->creator != NULL)
1199 free(p->creator);
1200 p->creator = NULL;
1201 if (p->description != NULL)
1202 free(p->description);
1203 p->description = NULL;
1204 if (p->data != NULL) {
1205 for (i = 0; i < p->count; i++)
1206 clear_nce(&p->data[i]);
1207 free(p->data);
1208 p->data = NULL;
1209 }
1210 p->count = 0;
1211 }
1212 }
1213
1214 /* Delete it */
del_namedc(namedc * p)1215 static void del_namedc(namedc *p) {
1216 if (p != NULL) {
1217 clear_namedc(p);
1218 p->log = del_a1log(p->log);
1219 if (p->sp2cie != NULL)
1220 p->sp2cie->del(p->sp2cie);
1221 free(p);
1222 }
1223 }
1224
1225 /* Allocate a new, uninitialised namedc */
1226 /* Note thate black and white points aren't allocated */
new_namedc(a1log * log)1227 namedc *new_namedc(a1log *log) {
1228 namedc *p;
1229
1230 a1logd(log, 1, "new_cxf\n");
1231
1232 if ((p = (namedc *)calloc(1, sizeof(namedc))) == NULL) {
1233 a1logd(p->log, 1, "new_cxf: calloc failed\n");
1234 return NULL;
1235 }
1236
1237 p->log = new_a1log_d(log);
1238
1239 /* Init method pointers */
1240 p->del = del_namedc;
1241 p->read_cxf = read_cxf;
1242 p->read_icc = read_icc;
1243 p->read = read_nc;
1244 p->match = match;
1245
1246 p->chrom[0][0] = -1e38;
1247
1248 return p;
1249 }
1250
1251
1252 /* =========================================================================== */
1253
1254 #else /* STANDALONE_TEST */
1255
usage(char * diag,...)1256 void usage(char *diag, ...) {
1257 fprintf(stderr,"Test namedc library\n");
1258 fprintf(stderr,"Author: Graeme W. Gill\n");
1259 if (diag != NULL) {
1260 va_list args;
1261 fprintf(stderr," Diagnostic: ");
1262 va_start(args, diag);
1263 vfprintf(stderr, diag, args);
1264 va_end(args);
1265 fprintf(stderr,"\n");
1266 }
1267 fprintf(stderr,"usage: namedc [-v level] infile\n");
1268 fprintf(stderr," -D level Debug level 1-9\n");
1269 exit(1);
1270 }
1271
1272 int
main(int argc,char * argv[])1273 main(int argc, char *argv[]) {
1274 int fa,nfa; /* argument we're looking at */
1275 char inname[MAXNAMEL+1];
1276 int debug = 0, i;
1277 namedc *p;
1278
1279 /* Process the arguments */
1280 for(fa = 1;fa < argc;fa++) {
1281 nfa = fa; /* skip to nfa if next argument is used */
1282 if (argv[fa][0] == '-') { /* Look for any flags */
1283 char *na = NULL; /* next argument after flag, null if none */
1284
1285 if (argv[fa][2] != '\000')
1286 na = &argv[fa][2]; /* next is directly after flag */
1287 else {
1288 if ((fa+1) < argc) {
1289 if (argv[fa+1][0] != '-') {
1290 nfa = fa + 1;
1291 na = argv[nfa]; /* next is seperate non-flag argument */
1292 }
1293 }
1294 }
1295
1296 if (argv[fa][1] == '?')
1297 usage(NULL);
1298
1299 /* Debug level */
1300 else if (argv[fa][1] == 'D') {
1301 fa = nfa;
1302 if (na != NULL)
1303 debug = atoi(na);
1304 }
1305
1306 else
1307 usage("Unknown option '%c'",argv[fa][1]);
1308 }
1309 else
1310 break;
1311 }
1312
1313 if (fa >= argc || argv[fa][0] == '-') usage("Missing input filename");
1314 strncpy(inname,argv[fa++],MAXNAMEL); inname[MAXNAMEL] = '\000';
1315
1316 g_log->debug = debug;
1317
1318 if ((p = new_namedc(g_log)) == NULL)
1319 error("new_namedc failed\n");
1320
1321 /* Open non-data */
1322 if (p->read(p, inname, NAMEDC_OP_NODATA)) {
1323 error("read failed with '%s'\n",p->err);
1324 }
1325
1326 printf("Palette is '%s'\n",p->description);
1327 {
1328 double Lab[3], de;
1329 int ix;
1330
1331 Lab[0] = 50.0;
1332 Lab[1] = 20.0;
1333 Lab[2] = -10.0;
1334
1335 if ((ix = p->match(p, &de, Lab, NULL, 0)) < 0)
1336 error(" match failed with '%s'\n",p->err);
1337 printf("Matched color '%s' with DE76 %f\n",p->data[ix].name,de);
1338
1339 if ((ix = p->match(p, &de, Lab, NULL, 1)) < 0)
1340 error(" match failed with '%s'\n",p->err);
1341 printf("Matched color '%s' with DE94 %f\n",p->data[ix].name,de);
1342
1343 if ((ix = p->match(p, &de, Lab, NULL, 2)) < 0)
1344 error(" match failed with '%s'\n",p->err);
1345 printf("Matched color '%s' with DE00 %f\n",p->data[ix].name,de);
1346 }
1347
1348 #ifdef NEVER
1349 printf("Loaded %d colors\n",p->count);
1350 for (i = 0; i < p->count; i++) {
1351 printf("Color %d name '%s' = %f %f %f\n",
1352 i, p->data[i].name, p->data[i].Lab[0], p->data[i].Lab[1], p->data[i].Lab[2]);
1353 if (p->data[i].devSig != icMaxEnumData) {
1354 printf("%s = %s\n", icm2str(icmColorSpaceSignature, p->data[i].devSig),
1355 icmPdv(p->data[i].dev_n, p->data[i].dev));
1356 }
1357 }
1358 #endif
1359 p->del(p);
1360
1361 return 0;
1362 }
1363
1364
1365
1366 #endif /* STANDALONE_TEST */
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391