xref: /plan9/sys/src/cmd/gs/icclib/icc.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 
2 /*
3  * International Color Consortium Format Library (icclib)
4  * For ICC profile version 3.4
5  *
6  * Author:  Graeme W. Gill
7  * Date:    2002/04/22
8  * Version: 2.02
9  *
10  * Copyright 1997 - 2002 Graeme W. Gill
11  * See Licence.txt file for conditions of use.
12  */
13 
14 /*
15  * TTBD:
16  *
17  *      Add a "warning mode" to file reading, in which file format
18  *      errors are ignored where possible, rather than generating
19  *      a fatal error (see ICM_STRICT #define).
20  *
21  *      NameColor Dump doesn't handle device space correctly -
22  *	    should use appropriate interpretation in case device is Lab etc.
23  *
24  *      Should recognise & honour unicode 0xFFFE endian marker.
25  *      Should generate it on writing too ?
26  *
27  *		Should fix all write_number failure errors to indicate failed value.
28  *		(Partially implemented - need to check all write_number functions)
29  *
30  *		Make write fail error messages be specific on which element failed.
31  *
32  *		Should add named color space lookup function support.
33  *
34  *		Should probably reject reading or writing profiles with majv != 2 ?
35  *
36  *      Would be nice to add generic ability to add new tag type handling,
37  *      so that the base library doesn't need to be modified (ie. VideoCardGamma) ?
38  *
39  *		Need to add DeviceSettings and OutputResponse tags to bring up to
40  *		ICC.1:1998-09 [started but not complete]
41  *
42  */
43 
44 #undef ICM_STRICT	/* Not fully implimented - switch off strict checking of file format */
45 
46 /* Trial: Make the default grid points of the Lab clut be symetrical about */
47 /*        a/b 0.0, and also make L = 100.0 fall on a grid point. */
48 /*        This seems a good idea. */
49 
50 #define SYMETRICAL_DEFAULT_LAB_RANGE
51 
52 /*
53  * Change History:
54  *
55  * 2.02
56  *      Merged rename of [u]int64 to icm[Ui][I]nt64 (to work around
57  *      AIX 5.1L portability bug) from Raph Levien.
58  *
59  *      Fixed stray , in icmLookupOrder structure definition (from Dan Coby)
60  *
61  * 2.01
62  *		Change TextDescription code to not barf if #undef ICM_STRICT and
63  *      Apple scriptcode not padded to 67 bytes.
64  *
65  *      Add get_ranges() method to all Lu types, not just LuLut.
66  *      Fix bug in PCS overide logic that was causing
67  *		reverse conversions to apply the wrong conversion.
68  *
69  *      Added Delta E convenience functions icmLabDE() and
70  *      icmCIE94() etc.
71  *
72  *		Merged Raph Levien's cleanups, to quiet gcc warnings.
73  *
74  *      Merged another couple of warning cleanups from Jouk Jansen.
75  *
76  * 2.00
77  *      Change absolute conversion to be white point only, and use
78  *      Bradford transform by default. (ie. we are now ignoring the
79  *      comment in section 6.4.22 of the 1998 spec. about the
80  *      media black point being used for absolute colorimetry,
81  *      ignoring the recommendation on page 118 in section E.5,
82  *      and are taking up the recommendation on page 124 in section
83  *      E.16 that a more sophisticated chromatic adaption model be used.)
84  *
85  *      This is for better compatibility with other CMM's, and to
86  *      improve the results when using simple links between
87  *      profiles with non-D50 white points. Standard profiles
88  *      like sRGB will also be more accurate when interpreted
89  *      with absolute colorimetric intent.
90  *      This will cause some slight incompatibilty with previous
91  *      versions of icclib.
92  *
93  *      Added ColorSync 2.5 specific VideoCardGamma tag support
94  *      (from Neil Okamoto)
95  *
96  * 1.31
97  *      Added file I/O class to allow substitution of alternative ICC profile
98  *      file access. Provide standard file class instance, and memory image
99  *      instance of file I/O class as default and example.
100  *      Added an optional new_icc_a() object creator, that takes a memory
101  *      allocator class instance. This allows an alternate memory heap to
102  *      be used with the icc class.
103  *      Renamed object free() methods to del() for more consistency with new().
104  *
105  * 1.30
106  *      Added workaround for reading some Adobe profiles with confused header DateTime.
107  *      Enhanced tag allocate() methods so that they can resize allocations.
108  *      Enhanced icmLut_set_tables() to access grid points in a cache friendly order.
109  *      Fixed bug in check_icc_legal() that caused bogus errors, removed
110  *      uneccessary static declarations in icc.h, and fixed a bug in
111  *      icmTable_lookup_bwd() that effected both accuracy and speed. (Thanks to Andrei Frolov)
112  *      Removed icmAbsoluteColorimetricXYZ intent, and replaced it with
113  *      a PCS overide capability. This adds a new parameter to get_luobj()
114  *      Added Lab translations of some XYZ "dump" strings.
115  *      Fix memory leak after failed tag read + rename_tag function
116  *      + shared library support changes. (Thanks to Carles Llopis).
117  *		Changed all the public 2str utility routines to a single function
118  *      that can be used to interpret an enumeration or tag in a human
119  *      readable form.
120  *
121  * 1.23
122  *      Fixed important bug in Lut read/write. The matrix values had their
123  *      rows and columns switched. Not many profiles exercise this code.
124  *      Thanks to David Gillman for discovering this problem.
125  *      Fixup compiler complains about illegal enum values for icmCurveStyle,
126  *      and icmDataStyle. Malloc memory icmLut_lookup_clut_nl for gw[], so that
127  *      it is more friendly to systems with a limited stack. (Thanks to Dave White)
128  *
129  * 1.22	99/11/11 Snapshot of current code.
130  *      Added more hooks to support inherited implementation of
131  *      color conversion, used in Argyll to support reversing
132  *      multi-dimentional table lookups.
133  *      Cleaned up color conversion code to make it easier to follow.
134  *      Adding simplex interpolation for non-Lab style input space interpolation.
135  *      Fix Sun misalignment and realloc problems (Thanks to Allan N. Hessenflow)
136  *      Fixed endian problem with Unicode on read and write.
137  *      Expanded icmTextDescription_dump() to do hex dump of Unicode and ScriptCode.
138  *      Changed over to ICC.1:1998-09 .h file.
139  *      Started implementing ICC.1:1998-09, but not complete yet!
140  *
141  * 1.21	99/2/14
142  *     	After re-reading Michael Bourgoin's 1998 SIGGRAPH notes,
143  *      I have consolidated the Lut input index, and table value encodings.
144  *      The default set_tables() scaling has been adjusted appropriately
145  *      for this correction of Lab encoding.
146  *      Trying to create an 8 bit XYZ Lut will now fail if icclib helper
147  *      functions are used to create it.
148  *
149  * 1.20	99/2/7
150  *      Added profile color lookup functon.
151  *      Added set_tables() support.
152  *      Various bug fixes and enhancements.
153  */
154 
155 #include <stdio.h>
156 #include <stdlib.h>
157 #include <stdarg.h>
158 #include <sys/types.h>
159 #include <string.h>
160 #include <ctype.h>
161 #include <math.h>
162 #include <time.h>
163 #ifdef __sun
164 #include <unistd.h>
165 #endif
166 #if defined(__IBMC__) && defined(_M_IX86)
167 #include <float.h>
168 #endif
169 #include "icc.h"
170 
171 /* ========================================================== */
172 /* Default system interface object implementations */
173 
174 /* Standard Stream file I/O icmFile compatible class */
175 /* Note that this uses malloc, so replace class if */
176 /* you need a different memory allocator. */
177 
178 /* Set current position to offset. Return 0 on success, nz on failure. */
icmFileStd_seek(icmFile * pp,long int offset)179 static int icmFileStd_seek(
180 icmFile *pp,
181 long int offset
182 ) {
183 	icmFileStd *p = (icmFileStd *)pp;
184 
185 	return fseek(p->fp, offset, SEEK_SET);
186 }
187 
188 /* Read count items of size length. Return number of items successfully read. */
icmFileStd_read(icmFile * pp,void * buffer,size_t size,size_t count)189 static size_t icmFileStd_read(
190 icmFile *pp,
191 void *buffer,
192 size_t size,
193 size_t count
194 ) {
195 	icmFileStd *p = (icmFileStd *)pp;
196 
197 	return fread(buffer, size, count, p->fp);
198 }
199 
200 /* write count items of size length. Return number of items successfully written. */
icmFileStd_write(icmFile * pp,void * buffer,size_t size,size_t count)201 static size_t icmFileStd_write(
202 icmFile *pp,
203 void *buffer,
204 size_t size,
205 size_t count
206 ) {
207 	icmFileStd *p = (icmFileStd *)pp;
208 
209 	return fwrite(buffer, size, count, p->fp);
210 }
211 
212 
213 /* flush all write data out to secondary storage. Return nz on failure. */
icmFileStd_flush(icmFile * pp)214 static int icmFileStd_flush(
215 icmFile *pp
216 ) {
217 	icmFileStd *p = (icmFileStd *)pp;
218 
219 	return fflush(p->fp);
220 }
221 
222 /* we're done with the file object, return nz on failure */
icmFileStd_delete(icmFile * pp)223 static int icmFileStd_delete(
224 icmFile *pp
225 ) {
226 	icmFileStd *p = (icmFileStd *)pp;
227 
228 	if (p->doclose != 0) {
229 		if (fclose(p->fp) != 0)
230 			return 2;
231 	}
232 
233 	free(p);
234 	return 0;
235 }
236 
237 /* Create icmFile given a (binary) FILE* */
new_icmFileStd_fp(FILE * fp)238 icmFile *new_icmFileStd_fp(
239 FILE *fp
240 ) {
241 	icmFileStd *p;
242 	if ((p = (icmFileStd *) calloc(1,sizeof(icmFileStd))) == NULL)
243 		return NULL;
244 	p->seek  = icmFileStd_seek;
245 	p->read  = icmFileStd_read;
246 	p->write = icmFileStd_write;
247 	p->flush = icmFileStd_flush;
248 	p->del   = icmFileStd_delete;
249 
250 	p->fp = fp;
251 	p->doclose = 0;
252 
253 	return (icmFile *)p;
254 }
255 
256 /* Create icmFile given a file name */
new_icmFileStd_name(char * name,char * mode)257 icmFile *new_icmFileStd_name(
258 char *name,
259 char *mode
260 ) {
261 	FILE *fp;
262 	icmFile *p;
263 #if defined(O_BINARY)
264 	char nmode[50];
265 #endif
266 
267 	if ((fp = fopen(name,mode)) == NULL)
268 		return NULL;
269 
270 #if defined(O_BINARY)
271 	strcpy(nmode, mode);
272 	strcat(nmode, "b");
273 	if ((fp = freopen(name, nmode, fp)) == NULL)
274 		return NULL;
275 #endif
276 
277 	p = new_icmFileStd_fp(fp);
278 
279 	if (p != NULL) {
280 		icmFileStd *pp = (icmFileStd *)p;
281 		pp->doclose = 1;
282 	}
283 	return p;
284 }
285 
286 /* ------------------------------------------------- */
287 /* Memory image icmFile compatible class */
288 /* Note that this uses malloc, so replace class if */
289 /* you need a different memory allocator. */
290 
291 /* Set current position to offset. Return 0 on success, nz on failure. */
icmFileMem_seek(icmFile * pp,long int offset)292 static int icmFileMem_seek(
293 icmFile *pp,
294 long int offset
295 ) {
296 	icmFileMem *p = (icmFileMem *)pp;
297 	unsigned char *np;
298 
299 	np = p->start + offset;
300 	if (np < p->start || np >= p->end)
301 		return 1;
302 	p->cur = np;
303 	return 0;
304 }
305 
306 /* Read count items of size length. Return number of items successfully read. */
icmFileMem_read(icmFile * pp,void * buffer,size_t size,size_t count)307 static size_t icmFileMem_read(
308 icmFile *pp,
309 void *buffer,
310 size_t size,
311 size_t count
312 ) {
313 	icmFileMem *p = (icmFileMem *)pp;
314 	size_t len;
315 
316 	len = size * count;
317 	if ((p->cur + len) >= p->end) {		/* Too much */
318 		if (size > 0)
319 			count = (p->end - p->cur)/size;
320 		else
321 			count = 0;
322 	}
323 	len = size * count;
324 	if (len > 0)
325 		memcpy (buffer, p->cur, len);
326 	p->cur += len;
327 	return count;
328 }
329 
330 /* write count items of size length. Return number of items successfully written. */
icmFileMem_write(icmFile * pp,void * buffer,size_t size,size_t count)331 static size_t icmFileMem_write(
332 icmFile *pp,
333 void *buffer,
334 size_t size,
335 size_t count
336 ) {
337 	icmFileMem *p = (icmFileMem *)pp;
338 	size_t len;
339 
340 	len = size * count;
341 	if ((p->cur + len) >= p->end) {		/* Too much */
342 		if (size > 0)
343 			count = (p->end - p->cur)/size;
344 		else
345 			count = 0;
346 	}
347 	len = size * count;
348 	if (len > 0)
349 		memcpy (p->cur, buffer, len);
350 	p->cur += len;
351 	return count;
352 }
353 
354 
355 /* flush all write data out to secondary storage. Return nz on failure. */
icmFileMem_flush(icmFile * pp)356 static int icmFileMem_flush(
357 icmFile *pp
358 ) {
359 	return 0;
360 }
361 
362 /* we're done with the file object, return nz on failure */
icmFileMem_delete(icmFile * pp)363 static int icmFileMem_delete(
364 icmFile *pp
365 ) {
366 	icmFileMem *p = (icmFileMem *)pp;
367 
368 	free(p);
369 	return 0;
370 }
371 
372 /* Create a memory image file access class */
new_icmFileMem(void * base,size_t length)373 icmFile *new_icmFileMem(
374 void *base,				/* Pointer to base of memory buffer */
375 size_t length			/* Number of bytes in buffer */
376 ) {
377 	icmFileMem *p;
378 	if ((p = (icmFileMem *) calloc(1,sizeof(icmFileMem))) == NULL)
379 		return NULL;
380 	p->seek  = icmFileMem_seek;
381 	p->read  = icmFileMem_read;
382 	p->write = icmFileMem_write;
383 	p->flush = icmFileMem_flush;
384 	p->del   = icmFileMem_delete;
385 
386 	p->cur = p->start = base;
387 	p->end = p->start + length;
388 
389 	return (icmFile *)p;
390 }
391 
392 /* ------------------------------------------------- */
393 /* Standard Heap allocator icmAlloc compatible class */
394 /* Just call the standard system function */
395 
icmAllocStd_malloc(struct _icmAlloc * pp,size_t size)396 static void *icmAllocStd_malloc(
397 struct _icmAlloc *pp,
398 size_t size
399 ) {
400 	return malloc(size);
401 }
402 
icmAllocStd_calloc(struct _icmAlloc * pp,size_t num,size_t size)403 static void *icmAllocStd_calloc(
404 struct _icmAlloc *pp,
405 size_t num,
406 size_t size
407 ) {
408 	return calloc(num, size);
409 }
410 
icmAllocStd_realloc(struct _icmAlloc * pp,void * ptr,size_t size)411 static void *icmAllocStd_realloc(
412 struct _icmAlloc *pp,
413 void *ptr,
414 size_t size
415 ) {
416 	return realloc(ptr, size);
417 }
418 
419 
icmAllocStd_free(struct _icmAlloc * pp,void * ptr)420 static void icmAllocStd_free(
421 struct _icmAlloc *pp,
422 void *ptr
423 ) {
424 	free(ptr);
425 }
426 
427 /* we're done with the AllocStd object */
icmAllocStd_delete(icmAlloc * pp)428 static void icmAllocStd_delete(
429 icmAlloc *pp
430 ) {
431 	icmAllocStd *p = (icmAllocStd *)pp;
432 
433 	free(p);
434 }
435 
436 /* Create icmAllocStd */
new_icmAllocStd()437 icmAlloc *new_icmAllocStd() {
438 	icmAllocStd *p;
439 	if ((p = (icmAllocStd *) calloc(1,sizeof(icmAllocStd))) == NULL)
440 		return NULL;
441 	p->malloc  = icmAllocStd_malloc;
442 	p->calloc  = icmAllocStd_calloc;
443 	p->realloc = icmAllocStd_realloc;
444 	p->free    = icmAllocStd_free;
445 	p->del     = icmAllocStd_delete;
446 
447 	return (icmAlloc *)p;
448 }
449 
450 /* ========================================================== */
451 /* Conversion support functions */
452 /* Convert between ICC storage types and native C types */
453 /* Write routine return non-zero if numbers can't be represented */
454 
455 /* Unsigned */
read_UInt8Number(char * p)456 static unsigned int read_UInt8Number(char *p) {
457 	unsigned int rv;
458 	rv = (unsigned int)((ORD8 *)p)[0];
459 	return rv;
460 }
461 
write_UInt8Number(unsigned int d,char * p)462 static int write_UInt8Number(unsigned int d, char *p) {
463 	if (d > 255)
464 		return 1;
465 	((ORD8 *)p)[0] = (ORD8)d;
466 	return 0;
467 }
468 
read_UInt16Number(char * p)469 static unsigned int read_UInt16Number(char *p) {
470 	unsigned int rv;
471 	rv = 256 * (unsigned int)((ORD8 *)p)[0]
472 	   +       (unsigned int)((ORD8 *)p)[1];
473 	return rv;
474 }
475 
write_UInt16Number(unsigned int d,char * p)476 static int write_UInt16Number(unsigned int d, char *p) {
477 	if (d > 65535)
478 		return 1;
479 	((ORD8 *)p)[0] = (ORD8)(d >> 8);
480 	((ORD8 *)p)[1] = (ORD8)(d);
481 	return 0;
482 }
483 
read_UInt32Number(char * p)484 static unsigned int read_UInt32Number(char *p) {
485 	unsigned int rv;
486 	rv = 16777216 * (unsigned int)((ORD8 *)p)[0]
487 	   +    65536 * (unsigned int)((ORD8 *)p)[1]
488 	   +      256 * (unsigned int)((ORD8 *)p)[2]
489 	   +            (unsigned int)((ORD8 *)p)[3];
490 	return rv;
491 }
492 
write_UInt32Number(unsigned int d,char * p)493 static int write_UInt32Number(unsigned int d, char *p) {
494 	((ORD8 *)p)[0] = (ORD8)(d >> 24);
495 	((ORD8 *)p)[1] = (ORD8)(d >> 16);
496 	((ORD8 *)p)[2] = (ORD8)(d >> 8);
497 	((ORD8 *)p)[3] = (ORD8)(d);
498 	return 0;
499 }
500 
read_UInt64Number(icmUint64 * d,char * p)501 static void read_UInt64Number(icmUint64 *d, char *p) {
502 	d->h = 16777216 * (unsigned int)((ORD8 *)p)[0]
503 	     +    65536 * (unsigned int)((ORD8 *)p)[1]
504 	     +      256 * (unsigned int)((ORD8 *)p)[2]
505 	     +            (unsigned int)((ORD8 *)p)[3];
506 	d->l = 16777216 * (unsigned int)((ORD8 *)p)[4]
507 	     +    65536 * (unsigned int)((ORD8 *)p)[5]
508 	     +      256 * (unsigned int)((ORD8 *)p)[6]
509 	     +            (unsigned int)((ORD8 *)p)[7];
510 }
511 
write_UInt64Number(icmUint64 * d,char * p)512 static int write_UInt64Number(icmUint64 *d, char *p) {
513 	((ORD8 *)p)[0] = (ORD8)(d->h >> 24);
514 	((ORD8 *)p)[1] = (ORD8)(d->h >> 16);
515 	((ORD8 *)p)[2] = (ORD8)(d->h >> 8);
516 	((ORD8 *)p)[3] = (ORD8)(d->h);
517 	((ORD8 *)p)[4] = (ORD8)(d->l >> 24);
518 	((ORD8 *)p)[5] = (ORD8)(d->l >> 16);
519 	((ORD8 *)p)[6] = (ORD8)(d->l >> 8);
520 	((ORD8 *)p)[7] = (ORD8)(d->l);
521 	return 0;
522 }
523 
read_U8Fixed8Number(char * p)524 static double read_U8Fixed8Number(char *p) {
525 	ORD32 o32;
526 	o32 = 256 * (ORD32)((ORD8 *)p)[0]		/* Read big endian 16 bit unsigned */
527         +       (ORD32)((ORD8 *)p)[1];
528 	return (double)o32/256.0;
529 }
530 
write_U8Fixed8Number(double d,char * p)531 static int write_U8Fixed8Number(double d, char *p) {
532 	ORD32 o32;
533 	d = d * 256.0 + 0.5;
534 	if (d >= 65536.0)
535 		return 1;
536 	if (d < 0.0)
537 		return 1;
538 	o32 = (ORD32)d;
539 	((ORD8 *)p)[0] = (ORD8)((o32) >> 8);
540 	((ORD8 *)p)[1] = (ORD8)((o32));
541 	return 0;
542 }
543 
read_U16Fixed16Number(char * p)544 static double read_U16Fixed16Number(char *p) {
545 	ORD32 o32;
546 	o32 = 16777216 * (ORD32)((ORD8 *)p)[0]		/* Read big endian 32 bit unsigned */
547         +    65536 * (ORD32)((ORD8 *)p)[1]
548 	    +      256 * (ORD32)((ORD8 *)p)[2]
549 	    +            (ORD32)((ORD8 *)p)[3];
550 	return (double)o32/65536.0;
551 }
552 
write_U16Fixed16Number(double d,char * p)553 static int write_U16Fixed16Number(double d, char *p) {
554 	ORD32 o32;
555 	d = d * 65536.0 + 0.5;
556 	if (d >= 4294967296.0)
557 		return 1;
558 	if (d < 0.0)
559 		return 1;
560 	o32 = (ORD32)d;
561 	((ORD8 *)p)[0] = (ORD8)((o32) >> 24);
562 	((ORD8 *)p)[1] = (ORD8)((o32) >> 16);
563 	((ORD8 *)p)[2] = (ORD8)((o32) >> 8);
564 	((ORD8 *)p)[3] = (ORD8)((o32));
565 	return 0;
566 }
567 
568 
569 #ifdef NEVER	/* Not currently used anywhere */
570 
571 /* Signed numbers */
read_SInt8Number(char * p)572 static int read_SInt8Number(char *p) {
573 	int rv;
574 	rv = (int)((INR8 *)p)[0];
575 	return rv;
576 }
577 
write_SInt8Number(int d,char * p)578 static int write_SInt8Number(int d, char *p) {
579 	if (d > 127)
580 		return 1;
581 	else if (d < -128)
582 		return 1;
583 	((INR8 *)p)[0] = (INR8)d;
584 	return 0;
585 }
586 
read_SInt16Number(char * p)587 static int read_SInt16Number(char *p) {
588 	int rv;
589 	rv = 256 * (int)((INR8 *)p)[0]
590 	   +       (int)((ORD8 *)p)[1];
591 	return rv;
592 }
593 
write_SInt16Number(int d,char * p)594 static int write_SInt16Number(int d, char *p) {
595 	if (d > 32767)
596 		return 1;
597 	else if (d < -32768)
598 		return 1;
599 	((INR8 *)p)[0] = (INR8)(d >> 8);
600 	((ORD8 *)p)[1] = (ORD8)(d);
601 	return 0;
602 }
603 
604 #endif /* NEVER */
605 
read_SInt32Number(char * p)606 static int read_SInt32Number(char *p) {
607 	int rv;
608 	rv = 16777216 * (int)((INR8 *)p)[0]
609 	   +    65536 * (int)((ORD8 *)p)[1]
610 	   +      256 * (int)((ORD8 *)p)[2]
611 	   +            (int)((ORD8 *)p)[3];
612 	return rv;
613 }
614 
write_SInt32Number(int d,char * p)615 static int write_SInt32Number(int d, char *p) {
616 	((INR8 *)p)[0] = (INR8)(d >> 24);
617 	((ORD8 *)p)[1] = (ORD8)(d >> 16);
618 	((ORD8 *)p)[2] = (ORD8)(d >> 8);
619 	((ORD8 *)p)[3] = (ORD8)(d);
620 	return 0;
621 }
622 
623 #ifdef NEVER /* Not currently used anywhere */
624 
read_SInt64Number(icmInt64 * d,char * p)625 static void read_SInt64Number(icmInt64 *d, char *p) {
626 	d->h = 16777216 * (int)((INR8 *)p)[0]
627 	     +    65536 * (int)((ORD8 *)p)[1]
628 	     +      256 * (int)((ORD8 *)p)[2]
629 	     +            (int)((ORD8 *)p)[3];
630 	d->l = 16777216 * (unsigned int)((ORD8 *)p)[4]
631 	     +    65536 * (unsigned int)((ORD8 *)p)[5]
632 	     +      256 * (unsigned int)((ORD8 *)p)[6]
633 	     +            (unsigned int)((ORD8 *)p)[7];
634 }
635 
write_SInt64Number(icmInt64 * d,char * p)636 static int write_SInt64Number(icmInt64 *d, char *p) {
637 	((INR8 *)p)[0] = (INR8)(d->h >> 24);
638 	((ORD8 *)p)[1] = (ORD8)(d->h >> 16);
639 	((ORD8 *)p)[2] = (ORD8)(d->h >> 8);
640 	((ORD8 *)p)[3] = (ORD8)(d->h);
641 	((ORD8 *)p)[4] = (ORD8)(d->l >> 24);
642 	((ORD8 *)p)[5] = (ORD8)(d->l >> 16);
643 	((ORD8 *)p)[6] = (ORD8)(d->l >> 8);
644 	((ORD8 *)p)[7] = (ORD8)(d->l);
645 	return 0;
646 }
647 
648 #endif /* NEVER */
649 
read_S15Fixed16Number(char * p)650 static double read_S15Fixed16Number(char *p) {
651 	INR32 i32;
652 	i32 = 16777216 * (INR32)((INR8 *)p)[0]		/* Read big endian 32 bit signed */
653         +    65536 * (INR32)((ORD8 *)p)[1]
654 	    +      256 * (INR32)((ORD8 *)p)[2]
655 	    +            (INR32)((ORD8 *)p)[3];
656 	return (double)i32/65536.0;
657 }
658 
write_S15Fixed16Number(double d,char * p)659 static int write_S15Fixed16Number(double d, char *p) {
660 	INR32 i32;
661 	d = ceil(d * 65536.0);		/* Beware! (int)(d + 0.5) doesn't work! */
662 	if (d >= 2147483648.0)
663 		return 1;
664 	if (d < -2147483648.0)
665 		return 1;
666 	i32 = (INR32)d;
667 	((INR8 *)p)[0] = (INR8)((i32) >> 24);		/* Write big endian 32 bit signed */
668 	((ORD8 *)p)[1] = (ORD8)((i32) >> 16);
669 	((ORD8 *)p)[2] = (ORD8)((i32) >> 8);
670 	((ORD8 *)p)[3] = (ORD8)((i32));
671 	return 0;
672 }
673 
674 /* PCS encoded numbers */
675 
676 /* 16 bit XYZ  - value range 0.0 - 1.9997 */
read_PCSXYZ16Number(char * p)677 static double read_PCSXYZ16Number(char *p) {
678 	ORD32 o32;
679 	o32 = 256 * (ORD32)((ORD8 *)p)[0]		/* Read big endian 16 bit unsigned */
680         +       (ORD32)((ORD8 *)p)[1];
681 	return (double)o32/32768.0;
682 }
683 
write_PCSXYZ16Number(double d,char * p)684 static int write_PCSXYZ16Number(double d, char *p) {
685 	ORD32 o32;
686 	d = d * 32768.0 + 0.5;
687 	if (d >= 65536.0)
688 		return 1;
689 	if (d < 0.0)
690 		return 1;
691 	o32 = (ORD32)d;
692 	((ORD8 *)p)[0] = (ORD8)((o32) >> 8);
693 	((ORD8 *)p)[1] = (ORD8)((o32));
694 	return 0;
695 }
696 
697 #ifdef NEVER /* Not currently used */
698 
699 /* L part of 8 bit Lab - value range 0.0 - 100.0 */
read_PCSL8Number(char * p)700 static double read_PCSL8Number(char *p) {
701 	ORD32 o32;
702 	o32 = (ORD32)((ORD8 *)p)[0];		/* Read big endian 8 bit unsigned */
703 	return (double)o32/2.550;
704 }
705 
write_PCSL8Number(double d,char * p)706 static int write_PCSL8Number(double d, char *p) {
707 	ORD32 o32;
708 	d = d * 2.550 + 0.5;
709 	if (d >= 256.0)
710 		return 1;
711 	if (d < 0.0)
712 		return 1;
713 	o32 = (ORD32)d;
714 	((ORD8 *)p)[0] = (ORD8)((o32));
715 	return 0;
716 }
717 
718 /* ab part of 8 bit Lab - value range -128.0 - 127.0 */
read_PCSab8Number(char * p)719 static double read_PCSab8Number(char *p) {
720 	ORD32 o32;
721 	o32 = (ORD32)((ORD8 *)p)[0];	/* Read big endian 8 bit unsigned */
722 	return (double)o32-128.0;
723 }
724 
write_PCSab8Number(double d,char * p)725 static int write_PCSab8Number(double d, char *p) {
726 	ORD32 o32;
727 	d = (d+128.0) + 0.5;
728 	if (d >= 256.0)
729 		return 1;
730 	if (d < 0.0)
731 		return 1;
732 	o32 = (ORD32)d;
733 	((ORD8 *)p)[0] = (ORD8)((o32));
734 	return 0;
735 }
736 
737 #endif /* NEVER */
738 
739 /* L part of 16 bit Lab - value range 0.0 - 100.0 */
read_PCSL16Number(char * p)740 static double read_PCSL16Number(char *p) {
741 	ORD32 o32;
742 	o32 = 256 * (ORD32)((ORD8 *)p)[0]		/* Read big endian 16 bit unsigned */
743         +       (ORD32)((ORD8 *)p)[1];
744 	return (double)o32/652.800;				/* 0xff00/100.0 */
745 }
746 
write_PCSL16Number(double d,char * p)747 static int write_PCSL16Number(double d, char *p) {
748 	ORD32 o32;
749 	d = d * 652.800 + 0.5;
750 	if (d >= 65536.0)
751 		return 1;
752 	if (d < 0.0)
753 		return 1;
754 	o32 = (ORD32)d;
755 	((ORD8 *)p)[0] = (ORD8)((o32) >> 8);
756 	((ORD8 *)p)[1] = (ORD8)((o32));
757 	return 0;
758 }
759 
760 /* ab part of 16 bit Lab - value range -128.0 - 127.9961 */
read_PCSab16Number(char * p)761 static double read_PCSab16Number(char *p) {
762 	ORD32 o32;
763 	o32 = 256 * (ORD32)((ORD8 *)p)[0]		/* Read big endian 16 bit unsigned */
764         +       (ORD32)((ORD8 *)p)[1];
765 	return ((double)o32/256.0)-128.0;
766 }
767 
write_PCSab16Number(double d,char * p)768 static int write_PCSab16Number(double d, char *p) {
769 	ORD32 o32;
770 	d = (d+128.0) * 256.0 + 0.5;
771 	if (d >= 65536.0)
772 		return 1;
773 	if (d < 0.0)
774 		return 1;
775 	o32 = (ORD32)d;
776 	((ORD8 *)p)[0] = (ORD8)((o32) >> 8);
777 	((ORD8 *)p)[1] = (ORD8)((o32));
778 	return 0;
779 }
780 
781 /* Device coordinate as 8 bit value range 0.0 - 1.0 */
read_DCS8Number(char * p)782 static double read_DCS8Number(char *p) {
783 	unsigned int rv;
784 	rv =   (unsigned int)((ORD8 *)p)[0];
785 	return (double)rv/255.0;
786 }
787 
write_DCS8Number(double d,char * p)788 static int write_DCS8Number(double d, char *p) {
789 	ORD32 o32;
790 	d = d * 255.0 + 0.5;
791 	if (d >= 256.0)
792 		return 1;
793 	if (d < 0.0)
794 		return 1;
795 	o32 = (ORD32)d;
796 	((ORD8 *)p)[0] = (ORD8)(o32);
797 	return 0;
798 }
799 
800 /* Device coordinate as 16 bit value range 0.0 - 1.0 */
read_DCS16Number(char * p)801 static double read_DCS16Number(char *p) {
802 	unsigned int rv;
803 	rv = 256 * (unsigned int)((ORD8 *)p)[0]
804 	   +       (unsigned int)((ORD8 *)p)[1];
805 	return (double)rv/65535.0;
806 }
807 
write_DCS16Number(double d,char * p)808 static int write_DCS16Number(double d, char *p) {
809 	ORD32 o32;
810 	d = d * 65535.0 + 0.5;
811 	if (d >= 65536.0)
812 		return 1;
813 	if (d < 0.0)
814 		return 1;
815 	o32 = (ORD32)d;
816 	((ORD8 *)p)[0] = (ORD8)(o32 >> 8);
817 	((ORD8 *)p)[1] = (ORD8)(o32);
818 	return 0;
819 }
820 
821 /* ---------------------------------------------------------- */
822 /* Auiliary function - return a string that represents a tag */
823 /* Note - returned buffers are static, can only be used 5 */
824 /* times before buffers get reused. */
tag2str(int tag)825 char *tag2str(
826 	int tag
827 ) {
828 	int i;
829 	static int si = 0;			/* String buffer index */
830 	static char buf[5][20];		/* String buffers */
831 	char *bp;
832 	unsigned char c[4];
833 
834 	bp = buf[si++];
835 	si %= 5;				/* Rotate through buffers */
836 
837 	c[0] = 0xff & (tag >> 24);
838 	c[1] = 0xff & (tag >> 16);
839 	c[2] = 0xff & (tag >> 8);
840 	c[3] = 0xff & (tag >> 0);
841 	for (i = 0; i < 4; i++) {	/* Can we represent it as a string ? */
842 		if (!isprint(c[i]))
843 			break;
844 	}
845 	if (i < 4) {	/* Not printable - use hex */
846 		sprintf(bp,"0x%x",tag);
847 	} else {		/* Printable */
848 		sprintf(bp,"'%c%c%c%c'",c[0],c[1],c[2],c[3]);
849 	}
850 	return bp;
851 }
852 
853 /* Auiliary function - return a tag created from a string */
str2tag(const char * str)854 int str2tag(
855 	const char *str
856 ) {
857 	unsigned long tag;
858 	tag = (((unsigned long)str[0]) << 24)
859 	    + (((unsigned long)str[1]) << 16)
860 	    + (((unsigned long)str[2]) << 8)
861 	    + (((unsigned long)str[3]));
862 	return (int)tag;
863 }
864 
865 /* helper - return 1 if the string doesn't have a */
866 /*  null terminator, return 0 if it does. */
867 /* Note: will return 1 if len == 0 */
check_null_string(char * cp,int len)868 static int check_null_string(char *cp, int len) {
869 	for (; len > 0; len--) {
870 		if (*cp++ == '\000')
871 			break;
872 	}
873 	if (len == 0)
874 		return 1;
875 	return 0;
876 }
877 
878 /* helper - return 1 if the string doesn't have a */
879 /*  null terminator, return 0 if it does. */
880 /* Note: will return 1 if len == 0 */
881 /* Unicode version */
check_null_string16(char * cp,int len)882 static int check_null_string16(char *cp, int len) {
883 	for (; len > 0; len--) {	/* Length is in characters */
884 		if (cp[0] == 0 && cp[1] == 0)
885 			break;
886 		cp += 2;
887 	}
888 	if (len == 0)
889 		return 1;
890 	return 0;
891 }
892 
893 /* Color Space to number of component conversion */
894 /* Return 0 on error */
number_ColorSpaceSignature(icColorSpaceSignature sig)895 static unsigned int number_ColorSpaceSignature(icColorSpaceSignature sig) {
896 	switch(sig) {
897 		case icSigXYZData:
898 			return 3;
899 		case icSigLabData:
900 			return 3;
901 		case icSigLuvData:
902 			return 3;
903 		case icSigYCbCrData:
904 			return 3;
905 		case icSigYxyData:
906 			return 3;
907 		case icSigRgbData:
908 			return 3;
909 		case icSigGrayData:
910 			return 1;
911 		case icSigHsvData:
912 			return 3;
913 		case icSigHlsData:
914 			return 3;
915 		case icSigCmykData:
916 			return 4;
917 		case icSigCmyData:
918 			return 3;
919 		case icSig2colorData:
920 			return 2;
921 		case icSig3colorData:
922 			return 3;
923 		case icSig4colorData:
924 			return 4;
925 		case icSig5colorData:
926 		case icSigMch5Data:
927 			return 5;
928 		case icSig6colorData:
929 		case icSigMch6Data:
930 			return 6;
931 		case icSig7colorData:
932 		case icSigMch7Data:
933 			return 7;
934 		case icSig8colorData:
935 		case icSigMch8Data:
936 			return 8;
937 		case icSig9colorData:
938 			return 9;
939 		case icSig10colorData:
940 			return 10;
941 		case icSig11colorData:
942 			return 11;
943 		case icSig12colorData:
944 			return 12;
945 		case icSig13colorData:
946 			return 13;
947 		case icSig14colorData:
948 			return 14;
949 		case icSig15colorData:
950 			return 15;
951 		default:
952 			return 0;
953 	}
954 }
955 
956 /* ------------------------------------------------------- */
957 /* Flag dump functions */
958 /* Note - returned buffers are static, can only be used 5 */
959 /* times before buffers get reused. */
960 
961 /* Screening Encodings */
string_ScreenEncodings(unsigned long flags)962 static char *string_ScreenEncodings(unsigned long flags) {
963 	static int si = 0;			/* String buffer index */
964 	static char buf[5][80];		/* String buffers */
965 	char *bp, *cp;
966 
967 	cp = bp = buf[si++];
968 	si %= 5;				/* Rotate through buffers */
969 
970 	if (flags & icPrtrDefaultScreensTrue) {
971 		sprintf(cp,"Default Screen");
972 	} else {
973 		sprintf(cp,"No Default Screen");
974 	}
975 	cp = cp + strlen(cp);
976 	if (flags & icLinesPerInch) {
977 		sprintf(cp,", Lines Per Inch");
978 	} else {
979 		sprintf(cp,", Lines Per cm");
980 	}
981 	cp = cp + strlen(cp);
982 
983 	return bp;
984 }
985 
986 /* Device attributes */
string_DeviceAttributes(unsigned long flags)987 static char *string_DeviceAttributes(unsigned long flags) {
988 	static int si = 0;			/* String buffer index */
989 	static char buf[5][80];		/* String buffers */
990 	char *bp, *cp;
991 
992 	cp = bp = buf[si++];
993 	si %= 5;				/* Rotate through buffers */
994 
995 	if (flags & icTransparency) {
996 		sprintf(cp,"Transparency");
997 	} else {
998 		sprintf(cp,"Reflective");
999 	}
1000 	cp = cp + strlen(cp);
1001 	if (flags & icMatte) {
1002 		sprintf(cp,", Matte");
1003 	} else {
1004 		sprintf(cp,", Glossy");
1005 	}
1006 	cp = cp + strlen(cp);
1007 
1008 	return bp;
1009 }
1010 
1011 /* Profile header flags */
string_ProfileHeaderFlags(unsigned long flags)1012 static char *string_ProfileHeaderFlags(unsigned long flags) {
1013 	static int si = 0;			/* String buffer index */
1014 	static char buf[5][80];		/* String buffers */
1015 	char *bp, *cp;
1016 
1017 	cp = bp = buf[si++];
1018 	si %= 5;				/* Rotate through buffers */
1019 
1020 	if (flags & icEmbeddedProfileTrue) {
1021 		sprintf(cp,"Embedded Profile");
1022 	} else {
1023 		sprintf(cp,"Not Embedded Profile");
1024 	}
1025 	cp = cp + strlen(cp);
1026 	if (flags & icUseWithEmbeddedDataOnly) {
1027 		sprintf(cp,", Use with embedded data only");
1028 	} else {
1029 		sprintf(cp,", Use anywhere");
1030 	}
1031 	cp = cp + strlen(cp);
1032 
1033 	return bp;
1034 }
1035 
1036 
string_AsciiOrBinaryData(unsigned long flags)1037 static char *string_AsciiOrBinaryData(unsigned long flags) {
1038 	static int si = 0;			/* String buffer index */
1039 	static char buf[5][80];		/* String buffers */
1040 	char *bp, *cp;
1041 
1042 	cp = bp = buf[si++];
1043 	si %= 5;				/* Rotate through buffers */
1044 
1045 	if (flags & icBinaryData) {
1046 		sprintf(cp,"Binary");
1047 	} else {
1048 		sprintf(cp,"Ascii");
1049 	}
1050 	cp = cp + strlen(cp);
1051 
1052 	return bp;
1053 }
1054 
1055 /* ------------------------------------------------------------ */
1056 /* Enumeration dump functions */
1057 /* Note - returned buffers are static, can only be used once */
1058 /* before buffers get reused if type is unknown. */
1059 
1060 /* public tags and sizes */
string_TagSignature(icTagSignature sig)1061 static const char *string_TagSignature(icTagSignature sig) {
1062 	static char buf[80];
1063 	switch(sig) {
1064 		case icSigAToB0Tag:
1065 			return "AToB0 Multidimentional Transform";
1066 		case icSigAToB1Tag:
1067 			return "AToB1 Multidimentional Transform";
1068 		case icSigAToB2Tag:
1069 			return "AToB2 Multidimentional Transform";
1070 		case icSigBlueColorantTag:
1071 			return "Blue Colorant";
1072 		case icSigBlueTRCTag:
1073 			return "Blue Tone Reproduction Curve";
1074 		case icSigBToA0Tag:
1075 			return "BToA0 Multidimentional Transform";
1076 		case icSigBToA1Tag:
1077 			return "BToA1 Multidimentional Transform";
1078 		case icSigBToA2Tag:
1079 			return "BToA2 Multidimentional Transform";
1080 		case icSigCalibrationDateTimeTag:
1081 			return "Calibration Date & Time";
1082 		case icSigCharTargetTag:
1083 			return "Characterization Target";
1084 		case icSigCopyrightTag:
1085 			return "Copyright";
1086 		case icSigCrdInfoTag:
1087 			return "CRD Info";
1088 		case icSigDeviceMfgDescTag:
1089 			return "Device Manufacturer Description";
1090 		case icSigDeviceModelDescTag:
1091 			return "Device Model Description";
1092 		case icSigGamutTag:
1093 			return "Gamut";
1094 		case icSigGrayTRCTag:
1095 			return "Gray Tone Reproduction Curve";
1096 		case icSigGreenColorantTag:
1097 			return "Green Colorant";
1098 		case icSigGreenTRCTag:
1099 			return "Green Tone Reproduction Curve";
1100 		case icSigLuminanceTag:
1101 			return "Luminance";
1102 		case icSigMeasurementTag:
1103 			return "Measurement";
1104 		case icSigMediaBlackPointTag:
1105 			return "Media Black Point";
1106 		case icSigMediaWhitePointTag:
1107 			return "Media White Point";
1108 		case icSigNamedColorTag:
1109 			return "Named Color";
1110 		case icSigNamedColor2Tag:
1111 			return "Named Color 2";
1112 		case icSigPreview0Tag:
1113 			return "Preview0";
1114 		case icSigPreview1Tag:
1115 			return "Preview1";
1116 		case icSigPreview2Tag:
1117 			return "Preview2";
1118 		case icSigProfileDescriptionTag:
1119 			return "Profile Description";
1120 		case icSigProfileSequenceDescTag:
1121 			return "Profile Sequence";
1122 		case icSigPs2CRD0Tag:
1123 			return "PS Level 2 CRD perceptual";
1124 		case icSigPs2CRD1Tag:
1125 			return "PS Level 2 CRD colorimetric";
1126 		case icSigPs2CRD2Tag:
1127 			return "PS Level 2 CRD saturation";
1128 		case icSigPs2CRD3Tag:
1129 			return "PS Level 2 CRD absolute";
1130 		case icSigPs2CSATag:
1131 			return "PS Level 2 color space array";
1132 		case icSigPs2RenderingIntentTag:
1133 			return "PS Level 2 Rendering Intent";
1134 		case icSigRedColorantTag:
1135 			return "Red Colorant";
1136 		case icSigRedTRCTag:
1137 			return "Red Tone Reproduction Curve";
1138 		case icSigScreeningDescTag:
1139 			return "Screening Description";
1140 		case icSigScreeningTag:
1141 			return "Screening Attributes";
1142 		case icSigTechnologyTag:
1143 			return "Device Technology";
1144 		case icSigUcrBgTag:
1145 			return "Under Color Removal & Black Generation";
1146 		case icSigVideoCardGammaTag:
1147 			return "Video Card Gamma Curve";
1148 		case icSigViewingCondDescTag:
1149 			return "Viewing Condition Description";
1150 		case icSigViewingConditionsTag:
1151 			return "Viewing Condition Paramaters";
1152 		default:
1153 			sprintf(buf,"Unrecognized - %s",tag2str(sig));
1154 			return buf;
1155 	}
1156 }
1157 
1158 /* technology signature descriptions */
string_TechnologySignature(icTechnologySignature sig)1159 static const char *string_TechnologySignature(icTechnologySignature sig) {
1160 	static char buf[80];
1161 	switch(sig) {
1162 		case icSigDigitalCamera:
1163 			return "Digital Camera";
1164 		case icSigFilmScanner:
1165 			return "Film Scanner";
1166 		case icSigReflectiveScanner:
1167 			return "Reflective Scanner";
1168 		case icSigInkJetPrinter:
1169 			return "InkJet Printer";
1170 		case icSigThermalWaxPrinter:
1171 			return "Thermal WaxPrinter";
1172 		case icSigElectrophotographicPrinter:
1173 			return "Electrophotographic Printer";
1174 		case icSigElectrostaticPrinter:
1175 			return "Electrostatic Printer";
1176 		case icSigDyeSublimationPrinter:
1177 			return "DyeSublimation Printer";
1178 		case icSigPhotographicPaperPrinter:
1179 			return "Photographic Paper Printer";
1180 		case icSigFilmWriter:
1181 			return "Film Writer";
1182 		case icSigVideoMonitor:
1183 			return "Video Monitor";
1184 		case icSigVideoCamera:
1185 			return "Video Camera";
1186 		case icSigProjectionTelevision:
1187 			return "Projection Television";
1188 		case icSigCRTDisplay:
1189 			return "Cathode Ray Tube Display";
1190 		case icSigPMDisplay:
1191 			return "Passive Matrix Display";
1192 		case icSigAMDisplay:
1193 			return "Active Matrix Display";
1194 		case icSigPhotoCD:
1195 			return "Photo CD";
1196 		case icSigPhotoImageSetter:
1197 			return "Photo ImageSetter";
1198 		case icSigGravure:
1199 			return "Gravure";
1200 		case icSigOffsetLithography:
1201 			return "Offset Lithography";
1202 		case icSigSilkscreen:
1203 			return "Silkscreen";
1204 		case icSigFlexography:
1205 			return "Flexography";
1206 		default:
1207 			sprintf(buf,"Unrecognized - %s",tag2str(sig));
1208 			return buf;
1209 	}
1210 }
1211 
1212 /* type signatures */
string_TypeSignature(icTagTypeSignature sig)1213 static const char *string_TypeSignature(icTagTypeSignature sig) {
1214 	static char buf[80];
1215 	switch(sig) {
1216 		case icSigCurveType:
1217 			return "Curve";
1218 		case icSigDataType:
1219 			return "Data";
1220 		case icSigDateTimeType:
1221 			return "DateTime";
1222 		case icSigLut16Type:
1223 			return "Lut16";
1224 		case icSigLut8Type:
1225 			return "Lut8";
1226 		case icSigMeasurementType:
1227 			return "Measurement";
1228 		case icSigNamedColorType:
1229 			return "Named Color";
1230 		case icSigProfileSequenceDescType:
1231 			return "Profile Sequence Desc";
1232 		case icSigS15Fixed16ArrayType:
1233 			return "S15Fixed16 Array";
1234 		case icSigScreeningType:
1235 			return "Screening";
1236 		case icSigSignatureType:
1237 			return "Signature";
1238 		case icSigTextType:
1239 			return "Text";
1240 		case icSigTextDescriptionType:
1241 			return "Text Description";
1242 		case icSigU16Fixed16ArrayType:
1243 			return "U16Fixed16 Array";
1244 		case icSigUcrBgType:
1245 			return "Under Color Removal & Black Generation";
1246 		case icSigUInt16ArrayType:
1247 			return "UInt16 Array";
1248 		case icSigUInt32ArrayType:
1249 			return "UInt32 Array";
1250 		case icSigUInt64ArrayType:
1251 			return "UInt64 Array";
1252 		case icSigUInt8ArrayType:
1253 			return "UInt8 Array";
1254 		case icSigVideoCardGammaType:
1255 			return "Video Card Gamma";
1256 		case icSigViewingConditionsType:
1257 			return "Viewing Conditions";
1258 		case icSigXYZType:
1259 			return "XYZ (Array?)";
1260 		case icSigNamedColor2Type:
1261 			return "Named Color 2";
1262 		case icSigCrdInfoType:
1263 			return "CRD Info";
1264 		default:
1265 			sprintf(buf,"Unrecognized - %s",tag2str(sig));
1266 			return buf;
1267 	}
1268 }
1269 
1270 /* Color Space Signatures */
string_ColorSpaceSignature(icColorSpaceSignature sig)1271 static const char *string_ColorSpaceSignature(icColorSpaceSignature sig) {
1272 	static char buf[80];
1273 	switch(sig) {
1274 		case icSigXYZData:
1275 			return "XYZ";
1276 		case icSigLabData:
1277 			return "Lab";
1278 		case icSigLuvData:
1279 			return "Luv";
1280 		case icSigYCbCrData:
1281 			return "YCbCr";
1282 		case icSigYxyData:
1283 			return "Yxy";
1284 		case icSigRgbData:
1285 			return "RGB";
1286 		case icSigGrayData:
1287 			return "Gray";
1288 		case icSigHsvData:
1289 			return "HSV";
1290 		case icSigHlsData:
1291 			return "HLS";
1292 		case icSigCmykData:
1293 			return "CMYK";
1294 		case icSigCmyData:
1295 			return "CMY";
1296 		case icSig2colorData:
1297 			return "2 Color";
1298 		case icSig3colorData:
1299 			return "3 Color";
1300 		case icSig4colorData:
1301 			return "4 Color";
1302 		case icSig5colorData:
1303 		case icSigMch5Data:
1304 			return "5 Color";
1305 		case icSig6colorData:
1306 		case icSigMch6Data:
1307 			return "6 Color";
1308 		case icSig7colorData:
1309 		case icSigMch7Data:
1310 			return "7 Color";
1311 		case icSig8colorData:
1312 		case icSigMch8Data:
1313 			return "8 Color";
1314 		case icSig9colorData:
1315 			return "9 Color";
1316 		case icSig10colorData:
1317 			return "10 Color";
1318 		case icSig11colorData:
1319 			return "11 Color";
1320 		case icSig12colorData:
1321 			return "12 Color";
1322 		case icSig13colorData:
1323 			return "13 Color";
1324 		case icSig14colorData:
1325 			return "14 Color";
1326 		case icSig15colorData:
1327 			return "15 Color";
1328 		default:
1329 			sprintf(buf,"Unrecognized - %s",tag2str(sig));
1330 			return buf;
1331 	}
1332 }
1333 
1334 #ifdef NEVER
1335 /* Public version of above */
ColorSpaceSignature2str(icColorSpaceSignature sig)1336 char *ColorSpaceSignature2str(icColorSpaceSignature sig) {
1337 	return string_ColorSpaceSignature(sig);
1338 }
1339 #endif
1340 
1341 
1342 /* profileClass enumerations */
string_ProfileClassSignature(icProfileClassSignature sig)1343 static const char *string_ProfileClassSignature(icProfileClassSignature sig) {
1344 	static char buf[80];
1345 	switch(sig) {
1346 		case icSigInputClass:
1347 			return "Input";
1348 		case icSigDisplayClass:
1349 			return "Display";
1350 		case icSigOutputClass:
1351 			return "Output";
1352 		case icSigLinkClass:
1353 			return "Link";
1354 		case icSigAbstractClass:
1355 			return "Abstract";
1356 		case icSigColorSpaceClass:
1357 			return "Color Space";
1358 		case icSigNamedColorClass:
1359 			return "Named Color";
1360 		default:
1361 			sprintf(buf,"Unrecognized - %s",tag2str(sig));
1362 			return buf;
1363 	}
1364 }
1365 
1366 /* Platform Signatures */
string_PlatformSignature(icPlatformSignature sig)1367 static const char *string_PlatformSignature(icPlatformSignature sig) {
1368 	static char buf[80];
1369 	switch(sig) {
1370 		case icSigMacintosh:
1371 			return "Macintosh";
1372 		case icSigMicrosoft:
1373 			return "Microsoft";
1374 		case icSigSolaris:
1375 			return "Solaris";
1376 		case icSigSGI:
1377 			return "SGI";
1378 		case icSigTaligent:
1379 			return "Taligent";
1380 		default:
1381 			sprintf(buf,"Unrecognized - %s",tag2str(sig));
1382 			return buf;
1383 	}
1384 }
1385 
1386 /* Measurement Geometry, used in the measurmentType tag */
string_MeasurementGeometry(icMeasurementGeometry sig)1387 static const char *string_MeasurementGeometry(icMeasurementGeometry sig) {
1388 	static char buf[30];
1389 	switch(sig) {
1390 		case icGeometryUnknown:
1391 			return "Unknown";
1392 		case icGeometry045or450:
1393 			return "0/45 or 45/0";
1394 		case icGeometry0dord0:
1395 			return "0/d or d/0";
1396 		default:
1397 			sprintf(buf,"Unrecognized - 0x%x",sig);
1398 			return buf;
1399 	}
1400 }
1401 
1402 /* Rendering Intents, used in the profile header */
string_RenderingIntent(icRenderingIntent sig)1403 static const char *string_RenderingIntent(icRenderingIntent sig) {
1404 	static char buf[30];
1405 	switch(sig) {
1406 		case icPerceptual:
1407 			return "Perceptual";
1408 		case icRelativeColorimetric:
1409 	    		return "Relative Colorimetric";
1410 		case icSaturation:
1411 	    		return "Saturation";
1412 		case icAbsoluteColorimetric:
1413 	    		return "Absolute Colorimetric";
1414 		default:
1415 			sprintf(buf,"Unrecognized - 0x%x",sig);
1416 			return buf;
1417 	}
1418 }
1419 
1420 /* Different Spot Shapes currently defined, used for screeningType */
string_SpotShape(icSpotShape sig)1421 static const char *string_SpotShape(icSpotShape sig) {
1422 	static char buf[30];
1423 	switch(sig) {
1424 		case icSpotShapeUnknown:
1425 			return "Unknown";
1426 		case icSpotShapePrinterDefault:
1427 			return "Printer Default";
1428 		case icSpotShapeRound:
1429 			return "Round";
1430 		case icSpotShapeDiamond:
1431 			return "Diamond";
1432 		case icSpotShapeEllipse:
1433 			return "Ellipse";
1434 		case icSpotShapeLine:
1435 			return "Line";
1436 		case icSpotShapeSquare:
1437 			return "Square";
1438 		case icSpotShapeCross:
1439 			return "Cross";
1440 		default:
1441 			sprintf(buf,"Unrecognized - 0x%x",sig);
1442 			return buf;
1443 	}
1444 }
1445 
1446 /* Standard Observer, used in the measurmentType tag */
string_StandardObserver(icStandardObserver sig)1447 static const char *string_StandardObserver(icStandardObserver sig) {
1448 	static char buf[30];
1449 	switch(sig) {
1450 		case icStdObsUnknown:
1451 			return "Unknown";
1452 		case icStdObs1931TwoDegrees:
1453 			return "1931 Two Degrees";
1454 		case icStdObs1964TenDegrees:
1455 			return "1964 Ten Degrees";
1456 		default:
1457 			sprintf(buf,"Unrecognized - 0x%x",sig);
1458 			return buf;
1459 	}
1460 }
1461 
1462 /* Pre-defined illuminants, used in measurement and viewing conditions type */
string_Illuminant(icIlluminant sig)1463 static const char *string_Illuminant(icIlluminant sig) {
1464 	static char buf[30];
1465 	switch(sig) {
1466 		case icIlluminantUnknown:
1467 			return "Unknown";
1468 		case icIlluminantD50:
1469 			return "D50";
1470 		case icIlluminantD65:
1471 			return "D65";
1472 		case icIlluminantD93:
1473 			return "D93";
1474 		case icIlluminantF2:
1475 			return "F2";
1476 		case icIlluminantD55:
1477 			return "D55";
1478 		case icIlluminantA:
1479 			return "A";
1480 		case icIlluminantEquiPowerE:
1481 			return "Equi-Power(E)";
1482 		case icIlluminantF8:
1483 			return "F8";
1484 		default:
1485 			sprintf(buf,"Unrecognized - 0x%x",sig);
1486 			return buf;
1487 	}
1488 }
1489 
1490 /* Return a text abreviation of a color lookup algorithm */
string_LuAlg(icmLuAlgType alg)1491 static const char *string_LuAlg(icmLuAlgType alg) {
1492 	static char buf[80];
1493 
1494 	switch(alg) {
1495     	case icmMonoFwdType:
1496 			return "MonoFwd";
1497     	case icmMonoBwdType:
1498 			return "MonoBwd";
1499     	case icmMatrixFwdType:
1500 			return "MatrixFwd";
1501     	case icmMatrixBwdType:
1502 			return "MatrixBwd";
1503     	case icmLutType:
1504 			return "Lut";
1505 	default:
1506 		sprintf(buf,"Unrecognized - %d",alg);
1507 		return buf;
1508 	}
1509 }
1510 
1511 /* Return a string description of the given enumeration value */
1512 /* Public: */
icm2str(icmEnumType etype,int enumval)1513 const char *icm2str(icmEnumType etype, int enumval) {
1514 
1515 	switch(etype) {
1516 	    case icmScreenEncodings:
1517 			return string_ScreenEncodings((unsigned long) enumval);
1518 	    case icmDeviceAttributes:
1519 			return string_DeviceAttributes((unsigned long) enumval);
1520 		case icmProfileHeaderFlags:
1521 			return string_ProfileHeaderFlags((unsigned long) enumval);
1522 		case icmAsciiOrBinaryData:
1523 			return string_AsciiOrBinaryData((unsigned long) enumval);
1524 		case icmTagSignature:
1525 			return string_TagSignature((icTagSignature) enumval);
1526 		case icmTechnologySignature:
1527 			return string_TechnologySignature((icTechnologySignature) enumval);
1528 		case icmTypeSignature:
1529 			return string_TypeSignature((icTagTypeSignature) enumval);
1530 		case icmColorSpaceSignature:
1531 			return string_ColorSpaceSignature((icColorSpaceSignature) enumval);
1532 		case icmProfileClassSignaure:
1533 			return string_ProfileClassSignature((icProfileClassSignature) enumval);
1534 		case icmPlatformSignature:
1535 			return string_PlatformSignature((icPlatformSignature) enumval);
1536 		case icmMeasurementGeometry:
1537 			return string_MeasurementGeometry((icMeasurementGeometry) enumval);
1538 		case icmRenderingIntent:
1539 			return string_RenderingIntent((icRenderingIntent) enumval);
1540 		case icmSpotShape:
1541 			return string_SpotShape((icSpotShape) enumval);
1542 		case icmStandardObserver:
1543 			return string_StandardObserver((icStandardObserver) enumval);
1544 		case icmIlluminant:
1545 			return string_Illuminant((icIlluminant) enumval);
1546 		case icmLuAlg:
1547 			return string_LuAlg((icmLuAlgType) enumval);
1548 		default:
1549 			return "enum2str got unknown type";
1550 	}
1551 }
1552 
1553 /* ========================================================== */
1554 /* Object I/O routines                                        */
1555 /* ========================================================== */
1556 /* icmUInt8Array object */
1557 
1558 /* Return the number of bytes needed to write this tag */
icmUInt8Array_get_size(icmBase * pp)1559 static unsigned int icmUInt8Array_get_size(
1560 	icmBase *pp
1561 ) {
1562 	icmUInt8Array *p = (icmUInt8Array *)pp;
1563 	unsigned int len = 0;
1564 	len += 8;			/* 8 bytes for tag and padding */
1565 	len += p->size * 1;	/* 1 byte for each UInt8 */
1566 	return len;
1567 }
1568 
1569 /* read the object, return 0 on success, error code on fail */
icmUInt8Array_read(icmBase * pp,unsigned long len,unsigned long of)1570 static int icmUInt8Array_read(
1571 	icmBase *pp,
1572 	unsigned long len,		/* tag length */
1573 	unsigned long of		/* start offset within file */
1574 ) {
1575 	icmUInt8Array *p = (icmUInt8Array *)pp;
1576 	icc *icp = p->icp;
1577 	int rv = 0;
1578 	unsigned long i, size;
1579 	char *bp, *buf;
1580 
1581 	if (len < 8) {
1582 		sprintf(icp->err,"icmUInt8Array_read: Tag too small to be legal");
1583 		return icp->errc = 1;
1584 	}
1585 
1586 	/* Allocate a file read buffer */
1587 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
1588 		sprintf(icp->err,"icmUInt8Array_read: malloc() failed");
1589 		return icp->errc = 2;
1590 	}
1591 	bp = buf;
1592 
1593 	/* Read portion of file into buffer */
1594 	if (   icp->fp->seek(icp->fp, of) != 0
1595 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
1596 		sprintf(icp->err,"icmUInt8Array_read: fseek() or fread() failed");
1597 		icp->al->free(icp->al, buf);
1598 		return icp->errc = 1;
1599 	}
1600 	p->size = size = (len - 8)/1;		/* Number of elements in the array */
1601 
1602 	if ((rv = p->allocate((icmBase *)p)) != 0) {
1603 		icp->al->free(icp->al, buf);
1604 		return rv;
1605 	}
1606 
1607 	/* Read type descriptor from the buffer */
1608 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
1609 		icp->al->free(icp->al, buf);
1610 		sprintf(icp->err,"icmUInt8Array_read: Wrong tag type for icmUInt8Array");
1611 		return icp->errc = 1;
1612 	}
1613 	bp += 8;	/* Skip padding */
1614 
1615 	/* Read all the data from the buffer */
1616 	for (i = 0; i < size; i++, bp += 1) {
1617 		p->data[i] = read_UInt8Number(bp);
1618 	}
1619 	icp->al->free(p->icp->al, buf);
1620 	return 0;
1621 }
1622 
1623 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmUInt8Array_write(icmBase * pp,unsigned long of)1624 static int icmUInt8Array_write(
1625 	icmBase *pp,
1626 	unsigned long of			/* File offset to write from */
1627 ) {
1628 	icmUInt8Array *p = (icmUInt8Array *)pp;
1629 	icc *icp = p->icp;
1630 	unsigned long i;
1631 	unsigned int len;
1632 	char *bp, *buf;		/* Buffer to write from */
1633 	int rv = 0;
1634 
1635 	/* Allocate a file write buffer */
1636 	len = p->get_size((icmBase *)p);
1637 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
1638 		sprintf(icp->err,"icmUInt8Array_write malloc() failed");
1639 		return icp->errc = 2;
1640 	}
1641 	bp = buf;
1642 
1643 	/* Write type descriptor to the buffer */
1644 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
1645 		sprintf(icp->err,"icmUInt8Array_write: write_SInt32Number() failed");
1646 		icp->al->free(icp->al, buf);
1647 		return icp->errc = rv;
1648 	}
1649 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
1650 	bp += 8;	/* Skip padding */
1651 
1652 	/* Write all the data to the buffer */
1653 	for (i = 0; i < p->size; i++, bp += 1) {
1654 		if ((rv = write_UInt8Number(p->data[i],bp)) != 0) {
1655 			sprintf(icp->err,"icmUInt8Array_write: write_UInt8umber() failed");
1656 			icp->al->free(icp->al, buf);
1657 			return icp->errc = rv;
1658 		}
1659 	}
1660 
1661 	/* Write to the file */
1662 	if (   icp->fp->seek(icp->fp, of) != 0
1663 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
1664 		sprintf(icp->err,"icmUInt8Array_write fseek() or fwrite() failed");
1665 		icp->al->free(icp->al, buf);
1666 		return icp->errc = 2;
1667 	}
1668 	icp->al->free(icp->al, buf);
1669 	return 0;
1670 }
1671 
1672 /* Dump a text description of the object */
icmUInt8Array_dump(icmBase * pp,FILE * op,int verb)1673 static void icmUInt8Array_dump(
1674 	icmBase *pp,
1675 	FILE *op,		/* Output to dump to */
1676 	int   verb		/* Verbosity level */
1677 ) {
1678 	icmUInt8Array *p = (icmUInt8Array *)pp;
1679 	if (verb <= 0)
1680 		return;
1681 
1682 	fprintf(op,"UInt8Array:\n");
1683 	fprintf(op,"  No. elements = %lu\n",p->size);
1684 	if (verb >= 2) {
1685 		unsigned long i;
1686 		for (i = 0; i < p->size; i++)
1687 			fprintf(op,"    %lu:  %u\n",i,p->data[i]);
1688 	}
1689 }
1690 
1691 /* Allocate variable sized data elements */
icmUInt8Array_allocate(icmBase * pp)1692 static int icmUInt8Array_allocate(
1693 	icmBase *pp
1694 ) {
1695 	icmUInt8Array *p = (icmUInt8Array *)pp;
1696 	icc *icp = p->icp;
1697 
1698 	if (p->size != p->_size) {
1699 		if (p->data != NULL)
1700 			icp->al->free(icp->al, p->data);
1701 		if ((p->data = (unsigned int *) icp->al->malloc(icp->al, p->size * sizeof(unsigned int))) == NULL) {
1702 			sprintf(icp->err,"icmUInt8Array_alloc: malloc() of icmUInt8Array data failed");
1703 			return icp->errc = 2;
1704 		}
1705 		p->_size = p->size;
1706 	}
1707 	return 0;
1708 }
1709 
1710 /* Free all storage in the object */
icmUInt8Array_delete(icmBase * pp)1711 static void icmUInt8Array_delete(
1712 	icmBase *pp
1713 ) {
1714 	icmUInt8Array *p = (icmUInt8Array *)pp;
1715 	icc *icp = p->icp;
1716 
1717 	if (p->data != NULL)
1718 		icp->al->free(icp->al, p->data);
1719 	icp->al->free(icp->al, p);
1720 }
1721 
1722 /* Create an empty object. Return null on error */
new_icmUInt8Array(icc * icp)1723 static icmBase *new_icmUInt8Array(
1724 	icc *icp
1725 ) {
1726 	icmUInt8Array *p;
1727 	if ((p = (icmUInt8Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt8Array))) == NULL)
1728 		return NULL;
1729 	p->ttype    = icSigUInt8ArrayType;
1730 	p->refcount = 1;
1731 	p->get_size = icmUInt8Array_get_size;
1732 	p->read     = icmUInt8Array_read;
1733 	p->write    = icmUInt8Array_write;
1734 	p->dump     = icmUInt8Array_dump;
1735 	p->allocate = icmUInt8Array_allocate;
1736 	p->del      = icmUInt8Array_delete;
1737 	p->icp      = icp;
1738 
1739 	return (icmBase *)p;
1740 }
1741 
1742 /* ---------------------------------------------------------- */
1743 /* icmUInt16Array object */
1744 
1745 /* Return the number of bytes needed to write this tag */
icmUInt16Array_get_size(icmBase * pp)1746 static unsigned int icmUInt16Array_get_size(
1747 	icmBase *pp
1748 ) {
1749 	icmUInt16Array *p = (icmUInt16Array *)pp;
1750 	unsigned int len = 0;
1751 	len += 8;			/* 8 bytes for tag and padding */
1752 	len += p->size * 2;	/* 2 bytes for each UInt16 */
1753 	return len;
1754 }
1755 
1756 /* read the object, return 0 on success, error code on fail */
icmUInt16Array_read(icmBase * pp,unsigned long len,unsigned long of)1757 static int icmUInt16Array_read(
1758 	icmBase *pp,
1759 	unsigned long len,		/* tag length */
1760 	unsigned long of		/* start offset within file */
1761 ) {
1762 	icmUInt16Array *p = (icmUInt16Array *)pp;
1763 	icc *icp = p->icp;
1764 	int rv = 0;
1765 	unsigned long i, size;
1766 	char *bp, *buf;
1767 
1768 	if (len < 8) {
1769 		sprintf(icp->err,"icmUInt16Array_read: Tag too small to be legal");
1770 		return icp->errc = 1;
1771 	}
1772 
1773 	/* Allocate a file read buffer */
1774 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
1775 		sprintf(icp->err,"icmUInt16Array_read: malloc() failed");
1776 		return icp->errc = 2;
1777 	}
1778 	bp = buf;
1779 
1780 	/* Read portion of file into buffer */
1781 	if (   icp->fp->seek(icp->fp, of) != 0
1782 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
1783 		sprintf(icp->err,"icmUInt16Array_read: fseek() or fread() failed");
1784 		icp->al->free(icp->al, buf);
1785 		return icp->errc = 1;
1786 	}
1787 	p->size = size = (len - 8)/2;		/* Number of elements in the array */
1788 
1789 	if ((rv = p->allocate((icmBase *)p)) != 0) {
1790 		icp->al->free(icp->al, buf);
1791 		return rv;
1792 	}
1793 
1794 	/* Read type descriptor from the buffer */
1795 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
1796 		sprintf(icp->err,"icmUInt16Array_read: Wrong tag type for icmUInt16Array");
1797 		icp->al->free(icp->al, buf);
1798 		return icp->errc = 1;
1799 	}
1800 	bp += 8;	/* Skip padding */
1801 
1802 	/* Read all the data from the buffer */
1803 	for (i = 0; i < size; i++, bp += 2) {
1804 		p->data[i] = read_UInt16Number(bp);
1805 	}
1806 	icp->al->free(icp->al, buf);
1807 	return 0;
1808 }
1809 
1810 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmUInt16Array_write(icmBase * pp,unsigned long of)1811 static int icmUInt16Array_write(
1812 	icmBase *pp,
1813 	unsigned long of			/* File offset to write from */
1814 ) {
1815 	icmUInt16Array *p = (icmUInt16Array *)pp;
1816 	icc *icp = p->icp;
1817 	unsigned long i;
1818 	unsigned int len;
1819 	char *bp, *buf;		/* Buffer to write from */
1820 	int rv = 0;
1821 
1822 	/* Allocate a file write buffer */
1823 	len = p->get_size((icmBase *)p);
1824 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
1825 		sprintf(icp->err,"icmUInt16Array_write malloc() failed");
1826 		return icp->errc = 2;
1827 	}
1828 	bp = buf;
1829 
1830 	/* Write type descriptor to the buffer */
1831 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
1832 		sprintf(icp->err,"icmUInt16Array_write: write_SInt32Number() failed");
1833 		icp->al->free(icp->al, buf);
1834 		return icp->errc = rv;
1835 	}
1836 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
1837 
1838 	/* Write all the data to the buffer */
1839 	bp += 8;	/* Skip padding */
1840 	for (i = 0; i < p->size; i++, bp += 2) {
1841 		if ((rv = write_UInt16Number(p->data[i],bp)) != 0) {
1842 			sprintf(icp->err,"icmUInt16Array_write: write_UInt16umber() failed");
1843 			icp->al->free(icp->al, buf);
1844 			return icp->errc = rv;
1845 		}
1846 	}
1847 
1848 	/* Write to the file */
1849 	if (   icp->fp->seek(icp->fp, of) != 0
1850 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
1851 		sprintf(icp->err,"icmUInt16Array_write fseek() or fwrite() failed");
1852 		icp->al->free(icp->al, buf);
1853 		return icp->errc = 2;
1854 	}
1855 	icp->al->free(icp->al, buf);
1856 	return 0;
1857 }
1858 
1859 /* Dump a text description of the object */
icmUInt16Array_dump(icmBase * pp,FILE * op,int verb)1860 static void icmUInt16Array_dump(
1861 	icmBase *pp,
1862 	FILE *op,		/* Output to dump to */
1863 	int   verb		/* Verbosity level */
1864 ) {
1865 	icmUInt16Array *p = (icmUInt16Array *)pp;
1866 	if (verb <= 0)
1867 		return;
1868 
1869 	fprintf(op,"UInt16Array:\n");
1870 	fprintf(op,"  No. elements = %lu\n",p->size);
1871 	if (verb >= 2) {
1872 		unsigned long i;
1873 		for (i = 0; i < p->size; i++)
1874 			fprintf(op,"    %lu:  %u\n",i,p->data[i]);
1875 	}
1876 }
1877 
1878 /* Allocate variable sized data elements */
icmUInt16Array_allocate(icmBase * pp)1879 static int icmUInt16Array_allocate(
1880 	icmBase *pp
1881 ) {
1882 	icmUInt16Array *p = (icmUInt16Array *)pp;
1883 	icc *icp = p->icp;
1884 
1885 	if (p->size != p->_size) {
1886 		if (p->data != NULL)
1887 			icp->al->free(icp->al, p->data);
1888 		if ((p->data = (unsigned int *) icp->al->malloc(icp->al, p->size * sizeof(unsigned int))) == NULL) {
1889 			sprintf(icp->err,"icmUInt16Array_alloc: malloc() of icmUInt16Array data failed");
1890 			return icp->errc = 2;
1891 		}
1892 		p->_size = p->size;
1893 	}
1894 	return 0;
1895 }
1896 
1897 /* Free all storage in the object */
icmUInt16Array_delete(icmBase * pp)1898 static void icmUInt16Array_delete(
1899 	icmBase *pp
1900 ) {
1901 	icmUInt16Array *p = (icmUInt16Array *)pp;
1902 	icc *icp = p->icp;
1903 
1904 	if (p->data != NULL)
1905 		icp->al->free(icp->al, p->data);
1906 	icp->al->free(icp->al, p);
1907 }
1908 
1909 /* Create an empty object. Return null on error */
new_icmUInt16Array(icc * icp)1910 static icmBase *new_icmUInt16Array(
1911 	icc *icp
1912 ) {
1913 	icmUInt16Array *p;
1914 	if ((p = (icmUInt16Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt16Array))) == NULL)
1915 		return NULL;
1916 	p->ttype    = icSigUInt16ArrayType;
1917 	p->refcount = 1;
1918 	p->get_size = icmUInt16Array_get_size;
1919 	p->read     = icmUInt16Array_read;
1920 	p->write    = icmUInt16Array_write;
1921 	p->dump     = icmUInt16Array_dump;
1922 	p->allocate = icmUInt16Array_allocate;
1923 	p->del      = icmUInt16Array_delete;
1924 	p->icp      = icp;
1925 
1926 	return (icmBase *)p;
1927 }
1928 
1929 /* ---------------------------------------------------------- */
1930 /* icmUInt32Array object */
1931 
1932 /* Return the number of bytes needed to write this tag */
icmUInt32Array_get_size(icmBase * pp)1933 static unsigned int icmUInt32Array_get_size(
1934 	icmBase *pp
1935 ) {
1936 	icmUInt32Array *p = (icmUInt32Array *)pp;
1937 	unsigned int len = 0;
1938 	len += 8;			/* 8 bytes for tag and padding */
1939 	len += p->size * 4;	/* 4 bytes for each UInt32 */
1940 	return len;
1941 }
1942 
1943 /* read the object, return 0 on success, error code on fail */
icmUInt32Array_read(icmBase * pp,unsigned long len,unsigned long of)1944 static int icmUInt32Array_read(
1945 	icmBase *pp,
1946 	unsigned long len,		/* tag length */
1947 	unsigned long of		/* start offset within file */
1948 ) {
1949 	icmUInt32Array *p = (icmUInt32Array *)pp;
1950 	icc *icp = p->icp;
1951 	int rv = 0;
1952 	unsigned long i, size;
1953 	char *bp, *buf;
1954 
1955 	if (len < 8) {
1956 		sprintf(icp->err,"icmUInt32Array_read: Tag too small to be legal");
1957 		return icp->errc = 1;
1958 	}
1959 
1960 	/* Allocate a file read buffer */
1961 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
1962 		sprintf(icp->err,"icmUInt32Array_read: malloc() failed");
1963 		return icp->errc = 2;
1964 	}
1965 	bp = buf;
1966 
1967 	/* Read portion of file into buffer */
1968 	if (   icp->fp->seek(icp->fp, of) != 0
1969 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
1970 		sprintf(icp->err,"icmUInt32Array_read: fseek() or fread() failed");
1971 		icp->al->free(icp->al, buf);
1972 		return icp->errc = 1;
1973 	}
1974 	p->size = size = (len - 8)/4;		/* Number of elements in the array */
1975 
1976 	if ((rv = p->allocate((icmBase *)p)) != 0) {
1977 		icp->al->free(icp->al, buf);
1978 		return rv;
1979 	}
1980 
1981 	/* Read type descriptor from the buffer */
1982 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
1983 		sprintf(icp->err,"icmUInt32Array_read: Wrong tag type for icmUInt32Array");
1984 		icp->al->free(icp->al, buf);
1985 		return icp->errc = 1;
1986 	}
1987 	bp += 8;	/* Skip padding */
1988 
1989 	/* Read all the data from the buffer */
1990 	for (i = 0; i < size; i++, bp += 4) {
1991 		p->data[i] = read_UInt32Number(bp);
1992 	}
1993 	icp->al->free(icp->al, buf);
1994 	return 0;
1995 }
1996 
1997 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmUInt32Array_write(icmBase * pp,unsigned long of)1998 static int icmUInt32Array_write(
1999 	icmBase *pp,
2000 	unsigned long of			/* File offset to write from */
2001 ) {
2002 	icmUInt32Array *p = (icmUInt32Array *)pp;
2003 	icc *icp = p->icp;
2004 	unsigned long i;
2005 	unsigned int len;
2006 	char *bp, *buf;		/* Buffer to write from */
2007 	int rv = 0;
2008 
2009 	/* Allocate a file write buffer */
2010 	len = p->get_size((icmBase *)p);
2011 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2012 		sprintf(icp->err,"icmUInt32Array_write malloc() failed");
2013 		return icp->errc = 2;
2014 	}
2015 	bp = buf;
2016 
2017 	/* Write type descriptor to the buffer */
2018 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
2019 		sprintf(icp->err,"icmUInt32Array_write: write_SInt32Number() failed");
2020 		icp->al->free(icp->al, buf);
2021 		return icp->errc = rv;
2022 	}
2023 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
2024 
2025 	/* Write all the data to the buffer */
2026 	bp += 8;	/* Skip padding */
2027 	for (i = 0; i < p->size; i++, bp += 4) {
2028 		if ((rv = write_UInt32Number(p->data[i],bp)) != 0) {
2029 			sprintf(icp->err,"icmUInt32Array_write: write_UInt32umber() failed");
2030 			icp->al->free(icp->al, buf);
2031 			return icp->errc = rv;
2032 		}
2033 	}
2034 
2035 	/* Write to the file */
2036 	if (   icp->fp->seek(icp->fp, of) != 0
2037 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
2038 		sprintf(icp->err,"icmUInt32Array_write fseek() or fwrite() failed");
2039 		icp->al->free(icp->al, buf);
2040 		return icp->errc = 2;
2041 	}
2042 	icp->al->free(icp->al, buf);
2043 	return 0;
2044 }
2045 
2046 /* Dump a text description of the object */
icmUInt32Array_dump(icmBase * pp,FILE * op,int verb)2047 static void icmUInt32Array_dump(
2048 	icmBase *pp,
2049 	FILE *op,		/* Output to dump to */
2050 	int   verb		/* Verbosity level */
2051 ) {
2052 	icmUInt32Array *p = (icmUInt32Array *)pp;
2053 	if (verb <= 0)
2054 		return;
2055 
2056 	fprintf(op,"UInt32Array:\n");
2057 	fprintf(op,"  No. elements = %lu\n",p->size);
2058 	if (verb >= 2) {
2059 		unsigned long i;
2060 		for (i = 0; i < p->size; i++)
2061 			fprintf(op,"    %lu:  %u\n",i,p->data[i]);
2062 	}
2063 }
2064 
2065 /* Allocate variable sized data elements */
icmUInt32Array_allocate(icmBase * pp)2066 static int icmUInt32Array_allocate(
2067 	icmBase *pp
2068 ) {
2069 	icmUInt32Array *p = (icmUInt32Array *)pp;
2070 	icc *icp = p->icp;
2071 
2072 	if (p->size != p->_size) {
2073 		if (p->data != NULL)
2074 			icp->al->free(icp->al, p->data);
2075 		if ((p->data = (unsigned int *) icp->al->malloc(icp->al, p->size * sizeof(unsigned int))) == NULL) {
2076 			sprintf(icp->err,"icmUInt32Array_alloc: malloc() of icmUInt32Array data failed");
2077 			return icp->errc = 2;
2078 		}
2079 		p->_size = p->size;
2080 	}
2081 	return 0;
2082 }
2083 
2084 /* Free all storage in the object */
icmUInt32Array_delete(icmBase * pp)2085 static void icmUInt32Array_delete(
2086 	icmBase *pp
2087 ) {
2088 	icmUInt32Array *p = (icmUInt32Array *)pp;
2089 	icc *icp = p->icp;
2090 
2091 	if (p->data != NULL)
2092 		icp->al->free(icp->al, p->data);
2093 	icp->al->free(icp->al, p);
2094 }
2095 
2096 /* Create an empty object. Return null on error */
new_icmUInt32Array(icc * icp)2097 static icmBase *new_icmUInt32Array(
2098 	icc *icp
2099 ) {
2100 	icmUInt32Array *p;
2101 	if ((p = (icmUInt32Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt32Array))) == NULL)
2102 		return NULL;
2103 	p->ttype    = icSigUInt32ArrayType;
2104 	p->refcount = 1;
2105 	p->get_size = icmUInt32Array_get_size;
2106 	p->read     = icmUInt32Array_read;
2107 	p->write    = icmUInt32Array_write;
2108 	p->dump     = icmUInt32Array_dump;
2109 	p->allocate = icmUInt32Array_allocate;
2110 	p->del      = icmUInt32Array_delete;
2111 	p->icp      = icp;
2112 
2113 	return (icmBase *)p;
2114 }
2115 
2116 /* ---------------------------------------------------------- */
2117 /* icmUInt64Array object */
2118 
2119 /* Return the number of bytes needed to write this tag */
icmUInt64Array_get_size(icmBase * pp)2120 static unsigned int icmUInt64Array_get_size(
2121 	icmBase *pp
2122 ) {
2123 	icmUInt64Array *p = (icmUInt64Array *)pp;
2124 	unsigned int len = 0;
2125 	len += 8;			/* 8 bytes for tag and padding */
2126 	len += p->size * 8;	/* 8 bytes for each UInt64 */
2127 	return len;
2128 }
2129 
2130 /* read the object, return 0 on success, error code on fail */
icmUInt64Array_read(icmBase * pp,unsigned long len,unsigned long of)2131 static int icmUInt64Array_read(
2132 	icmBase *pp,
2133 	unsigned long len,		/* tag length */
2134 	unsigned long of		/* start offset within file */
2135 ) {
2136 	icmUInt64Array *p = (icmUInt64Array *)pp;
2137 	icc *icp = p->icp;
2138 	int rv = 0;
2139 	unsigned long i, size;
2140 	char *bp, *buf;
2141 
2142 	if (len < 8) {
2143 		sprintf(icp->err,"icmUInt64Array_read: Tag too small to be legal");
2144 		return icp->errc = 1;
2145 	}
2146 
2147 	/* Allocate a file read buffer */
2148 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2149 		sprintf(icp->err,"icmUInt64Array_read: malloc() failed");
2150 		return icp->errc = 2;
2151 	}
2152 	bp = buf;
2153 
2154 	/* Read portion of file into buffer */
2155 	if (   icp->fp->seek(icp->fp, of) != 0
2156 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
2157 		sprintf(icp->err,"icmUInt64Array_read: fseek() or fread() failed");
2158 		icp->al->free(icp->al, buf);
2159 		return icp->errc = 1;
2160 	}
2161 	p->size = size = (len - 8)/8;		/* Number of elements in the array */
2162 
2163 	if ((rv = p->allocate((icmBase *)p)) != 0) {
2164 		icp->al->free(icp->al, buf);
2165 		return rv;
2166 	}
2167 
2168 	/* Read type descriptor from the buffer */
2169 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
2170 		sprintf(icp->err,"icmUInt64Array_read: Wrong tag type for icmUInt64Array");
2171 		icp->al->free(icp->al, buf);
2172 		return icp->errc = 1;
2173 	}
2174 	bp += 8;	/* Skip padding */
2175 
2176 	/* Read all the data from the buffer */
2177 	for (i = 0; i < size; i++, bp += 8) {
2178 		read_UInt64Number(&p->data[i], bp);
2179 	}
2180 	icp->al->free(icp->al, buf);
2181 	return 0;
2182 }
2183 
2184 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmUInt64Array_write(icmBase * pp,unsigned long of)2185 static int icmUInt64Array_write(
2186 	icmBase *pp,
2187 	unsigned long of			/* File offset to write from */
2188 ) {
2189 	icmUInt64Array *p = (icmUInt64Array *)pp;
2190 	icc *icp = p->icp;
2191 	unsigned long i;
2192 	unsigned int len;
2193 	char *bp, *buf;		/* Buffer to write from */
2194 	int rv = 0;
2195 
2196 	/* Allocate a file write buffer */
2197 	len = p->get_size((icmBase *)p);
2198 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2199 		sprintf(icp->err,"icmUInt64Array_write malloc() failed");
2200 		return icp->errc = 2;
2201 	}
2202 	bp = buf;
2203 
2204 	/* Write type descriptor to the buffer */
2205 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
2206 		sprintf(icp->err,"icmUInt64Array_write: write_SInt32Number() failed");
2207 		icp->al->free(icp->al, buf);
2208 		return icp->errc = rv;
2209 	}
2210 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
2211 
2212 	/* Write all the data to the buffer */
2213 	bp += 8;	/* Skip padding */
2214 	for (i = 0; i < p->size; i++, bp += 8) {
2215 		if ((rv = write_UInt64Number(&p->data[i],bp)) != 0) {
2216 			sprintf(icp->err,"icmUInt64Array_write: write_UInt64umber() failed");
2217 			icp->al->free(icp->al, buf);
2218 			return icp->errc = rv;
2219 		}
2220 	}
2221 
2222 	/* Write to the file */
2223 	if (   icp->fp->seek(icp->fp, of) != 0
2224 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
2225 		sprintf(icp->err,"icmUInt64Array_write fseek() or fwrite() failed");
2226 		icp->al->free(icp->al, buf);
2227 		return icp->errc = 2;
2228 	}
2229 	icp->al->free(icp->al, buf);
2230 	return 0;
2231 }
2232 
2233 /* Dump a text description of the object */
icmUInt64Array_dump(icmBase * pp,FILE * op,int verb)2234 static void icmUInt64Array_dump(
2235 	icmBase *pp,
2236 	FILE *op,		/* Output to dump to */
2237 	int   verb		/* Verbosity level */
2238 ) {
2239 	icmUInt64Array *p = (icmUInt64Array *)pp;
2240 	if (verb <= 0)
2241 		return;
2242 
2243 	fprintf(op,"UInt64Array:\n");
2244 	fprintf(op,"  No. elements = %lu\n",p->size);
2245 	if (verb >= 2) {
2246 		unsigned long i;
2247 		for (i = 0; i < p->size; i++)
2248 			fprintf(op,"    %lu:  h=%lu, l=%lu\n",i,p->data[i].h,p->data[i].l);
2249 	}
2250 }
2251 
2252 /* Allocate variable sized data elements */
icmUInt64Array_allocate(icmBase * pp)2253 static int icmUInt64Array_allocate(
2254 	icmBase *pp
2255 ) {
2256 	icmUInt64Array *p = (icmUInt64Array *)pp;
2257 	icc *icp = p->icp;
2258 
2259 	if (p->size != p->_size) {
2260 		if (p->data != NULL)
2261 			icp->al->free(icp->al, p->data);
2262 		if ((p->data = (icmUint64 *) icp->al->malloc(icp->al, p->size * sizeof(icmUint64))) == NULL) {
2263 			sprintf(icp->err,"icmUInt64Array_alloc: malloc() of icmUInt64Array data failed");
2264 			return icp->errc = 2;
2265 		}
2266 		p->_size = p->size;
2267 	}
2268 	return 0;
2269 }
2270 
2271 /* Free all storage in the object */
icmUInt64Array_delete(icmBase * pp)2272 static void icmUInt64Array_delete(
2273 	icmBase *pp
2274 ) {
2275 	icmUInt64Array *p = (icmUInt64Array *)pp;
2276 	icc *icp = p->icp;
2277 
2278 	if (p->data != NULL)
2279 		icp->al->free(icp->al, p->data);
2280 	icp->al->free(icp->al, p);
2281 }
2282 
2283 /* Create an empty object. Return null on error */
new_icmUInt64Array(icc * icp)2284 static icmBase *new_icmUInt64Array(
2285 	icc *icp
2286 ) {
2287 	icmUInt64Array *p;
2288 	if ((p = (icmUInt64Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt64Array))) == NULL)
2289 		return NULL;
2290 	p->ttype    = icSigUInt64ArrayType;
2291 	p->refcount = 1;
2292 	p->get_size = icmUInt64Array_get_size;
2293 	p->read     = icmUInt64Array_read;
2294 	p->write    = icmUInt64Array_write;
2295 	p->dump     = icmUInt64Array_dump;
2296 	p->allocate = icmUInt64Array_allocate;
2297 	p->del      = icmUInt64Array_delete;
2298 	p->icp      = icp;
2299 
2300 	return (icmBase *)p;
2301 }
2302 
2303 /* ---------------------------------------------------------- */
2304 /* icmU16Fixed16Array object */
2305 
2306 /* Return the number of bytes needed to write this tag */
icmU16Fixed16Array_get_size(icmBase * pp)2307 static unsigned int icmU16Fixed16Array_get_size(
2308 	icmBase *pp
2309 ) {
2310 	icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
2311 	unsigned int len = 0;
2312 	len += 8;			/* 8 bytes for tag and padding */
2313 	len += p->size * 4;	/* 4 byte for each U16Fixed16 */
2314 	return len;
2315 }
2316 
2317 /* read the object, return 0 on success, error code on fail */
icmU16Fixed16Array_read(icmBase * pp,unsigned long len,unsigned long of)2318 static int icmU16Fixed16Array_read(
2319 	icmBase *pp,
2320 	unsigned long len,		/* tag length */
2321 	unsigned long of		/* start offset within file */
2322 ) {
2323 	icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
2324 	icc *icp = p->icp;
2325 	int rv = 0;
2326 	unsigned long i, size;
2327 	char *bp, *buf;
2328 
2329 	if (len < 8) {
2330 		sprintf(icp->err,"icmU16Fixed16Array_read: Tag too small to be legal");
2331 		return icp->errc = 1;
2332 	}
2333 
2334 	/* Allocate a file read buffer */
2335 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2336 		sprintf(icp->err,"icmU16Fixed16Array_read: malloc() failed");
2337 		return icp->errc = 2;
2338 	}
2339 	bp = buf;
2340 
2341 	/* Read portion of file into buffer */
2342 	if (   icp->fp->seek(icp->fp, of) != 0
2343 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
2344 		sprintf(icp->err,"icmU16Fixed16Array_read: fseek() or fread() failed");
2345 		icp->al->free(icp->al, buf);
2346 		return icp->errc = 1;
2347 	}
2348 	p->size = size = (len - 8)/4;		/* Number of elements in the array */
2349 
2350 	if ((rv = p->allocate((icmBase *)p)) != 0) {
2351 		icp->al->free(icp->al, buf);
2352 		return rv;
2353 	}
2354 
2355 	/* Read type descriptor from the buffer */
2356 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
2357 		sprintf(icp->err,"icmU16Fixed16Array_read: Wrong tag type for icmU16Fixed16Array");
2358 		icp->al->free(icp->al, buf);
2359 		return icp->errc = 1;
2360 	}
2361 	bp += 8;	/* Skip padding */
2362 
2363 	/* Read all the data from the buffer */
2364 	for (i = 0; i < size; i++, bp += 4) {
2365 		p->data[i] = read_U16Fixed16Number(bp);
2366 	}
2367 	icp->al->free(icp->al, buf);
2368 	return 0;
2369 }
2370 
2371 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmU16Fixed16Array_write(icmBase * pp,unsigned long of)2372 static int icmU16Fixed16Array_write(
2373 	icmBase *pp,
2374 	unsigned long of			/* File offset to write from */
2375 ) {
2376 	icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
2377 	icc *icp = p->icp;
2378 	unsigned long i;
2379 	unsigned int len;
2380 	char *bp, *buf;		/* Buffer to write from */
2381 	int rv = 0;
2382 
2383 	/* Allocate a file write buffer */
2384 	len = p->get_size((icmBase *)p);
2385 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2386 		sprintf(icp->err,"icmU16Fixed16Array_write malloc() failed");
2387 		return icp->errc = 2;
2388 	}
2389 	bp = buf;
2390 
2391 	/* Write type descriptor to the buffer */
2392 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
2393 		sprintf(icp->err,"icmU16Fixed16Array_write: write_SInt32Number() failed");
2394 		icp->al->free(icp->al, buf);
2395 		return icp->errc = rv;
2396 	}
2397 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
2398 
2399 	/* Write all the data to the buffer */
2400 	bp += 8;	/* Skip padding */
2401 	for (i = 0; i < p->size; i++, bp += 4) {
2402 		if ((rv = write_U16Fixed16Number(p->data[i],bp)) != 0) {
2403 			sprintf(icp->err,"icmU16Fixed16Array_write: write_U16Fixed16umber() failed");
2404 			icp->al->free(icp->al, buf);
2405 			return icp->errc = rv;
2406 		}
2407 	}
2408 
2409 	/* Write to the file */
2410 	if (   icp->fp->seek(icp->fp, of) != 0
2411 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
2412 		sprintf(icp->err,"icmU16Fixed16Array_write fseek() or fwrite() failed");
2413 		icp->al->free(icp->al, buf);
2414 		return icp->errc = 2;
2415 	}
2416 	icp->al->free(icp->al, buf);
2417 	return 0;
2418 }
2419 
2420 /* Dump a text description of the object */
icmU16Fixed16Array_dump(icmBase * pp,FILE * op,int verb)2421 static void icmU16Fixed16Array_dump(
2422 	icmBase *pp,
2423 	FILE *op,		/* Output to dump to */
2424 	int   verb		/* Verbosity level */
2425 ) {
2426 	icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
2427 	if (verb <= 0)
2428 		return;
2429 
2430 	fprintf(op,"U16Fixed16Array:\n");
2431 	fprintf(op,"  No. elements = %lu\n",p->size);
2432 	if (verb >= 2) {
2433 		unsigned long i;
2434 		for (i = 0; i < p->size; i++)
2435 			fprintf(op,"    %lu:  %f\n",i,p->data[i]);
2436 	}
2437 }
2438 
2439 /* Allocate variable sized data elements */
icmU16Fixed16Array_allocate(icmBase * pp)2440 static int icmU16Fixed16Array_allocate(
2441 	icmBase *pp
2442 ) {
2443 	icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
2444 	icc *icp = p->icp;
2445 
2446 	if (p->size != p->_size) {
2447 		if (p->data != NULL)
2448 			icp->al->free(icp->al, p->data);
2449 		if ((p->data = (double *) icp->al->malloc(icp->al, p->size * sizeof(double))) == NULL) {
2450 			sprintf(icp->err,"icmU16Fixed16Array_alloc: malloc() of icmU16Fixed16Array data failed");
2451 			return icp->errc = 2;
2452 		}
2453 		p->_size = p->size;
2454 	}
2455 	return 0;
2456 }
2457 
2458 /* Free all storage in the object */
icmU16Fixed16Array_delete(icmBase * pp)2459 static void icmU16Fixed16Array_delete(
2460 	icmBase *pp
2461 ) {
2462 	icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
2463 	icc *icp = p->icp;
2464 
2465 	if (p->data != NULL)
2466 		icp->al->free(icp->al, p->data);
2467 	icp->al->free(icp->al, p);
2468 }
2469 
2470 /* Create an empty object. Return null on error */
new_icmU16Fixed16Array(icc * icp)2471 static icmBase *new_icmU16Fixed16Array(
2472 	icc *icp
2473 ) {
2474 	icmU16Fixed16Array *p;
2475 	if ((p = (icmU16Fixed16Array *) icp->al->calloc(icp->al,1,sizeof(icmU16Fixed16Array))) == NULL)
2476 		return NULL;
2477 	p->ttype    = icSigU16Fixed16ArrayType;
2478 	p->refcount = 1;
2479 	p->get_size = icmU16Fixed16Array_get_size;
2480 	p->read     = icmU16Fixed16Array_read;
2481 	p->write    = icmU16Fixed16Array_write;
2482 	p->dump     = icmU16Fixed16Array_dump;
2483 	p->allocate = icmU16Fixed16Array_allocate;
2484 	p->del      = icmU16Fixed16Array_delete;
2485 	p->icp      = icp;
2486 
2487 	return (icmBase *)p;
2488 }
2489 
2490 /* ---------------------------------------------------------- */
2491 /* icmS15Fixed16Array object */
2492 
2493 /* Return the number of bytes needed to write this tag */
icmS15Fixed16Array_get_size(icmBase * pp)2494 static unsigned int icmS15Fixed16Array_get_size(
2495 	icmBase *pp
2496 ) {
2497 	icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
2498 	unsigned int len = 0;
2499 	len += 8;			/* 8 bytes for tag and padding */
2500 	len += p->size * 4;	/* 4 byte for each S15Fixed16 */
2501 	return len;
2502 }
2503 
2504 /* read the object, return 0 on success, error code on fail */
icmS15Fixed16Array_read(icmBase * pp,unsigned long len,unsigned long of)2505 static int icmS15Fixed16Array_read(
2506 	icmBase *pp,
2507 	unsigned long len,		/* tag length */
2508 	unsigned long of		/* start offset within file */
2509 ) {
2510 	icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
2511 	icc *icp = p->icp;
2512 	int rv = 0;
2513 	unsigned long i, size;
2514 	char *bp, *buf;
2515 
2516 	if (len < 8) {
2517 		sprintf(icp->err,"icmS15Fixed16Array_read: Tag too small to be legal");
2518 		return icp->errc = 1;
2519 	}
2520 
2521 	/* Allocate a file read buffer */
2522 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2523 		sprintf(icp->err,"icmS15Fixed16Array_read: malloc() failed");
2524 		return icp->errc = 2;
2525 	}
2526 	bp = buf;
2527 
2528 	/* Read portion of file into buffer */
2529 	if (   icp->fp->seek(icp->fp, of) != 0
2530 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
2531 		sprintf(icp->err,"icmS15Fixed16Array_read: fseek() or fread() failed");
2532 		icp->al->free(icp->al, buf);
2533 		return icp->errc = 1;
2534 	}
2535 	p->size = size = (len - 8)/4;		/* Number of elements in the array */
2536 
2537 	if ((rv = p->allocate((icmBase *)p)) != 0) {
2538 		icp->al->free(icp->al, buf);
2539 		return rv;
2540 	}
2541 
2542 	/* Read type descriptor from the buffer */
2543 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
2544 		sprintf(icp->err,"icmS15Fixed16Array_read: Wrong tag type for icmS15Fixed16Array");
2545 		icp->al->free(icp->al, buf);
2546 		return icp->errc = 1;
2547 	}
2548 	bp += 8;	/* Skip padding */
2549 
2550 	/* Read all the data from the buffer */
2551 	for (i = 0; i < size; i++, bp += 4) {
2552 		p->data[i] = read_S15Fixed16Number(bp);
2553 	}
2554 	icp->al->free(icp->al, buf);
2555 	return 0;
2556 }
2557 
2558 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmS15Fixed16Array_write(icmBase * pp,unsigned long of)2559 static int icmS15Fixed16Array_write(
2560 	icmBase *pp,
2561 	unsigned long of			/* File offset to write from */
2562 ) {
2563 	icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
2564 	icc *icp = p->icp;
2565 	unsigned long i;
2566 	unsigned int len;
2567 	char *bp, *buf;		/* Buffer to write from */
2568 	int rv = 0;
2569 
2570 	/* Allocate a file write buffer */
2571 	len = p->get_size((icmBase *)p);
2572 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2573 		sprintf(icp->err,"icmS15Fixed16Array_write malloc() failed");
2574 		return icp->errc = 2;
2575 	}
2576 	bp = buf;
2577 
2578 	/* Write type descriptor to the buffer */
2579 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
2580 		sprintf(icp->err,"icmS15Fixed16Array_write: write_SInt32Number() failed");
2581 		icp->al->free(icp->al, buf);
2582 		return icp->errc = rv;
2583 	}
2584 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
2585 
2586 	/* Write all the data to the buffer */
2587 	bp += 8;	/* Skip padding */
2588 	for (i = 0; i < p->size; i++, bp += 4) {
2589 		if ((rv = write_S15Fixed16Number(p->data[i],bp)) != 0) {
2590 			sprintf(icp->err,"icmS15Fixed16Array_write: write_S15Fixed16umber() failed");
2591 			icp->al->free(icp->al, buf);
2592 			return icp->errc = rv;
2593 		}
2594 	}
2595 
2596 	/* Write to the file */
2597 	if (   icp->fp->seek(icp->fp, of) != 0
2598 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
2599 		sprintf(icp->err,"icmS15Fixed16Array_write fseek() or fwrite() failed");
2600 		icp->al->free(icp->al, buf);
2601 		return icp->errc = 2;
2602 	}
2603 	icp->al->free(icp->al, buf);
2604 	return 0;
2605 }
2606 
2607 /* Dump a text description of the object */
icmS15Fixed16Array_dump(icmBase * pp,FILE * op,int verb)2608 static void icmS15Fixed16Array_dump(
2609 	icmBase *pp,
2610 	FILE *op,		/* Output to dump to */
2611 	int   verb		/* Verbosity level */
2612 ) {
2613 	icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
2614 	if (verb <= 0)
2615 		return;
2616 
2617 	fprintf(op,"S15Fixed16Array:\n");
2618 	fprintf(op,"  No. elements = %lu\n",p->size);
2619 	if (verb >= 2) {
2620 		unsigned long i;
2621 		for (i = 0; i < p->size; i++)
2622 			fprintf(op,"    %lu:  %f\n",i,p->data[i]);
2623 	}
2624 }
2625 
2626 /* Allocate variable sized data elements */
icmS15Fixed16Array_allocate(icmBase * pp)2627 static int icmS15Fixed16Array_allocate(
2628 	icmBase *pp
2629 ) {
2630 	icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
2631 	icc *icp = p->icp;
2632 
2633 	if (p->size != p->_size) {
2634 		if (p->data != NULL)
2635 			icp->al->free(icp->al, p->data);
2636 		if ((p->data = (double *) icp->al->malloc(icp->al, p->size * sizeof(double))) == NULL) {
2637 			sprintf(icp->err,"icmS15Fixed16Array_alloc: malloc() of icmS15Fixed16Array data failed");
2638 			return icp->errc = 2;
2639 		}
2640 		p->_size = p->size;
2641 	}
2642 	return 0;
2643 }
2644 
2645 /* Free all storage in the object */
icmS15Fixed16Array_delete(icmBase * pp)2646 static void icmS15Fixed16Array_delete(
2647 	icmBase *pp
2648 ) {
2649 	icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
2650 	icc *icp = p->icp;
2651 
2652 	if (p->data != NULL)
2653 		icp->al->free(icp->al, p->data);
2654 	icp->al->free(icp->al, p);
2655 }
2656 
2657 /* Create an empty object. Return null on error */
new_icmS15Fixed16Array(icc * icp)2658 static icmBase *new_icmS15Fixed16Array(
2659 	icc *icp
2660 ) {
2661 	icmS15Fixed16Array *p;
2662 	if ((p = (icmS15Fixed16Array *) icp->al->calloc(icp->al,1,sizeof(icmS15Fixed16Array))) == NULL)
2663 		return NULL;
2664 	p->ttype    = icSigS15Fixed16ArrayType;
2665 	p->refcount = 1;
2666 	p->get_size = icmS15Fixed16Array_get_size;
2667 	p->read     = icmS15Fixed16Array_read;
2668 	p->write    = icmS15Fixed16Array_write;
2669 	p->dump     = icmS15Fixed16Array_dump;
2670 	p->allocate = icmS15Fixed16Array_allocate;
2671 	p->del      = icmS15Fixed16Array_delete;
2672 	p->icp      = icp;
2673 
2674 	return (icmBase *)p;
2675 }
2676 
2677 /* ---------------------------------------------------------- */
2678 
2679 /* Data conversion support functions */
write_XYZNumber(icmXYZNumber * p,char * d)2680 static int write_XYZNumber(icmXYZNumber *p, char *d) {
2681 	int rv;
2682 	if ((rv = write_S15Fixed16Number(p->X, d + 0)) != 0)
2683 		return rv;
2684 	if ((rv = write_S15Fixed16Number(p->Y, d + 4)) != 0)
2685 		return rv;
2686 	if ((rv = write_S15Fixed16Number(p->Z, d + 8)) != 0)
2687 		return rv;
2688 	return 0;
2689 }
2690 
read_XYZNumber(icmXYZNumber * p,char * d)2691 static int read_XYZNumber(icmXYZNumber *p, char *d) {
2692 	p->X = read_S15Fixed16Number(d + 0);
2693 	p->Y = read_S15Fixed16Number(d + 4);
2694 	p->Z = read_S15Fixed16Number(d + 8);
2695 	return 0;
2696 }
2697 
2698 
2699 /* Helper: Return a string that shows the XYZ number value */
string_XYZNumber(icmXYZNumber * p)2700 static char *string_XYZNumber(icmXYZNumber *p) {
2701 	static char buf[40];
2702 
2703 	sprintf(buf,"%f, %f, %f", p->X, p->Y, p->Z);
2704 	return buf;
2705 }
2706 
2707 /* Helper: Return a string that shows the XYZ number value, */
2708 /* and the Lab D50 number in paren. */
string_XYZNumber_and_Lab(icmXYZNumber * p)2709 static char *string_XYZNumber_and_Lab(icmXYZNumber *p) {
2710 	static char buf[50];
2711 	double lab[3];
2712 	lab[0] = p->X;
2713 	lab[1] = p->Y;
2714 	lab[2] = p->Z;
2715 	icmXYZ2Lab(&icmD50, lab, lab);
2716 	sprintf(buf,"%f, %f, %f    [Lab %f, %f, %f]", p->X, p->Y, p->Z, lab[0], lab[1], lab[2]);
2717 	return buf;
2718 }
2719 
2720 /* icmXYZArray object */
2721 
2722 /* Return the number of bytes needed to write this tag */
icmXYZArray_get_size(icmBase * pp)2723 static unsigned int icmXYZArray_get_size(
2724 	icmBase *pp
2725 ) {
2726 	icmXYZArray *p = (icmXYZArray *)pp;
2727 	unsigned int len = 0;
2728 	len += 8;				/* 8 bytes for tag and padding */
2729 	len += p->size * 12;	/* 12 bytes for each XYZ */
2730 	return len;
2731 }
2732 
2733 /* read the object, return 0 on success, error code on fail */
icmXYZArray_read(icmBase * pp,unsigned long len,unsigned long of)2734 static int icmXYZArray_read(
2735 	icmBase *pp,
2736 	unsigned long len,		/* tag length */
2737 	unsigned long of		/* start offset within file */
2738 ) {
2739 	icmXYZArray *p = (icmXYZArray *)pp;
2740 	icc *icp = p->icp;
2741 	int rv = 0;
2742 	unsigned long i, size;
2743 	char *bp, *buf;
2744 
2745 	if (len < 8) {
2746 		sprintf(icp->err,"icmXYZArray_read: Tag too small to be legal");
2747 		return icp->errc = 1;
2748 	}
2749 
2750 	/* Allocate a file read buffer */
2751 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2752 		sprintf(icp->err,"icmXYZArray_read: malloc() failed");
2753 		return icp->errc = 2;
2754 	}
2755 	bp = buf;
2756 
2757 	/* Read portion of file into buffer */
2758 	if (   icp->fp->seek(icp->fp, of) != 0
2759 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
2760 		sprintf(icp->err,"icmXYZArray_read: fseek() or fread() failed");
2761 		icp->al->free(icp->al, buf);
2762 		return icp->errc = 1;
2763 	}
2764 	p->size = size = (len - 8)/12;		/* Number of elements in the array */
2765 
2766 	if ((rv = p->allocate((icmBase *)p)) != 0) {
2767 		icp->al->free(icp->al, buf);
2768 		return rv;
2769 	}
2770 
2771 	/* Read type descriptor from the buffer */
2772 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
2773 		sprintf(icp->err,"icmXYZArray_read: Wrong tag type for icmXYZArray");
2774 		icp->al->free(icp->al, buf);
2775 		return icp->errc = 1;
2776 	}
2777 	bp += 8;	/* Skip padding */
2778 
2779 	/* Read all the data from the buffer */
2780 	for (i = 0; i < size; i++, bp += 12) {
2781 		read_XYZNumber(&p->data[i], bp);
2782 	}
2783 	icp->al->free(icp->al, buf);
2784 	return 0;
2785 }
2786 
2787 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmXYZArray_write(icmBase * pp,unsigned long of)2788 static int icmXYZArray_write(
2789 	icmBase *pp,
2790 	unsigned long of			/* File offset to write from */
2791 ) {
2792 	icmXYZArray *p = (icmXYZArray *)pp;
2793 	icc *icp = p->icp;
2794 	unsigned long i;
2795 	unsigned int len;
2796 	char *bp, *buf;		/* Buffer to write from */
2797 	int rv = 0;
2798 
2799 	/* Allocate a file write buffer */
2800 	len = p->get_size((icmBase *)p);
2801 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2802 		sprintf(icp->err,"icmXYZArray_write malloc() failed");
2803 		return icp->errc = 2;
2804 	}
2805 	bp = buf;
2806 
2807 	/* Write type descriptor to the buffer */
2808 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
2809 		sprintf(icp->err,"icmXYZArray_write: write_SInt32Number() failed");
2810 		icp->al->free(icp->al, buf);
2811 		return icp->errc = rv;
2812 	}
2813 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
2814 
2815 	/* Write all the data to the buffer */
2816 	bp += 8;	/* Skip padding */
2817 	for (i = 0; i < p->size; i++, bp += 12) {
2818 		if ((rv = write_XYZNumber(&p->data[i],bp)) != 0) {
2819 			sprintf(icp->err,"icmXYZArray_write: write_XYZumber() failed");
2820 			icp->al->free(icp->al, buf);
2821 			return icp->errc = rv;
2822 		}
2823 	}
2824 
2825 	/* Write to the file */
2826 	if (   icp->fp->seek(icp->fp, of) != 0
2827 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
2828 		sprintf(icp->err,"icmXYZArray_write fseek() or fwrite() failed");
2829 		icp->al->free(icp->al, buf);
2830 		return icp->errc = 2;
2831 	}
2832 	icp->al->free(icp->al, buf);
2833 	return 0;
2834 }
2835 
2836 /* Dump a text description of the object */
icmXYZArray_dump(icmBase * pp,FILE * op,int verb)2837 static void icmXYZArray_dump(
2838 	icmBase *pp,
2839 	FILE *op,		/* Output to dump to */
2840 	int   verb		/* Verbosity level */
2841 ) {
2842 	icmXYZArray *p = (icmXYZArray *)pp;
2843 	if (verb <= 0)
2844 		return;
2845 
2846 	fprintf(op,"XYZArray:\n");
2847 	fprintf(op,"  No. elements = %lu\n",p->size);
2848 	if (verb >= 2) {
2849 		unsigned long i;
2850 		for (i = 0; i < p->size; i++) {
2851 			fprintf(op,"    %lu:  %s\n",i,string_XYZNumber_and_Lab(&p->data[i]));
2852 
2853 		}
2854 	}
2855 }
2856 
2857 
2858 /* Allocate variable sized data elements */
icmXYZArray_allocate(icmBase * pp)2859 static int icmXYZArray_allocate(
2860 	icmBase *pp
2861 ) {
2862 	icmXYZArray *p = (icmXYZArray *)pp;
2863 	icc *icp = p->icp;
2864 
2865 	if (p->size != p->_size) {
2866 		if (p->data != NULL)
2867 			icp->al->free(icp->al, p->data);
2868 		if ((p->data = (icmXYZNumber *) icp->al->malloc(icp->al, p->size * sizeof(icmXYZNumber))) == NULL) {
2869 			sprintf(icp->err,"icmXYZArray_alloc: malloc() of icmXYZArray data failed");
2870 			return icp->errc = 2;
2871 		}
2872 		p->_size = p->size;
2873 	}
2874 	return 0;
2875 }
2876 
2877 /* Free all storage in the object */
icmXYZArray_delete(icmBase * pp)2878 static void icmXYZArray_delete(
2879 	icmBase *pp
2880 ) {
2881 	icmXYZArray *p = (icmXYZArray *)pp;
2882 	icc *icp = p->icp;
2883 
2884 	if (p->data != NULL)
2885 		icp->al->free(icp->al, p->data);
2886 	icp->al->free(icp->al, p);
2887 }
2888 
2889 /* Create an empty object. Return null on error */
new_icmXYZArray(icc * icp)2890 static icmBase *new_icmXYZArray(
2891 	icc *icp
2892 ) {
2893 	icmXYZArray *p;
2894 	if ((p = (icmXYZArray *) icp->al->calloc(icp->al,1,sizeof(icmXYZArray))) == NULL)
2895 		return NULL;
2896 	p->ttype    = icSigXYZArrayType;
2897 	p->refcount = 1;
2898 	p->get_size = icmXYZArray_get_size;
2899 	p->read     = icmXYZArray_read;
2900 	p->write    = icmXYZArray_write;
2901 	p->dump     = icmXYZArray_dump;
2902 	p->allocate = icmXYZArray_allocate;
2903 	p->del      = icmXYZArray_delete;
2904 	p->icp      = icp;
2905 
2906 	return (icmBase *)p;
2907 }
2908 
2909 /* ---------------------------------------------------------- */
2910 /* icmCurve object */
2911 
2912 /* Do a forward lookup through the curve */
2913 /* Return 0 on success, 1 if clipping occured, 2 on other error */
icmCurve_lookup_fwd(icmCurve * p,double * out,double * in)2914 static int icmCurve_lookup_fwd(
2915 	icmCurve *p,
2916 	double *out,
2917 	double *in
2918 ) {
2919 	int rv = 0;
2920 	if (p->flag == icmCurveLin) {
2921 		*out = *in;
2922 	} else if (p->flag == icmCurveGamma) {
2923 		double val = *in;
2924 		if (val <= 0.0)
2925 			*out = 0.0;
2926 		else
2927 			*out = pow(val, p->data[0]);
2928 	} else { /* Use linear interpolation */
2929 		int ix;
2930 		double val, w;
2931 		double inputEnt_1 = (double)(p->size-1);
2932 
2933 		val = *in * inputEnt_1;
2934 		if (val < 0.0) {
2935 			val = 0.0;
2936 			rv |= 1;
2937 		} else if (val > inputEnt_1) {
2938 			val = inputEnt_1;
2939 			rv |= 1;
2940 		}
2941 		ix = (int)floor(val);		/* Coordinate */
2942 		if (ix > (p->size-2))
2943 			ix = (p->size-2);
2944 		w = val - (double)ix;		/* weight */
2945 		val = p->data[ix];
2946 		*out = val + w * (p->data[ix+1] - val);
2947 	}
2948 	return rv;
2949 }
2950 
2951 /* - - - - - - - - - - - - */
2952 /* Support for reverse interpolation of 1D lookup tables */
2953 
2954 /* Create a reverse curve lookup acceleration table */
2955 /* return non-zero on error, 2 = malloc error. */
icmTable_setup_bwd(icc * icp,icmRevTable * rt,unsigned long size,double * data)2956 static int icmTable_setup_bwd(
2957 	icc          *icp,			/* Base icc object */
2958 	icmRevTable  *rt,			/* Reverse table data to setup */
2959 	unsigned long size,			/* Size of fwd table */
2960 	double       *data			/* Table */
2961 ) {
2962 	int i;
2963 
2964 	rt->size = size;		/* Stash pointers to these away */
2965 	rt->data = data;
2966 
2967 	/* Find range of output values */
2968 	rt->rmin = 1e300;
2969 	rt->rmax = -1e300;
2970 	for (i = 0; i < rt->size; i++) {
2971 		if (rt->data[i] > rt->rmax)
2972 			rt->rmax = rt->data[i];
2973 		if (rt->data[i] < rt->rmin)
2974 			rt->rmin = rt->data[i];
2975 	}
2976 
2977 	/* Decide on reverse granularity */
2978 	rt->rsize = (rt->size+2)/2;
2979 	rt->qscale = (double)rt->rsize/(rt->rmax - rt->rmin);	/* Scale factor to quantize to */
2980 
2981 	/* Initialize the reverse lookup structures, and get overall min/max */
2982 	if ((rt->rlists = (int **) icp->al->calloc(icp->al, 1, rt->rsize * sizeof(int *))) == NULL) {
2983 		return 2;
2984 	}
2985 
2986 	/* Assign each output value range bucket lists it intersects */
2987 	for (i = 0; i < (rt->size-1); i++) {
2988 		int s, e, j;	/* Start and end indexes (inclusive) */
2989 		s = (int)((rt->data[i] - rt->rmin) * rt->qscale);
2990 		e = (int)((rt->data[i+1] - rt->rmin) * rt->qscale);
2991 		if (s > e) {	/* swap */
2992 			int t;
2993 			t = s; s = e; e = t;
2994 		}
2995 		if (e >= rt->rsize)
2996 			e = rt->rsize-1;
2997 
2998 		/* For all buckets that may contain this output range, add index of this output */
2999 		for (j = s; j <= e; j++) {
3000 			int as;			/* Allocation size */
3001 			int nf;			/* Next free slot */
3002 			if (rt->rlists[j] == NULL) {	/* No allocation */
3003 				as = 5;						/* Start with space for 5 */
3004 				if ((rt->rlists[j] = (int *) icp->al->malloc(icp->al, sizeof(int) * as)) == NULL) {
3005 					return 2;
3006 				}
3007 				rt->rlists[j][0] = as;
3008 				nf = rt->rlists[j][1] = 2;
3009 			} else {
3010 				as = rt->rlists[j][0];	/* Allocate space for this list */
3011 				nf = rt->rlists[j][1];	/* Next free location in list */
3012 				if (nf >= as) {			/* need to expand space */
3013 					as *= 2;
3014 					rt->rlists[j] = (int *) icp->al->realloc(icp->al,rt->rlists[j], sizeof(int) * as);
3015 					if (rt->rlists[j] == NULL) {
3016 						return 2;
3017 					}
3018 					rt->rlists[j][0] = as;
3019 				}
3020 			}
3021 			rt->rlists[j][nf++] = i;
3022 			rt->rlists[j][1] = nf;
3023 		}
3024 	}
3025 	rt->inited = 1;
3026 	return 0;
3027 }
3028 
3029 /* Free up any data */
icmTable_delete_bwd(icc * icp,icmRevTable * rt)3030 static void icmTable_delete_bwd(
3031 	icc          *icp,			/* Base icc */
3032 	icmRevTable  *rt			/* Reverse table data to setup */
3033 ) {
3034 	if (rt->inited != 0) {
3035 		while (rt->rsize > 0)
3036 			icp->al->free(icp->al, rt->rlists[--rt->rsize]);
3037 		icp->al->free(icp->al, rt->rlists);
3038 		rt->size = 0;			/* Don't keep these */
3039 		rt->data = NULL;
3040 	}
3041 }
3042 
3043 /* Do a reverse lookup through the curve */
3044 /* Return 0 on success, 1 if clipping occured, 2 on other error */
icmTable_lookup_bwd(icmRevTable * rt,double * out,double * in)3045 static int icmTable_lookup_bwd(
3046 	icmRevTable *rt,
3047 	double *out,
3048 	double *in
3049 ) {
3050 	int rv = 0;
3051 	int ix, i, k;
3052 	double oval, ival = *in, val;
3053 	double rsize_1;
3054 
3055 	/* Find appropriate reverse list */
3056 	rsize_1 = (double)(rt->rsize-1);
3057 	val = ((ival - rt->rmin) * rt->qscale);
3058 	if (val < 0.0)
3059 		val = 0.0;
3060 	else if (val > rsize_1)
3061 		val = rsize_1;
3062 	ix = (int)floor(val);		/* Coordinate */
3063 
3064 	if (ix > (rt->size-2))
3065 		ix = (rt->size-2);
3066 	if (rt->rlists[ix] != NULL)  {		/* There is a list of fwd candidates */
3067 		/* For each candidate forward range */
3068 		for (i = 2; i < rt->rlists[ix][1]; i++)  {	/* For all fwd indexes */
3069 			double lv,hv;
3070 			k = rt->rlists[ix][i];					/* Base index */
3071 			lv = rt->data[k];
3072 			hv = rt->data[k+1];
3073 			if ((ival >= lv && ival <= hv)	/* If this slot contains output value */
3074 			 || (ival >= hv && ival <= lv)) {
3075 				/* Reverse linear interpolation */
3076 				if (hv == lv) {	/* Technically non-monotonic - due to quantization ? */
3077 					oval = (k + 0.5)/(rt->size-1.0);
3078 				} else
3079 					oval = (k + ((ival - lv)/(hv - lv)))/(rt->size-1.0);
3080 				/* If we kept looking, we would find multiple */
3081 				/* solution for non-monotonic curve */
3082 				*out = oval;
3083 				return rv;
3084 			}
3085 		}
3086 	}
3087 
3088 	/* We have failed to find an exact value, so return the nearest value */
3089 	/* (This is slow !) */
3090 	val = fabs(ival - rt->data[0]);
3091 	for (k = 0, i = 1; i < rt->size; i++) {
3092 		double er;
3093 		er = fabs(ival - rt->data[i]);
3094 		if (er < val) {	/* new best */
3095 			val = er;
3096 			k = i;
3097 		}
3098 	}
3099 	*out = k/(rt->size-1.0);
3100 	rv |= 1;
3101 	return rv;
3102 }
3103 
3104 
3105 /* - - - - - - - - - - - - */
3106 
3107 /* Do a reverse lookup through the curve */
3108 /* Return 0 on success, 1 if clipping occured, 2 on other error */
icmCurve_lookup_bwd(icmCurve * p,double * out,double * in)3109 static int icmCurve_lookup_bwd(
3110 	icmCurve *p,
3111 	double *out,
3112 	double *in
3113 ) {
3114 	icc *icp = p->icp;
3115 	int rv = 0;
3116 	if (p->flag == icmCurveLin) {
3117 		*out = *in;
3118 	} else if (p->flag == icmCurveGamma) {
3119 		double val = *in;
3120 		if (val <= 0.0)
3121 			*out = 0.0;
3122 		else
3123 			*out = pow(val, 1.0/p->data[0]);
3124 	} else { /* Use linear interpolation */
3125 		if (p->rt.inited == 0) {
3126 			rv = icmTable_setup_bwd(icp, &p->rt, p->size, p->data);
3127 			if (rv != 0) {
3128 				sprintf(icp->err,"icmCurve_lookup: Malloc failure in reverse lookup init.");
3129 				return icp->errc = rv;
3130 			}
3131 		}
3132 		rv = icmTable_lookup_bwd(&p->rt, out, in);
3133 	}
3134 	return rv;
3135 }
3136 
3137 /* Return the number of bytes needed to write this tag */
icmCurve_get_size(icmBase * pp)3138 static unsigned int icmCurve_get_size(
3139 	icmBase *pp
3140 ) {
3141 	icmCurve *p = (icmCurve *)pp;
3142 	unsigned int len = 0;
3143 	len += 12;			/* 12 bytes for tag, padding and count */
3144 	len += p->size * 2;	/* 2 bytes for each UInt16 */
3145 	return len;
3146 }
3147 
3148 /* read the object, return 0 on success, error code on fail */
icmCurve_read(icmBase * pp,unsigned long len,unsigned long of)3149 static int icmCurve_read(
3150 	icmBase *pp,
3151 	unsigned long len,		/* tag length */
3152 	unsigned long of		/* start offset within file */
3153 ) {
3154 	icmCurve *p = (icmCurve *)pp;
3155 	icc *icp = p->icp;
3156 	int rv = 0;
3157 	unsigned long i;
3158 	char *bp, *buf, *end;
3159 
3160 	if (len < 12) {
3161 		sprintf(icp->err,"icmCurve_read: Tag too small to be legal");
3162 		return icp->errc = 1;
3163 	}
3164 
3165 	/* Allocate a file read buffer */
3166 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3167 		sprintf(icp->err,"icmCurve_read: malloc() failed");
3168 		return icp->errc = 2;
3169 	}
3170 	bp = buf;
3171 	end = buf + len;
3172 
3173 	/* Read portion of file into buffer */
3174 	if (   icp->fp->seek(icp->fp, of) != 0
3175 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
3176 		sprintf(icp->err,"icmCurve_read: fseek() or fread() failed");
3177 		icp->al->free(icp->al, buf);
3178 		return icp->errc = 1;
3179 	}
3180 
3181 	/* Read type descriptor from the buffer */
3182 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
3183 		sprintf(icp->err,"icmCurve_read: Wrong tag type for icmCurve");
3184 		icp->al->free(icp->al, buf);
3185 		return icp->errc = 1;
3186 	}
3187 
3188 	p->size = read_UInt32Number(bp+8);
3189 	bp = bp + 12;
3190 
3191 	/* Set flag up before allocating */
3192 	if (p->size == 0) {		/* Linear curve */
3193 		p->flag = icmCurveLin;
3194 	} else if (p->size == 1) {	/* Gamma curve */
3195 		p->flag = icmCurveGamma;
3196 	} else {
3197 		p->flag = icmCurveSpec;
3198 	}
3199 
3200 	if ((rv = p->allocate((icmBase *)p)) != 0) {
3201 		icp->al->free(icp->al, buf);
3202 		return rv;
3203 	}
3204 
3205 	if (p->flag == icmCurveGamma) {	/* Gamma curve */
3206 		if ((bp + 1) > end) {
3207 			sprintf(icp->err,"icmCurve_read: Data too short to curve gamma");
3208 			icp->al->free(icp->al, buf);
3209 			return icp->errc = 1;
3210 		}
3211 		p->data[0] = read_U8Fixed8Number(bp);
3212 	} else if (p->flag == icmCurveSpec) {
3213 		/* Read all the data from the buffer */
3214 		for (i = 0; i < p->size; i++, bp += 2) {
3215 			if ((bp + 2) > end) {
3216 				sprintf(icp->err,"icmData_read: Data too short to curve value");
3217 				icp->al->free(icp->al, buf);
3218 				return icp->errc = 1;
3219 			}
3220 			p->data[i] = read_DCS16Number(bp);
3221 		}
3222 	}
3223 	icp->al->free(icp->al, buf);
3224 	return 0;
3225 }
3226 
3227 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmCurve_write(icmBase * pp,unsigned long of)3228 static int icmCurve_write(
3229 	icmBase *pp,
3230 	unsigned long of			/* File offset to write from */
3231 ) {
3232 	icmCurve *p = (icmCurve *)pp;
3233 	icc *icp = p->icp;
3234 	unsigned long i;
3235 	unsigned int len;
3236 	char *bp, *buf;		/* Buffer to write from */
3237 	int rv = 0;
3238 
3239 	/* Allocate a file write buffer */
3240 	len = p->get_size((icmBase *)p);
3241 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3242 		sprintf(icp->err,"icmCurve_write malloc() failed");
3243 		return icp->errc = 2;
3244 	}
3245 	bp = buf;
3246 
3247 	/* Write type descriptor to the buffer */
3248 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
3249 		sprintf(icp->err,"icmCurve_write: write_SInt32Number() failed");
3250 		icp->al->free(icp->al, buf);
3251 		return icp->errc = rv;
3252 	}
3253 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
3254 	/* Write count */
3255 	if ((rv = write_UInt32Number(p->size,bp+8)) != 0) {
3256 		sprintf(icp->err,"icmCurve_write: write_UInt32Number() failed");
3257 		icp->al->free(icp->al, buf);
3258 		return icp->errc = rv;
3259 	}
3260 
3261 	/* Write all the data to the buffer */
3262 	bp += 12;	/* Skip padding */
3263 	if (p->flag == icmCurveLin) {
3264 		if (p->size != 0) {
3265 			sprintf(icp->err,"icmCurve_write: Must be exactly 0 entry for Linear");
3266 			icp->al->free(icp->al, buf);
3267 			return icp->errc = 1;
3268 		}
3269 	} else if (p->flag == icmCurveGamma) {
3270 		if (p->size != 1) {
3271 			sprintf(icp->err,"icmCurve_write: Must be exactly 1 entry for Gamma");
3272 			icp->al->free(icp->al, buf);
3273 			return icp->errc = 1;
3274 		}
3275 		if ((rv = write_U8Fixed8Number(p->data[0],bp)) != 0) {
3276 			sprintf(icp->err,"icmCurve_write: write_U8Fixed8umber(%f) failed",p->data[0]);
3277 			icp->al->free(icp->al, buf);
3278 			return icp->errc = rv;
3279 		}
3280 	} else if (p->flag == icmCurveSpec) {
3281 		if (p->size < 2) {
3282 			sprintf(icp->err,"icmCurve_write: Must be 2 or more entries for Specified curve");
3283 			icp->al->free(icp->al, buf);
3284 			return icp->errc = 1;
3285 		}
3286 		for (i = 0; i < p->size; i++, bp += 2) {
3287 			if ((rv = write_DCS16Number(p->data[i],bp)) != 0) {
3288 				sprintf(icp->err,"icmCurve_write: write_UInt16umber(%f) failed",p->data[i]);
3289 				icp->al->free(icp->al, buf);
3290 				return icp->errc = rv;
3291 			}
3292 		}
3293 	}
3294 
3295 	/* Write to the file */
3296 	if (   icp->fp->seek(icp->fp, of) != 0
3297 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
3298 		sprintf(icp->err,"icmCurve_write fseek() or fwrite() failed");
3299 		icp->al->free(icp->al, buf);
3300 		return icp->errc = 2;
3301 	}
3302 	icp->al->free(icp->al, buf);
3303 	return 0;
3304 }
3305 
3306 /* Dump a text description of the object */
icmCurve_dump(icmBase * pp,FILE * op,int verb)3307 static void icmCurve_dump(
3308 	icmBase *pp,
3309 	FILE *op,		/* Output to dump to */
3310 	int   verb		/* Verbosity level */
3311 ) {
3312 	icmCurve *p = (icmCurve *)pp;
3313 	if (verb <= 0)
3314 		return;
3315 
3316 	fprintf(op,"Curve:\n");
3317 
3318 	if (p->flag == icmCurveLin) {
3319 		fprintf(op,"  Curve is linear\n");
3320 	} else if (p->flag == icmCurveGamma) {
3321 		fprintf(op,"  Curve is gamma of %f\n",p->data[0]);
3322 	} else {
3323 		fprintf(op,"  No. elements = %lu\n",p->size);
3324 		if (verb >= 2) {
3325 			unsigned long i;
3326 			for (i = 0; i < p->size; i++)
3327 				fprintf(op,"    %3lu:  %f\n",i,p->data[i]);
3328 		}
3329 	}
3330 }
3331 
3332 /* Allocate variable sized data elements */
icmCurve_allocate(icmBase * pp)3333 static int icmCurve_allocate(
3334 	icmBase *pp
3335 ) {
3336 	icmCurve *p = (icmCurve *)pp;
3337 	icc *icp = p->icp;
3338 
3339 	if (p->flag == icmCurveUndef) {
3340 		sprintf(icp->err,"icmCurve_alloc: flag not set");
3341 		return icp->errc = 1;
3342 	} else if (p->flag == icmCurveLin) {
3343 		p->size = 0;
3344 	} else if (p->flag == icmCurveGamma) {
3345 		p->size = 1;
3346 	}
3347 	if (p->size != p->_size) {
3348 		if (p->data != NULL)
3349 			icp->al->free(icp->al, p->data);
3350 		if ((p->data = (double *) icp->al->malloc(icp->al, p->size * sizeof(double))) == NULL) {
3351 			sprintf(icp->err,"icmCurve_alloc: malloc() of icmCurve data failed");
3352 			return icp->errc = 2;
3353 		}
3354 		p->_size = p->size;
3355 	}
3356 	return 0;
3357 }
3358 
3359 /* Free all storage in the object */
icmCurve_delete(icmBase * pp)3360 static void icmCurve_delete(
3361 	icmBase *pp
3362 ) {
3363 	icmCurve *p = (icmCurve *)pp;
3364 	icc *icp = p->icp;
3365 
3366 	if (p->data != NULL)
3367 		icp->al->free(icp->al, p->data);
3368 	icmTable_delete_bwd(icp, &p->rt);	/* Free reverse table info */
3369 	icp->al->free(icp->al, p);
3370 }
3371 
3372 /* Create an empty object. Return null on error */
new_icmCurve(icc * icp)3373 static icmBase *new_icmCurve(
3374 	icc *icp
3375 ) {
3376 	icmCurve *p;
3377 	if ((p = (icmCurve *) icp->al->calloc(icp->al,1,sizeof(icmCurve))) == NULL)
3378 		return NULL;
3379 	p->ttype    = icSigCurveType;
3380 	p->refcount = 1;
3381 	p->get_size = icmCurve_get_size;
3382 	p->read     = icmCurve_read;
3383 	p->write    = icmCurve_write;
3384 	p->dump     = icmCurve_dump;
3385 	p->allocate = icmCurve_allocate;
3386 	p->del      = icmCurve_delete;
3387 	p->icp      = icp;
3388 
3389 	p->lookup_fwd = icmCurve_lookup_fwd;
3390 	p->lookup_bwd = icmCurve_lookup_bwd;
3391 
3392 	p->rt.inited = 0;
3393 
3394 	p->flag = icmCurveUndef;
3395 	return (icmBase *)p;
3396 }
3397 
3398 /* ---------------------------------------------------------- */
3399 /* icmData object */
3400 
3401 /* Return the number of bytes needed to write this tag */
icmData_get_size(icmBase * pp)3402 static unsigned int icmData_get_size(
3403 	icmBase *pp
3404 ) {
3405 	icmData *p = (icmData *)pp;
3406 	unsigned int len = 0;
3407 	len += 12;			/* 12 bytes for tag and padding */
3408 	len += p->size * 1;	/* 1 byte for each data element */
3409 	return len;
3410 }
3411 
3412 /* read the object, return 0 on success, error code on fail */
icmData_read(icmBase * pp,unsigned long len,unsigned long of)3413 static int icmData_read(
3414 	icmBase *pp,
3415 	unsigned long len,		/* tag length */
3416 	unsigned long of		/* start offset within file */
3417 ) {
3418 	icmData *p = (icmData *)pp;
3419 	icc *icp = p->icp;
3420 	int rv = 0;
3421 	unsigned size, f;
3422 	char *bp, *buf;
3423 
3424 	if (len < 12) {
3425 		sprintf(icp->err,"icmData_read: Tag too small to be legal");
3426 		return icp->errc = 1;
3427 	}
3428 
3429 	/* Allocate a file read buffer */
3430 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3431 		sprintf(icp->err,"icmData_read: malloc() failed");
3432 		return icp->errc = 2;
3433 	}
3434 	bp = buf;
3435 
3436 	/* Read portion of file into buffer */
3437 	if (   icp->fp->seek(icp->fp, of) != 0
3438 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
3439 		sprintf(icp->err,"icmData_read: fseek() or fread() failed");
3440 		icp->al->free(icp->al, buf);
3441 		return icp->errc = 1;
3442 	}
3443 	p->size = size = (len - 12)/1;		/* Number of elements in the array */
3444 
3445 	/* Read type descriptor from the buffer */
3446 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
3447 		sprintf(icp->err,"icmData_read: Wrong tag type for icmData");
3448 		icp->al->free(icp->al, buf);
3449 		return icp->errc = 1;
3450 	}
3451 	/* Read the data type flag */
3452 	f = read_UInt32Number(bp+8);
3453 	if (f == 0) {
3454 		p->flag = icmDataASCII;
3455 	} else if (f == 1) {
3456 		p->flag = icmDataBin;
3457 	} else {
3458 		sprintf(icp->err,"icmData_read: Unknown flag value 0x%x",f);
3459 		icp->al->free(icp->al, buf);
3460 		return icp->errc = 1;
3461 	}
3462 	bp += 12;	/* Skip padding and flag */
3463 
3464 	if (p->size > 0) {
3465 		if (p->flag == icmDataASCII) {
3466 			if (check_null_string(bp,p->size) != 0) {
3467 				sprintf(icp->err,"icmData_read: ACSII is not null terminated");
3468 				icp->al->free(icp->al, buf);
3469 				return icp->errc = 1;
3470 			}
3471 		}
3472 		if ((rv = p->allocate((icmBase *)p)) != 0) {
3473 			icp->al->free(icp->al, buf);
3474 			return rv;
3475 		}
3476 
3477 		memcpy((void *)p->data, (void *)bp, p->size);
3478 	}
3479 	icp->al->free(icp->al, buf);
3480 	return 0;
3481 }
3482 
3483 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmData_write(icmBase * pp,unsigned long of)3484 static int icmData_write(
3485 	icmBase *pp,
3486 	unsigned long of			/* File offset to write from */
3487 ) {
3488 	icmData *p = (icmData *)pp;
3489 	icc *icp = p->icp;
3490 	unsigned int len, f;
3491 	char *bp, *buf;		/* Buffer to write from */
3492 	int rv = 0;
3493 
3494 	/* Allocate a file write buffer */
3495 	len = p->get_size((icmBase *)p);
3496 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3497 		sprintf(icp->err,"icmData_write malloc() failed");
3498 		return icp->errc = 2;
3499 	}
3500 	bp = buf;
3501 
3502 	/* Write type descriptor to the buffer */
3503 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
3504 		sprintf(icp->err,"icmData_write: write_SInt32Number() failed");
3505 		icp->al->free(icp->al, buf);
3506 		return icp->errc = rv;
3507 	}
3508 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
3509 	switch(p->flag) {
3510 		case icmDataASCII:
3511 			f = 0;
3512 			break;
3513 		case icmDataBin:
3514 			f = 1;
3515 			break;
3516 		default:
3517 			sprintf(icp->err,"icmData_write: Unknown Data Flag value");
3518 			icp->al->free(icp->al, buf);
3519 			return icp->errc = 1;
3520 	}
3521 	/* Write data flag descriptor to the buffer */
3522 	if ((rv = write_UInt32Number(f,bp+8)) != 0) {
3523 		sprintf(icp->err,"icmData_write: write_SInt32Number() failed");
3524 		icp->al->free(icp->al, buf);
3525 		return icp->errc = rv;
3526 	}
3527 	bp += 12;	/* Skip padding */
3528 
3529 	if (p->data != NULL) {
3530 		if (p->flag == icmDataASCII) {
3531 			if ((rv = check_null_string((char *)p->data, p->size)) != 0) {
3532 				sprintf(icp->err,"icmData_write: ASCII is not null terminated");
3533 				icp->al->free(icp->al, buf);
3534 				return icp->errc = 1;
3535 			}
3536 		}
3537 		memcpy((void *)bp, (void *)p->data, p->size);
3538 	}
3539 
3540 	/* Write to the file */
3541 	if (   icp->fp->seek(icp->fp, of) != 0
3542 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
3543 		sprintf(icp->err,"icmData_write fseek() or fwrite() failed");
3544 		icp->al->free(icp->al, buf);
3545 		return icp->errc = 2;
3546 	}
3547 	icp->al->free(icp->al, buf);
3548 	return 0;
3549 }
3550 
3551 /* Dump a text description of the object */
icmData_dump(icmBase * pp,FILE * op,int verb)3552 static void icmData_dump(
3553 	icmBase *pp,
3554 	FILE *op,		/* Output to dump to */
3555 	int   verb		/* Verbosity level */
3556 ) {
3557 	icmData *p = (icmData *)pp;
3558 	unsigned long i, r, c, size = 0;
3559 
3560 	if (verb <= 0)
3561 		return;
3562 
3563 	fprintf(op,"Data:\n");
3564 	switch(p->flag) {
3565 		case icmDataASCII:
3566 			fprintf(op,"  ASCII data\n");
3567 			size = p->size > 0 ? p->size-1 : 0;
3568 			break;
3569 		case icmDataBin:
3570 			fprintf(op,"  Binary data\n");
3571 			size = p->size;
3572 			break;
3573 		case icmDataUndef:
3574 			fprintf(op,"  Undefined data\n");
3575 			size = p->size;
3576 			break;
3577 	}
3578 	fprintf(op,"  No. elements = %lu\n",p->size);
3579 
3580 	i = 0;
3581 	for (r = 1;; r++) {		/* count rows */
3582 		if (i >= size) {
3583 			fprintf(op,"\n");
3584 			break;
3585 		}
3586 		if (r > 1 && verb < 2) {
3587 			fprintf(op,"...\n");
3588 			break;			/* Print 1 row if not verbose */
3589 		}
3590 		c = 1;
3591 		fprintf(op,"    0x%04lx: ",i);
3592 		c += 10;
3593 		while (i < size && c < 75) {
3594 			if (p->flag == icmDataASCII) {
3595 				if (isprint(p->data[i])) {
3596 					fprintf(op,"%c",p->data[i]);
3597 					c++;
3598 				} else {
3599 					fprintf(op,"\\%03o",p->data[i]);
3600 					c += 4;
3601 				}
3602 			} else {
3603 				fprintf(op,"%02x ",p->data[i]);
3604 				c += 3;
3605 			}
3606 			i++;
3607 		}
3608 		if (i < size)
3609 			fprintf(op,"\n");
3610 	}
3611 }
3612 
3613 /* Allocate variable sized data elements */
icmData_allocate(icmBase * pp)3614 static int icmData_allocate(
3615 	icmBase *pp
3616 ) {
3617 	icmData *p = (icmData *)pp;
3618 	icc *icp = p->icp;
3619 
3620 	if (p->size != p->_size) {
3621 		if (p->data != NULL)
3622 			icp->al->free(icp->al, p->data);
3623 		if ((p->data = (unsigned char *) icp->al->malloc(icp->al, p->size * sizeof(unsigned char))) == NULL) {
3624 			sprintf(icp->err,"icmData_alloc: malloc() of icmData data failed");
3625 			return icp->errc = 2;
3626 		}
3627 		p->_size = p->size;
3628 	}
3629 	return 0;
3630 }
3631 
3632 /* Free all storage in the object */
icmData_delete(icmBase * pp)3633 static void icmData_delete(
3634 	icmBase *pp
3635 ) {
3636 	icmData *p = (icmData *)pp;
3637 	icc *icp = p->icp;
3638 
3639 	if (p->data != NULL)
3640 		icp->al->free(icp->al, p->data);
3641 	icp->al->free(icp->al, p);
3642 }
3643 
3644 /* Create an empty object. Return null on error */
new_icmData(icc * icp)3645 static icmBase *new_icmData(
3646 	icc *icp
3647 ) {
3648 	icmData *p;
3649 	if ((p = (icmData *) icp->al->calloc(icp->al,1,sizeof(icmData))) == NULL)
3650 		return NULL;
3651 	p->ttype    = icSigDataType;
3652 	p->refcount = 1;
3653 	p->get_size = icmData_get_size;
3654 	p->read     = icmData_read;
3655 	p->write    = icmData_write;
3656 	p->dump     = icmData_dump;
3657 	p->allocate = icmData_allocate;
3658 	p->del      = icmData_delete;
3659 	p->icp      = icp;
3660 
3661 	p->flag = icmDataUndef;
3662 	return (icmBase *)p;
3663 }
3664 
3665 /* ---------------------------------------------------------- */
3666 /* icmText object */
3667 
3668 /* Return the number of bytes needed to write this tag */
icmText_get_size(icmBase * pp)3669 static unsigned int icmText_get_size(
3670 	icmBase *pp
3671 ) {
3672 	icmText *p = (icmText *)pp;
3673 	unsigned int len = 0;
3674 	len += 8;			/* 8 bytes for tag and padding */
3675 	len += p->size;		/* 1 byte for each character element (inc. null) */
3676 	return len;
3677 }
3678 
3679 /* read the object, return 0 on success, error code on fail */
icmText_read(icmBase * pp,unsigned long len,unsigned long of)3680 static int icmText_read(
3681 	icmBase *pp,
3682 	unsigned long len,		/* tag length */
3683 	unsigned long of		/* start offset within file */
3684 ) {
3685 	icmText *p = (icmText *)pp;
3686 	icc *icp = p->icp;
3687 	int rv = 0;
3688 	char *bp, *buf;
3689 
3690 	if (len < 8) {
3691 		sprintf(icp->err,"icmText_read: Tag too short to be legal");
3692 		return icp->errc = 1;
3693 	}
3694 
3695 	/* Allocate a file read buffer */
3696 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3697 		sprintf(icp->err,"icmText_read: malloc() failed");
3698 		return icp->errc = 2;
3699 	}
3700 	bp = buf;
3701 
3702 	/* Read portion of file into buffer */
3703 	if (   icp->fp->seek(icp->fp, of) != 0
3704 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
3705 		sprintf(icp->err,"icmText_read: fseek() or fread() failed");
3706 		icp->al->free(icp->al, buf);
3707 		return icp->errc = 1;
3708 	}
3709 	p->size = (len - 8)/1;		/* Number of elements in the array */
3710 
3711 	/* Read type descriptor from the buffer */
3712 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
3713 		sprintf(icp->err,"icmText_read: Wrong tag type for icmText");
3714 		icp->al->free(icp->al, buf);
3715 		return icp->errc = 1;
3716 	}
3717 	bp = bp + 8;
3718 
3719 	if (p->size > 0) {
3720 		if (check_null_string(bp,p->size) != 0) {
3721 			sprintf(icp->err,"icmText_read: text is not null terminated");
3722 			icp->al->free(icp->al, buf);
3723 			return icp->errc = 1;
3724 		}
3725 		if ((rv = p->allocate((icmBase *)p)) != 0) {
3726 			icp->al->free(icp->al, buf);
3727 			return rv;
3728 		}
3729 		memcpy((void *)p->data, (void *)bp, p->size);
3730 	}
3731 	icp->al->free(icp->al, buf);
3732 	return 0;
3733 }
3734 
3735 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmText_write(icmBase * pp,unsigned long of)3736 static int icmText_write(
3737 	icmBase *pp,
3738 	unsigned long of			/* File offset to write from */
3739 ) {
3740 	icmText *p = (icmText *)pp;
3741 	icc *icp = p->icp;
3742 	unsigned int len;
3743 	char *bp, *buf;		/* Buffer to write from */
3744 	int rv = 0;
3745 
3746 	/* Allocate a file write buffer */
3747 	len = p->get_size((icmBase *)p);
3748 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3749 		sprintf(icp->err,"icmText_write malloc() failed");
3750 		return icp->errc = 2;
3751 	}
3752 	bp = buf;
3753 
3754 	/* Write type descriptor to the buffer */
3755 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
3756 		sprintf(icp->err,"icmText_write: write_SInt32Number() failed");
3757 		icp->al->free(icp->al, buf);
3758 		return icp->errc = rv;
3759 	}
3760 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
3761 	bp = bp + 8;
3762 
3763 	if (p->data != NULL) {
3764 		if ((rv = check_null_string(p->data, p->size)) != 0) {
3765 			sprintf(icp->err,"icmText_write: text is not null terminated");
3766 			icp->al->free(icp->al, buf);
3767 			return icp->errc = 1;
3768 		}
3769 		memcpy((void *)bp, (void *)p->data, p->size);
3770 	}
3771 
3772 	/* Write to the file */
3773 	if (   icp->fp->seek(icp->fp, of) != 0
3774 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
3775 		sprintf(icp->err,"icmText_write fseek() or fwrite() failed");
3776 		icp->al->free(icp->al, buf);
3777 		return icp->errc = 2;
3778 	}
3779 	icp->al->free(icp->al, buf);
3780 	return 0;
3781 }
3782 
3783 /* Dump a text description of the object */
icmText_dump(icmBase * pp,FILE * op,int verb)3784 static void icmText_dump(
3785 	icmBase *pp,
3786 	FILE *op,		/* Output to dump to */
3787 	int   verb		/* Verbosity level */
3788 ) {
3789 	icmText *p = (icmText *)pp;
3790 	unsigned long i, r, c, size;
3791 
3792 	if (verb <= 0)
3793 		return;
3794 
3795 	fprintf(op,"Text:\n");
3796 	fprintf(op,"  No. chars = %lu\n",p->size);
3797 
3798 	size = p->size > 0 ? p->size-1 : 0;
3799 	i = 0;
3800 	for (r = 1;; r++) {		/* count rows */
3801 		if (i >= size) {
3802 			fprintf(op,"\n");
3803 			break;
3804 		}
3805 		if (r > 1 && verb < 2) {
3806 			fprintf(op,"...\n");
3807 			break;			/* Print 1 row if not verbose */
3808 		}
3809 		c = 1;
3810 		fprintf(op,"    0x%04lx: ",i);
3811 		c += 10;
3812 		while (i < size && c < 75) {
3813 			if (isprint(p->data[i])) {
3814 				fprintf(op,"%c",p->data[i]);
3815 				c++;
3816 			} else {
3817 				fprintf(op,"\\%03o",p->data[i]);
3818 				c += 4;
3819 			}
3820 			i++;
3821 		}
3822 		if (i < size)
3823 			fprintf(op,"\n");
3824 	}
3825 }
3826 
3827 /* Allocate variable sized data elements */
icmText_allocate(icmBase * pp)3828 static int icmText_allocate(
3829 	icmBase *pp
3830 ) {
3831 	icmText *p = (icmText *)pp;
3832 	icc *icp = p->icp;
3833 
3834 	if (p->size != p->_size) {
3835 		if (p->data != NULL)
3836 			icp->al->free(icp->al, p->data);
3837 		if ((p->data = (char *) icp->al->malloc(icp->al, p->size * sizeof(char))) == NULL) {
3838 			sprintf(icp->err,"icmText_alloc: malloc() of icmText data failed");
3839 			return icp->errc = 2;
3840 		}
3841 		p->_size = p->size;
3842 	}
3843 	return 0;
3844 }
3845 
3846 /* Free all storage in the object */
icmText_delete(icmBase * pp)3847 static void icmText_delete(
3848 	icmBase *pp
3849 ) {
3850 	icmText *p = (icmText *)pp;
3851 	icc *icp = p->icp;
3852 
3853 	if (p->data != NULL)
3854 		icp->al->free(icp->al, p->data);
3855 	icp->al->free(icp->al, p);
3856 }
3857 
3858 /* Create an empty object. Return null on error */
new_icmText(icc * icp)3859 static icmBase *new_icmText(
3860 	icc *icp
3861 ) {
3862 	icmText *p;
3863 	if ((p = (icmText *) icp->al->calloc(icp->al,1,sizeof(icmText))) == NULL)
3864 		return NULL;
3865 	p->ttype    = icSigTextType;
3866 	p->refcount = 1;
3867 	p->get_size = icmText_get_size;
3868 	p->read     = icmText_read;
3869 	p->write    = icmText_write;
3870 	p->dump     = icmText_dump;
3871 	p->allocate = icmText_allocate;
3872 	p->del      = icmText_delete;
3873 	p->icp      = icp;
3874 
3875 	return (icmBase *)p;
3876 }
3877 
3878 /* ---------------------------------------------------------- */
3879 
3880 /* Data conversion support functions */
write_DateTimeNumber(icmDateTimeNumber * p,char * d)3881 static int write_DateTimeNumber(icmDateTimeNumber *p, char *d) {
3882 	int rv;
3883 	if (p->year < 1900 || p->year > 3000
3884 	 || p->month == 0 || p->month > 12
3885 	 || p->day == 0 || p->day > 31
3886 	 || p->hours > 23
3887 	 || p->minutes > 59
3888 	 || p->seconds > 59)
3889 		return 1;
3890 
3891 	if ((rv = write_UInt16Number(p->year,    d + 0)) != 0)
3892 		return rv;
3893 	if ((rv = write_UInt16Number(p->month,   d + 2)) != 0)
3894 		return rv;
3895 	if ((rv = write_UInt16Number(p->day,     d + 4)) != 0)
3896 		return rv;
3897 	if ((rv = write_UInt16Number(p->hours,   d + 6)) != 0)
3898 		return rv;
3899 	if ((rv = write_UInt16Number(p->minutes, d + 8)) != 0)
3900 		return rv;
3901 	if ((rv = write_UInt16Number(p->seconds, d + 10)) != 0)
3902 		return rv;
3903 	return 0;
3904 }
3905 
read_DateTimeNumber(icmDateTimeNumber * p,char * d)3906 static int read_DateTimeNumber(icmDateTimeNumber *p, char *d) {
3907 	p->year    = read_UInt16Number(d + 0);
3908 	p->month   = read_UInt16Number(d + 2);
3909 	p->day     = read_UInt16Number(d + 4);
3910 	p->hours   = read_UInt16Number(d + 6);
3911 	p->minutes = read_UInt16Number(d + 8);
3912 	p->seconds = read_UInt16Number(d + 10);
3913 
3914 	if (p->year < 1900 || p->year > 3000
3915 	 || p->month == 0 || p->month > 12
3916 	 || p->day == 0 || p->day > 31
3917 	 || p->hours > 23
3918 	 || p->minutes > 59
3919 	 || p->seconds > 59) {
3920 		unsigned int tt;
3921 
3922 		/* Check for Adobe problem */
3923 		if (p->month < 1900 || p->month > 3000
3924 		 || p->year == 0 || p->year > 12
3925 		 || p->hours == 0 || p->hours > 31
3926 		 || p->day > 23
3927 		 || p->seconds > 59
3928 		 || p->minutes > 59)
3929 			return 1;			/* Nope */
3930 
3931 		/* Correct Adobe's faulty profile */
3932 		tt = p->month; p->month = p->year; p->year = tt;
3933 		tt = p->hours; p->hours = p->day; p->day = tt;
3934 		tt = p->seconds; p->seconds = p->minutes; p->minutes = tt;
3935 		return 0;
3936 	}
3937 	return 0;
3938 }
3939 
3940 /* Return a string that shows the given date and time */
string_DateTimeNumber(icmDateTimeNumber * p)3941 static char *string_DateTimeNumber(icmDateTimeNumber *p) {
3942 	static const char *mstring[13] = {"Bad", "Jan","Feb","Mar","Apr","May","Jun",
3943 					  "Jul","Aug","Sep","Oct","Nov","Dec"};
3944 	static char buf[80];
3945 
3946 	sprintf(buf,"%d %s %4d, %d:%02d:%02d",
3947 	                p->day, mstring[p->month > 12 ? 0 : p->month], p->year,
3948 	                p->hours, p->minutes, p->seconds);
3949 	return buf;
3950 }
3951 
3952 /* Set the DateTime structure to the current date and time */
setcur_DateTimeNumber(icmDateTimeNumber * p)3953 static void setcur_DateTimeNumber(icmDateTimeNumber *p) {
3954 	time_t cclk;
3955 	struct tm *ctm;
3956 
3957 	cclk = time(NULL);
3958 	ctm = localtime(&cclk);
3959 
3960 	p->year    = ctm->tm_year + 1900;
3961 	p->month   = ctm->tm_mon + 1;
3962 	p->day     = ctm->tm_mday;
3963 	p->hours   = ctm->tm_hour;
3964 	p->minutes = ctm->tm_min;
3965 	p->seconds = ctm->tm_sec;
3966 }
3967 
3968 /* Return the number of bytes needed to write this tag */
icmDateTimeNumber_get_size(icmBase * pp)3969 static unsigned int icmDateTimeNumber_get_size(
3970 	icmBase *pp
3971 ) {
3972 	unsigned int len = 0;
3973 	len += 8;			/* 8 bytes for tag and padding */
3974 	len += 12;			/* 12 bytes for Date & Time */
3975 	return len;
3976 }
3977 
3978 /* read the object, return 0 on success, error code on fail */
icmDateTimeNumber_read(icmBase * pp,unsigned long len,unsigned long of)3979 static int icmDateTimeNumber_read(
3980 	icmBase *pp,
3981 	unsigned long len,		/* tag length */
3982 	unsigned long of		/* start offset within file */
3983 ) {
3984 	icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
3985 	icc *icp = p->icp;
3986 	int rv;
3987 	char *bp, *buf;
3988 
3989 	if (len < 20) {
3990 		sprintf(icp->err,"icmDateTimeNumber_read: Tag too small to be legal");
3991 		return icp->errc = 1;
3992 	}
3993 
3994 	/* Allocate a file read buffer */
3995 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3996 		sprintf(icp->err,"icmDateTimeNumber_read: malloc() failed");
3997 		return icp->errc = 2;
3998 	}
3999 	bp = buf;
4000 
4001 	/* Read portion of file into buffer */
4002 	if (   icp->fp->seek(icp->fp, of) != 0
4003 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
4004 		sprintf(icp->err,"icmDateTimeNumber_read: fseek() or fread() failed");
4005 		icp->al->free(icp->al, buf);
4006 		return icp->errc = 1;
4007 	}
4008 
4009 	/* Read type descriptor from the buffer */
4010 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
4011 		sprintf(icp->err,"icmDateTimeNumber_read: Wrong tag type for icmDateTimeNumber");
4012 		icp->al->free(icp->al, buf);
4013 		return icp->errc = 1;
4014 	}
4015 	bp += 8;	/* Skip padding */
4016 
4017 	/* Read the time and date from buffer */
4018 	if((rv = read_DateTimeNumber(p, bp)) != 0) {
4019 		sprintf(icp->err,"icmDateTimeNumber_read: Corrupted DateTime");
4020 		icp->al->free(icp->al, buf);
4021 		return icp->errc = rv;
4022 	}
4023 
4024 	icp->al->free(icp->al, buf);
4025 	return 0;
4026 }
4027 
4028 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmDateTimeNumber_write(icmBase * pp,unsigned long of)4029 static int icmDateTimeNumber_write(
4030 	icmBase *pp,
4031 	unsigned long of			/* File offset to write from */
4032 ) {
4033 	icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
4034 	icc *icp = p->icp;
4035 	unsigned int len;
4036 	char *bp, *buf;		/* Buffer to write from */
4037 	int rv = 0;
4038 
4039 	/* Allocate a file write buffer */
4040 	len = p->get_size((icmBase *)p);
4041 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
4042 		sprintf(icp->err,"icmDateTimeNumber_write malloc() failed");
4043 		return icp->errc = 2;
4044 	}
4045 	bp = buf;
4046 
4047 	/* Write type descriptor to the buffer */
4048 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
4049 		sprintf(icp->err,"icmDateTimeNumber_write: write_SInt32Number() failed");
4050 		icp->al->free(icp->al, buf);
4051 		return icp->errc = rv;
4052 	}
4053 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
4054 
4055 	/* Write all the data to the buffer */
4056 	bp += 8;	/* Skip padding */
4057 	if ((rv = write_DateTimeNumber(p, bp)) != 0) {
4058 		sprintf(icp->err,"icmDateTimeNumber_write: write_DateTimeNumber() failed");
4059 		icp->al->free(icp->al, buf);
4060 		return icp->errc = rv;
4061 	}
4062 
4063 	/* Write to the file */
4064 	if (   icp->fp->seek(icp->fp, of) != 0
4065 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
4066 		sprintf(icp->err,"icmDateTimeNumber_write fseek() or fwrite() failed");
4067 		icp->al->free(icp->al, buf);
4068 		return icp->errc = 2;
4069 	}
4070 	icp->al->free(icp->al, buf);
4071 	return 0;
4072 }
4073 
4074 /* Dump a text description of the object */
icmDateTimeNumber_dump(icmBase * pp,FILE * op,int verb)4075 static void icmDateTimeNumber_dump(
4076 	icmBase *pp,
4077 	FILE *op,		/* Output to dump to */
4078 	int   verb		/* Verbosity level */
4079 ) {
4080 	icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
4081 	if (verb <= 0)
4082 		return;
4083 
4084 	fprintf(op,"DateTimeNumber:\n");
4085 	fprintf(op,"  Date = %s\n", string_DateTimeNumber(p));
4086 }
4087 
4088 /* Allocate variable sized data elements */
icmDateTimeNumber_allocate(icmBase * pp)4089 static int icmDateTimeNumber_allocate(
4090 	icmBase *pp
4091 ) {
4092 	/* Nothing to do */
4093 	return 0;
4094 }
4095 
4096 /* Free all storage in the object */
icmDateTimeNumber_delete(icmBase * pp)4097 static void icmDateTimeNumber_delete(
4098 	icmBase *pp
4099 ) {
4100 	icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
4101 	icc *icp = p->icp;
4102 
4103 	icp->al->free(icp->al, p);
4104 }
4105 
4106 /* Create an empty object. Return null on error */
new_icmDateTimeNumber(icc * icp)4107 static icmBase *new_icmDateTimeNumber(
4108 	icc *icp
4109 ) {
4110 	icmDateTimeNumber *p;
4111 	if ((p = (icmDateTimeNumber *) icp->al->calloc(icp->al,1,sizeof(icmDateTimeNumber))) == NULL)
4112 		return NULL;
4113 	p->ttype    = icSigDateTimeType;
4114 	p->refcount = 1;
4115 	p->get_size = icmDateTimeNumber_get_size;
4116 	p->read     = icmDateTimeNumber_read;
4117 	p->write    = icmDateTimeNumber_write;
4118 	p->dump     = icmDateTimeNumber_dump;
4119 	p->allocate = icmDateTimeNumber_allocate;
4120 	p->del      = icmDateTimeNumber_delete;
4121 	p->icp      = icp;
4122 
4123 	setcur_DateTimeNumber(p);	/* Default to current date and time */
4124 	return (icmBase *)p;
4125 }
4126 
4127 /* ---------------------------------------------------------- */
4128 /* icmLut object */
4129 
4130 /* Utility function - raise one integer to an integer power */
uipow(unsigned int a,unsigned int b)4131 static unsigned int uipow(unsigned int a, unsigned int b) {
4132 	unsigned int rv = 1;
4133 	for (; b > 0; b--)
4134 		rv *= a;
4135 	return rv;
4136 }
4137 
4138 /* - - - - - - - - - - - - - - - - */
4139 /* Check if the matrix is non-zero */
icmLut_nu_matrix(icmLut * p)4140 static int icmLut_nu_matrix(
4141 	icmLut *p		/* Pointer to Lut object */
4142 ) {
4143 	int i, j;
4144 
4145 	for (j = 0; j < 3; j++) {		/* Rows */
4146 		for (i = 0; i < 3; i++) {	/* Columns */
4147 			if (   (i == j && p->e[j][i] != 1.0)
4148 			    || (i != j && p->e[j][i] != 0.0))
4149 				return 1;
4150 		}
4151 	}
4152 	return 0;
4153 }
4154 
4155 /* return the locations of the minimum and */
4156 /* maximum values of the given channel, in the clut */
icmLut_min_max(icmLut * p,double * minp,double * maxp,int chan)4157 static void icmLut_min_max(
4158 	icmLut *p,		/* Pointer to Lut object */
4159 	double *minp,	/* Return position of min/max */
4160 	double *maxp,
4161 	int chan		/* Channel, -1 for average of all */
4162 ) {
4163 	double *tp;
4164 	double minv, maxv;	/* Values */
4165 	int e, ee, f;
4166 	double gc[MAX_CHAN];	/* Grid coordinate */
4167 
4168 	minv = 1e6;
4169 	maxv = -1e6;
4170 
4171 	for (e = 0; e < p->inputChan; e++)
4172 		gc[e] = 0;	/* init coords */
4173 
4174 	/* Search the whole table */
4175 	for (tp = p->clutTable, e = 0; e < p->inputChan; tp += p->outputChan) {
4176 		double v;
4177 		if (chan == -1) {
4178 			for (v = 0.0, f = 0; f < p->outputChan; f++)
4179 				v += tp[f];
4180 		} else {
4181 			v = tp[chan];
4182 		}
4183 		if (v < minv) {
4184 			minv = v;
4185 			for (ee = 0; ee < p->inputChan; ee++)
4186 				minp[ee] = gc[ee]/(p->clutPoints-1.0);
4187 		}
4188 		if (v > maxv) {
4189 			maxv = v;
4190 			for (ee = 0; ee < p->inputChan; ee++)
4191 				maxp[ee] = gc[ee]/(p->clutPoints-1.0);
4192 		}
4193 
4194 		/* Increment coord */
4195 		for (e = 0; e < p->inputChan; e++) {
4196 			gc[e]++;
4197 			if (gc[e] < p->clutPoints)
4198 				break;	/* No carry */
4199 			gc[e] = 0;
4200 		}
4201 	}
4202 }
4203 
4204 /* Convert XYZ throught Luts matrix */
4205 /* Return 0 on success, 1 if clipping occured, 2 on other error */
icmLut_lookup_matrix(icmLut * p,double * out,double * in)4206 static int icmLut_lookup_matrix(
4207 icmLut *p,		/* Pointer to Lut object */
4208 double *out,	/* Output array[outputChan] in ICC order - see Table 39 in 6.5.5 */
4209 double *in		/* Input array[inputChan] */
4210 ) {
4211 	double t0,t1;	/* Take care if out == in */
4212 	t0     = p->e[0][0] * in[0] + p->e[0][1] * in[1] + p->e[0][2] * in[2];
4213 	t1     = p->e[1][0] * in[0] + p->e[1][1] * in[1] + p->e[1][2] * in[2];
4214 	out[2] = p->e[2][0] * in[0] + p->e[2][1] * in[1] + p->e[2][2] * in[2];
4215 	out[0] = t0;
4216 	out[1] = t1;
4217 
4218 	return 0;
4219 }
4220 
4221 /* Convert normalized numbers though this Luts input tables. */
4222 /* Return 0 on success, 1 if clipping occured, 2 on other error */
icmLut_lookup_input(icmLut * p,double * out,double * in)4223 static int icmLut_lookup_input(
4224 icmLut *p,		/* Pointer to Lut object */
4225 double *out,	/* Output array[inputChan] */
4226 double *in		/* Input array[inputChan] */
4227 ) {
4228 	int rv = 0;
4229 	int ix,n;
4230 	double inputEnt_1 = (double)(p->inputEnt-1);
4231 	double *table = p->inputTable;
4232 
4233 	/* Use linear interpolation */
4234 	for (n = 0; n < p->inputChan; n++, table += p->inputEnt) {
4235 		double val, w;
4236 		val = in[n] * inputEnt_1;
4237 		if (val < 0.0) {
4238 			val = 0.0;
4239 			rv |= 1;
4240 		} else if (val > inputEnt_1) {
4241 			val = inputEnt_1;
4242 			rv |= 1;
4243 		}
4244 		ix = (int)floor(val);		/* Grid coordinate */
4245 		if (ix > (p->inputEnt-2))
4246 			ix = (p->inputEnt-2);
4247 		w = val - (double)ix;		/* weight */
4248 		val = table[ix];
4249 		out[n] = val + w * (table[ix+1] - val);
4250 	}
4251 	return rv;
4252 }
4253 
4254 /* Convert normalized numbers though this Luts multi-dimensional table. */
4255 /* using n-linear interpolation. */
icmLut_lookup_clut_nl(icmLut * p,double * out,double * in)4256 static int icmLut_lookup_clut_nl(
4257 /* Return 0 on success, 1 if clipping occured, 2 on other error */
4258 icmLut *p,		/* Pointer to Lut object */
4259 double *out,	/* Output array[inputChan] */
4260 double *in		/* Input array[outputChan] */
4261 ) {
4262 	icc *icp = p->icp;
4263 	int rv = 0;
4264 	double *gp;					/* Pointer to grid cube base */
4265 	double co[MAX_CHAN];		/* Coordinate offset with the grid cell */
4266 	double *gw, GW[1 << 8];		/* weight for each grid cube corner */
4267 
4268 	if (p->inputChan <= 8) {
4269 		gw = GW;				/* Use stack allocation */
4270 	} else {
4271 		if ((gw = (double *) icp->al->malloc(icp->al, (1 << p->inputChan) * sizeof(double))) == NULL) {
4272 			sprintf(icp->err,"icmLut_lookup_clut: malloc() failed");
4273 			return icp->errc = 2;
4274 		}
4275 	}
4276 
4277 	/* We are using an n-linear (ie. Trilinear for 3D input) interpolation. */
4278 	/* The implementation here uses more multiplies that some other schemes, */
4279 	/* (for instance, see "Tri-Linear Interpolation" by Steve Hill, */
4280 	/* Graphics Gems IV, page 521), but has less involved bookeeping, */
4281 	/* needs less local storage for intermediate output values, does fewer */
4282 	/* output and intermediate value reads, and fp multiplies are fast on */
4283 	/* todays processors! */
4284 
4285 	/* Compute base index into grid and coordinate offsets */
4286 	{
4287 		int e;
4288 		double clutPoints_1 = (double)(p->clutPoints-1);
4289 		int    clutPoints_2 = p->clutPoints-2;
4290 		gp = p->clutTable;		/* Base of grid array */
4291 
4292 		for (e = 0; e < p->inputChan; e++) {
4293 			int x;
4294 			double val;
4295 			val = in[e] * clutPoints_1;
4296 			if (val < 0.0) {
4297 				val = 0.0;
4298 				rv |= 1;
4299 			} else if (val > clutPoints_1) {
4300 				val = clutPoints_1;
4301 				rv |= 1;
4302 			}
4303 			x = (int)floor(val);		/* Grid coordinate */
4304 			if (x > clutPoints_2)
4305 				x = clutPoints_2;
4306 			co[e] = val - (double)x;	/* 1.0 - weight */
4307 			gp += x * p->dinc[e];		/* Add index offset for base of cube */
4308 		}
4309 	}
4310 	/* Compute corner weights needed for interpolation */
4311 	{
4312 		int e, i, g = 1;
4313 		gw[0] = 1.0;
4314 		for (e = 0; e < p->inputChan; e++) {
4315 			for (i = 0; i < g; i++) {
4316 				gw[g+i] = gw[i] * co[e];
4317 				gw[i] *= (1.0 - co[e]);
4318 			}
4319 			g *= 2;
4320 		}
4321 	}
4322 	/* Now compute the output values */
4323 	{
4324 		int i, f;
4325 		double w = gw[0];
4326 		double *d = gp + p->dcube[0];
4327 		for (f = 0; f < p->outputChan; f++)			/* Base of cube */
4328 			out[f] = w * d[f];
4329 		for (i = 1; i < (1 << p->inputChan); i++) {	/* For all other corners of cube */
4330 			w = gw[i];				/* Strength reduce */
4331 			d = gp + p->dcube[i];
4332 			for (f = 0; f < p->outputChan; f++) {
4333 				out[f] += w * d[f];
4334 			}
4335 		}
4336 	}
4337 	if (gw != GW)
4338 		icp->al->free(icp->al, (void *)gw);
4339 	return rv;
4340 }
4341 
4342 /* Convert normalized numbers though this Luts multi-dimensional table */
4343 /* using simplex interpolation. */
icmLut_lookup_clut_sx(icmLut * p,double * out,double * in)4344 static int icmLut_lookup_clut_sx(
4345 /* Return 0 on success, 1 if clipping occured, 2 on other error */
4346 icmLut *p,		/* Pointer to Lut object */
4347 double *out,	/* Output array[inputChan] */
4348 double *in		/* Input array[outputChan] */
4349 ) {
4350 	int rv = 0;
4351 	double *gp;					/* Pointer to grid cube base */
4352 	double co[MAX_CHAN];		/* Coordinate offset with the grid cell */
4353 	int    si[MAX_CHAN];		/* co[] Sort index, [0] = smalest */
4354 
4355 	/* We are using a simplex (ie. tetrahedral for 3D input) interpolation. */
4356 	/* This method is more appropriate for XYZ/RGB/CMYK input spaces, */
4357 
4358 	/* Compute base index into grid and coordinate offsets */
4359 	{
4360 		int e;
4361 		double clutPoints_1 = (double)(p->clutPoints-1);
4362 		int    clutPoints_2 = p->clutPoints-2;
4363 		gp = p->clutTable;		/* Base of grid array */
4364 
4365 		for (e = 0; e < p->inputChan; e++) {
4366 			int x;
4367 			double val;
4368 			val = in[e] * clutPoints_1;
4369 			if (val < 0.0) {
4370 				val = 0.0;
4371 				rv |= 1;
4372 			} else if (val > clutPoints_1) {
4373 				val = clutPoints_1;
4374 				rv |= 1;
4375 			}
4376 			x = (int)floor(val);		/* Grid coordinate */
4377 			if (x > clutPoints_2)
4378 				x = clutPoints_2;
4379 			co[e] = val - (double)x;	/* 1.0 - weight */
4380 			gp += x * p->dinc[e];		/* Add index offset for base of cube */
4381 		}
4382 	}
4383 	/* Do selection sort on coordinates */
4384 	{
4385 		int e, f;
4386 		for (e = 0; e < p->inputChan; e++)
4387 			si[e] = e;						/* Initial unsorted indexes */
4388 		for (e = 0; e < (p->inputChan-1); e++) {
4389 			double cosn;
4390 			cosn = co[si[e]];				/* Current smallest value */
4391 			for (f = e+1; f < p->inputChan; f++) {	/* Check against rest */
4392 				int tt;
4393 				tt = si[f];
4394 				if (cosn > co[tt]) {
4395 					si[f] = si[e]; 			/* Exchange */
4396 					si[e] = tt;
4397 					cosn = co[tt];
4398 				}
4399 			}
4400 		}
4401 	}
4402 	/* Now compute the weightings, simplex vertices and output values */
4403 	{
4404 		int e, f;
4405 		double w;		/* Current vertex weight */
4406 
4407 		w = 1.0 - co[si[p->inputChan-1]];		/* Vertex at base of cell */
4408 		for (f = 0; f < p->outputChan; f++)
4409 			out[f] = w * gp[f];
4410 
4411 		for (e = p->inputChan-1; e > 0; e--) {	/* Middle verticies */
4412 			w = co[si[e]] - co[si[e-1]];
4413 			gp += p->dinc[si[e]];				/* Move to top of cell in next largest dimension */
4414 			for (f = 0; f < p->outputChan; f++)
4415 				out[f] += w * gp[f];
4416 		}
4417 
4418 		w = co[si[0]];
4419 		gp += p->dinc[si[0]];		/* Far corner from base of cell */
4420 		for (f = 0; f < p->outputChan; f++)
4421 			out[f] += w * gp[f];
4422 	}
4423 	return rv;
4424 }
4425 
4426 /* Convert normalized numbers though this Luts output tables. */
4427 /* Return 0 on success, 1 if clipping occured, 2 on other error */
icmLut_lookup_output(icmLut * p,double * out,double * in)4428 static int icmLut_lookup_output(
4429 icmLut *p,		/* Pointer to Lut object */
4430 double *out,	/* Output array[outputChan] */
4431 double *in		/* Input array[outputChan] */
4432 ) {
4433 	int rv = 0;
4434 	int ix,n;
4435 	double outputEnt_1 = (double)(p->outputEnt-1);
4436 	double *table = p->outputTable;
4437 
4438 	/* Use linear interpolation */
4439 	for (n = 0; n < p->outputChan; n++, table += p->outputEnt) {
4440 		double val, w;
4441 		val = in[n] * outputEnt_1;
4442 		if (val < 0.0) {
4443 			val = 0.0;
4444 			rv |= 1;
4445 		} else if (val > outputEnt_1) {
4446 			val = outputEnt_1;
4447 			rv |= 1;
4448 		}
4449 		ix = (int)floor(val);		/* Grid coordinate */
4450 		if (ix > (p->outputEnt-2))
4451 			ix = (p->outputEnt-2);
4452 		w = val - (double)ix;		/* weight */
4453 		val = table[ix];
4454 		out[n] = val + w * (table[ix+1] - val);
4455 	}
4456 	return rv;
4457 }
4458 
4459 /* ----------------------------------------------- */
4460 /* Pseudo - Hilbert count sequencer */
4461 
4462 /* This multi-dimensional count sequence is a distributed */
4463 /* Gray code sequence, with direction reversal on every */
4464 /* alternate power of 2 scale. */
4465 /* It is intended to aid cache coherence in multi-dimensional */
4466 /* regular sampling. It approximates the Hilbert curve sequence. */
4467 
4468 /* Initialise, returns total usable count */
4469 unsigned
psh_init(psh * p,int di,unsigned res,int co[])4470 psh_init(
4471 psh *p,	/* Pointer to structure to initialise */
4472 int      di,	/* Dimensionality */
4473 unsigned res,	/* Size per coordinate */
4474 int co[]		/* Coordinates to initialise (May be NULL) */
4475 ) {
4476 	int e;
4477 
4478 	p->di = di;
4479 	p->res = res;
4480 
4481 	/* Compute bits */
4482 	for (p->bits = 0; (1 << p->bits) < res; p->bits++)
4483 		;
4484 
4485 	/* Compute the total count mask */
4486 	p->tmask = ((((unsigned)1) << (p->bits * di))-1);
4487 
4488 	/* Compute usable count */
4489 	p->count = 1;
4490 	for (e = 0; e < di; e++)
4491 		p->count *= res;
4492 
4493 	p->ix = 0;
4494 
4495 	if (co != NULL) {
4496 		for (e = 0; e < di; e++)
4497 			co[e] = 0;
4498 	}
4499 
4500 	return p->count;
4501 }
4502 
4503 /* Reset the counter */
4504 void
psh_reset(psh * p)4505 psh_reset(
4506 psh *p	/* Pointer to structure */
4507 ) {
4508 	p->ix = 0;
4509 }
4510 
4511 /* Increment pseudo-hilbert coordinates */
4512 /* Return non-zero if count rolls over to 0 */
4513 int
psh_inc(psh * p,int co[])4514 psh_inc(
4515 psh *p,	/* Pointer to structure */
4516 int co[]		/* Coordinates to return */
4517 ) {
4518 	int di = p->di;
4519 	int res = p->res;
4520 	int bits = p->bits;
4521 	int e;
4522 
4523 	do {
4524 		int b;
4525 		int gix;	/* Gray code index */
4526 
4527 		p->ix = (p->ix + 1) & p->tmask;
4528 
4529 		gix = p->ix ^ (p->ix >> 1);		/* Convert to gray code index */
4530 
4531 		for (e = 0; e < di; e++)
4532 			co[e] = 0;
4533 
4534 		for (b = 0; b < bits; b++) {	/* Distribute bits */
4535 			if (b & 1) {
4536 				for (e = di-1; e >= 0; e--)  {		/* In reverse order */
4537 					co[e] |= (gix & 1) << b;
4538 					gix >>= 1;
4539 				}
4540 			} else {
4541 				for (e = 0; e < di; e++)  {			/* In normal order */
4542 					co[e] |= (gix & 1) << b;
4543 					gix >>= 1;
4544 				}
4545 			}
4546 		}
4547 
4548 		/* Convert from Gray to binary coordinates */
4549 		for (e = 0; e < di; e++)  {
4550 			unsigned sh, tv;
4551 
4552 			for(sh = 1, tv = co[e];; sh <<= 1) {
4553 				unsigned ptv = tv;
4554 				tv ^= (tv >> sh);
4555 				if (ptv <= 1 || sh == 16)
4556 					break;
4557 			}
4558 			if (tv >= res)	/* Dumbo filter - increment again if outside cube range */
4559 				break;
4560 			co[e] = tv;
4561 		}
4562 
4563 	} while (e < di);
4564 
4565 	return (p->ix == 0);
4566 }
4567 
4568 /* ------------------------------------------------------- */
4569 /* Parameter to getNormFunc function */
4570 typedef enum {
4571     icmFromLuti   = 0,  /* return "fromo Lut normalized index" conversion function */
4572     icmToLuti     = 1,  /* return "to Lut normalized index" conversion function */
4573     icmFromLutv   = 2,  /* return "from Lut normalized value" conversion function */
4574     icmToLutv     = 3   /* return "to Lut normalized value" conversion function */
4575 } icmNormFlag;
4576 
4577 /* Return an appropriate color space normalization function, */
4578 /* given the color space and Lut type */
4579 /* Return 0 on success, 1 on match failure */
4580 static int getNormFunc(
4581 	icColorSpaceSignature csig,
4582 	icTagTypeSignature    tagSig,
4583 	icmNormFlag           flag,
4584 	void               (**nfunc)(double *out, double *in)
4585 );
4586 
4587 /* Helper function to initialize the three tables contents */
4588 /* from supplied transfer functions. */
4589 /* Set errc and return error number */
icmLut_set_tables(icmLut * p,void * cbctx,icColorSpaceSignature insig,icColorSpaceSignature outsig,void (* infunc)(void * cbcntx,double * out,double * in),double * inmin,double * inmax,void (* clutfunc)(void * cbctx,double * out,double * in),double * clutmin,double * clutmax,void (* outfunc)(void * cbctx,double * out,double * in))4590 static int icmLut_set_tables (
4591 icmLut *p,									/* Pointer to Lut object */
4592 void   *cbctx,								/* Opaque callback context pointer value */
4593 icColorSpaceSignature insig, 				/* Input color space */
4594 icColorSpaceSignature outsig, 				/* Output color space */
4595 void (*infunc)(void *cbcntx, double *out, double *in),
4596 								/* Input transfer function, inspace->inspace' (NULL = default) */
4597 double *inmin, double *inmax,				/* Maximum range of inspace' values (NULL = default) */
4598 void (*clutfunc)(void *cbctx, double *out, double *in),
4599 								/* inspace' -> outspace' transfer function */
4600 double *clutmin, double *clutmax,			/* Maximum range of outspace' values (NULL = default) */
4601 void (*outfunc)(void *cbctx, double *out, double *in)
4602 								/* Output transfer function, outspace'->outspace (NULL = deflt) */
4603 ) {
4604 	icc *icp = p->icp;
4605 	int n, e;
4606 	int ii[MAX_CHAN];		/* Index value */
4607 	psh counter;			/* Pseudo-Hilbert counter */
4608 	double _iv[2 * MAX_CHAN], *iv = &_iv[MAX_CHAN];	/* Real index value/table value */
4609 	double imin[MAX_CHAN], imax[MAX_CHAN];
4610 	double omin[MAX_CHAN], omax[MAX_CHAN];
4611 	void (*ifromindex)(double *out, double *in);	/* Index to input color space function */
4612 	void (*itoentry)(double *out, double *in);		/* Input color space to entry function */
4613 	void (*ifromentry)(double *out, double *in);	/* Entry to input color space function */
4614 	void (*otoentry)(double *out, double *in);		/* Output colorspace to table value function */
4615 	void (*ofromentry)(double *out, double *in);	/* Table value to output color space function */
4616 
4617 	if (getNormFunc(insig, p->ttype, icmFromLuti, &ifromindex) != 0) {
4618 		sprintf(icp->err,"icmLut_set_tables index to input colorspace function lookup failed");
4619 		return icp->errc = 1;
4620 	}
4621 	if (getNormFunc(insig, p->ttype, icmToLutv, &itoentry) != 0) {
4622 		sprintf(icp->err,"icmLut_set_tables input colorspace to table entry function lookup failed");
4623 		return icp->errc = 1;
4624 	}
4625 	if (getNormFunc(insig, p->ttype, icmFromLutv, &ifromentry) != 0) {
4626 		sprintf(icp->err,"icmLut_set_tables table entry to input colorspace function lookup failed");
4627 		return icp->errc = 1;
4628 	}
4629 
4630 	if (getNormFunc(outsig, p->ttype, icmToLutv, &otoentry) != 0) {
4631 		sprintf(icp->err,"icmLut_set_tables output colorspace to table entry function lookup failed");
4632 		return icp->errc = 1;
4633 	}
4634 	if (getNormFunc(outsig, p->ttype, icmFromLutv, &ofromentry) != 0) {
4635 		sprintf(icp->err,"icmLut_set_tables table entry to output colorspace function lookup failed");
4636 		return icp->errc = 1;
4637 	}
4638 
4639 	/* Setup input table value min-max */
4640 	if (inmin == NULL) {
4641 #ifdef SYMETRICAL_DEFAULT_LAB_RANGE	/* Try symetrical range */
4642 		if (insig == icSigLabData) { /* Special case Lab */
4643 			double mn[3], mx[3];
4644 			/* Because the symetric range will cause slight clipping, */
4645 			/* only do it if the input table has sufficient resolution */
4646 			/* to represent the clipping faithfuly. */
4647 			if (p->inputEnt >= 64) {
4648 				mn[0] =   0.0, mn[1] = -127.0, mn[2] = -127.0;
4649 				mx[0] = 100.0, mx[1] =  127.0, mx[2] =  127.0;
4650 				itoentry(imin, mn);	/* Convert from input color space to table representation */
4651 				itoentry(imax, mx);
4652 			} else {
4653 				for (e = 0; e < p->inputChan; e++) {
4654 					imin[e] = 0.0;
4655 					imax[e] = 1.0;
4656 				}
4657 			}
4658 		} else
4659 #endif
4660 		{
4661 			for (e = 0; e < p->inputChan; e++) {
4662 				imin[e] = 0.0;		/* We are assuming this is true for all other color spaces. */
4663 				imax[e] = 1.0;
4664 			}
4665 		}
4666 	} else {
4667 		itoentry(imin, inmin);	/* Convert from input color space to table representation */
4668 		itoentry(imax, inmax);
4669 	}
4670 
4671 	/* Setup output table value min-max */
4672 	if (clutmin == NULL) {
4673 #ifdef SYMETRICAL_DEFAULT_LAB_RANGE	/* Try symetrical range */
4674 		if (outsig == icSigLabData) { /* Special case Lab */
4675 			double mn[3], mx[3];
4676 			/* Because the symetric range will cause slight clipping, */
4677 			/* only do it if the output table has sufficient resolution */
4678 			/* to represent the clipping faithfuly. */
4679 			if (p->outputEnt >= 64) {
4680 				mn[0] =   0.0, mn[1] = -127.0, mn[2] = -127.0;
4681 				mx[0] = 100.0, mx[1] =  127.0, mx[2] =  127.0;
4682 				otoentry(omin, mn);/* Convert from output color space to table representation */
4683 				otoentry(omax, mx);
4684 			} else {
4685 				for (e = 0; e < p->inputChan; e++) {
4686 					omin[e] = 0.0;
4687 					omax[e] = 1.0;
4688 				}
4689 			}
4690 		} else
4691 #endif
4692 		{
4693 			for (e = 0; e < p->outputChan; e++) {
4694 				omin[e] = 0.0;		/* We are assuming this is true for all other color spaces. */
4695 				omax[e] = 1.0;
4696 			}
4697 		}
4698 	} else {
4699 		otoentry(omin, clutmin);/* Convert from output color space to table representation */
4700 		otoentry(omax, clutmax);
4701 	}
4702 
4703 	/* Create the input table entry values */
4704 	for (n = 0; n < p->inputEnt; n++) {
4705 		double fv;
4706 		fv = n/(p->inputEnt-1.0);
4707 		for (e = 0; e < p->inputChan; e++)
4708 			iv[e] = fv;
4709 
4710 		ifromindex(iv,iv);			/* Convert from index value to input color space value */
4711 
4712 		if (infunc != NULL)
4713 			infunc(cbctx, iv, iv);	/* In colorspace -> input table -> In colorspace. */
4714 
4715 		itoentry(iv,iv);			/* Convert from input color space value to table value */
4716 
4717 		/* Expand used range to 0.0 - 1.0, and clip to legal values */
4718 		/* Note that if the range is reduced, and clipping occurs, */
4719 		/* then there should be enough resolution within the input */
4720 		/* table, to represent the sharp edges of the clipping. */
4721 		for (e = 0; e < p->inputChan; e++) {
4722 			double tt;
4723 			tt = (iv[e] - imin[e])/(imax[e] - imin[e]);
4724 			if (tt < 0.0)
4725 				tt = 0.0;
4726 			else if (tt > 1.0)
4727 				tt = 1.0;
4728 			iv[e] = tt;
4729 		}
4730 
4731 		for (e = 0; e < p->inputChan; e++) 		/* Input tables */
4732 			p->inputTable[e * p->inputEnt + n] = iv[e];
4733 	}
4734 
4735 	/* Create the multi-dimensional lookup table values */
4736 
4737 	/* To make this clut function cache friendly, we use the pseudo-hilbert */
4738 	/* count sequence. This keeps each point close to the last in the */
4739 	/* multi-dimensional space. */
4740 
4741 	psh_init(&counter, p->inputChan, p->clutPoints, ii);	/* Initialise counter */
4742 
4743 	/* Itterate through all verticies in the grid */
4744 	for (;;) {
4745 		int ti;			/* Table index */
4746 
4747 		for (ti = e = 0; e < p->inputChan; e++) { 	/* Input tables */
4748 			ti += ii[e] * p->dinc[e];				/* Clut index */
4749 			iv[e] = ii[e]/(p->clutPoints-1.0);		/* Vertex coordinates */
4750 			iv[e] = iv[e] * (imax[e] - imin[e]) + imin[e]; /* Undo expansion to 0.0 - 1.0 */
4751 			*((int *)&iv[-e-1]) = ii[e];			/* Trick to supply grid index in iv[] */
4752 		}
4753 
4754 		ifromentry(iv,iv);			/* Convert from table value to input color space */
4755 
4756 		/* Apply incolor -> outcolor function we want to represent */
4757 		clutfunc(cbctx, iv, iv);
4758 
4759 		otoentry(iv,iv);			/* Convert from output color space value to table value */
4760 
4761 		/* Expand used range to 0.0 - 1.0, and clip to legal values */
4762 		for (e = 0; e < p->outputChan; e++) {
4763 			double tt;
4764 			tt = (iv[e] - omin[e])/(omax[e] - omin[e]);
4765 			if (tt < 0.0)
4766 				tt = 0.0;
4767 			else if (tt > 1.0)
4768 				tt = 1.0;
4769 			iv[e] = tt;
4770 		}
4771 
4772 		for (e = 0; e < p->outputChan; e++) 	/* Output chans */
4773 			p->clutTable[ti++] = iv[e];
4774 
4775 		/* Increment index within block (Reverse index significancd) */
4776 		if (psh_inc(&counter, ii))
4777 			break;
4778 	}
4779 
4780 	/* Create the output table entry values */
4781 	for (n = 0; n < p->outputEnt; n++) {
4782 		double fv;
4783 		fv = n/(p->outputEnt-1.0);
4784 		for (e = 0; e < p->outputChan; e++)
4785 			iv[e] = fv;
4786 
4787 		/* Undo expansion to 0.0 - 1.0 */
4788 		for (e = 0; e < p->outputChan; e++) 		/* Output tables */
4789 			iv[e] = iv[e] * (omax[e] - omin[e]) + omin[e];
4790 
4791 		ofromentry(iv,iv);			/* Convert from table value to output color space value */
4792 
4793 		if (outfunc != NULL)
4794 			outfunc(cbctx, iv, iv);	/* Out colorspace -> output table -> out colorspace. */
4795 
4796 		otoentry(iv,iv);			/* Convert from output color space value to table value */
4797 
4798 		/* Clip to legal values */
4799 		for (e = 0; e < p->outputChan; e++) {
4800 			double tt;
4801 			tt = iv[e];
4802 			if (tt < 0.0)
4803 				tt = 0.0;
4804 			else if (tt > 1.0)
4805 				tt = 1.0;
4806 			iv[e] = tt;
4807 		}
4808 
4809 		for (e = 0; e < p->outputChan; e++) 		/* Input tables */
4810 			p->outputTable[e * p->outputEnt + n] = iv[e];
4811 	}
4812 	return 0;
4813 }
4814 
4815 /* - - - - - - - - - - - - - - - - */
4816 /* Return the number of bytes needed to write this tag */
icmLut_get_size(icmBase * pp)4817 static unsigned int icmLut_get_size(
4818 	icmBase *pp
4819 ) {
4820 	icmLut *p = (icmLut *)pp;
4821 	unsigned int len = 0;
4822 
4823 	if (p->ttype == icSigLut8Type) {
4824 		len += 48;			/* tag and header */
4825 		len += 1 * (p->inputChan * p->inputEnt);
4826 		len += 1 * (p->outputChan * uipow(p->clutPoints,p->inputChan));
4827 		len += 1 * (p->outputChan * p->outputEnt);
4828 	} else {
4829 		len += 52;			/* tag and header */
4830 		len += 2 * (p->inputChan * p->inputEnt);
4831 		len += 2 * (p->outputChan * uipow(p->clutPoints,p->inputChan));
4832 		len += 2 * (p->outputChan * p->outputEnt);
4833 	}
4834 	return len;
4835 }
4836 
4837 /* read the object, return 0 on success, error code on fail */
icmLut_read(icmBase * pp,unsigned long len,unsigned long of)4838 static int icmLut_read(
4839 	icmBase *pp,
4840 	unsigned long len,		/* tag length */
4841 	unsigned long of		/* start offset within file */
4842 ) {
4843 	icmLut *p = (icmLut *)pp;
4844 	icc *icp = p->icp;
4845 	int rv = 0;
4846 	unsigned long i, j, g, size;
4847 	char *bp, *buf;
4848 
4849 	if (len < 4) {
4850 		sprintf(icp->err,"icmLut_read: Tag too small to be legal");
4851 		return icp->errc = 1;
4852 	}
4853 
4854 	/* Allocate a file read buffer */
4855 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
4856 		sprintf(icp->err,"icmLut_read: malloc() failed");
4857 		return icp->errc = 2;
4858 	}
4859 	bp = buf;
4860 
4861 	/* Read portion of file into buffer */
4862 	if (   icp->fp->seek(icp->fp, of) != 0
4863 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
4864 		sprintf(icp->err,"icmLut_read: fseek() or fread() failed");
4865 		icp->al->free(icp->al, buf);
4866 		return icp->errc = 1;
4867 	}
4868 
4869 	/* Read type descriptor from the buffer */
4870 	p->ttype = (icTagTypeSignature)read_SInt32Number(bp);
4871 	if (p->ttype != icSigLut8Type && p->ttype != icSigLut16Type) {
4872 		sprintf(icp->err,"icmLut_read: Wrong tag type for icmLut");
4873 		icp->al->free(icp->al, buf);
4874 		return icp->errc = 1;
4875 	}
4876 
4877 	if (p->ttype == icSigLut8Type) {
4878 		if (len < 48) {
4879 			sprintf(icp->err,"icmLut_read: Tag too small to be legal");
4880 			icp->al->free(icp->al, buf);
4881 			return icp->errc = 1;
4882 		}
4883 	} else {
4884 		if (len < 52) {
4885 			sprintf(icp->err,"icmLut_read: Tag too small to be legal");
4886 			icp->al->free(icp->al, buf);
4887 			return icp->errc = 1;
4888 		}
4889 	}
4890 
4891 	/* Read in the info common to 8 and 16 bit Lut */
4892 	p->inputChan = read_UInt8Number(bp+8);
4893 	p->outputChan = read_UInt8Number(bp+9);
4894 	p->clutPoints = read_UInt8Number(bp+10);
4895 
4896 	/* Sanity check */
4897 	if (p->inputChan > MAX_CHAN) {
4898 		sprintf(icp->err,"icmLut_read: Can't handle > %d input channels\n",MAX_CHAN);
4899 		return icp->errc = 1;
4900 	}
4901 
4902 	if (p->outputChan > MAX_CHAN) {
4903 		sprintf(icp->err,"icmLut_read: Can't handle > %d output channels\n",MAX_CHAN);
4904 		return icp->errc = 1;
4905 	}
4906 
4907 	/* Read 3x3 transform matrix */
4908 	for (j = 0; j < 3; j++) {		/* Rows */
4909 		for (i = 0; i < 3; i++) {	/* Columns */
4910 			p->e[j][i] = read_S15Fixed16Number(bp + 12 + ((j * 3 + i) * 4));
4911 		}
4912 	}
4913 	/* Read 16 bit specific stuff */
4914 	if (p->ttype == icSigLut8Type) {
4915 		p->inputEnt  = 256;	/* By definition */
4916 		p->outputEnt = 256;	/* By definition */
4917 		bp = buf+48;
4918 	} else {
4919 		p->inputEnt  = read_UInt16Number(bp+48);
4920 		p->outputEnt = read_UInt16Number(bp+50);
4921 		bp = buf+52;
4922 	}
4923 
4924 	if (len < icmLut_get_size((icmBase *)p)) {
4925 		sprintf(icp->err,"icmLut_read: Tag too small for contents");
4926 		icp->al->free(icp->al, buf);
4927 		return icp->errc = 1;
4928 	}
4929 
4930 	/* Read the input tables */
4931 	size = (p->inputChan * p->inputEnt);
4932 	if ((rv = p->allocate((icmBase *)p)) != 0) {
4933 		icp->al->free(icp->al, buf);
4934 		return rv;
4935 	}
4936 	if (p->ttype == icSigLut8Type) {
4937 		for (i = 0; i < size; i++, bp += 1)
4938 			p->inputTable[i] = read_DCS8Number(bp);
4939 	} else {
4940 		for (i = 0; i < size; i++, bp += 2)
4941 			p->inputTable[i] = read_DCS16Number(bp);
4942 	}
4943 
4944 	/* Read the clut table */
4945 	size = (p->outputChan * uipow(p->clutPoints,p->inputChan));
4946 	if ((rv = p->allocate((icmBase *)p)) != 0) {
4947 		icp->al->free(icp->al, buf);
4948 		return rv;
4949 	}
4950 	if (p->ttype == icSigLut8Type) {
4951 		for (i = 0; i < size; i++, bp += 1)
4952 			p->clutTable[i] = read_DCS8Number(bp);
4953 	} else {
4954 		for (i = 0; i < size; i++, bp += 2)
4955 			p->clutTable[i] = read_DCS16Number(bp);
4956 	}
4957 
4958 	/* Read the output tables */
4959 	size = (p->outputChan * p->outputEnt);
4960 	if ((rv = p->allocate((icmBase *)p)) != 0) {
4961 		icp->al->free(icp->al, buf);
4962 		return rv;
4963 	}
4964 	if (p->ttype == icSigLut8Type) {
4965 		for (i = 0; i < size; i++, bp += 1)
4966 			p->outputTable[i] = read_DCS8Number(bp);
4967 	} else {
4968 		for (i = 0; i < size; i++, bp += 2)
4969 			p->outputTable[i] = read_DCS16Number(bp);
4970 	}
4971 
4972 	/* Private: compute dimensional increment though clut */
4973 	/* Note that first channel varies least rapidly. */
4974 	i = p->inputChan-1;
4975 	p->dinc[i--] = p->outputChan;
4976 	for (; i < p->inputChan; i--)
4977 		p->dinc[i] = p->dinc[i+1] * p->clutPoints;
4978 
4979 	/* Private: compute offsets from base of cube to other corners */
4980 	for (p->dcube[0] = 0, g = 1, j = 0; j < p->inputChan; j++) {
4981 		for (i = 0; i < g; i++)
4982 			p->dcube[g+i] = p->dcube[i] + p->dinc[j];
4983 		g *= 2;
4984 	}
4985 
4986 	icp->al->free(icp->al, buf);
4987 	return 0;
4988 }
4989 
4990 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmLut_write(icmBase * pp,unsigned long of)4991 static int icmLut_write(
4992 	icmBase *pp,
4993 	unsigned long of			/* File offset to write from */
4994 ) {
4995 	icmLut *p = (icmLut *)pp;
4996 	icc *icp = p->icp;
4997 	unsigned long i,j;
4998 	unsigned int len, size;
4999 	char *bp, *buf;		/* Buffer to write from */
5000 	int rv = 0;
5001 
5002 	/* Allocate a file write buffer */
5003 	len = p->get_size((icmBase *)p);
5004 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
5005 		sprintf(icp->err,"icmLut_write malloc() failed");
5006 		return icp->errc = 2;
5007 	}
5008 	bp = buf;
5009 
5010 	/* Write type descriptor to the buffer */
5011 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
5012 		sprintf(icp->err,"icmLut_write: write_SInt32Number() failed");
5013 		icp->al->free(icp->al, buf);
5014 		return icp->errc = rv;
5015 	}
5016 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
5017 
5018 	/* Write the info common to 8 and 16 bit Lut */
5019 	if ((rv = write_UInt8Number(p->inputChan, bp+8)) != 0) {
5020 		sprintf(icp->err,"icmLut_write: write_UInt8Number() failed");
5021 		icp->al->free(icp->al, buf);
5022 		return icp->errc = rv;
5023 	}
5024 	if ((rv = write_UInt8Number(p->outputChan, bp+9)) != 0) {
5025 		sprintf(icp->err,"icmLut_write: write_UInt8Number() failed");
5026 		icp->al->free(icp->al, buf);
5027 		return icp->errc = rv;
5028 	}
5029 	if ((rv = write_UInt8Number(p->clutPoints, bp+10)) != 0) {
5030 		sprintf(icp->err,"icmLut_write: write_UInt8Number() failed");
5031 		icp->al->free(icp->al, buf);
5032 		return icp->errc = rv;
5033 	}
5034 
5035 	/* Write 3x3 transform matrix */
5036 	for (j = 0; j < 3; j++) {		/* Rows */
5037 		for (i = 0; i < 3; i++) {	/* Columns */
5038 			if ((rv = write_S15Fixed16Number(p->e[j][i],bp + 12 + ((j * 3 + i) * 4))) != 0) {
5039 				sprintf(icp->err,"icmLut_write: write_S15Fixed16Number() failed");
5040 				icp->al->free(icp->al, buf);
5041 				return icp->errc = rv;
5042 			}
5043 		}
5044 	}
5045 
5046 	/* Write 16 bit specific stuff */
5047 	if (p->ttype == icSigLut8Type) {
5048 		if (p->inputEnt != 256 || p->outputEnt != 256) {
5049 			sprintf(icp->err,"icmLut_write: 8 bit Input and Output tables must be 256 entries");
5050 			icp->al->free(icp->al, buf);
5051 			return icp->errc = 1;
5052 		}
5053 		bp = buf+48;
5054 	} else {
5055 		if ((rv = write_UInt16Number(p->inputEnt, bp+48)) != 0) {
5056 			sprintf(icp->err,"icmLut_write: write_UInt16Number() failed");
5057 			icp->al->free(icp->al, buf);
5058 			return icp->errc = rv;
5059 		}
5060 		if ((rv = write_UInt16Number(p->outputEnt, bp+50)) != 0) {
5061 			sprintf(icp->err,"icmLut_write: write_UInt16Number() failed");
5062 			icp->al->free(icp->al, buf);
5063 			return icp->errc = rv;
5064 		}
5065 		bp = buf+52;
5066 	}
5067 
5068 	/* Write the input tables */
5069 	size = (p->inputChan * p->inputEnt);
5070 	if (p->ttype == icSigLut8Type) {
5071 		for (i = 0; i < size; i++, bp += 1) {
5072 			if ((rv = write_DCS8Number(p->inputTable[i], bp)) != 0) {
5073 				sprintf(icp->err,"icmLut_write: inputTable write_DCS8Number() failed");
5074 				icp->al->free(icp->al, buf);
5075 				return icp->errc = rv;
5076 			}
5077 		}
5078 	} else {
5079 		for (i = 0; i < size; i++, bp += 2) {
5080 			if ((rv = write_DCS16Number(p->inputTable[i], bp)) != 0) {
5081 				sprintf(icp->err,"icmLut_write: inputTable write_DCS16Number(%f) failed",p->inputTable[i]);
5082 				icp->al->free(icp->al, buf);
5083 				return icp->errc = rv;
5084 			}
5085 		}
5086 	}
5087 
5088 	/* Write the clut table */
5089 	size = (p->outputChan * uipow(p->clutPoints,p->inputChan));
5090 	if (p->ttype == icSigLut8Type) {
5091 		for (i = 0; i < size; i++, bp += 1) {
5092 			if ((rv = write_DCS8Number(p->clutTable[i], bp)) != 0) {
5093 				sprintf(icp->err,"icmLut_write: clutTable write_DCS8Number() failed");
5094 				icp->al->free(icp->al, buf);
5095 				return icp->errc = rv;
5096 			}
5097 		}
5098 	} else {
5099 		for (i = 0; i < size; i++, bp += 2) {
5100 			if ((rv = write_DCS16Number(p->clutTable[i], bp)) != 0) {
5101 				sprintf(icp->err,"icmLut_write: clutTable write_DCS16Number(%f) failed",p->clutTable[i]);
5102 				icp->al->free(icp->al, buf);
5103 				return icp->errc = rv;
5104 			}
5105 		}
5106 	}
5107 
5108 	/* Write the output tables */
5109 	size = (p->outputChan * p->outputEnt);
5110 	if (p->ttype == icSigLut8Type) {
5111 		for (i = 0; i < size; i++, bp += 1) {
5112 			if ((rv = write_DCS8Number(p->outputTable[i], bp)) != 0) {
5113 				sprintf(icp->err,"icmLut_write: outputTable write_DCS8Number() failed");
5114 				icp->al->free(icp->al, buf);
5115 				return icp->errc = rv;
5116 			}
5117 		}
5118 	} else {
5119 		for (i = 0; i < size; i++, bp += 2) {
5120 			if ((rv = write_DCS16Number(p->outputTable[i], bp)) != 0) {
5121 				sprintf(icp->err,"icmLut_write: outputTable write_DCS16Number(%f) failed",p->outputTable[i]);
5122 				icp->al->free(icp->al, buf);
5123 				return icp->errc = rv;
5124 			}
5125 		}
5126 	}
5127 
5128 	/* Write buffer to the file */
5129 	if (   icp->fp->seek(icp->fp, of) != 0
5130 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
5131 		sprintf(icp->err,"icmLut_write fseek() or fwrite() failed");
5132 		icp->al->free(icp->al, buf);
5133 		return icp->errc = 2;
5134 	}
5135 	icp->al->free(icp->al, buf);
5136 	return 0;
5137 }
5138 
5139 /* Dump a text description of the object */
icmLut_dump(icmBase * pp,FILE * op,int verb)5140 static void icmLut_dump(
5141 	icmBase *pp,
5142 	FILE *op,		/* Output to dump to */
5143 	int   verb		/* Verbosity level */
5144 ) {
5145 	icmLut *p = (icmLut *)pp;
5146 	if (verb <= 0)
5147 		return;
5148 
5149 	if (p->ttype == icSigLut8Type) {
5150 		fprintf(op,"Lut8:\n");
5151 	} else {
5152 		fprintf(op,"Lut16:\n");
5153 	}
5154 	fprintf(op,"  Input Channels = %u\n",p->inputChan);
5155 	fprintf(op,"  Output Channels = %u\n",p->outputChan);
5156 	fprintf(op,"  CLUT resolution = %u\n",p->clutPoints);
5157 	fprintf(op,"  Input Table entries = %u\n",p->inputEnt);
5158 	fprintf(op,"  Output Table entries = %u\n",p->outputEnt);
5159 	fprintf(op,"  XYZ matrix =  %f, %f, %f\n",p->e[0][0],p->e[0][1],p->e[0][2]);
5160 	fprintf(op,"                %f, %f, %f\n",p->e[1][0],p->e[1][1],p->e[1][2]);
5161 	fprintf(op,"                %f, %f, %f\n",p->e[2][0],p->e[2][1],p->e[2][2]);
5162 
5163 	if (verb >= 2) {
5164 		unsigned int i,size;
5165 		int j;
5166 		unsigned int ii[MAX_CHAN];	/* maximum no of input channels */
5167 
5168 		fprintf(op,"  Input table:\n");
5169 		for (i = 0; i < p->inputEnt; i++) {
5170 			fprintf(op,"    %3u: ",i);
5171 			for (j = 0; j < p->inputChan; j++)
5172 				fprintf(op," %1.10f",p->inputTable[j * p->inputEnt + i]);
5173 			fprintf(op,"\n");
5174 		}
5175 
5176 		fprintf(op,"\n  CLUT table:\n");
5177 		if (p->inputChan > MAX_CHAN) {
5178 			fprintf(op,"  !!Can't dump > %d input channel CLUT table!!\n",MAX_CHAN);
5179 		} else {
5180 			size = (p->outputChan * uipow(p->clutPoints,p->inputChan));
5181 			for (j = 0; j < p->inputChan; j++)
5182 				ii[j] = 0;
5183 			for (i = 0; i < size;) {
5184 				int k;
5185 				/* Print table entry index */
5186 				fprintf(op,"   ");
5187 				for (j = p->inputChan-1; j >= 0; j--)
5188 					fprintf(op," %2u",ii[j]);
5189 				fprintf(op,":");
5190 				/* Print table entry contents */
5191 				for (k = 0; k < p->outputChan; k++, i++)
5192 					fprintf(op," %1.10f",p->clutTable[i]);
5193 				fprintf(op,"\n");
5194 
5195 				for (j = 0; j < p->inputChan; j++) { /* Increment index */
5196 					ii[j]++;
5197 					if (ii[j] < p->clutPoints)
5198 						break;	/* No carry */
5199 					ii[j] = 0;
5200 				}
5201 			}
5202 		}
5203 
5204 		fprintf(op,"\n  Output table:\n");
5205 		for (i = 0; i < p->outputEnt; i++) {
5206 			fprintf(op,"    %3u: ",i);
5207 			for (j = 0; j < p->outputChan; j++)
5208 				fprintf(op," %1.10f",p->outputTable[j * p->outputEnt + i]);
5209 			fprintf(op,"\n");
5210 		}
5211 
5212 	}
5213 }
5214 
5215 /* Allocate variable sized data elements */
icmLut_allocate(icmBase * pp)5216 static int icmLut_allocate(
5217 	icmBase *pp
5218 ) {
5219 	unsigned int i, j, g, size;
5220 	icmLut *p = (icmLut *)pp;
5221 	icc *icp = p->icp;
5222 
5223 	/* Sanity check */
5224 	if (p->inputChan > MAX_CHAN) {
5225 		sprintf(icp->err,"icmLut_alloc: Can't handle > %d input channels\n",MAX_CHAN);
5226 		return icp->errc = 1;
5227 	}
5228 
5229 	if (p->outputChan > MAX_CHAN) {
5230 		sprintf(icp->err,"icmLut_alloc: Can't handle > %d output channels\n",MAX_CHAN);
5231 		return icp->errc = 1;
5232 	}
5233 
5234 	size = (p->inputChan * p->inputEnt);
5235 	if (size != p->inputTable_size) {
5236 		if (p->inputTable != NULL)
5237 			icp->al->free(icp->al, p->inputTable);
5238 		if ((p->inputTable = (double *) icp->al->calloc(icp->al,sizeof(double), size)) == NULL) {
5239 			sprintf(icp->err,"icmLut_alloc: calloc() of Lut inputTable data failed");
5240 			return icp->errc = 2;
5241 		}
5242 		p->inputTable_size = size;
5243 	}
5244 	size = (p->outputChan * uipow(p->clutPoints,p->inputChan));
5245 	if (size != p->clutTable_size) {
5246 		if (p->clutTable != NULL)
5247 			icp->al->free(icp->al, p->clutTable);
5248 		if ((p->clutTable = (double *) icp->al->calloc(icp->al,sizeof(double), size)) == NULL) {
5249 			sprintf(icp->err,"icmLut_alloc: calloc() of Lut clutTable data failed");
5250 			return icp->errc = 2;
5251 		}
5252 		p->clutTable_size = size;
5253 	}
5254 	size = (p->outputChan * p->outputEnt);
5255 	if (size != p->outputTable_size) {
5256 		if (p->outputTable != NULL)
5257 			icp->al->free(icp->al, p->outputTable);
5258 		if ((p->outputTable = (double *) icp->al->calloc(icp->al,sizeof(double), size)) == NULL) {
5259 			sprintf(icp->err,"icmLut_alloc: calloc() of Lut outputTable data failed");
5260 			return icp->errc = 2;
5261 		}
5262 		p->outputTable_size = size;
5263 	}
5264 
5265 	/* Private: compute dimensional increment though clut */
5266 	/* Note that first channel varies least rapidly. */
5267 	i = p->inputChan-1;
5268 	p->dinc[i--] = p->outputChan;
5269 	for (; i < p->inputChan; i--)
5270 		p->dinc[i] = p->dinc[i+1] * p->clutPoints;
5271 
5272 	/* Private: compute offsets from base of cube to other corners */
5273 	for (p->dcube[0] = 0, g = 1, j = 0; j < p->inputChan; j++) {
5274 		for (i = 0; i < g; i++)
5275 			p->dcube[g+i] = p->dcube[i] + p->dinc[j];
5276 		g *= 2;
5277 	}
5278 
5279 	return 0;
5280 }
5281 
5282 /* Free all storage in the object */
icmLut_delete(icmBase * pp)5283 static void icmLut_delete(
5284 	icmBase *pp
5285 ) {
5286 	icmLut *p = (icmLut *)pp;
5287 	icc *icp = p->icp;
5288 
5289 	if (p->inputTable != NULL)
5290 		icp->al->free(icp->al, p->inputTable);
5291 	if (p->clutTable != NULL)
5292 		icp->al->free(icp->al, p->clutTable);
5293 	if (p->outputTable != NULL)
5294 		icp->al->free(icp->al, p->outputTable);
5295 	icmTable_delete_bwd(icp, &p->rit);
5296 	icmTable_delete_bwd(icp, &p->rot);
5297 	icp->al->free(icp->al, p);
5298 }
5299 
5300 /* Create an empty object. Return null on error */
new_icmLut(icc * icp)5301 static icmBase *new_icmLut(
5302 	icc *icp
5303 ) {
5304 	int i,j;
5305 	icmLut *p;
5306 	if ((p = (icmLut *) icp->al->calloc(icp->al,1,sizeof(icmLut))) == NULL)
5307 		return NULL;
5308 	p->ttype    = icSigLut16Type;
5309 	p->refcount = 1;
5310 	p->get_size = icmLut_get_size;
5311 	p->read     = icmLut_read;
5312 	p->write    = icmLut_write;
5313 	p->dump     = icmLut_dump;
5314 	p->allocate = icmLut_allocate;
5315 	p->del      = icmLut_delete;
5316 
5317 	/* Lookup methods */
5318 	p->nu_matrix      = icmLut_nu_matrix;
5319 	p->min_max        = icmLut_min_max;
5320 	p->lookup_matrix  = icmLut_lookup_matrix;
5321 	p->lookup_input   = icmLut_lookup_input;
5322 	p->lookup_clut_nl = icmLut_lookup_clut_nl;
5323 	p->lookup_clut_sx = icmLut_lookup_clut_sx;
5324 	p->lookup_output  = icmLut_lookup_output;
5325 
5326 	/* Set method */
5327 	p->set_tables = icmLut_set_tables;
5328 
5329 	p->icp      = icp;
5330 
5331 	/* Set matrix to reasonable default */
5332 	for (i = 0; i < 3; i++)
5333 		for (j = 0; j < 3; j++) {
5334 			if (i == j)
5335 				p->e[i][j] = 1.0;
5336 			else
5337 				p->e[i][j] = 0.0;
5338 		}
5339 
5340 	/* Init lookups to non-dangerous values */
5341 	for (i = 0; i < MAX_CHAN; i++)
5342 		p->dinc[i] = 0;
5343 
5344 	for (i = 0; i < (1 << MAX_CHAN); i++)
5345 		p->dcube[i] = 0;
5346 
5347 	p->rit.inited = 0;
5348 	p->rot.inited = 0;
5349 
5350 	return (icmBase *)p;
5351 }
5352 
5353 /* ---------------------------------------------------------- */
5354 /* Measurement */
5355 
5356 /* Return the number of bytes needed to write this tag */
icmMeasurement_get_size(icmBase * pp)5357 static unsigned int icmMeasurement_get_size(
5358 	icmBase *pp
5359 ) {
5360 	unsigned int len = 0;
5361 	len += 8;			/* 8 bytes for tag and padding */
5362 	len += 4;			/* 4 for standard observer */
5363 	len += 12;			/* 12 for XYZ of measurement backing */
5364 	len += 4;			/* 4 for measurement geometry */
5365 	len += 4;			/* 4 for measurement flare */
5366 	len += 4;			/* 4 for standard illuminant */
5367 	return len;
5368 }
5369 
5370 /* read the object, return 0 on success, error code on fail */
icmMeasurement_read(icmBase * pp,unsigned long len,unsigned long of)5371 static int icmMeasurement_read(
5372 	icmBase *pp,
5373 	unsigned long len,		/* tag length */
5374 	unsigned long of		/* start offset within file */
5375 ) {
5376 	icmMeasurement *p = (icmMeasurement *)pp;
5377 	icc *icp = p->icp;
5378 	int rv;
5379 	char *bp, *buf;
5380 
5381 	if (len < 36) {
5382 		sprintf(icp->err,"icmMeasurement_read: Tag too small to be legal");
5383 		return icp->errc = 1;
5384 	}
5385 
5386 	/* Allocate a file read buffer */
5387 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
5388 		sprintf(icp->err,"icmMeasurement_read: malloc() failed");
5389 		return icp->errc = 2;
5390 	}
5391 	bp = buf;
5392 
5393 	/* Read portion of file into buffer */
5394 	if (   icp->fp->seek(icp->fp, of) != 0
5395 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
5396 		sprintf(icp->err,"icmMeasurement_read: fseek() or fread() failed");
5397 		icp->al->free(icp->al, buf);
5398 		return icp->errc = 1;
5399 	}
5400 
5401 	/* Read type descriptor from the buffer */
5402 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
5403 		sprintf(icp->err,"icmMeasurement_read: Wrong tag type for icmMeasurement");
5404 		icp->al->free(icp->al, buf);
5405 		return icp->errc = 1;
5406 	}
5407 
5408 	/* Read the encoded standard observer */
5409 	p->observer = (icStandardObserver)read_SInt32Number(bp + 8);
5410 
5411 	/* Read the XYZ values for measurement backing */
5412 	if ((rv = read_XYZNumber(&p->backing, bp+12)) != 0) {
5413 		sprintf(icp->err,"icmMeasurement: read_XYZNumber error");
5414 		icp->al->free(icp->al, buf);
5415 		return icp->errc = rv;
5416 	}
5417 
5418 	/* Read the encoded measurement geometry */
5419 	p->geometry = (icMeasurementGeometry)read_SInt32Number(bp + 24);
5420 
5421 	/* Read the proportion of flare  */
5422 	p->flare = read_U16Fixed16Number(bp + 28);
5423 
5424 	/* Read the encoded standard illuminant */
5425 	p->illuminant = (icIlluminant)read_SInt32Number(bp + 32);
5426 
5427 	icp->al->free(icp->al, buf);
5428 	return 0;
5429 }
5430 
5431 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmMeasurement_write(icmBase * pp,unsigned long of)5432 static int icmMeasurement_write(
5433 	icmBase *pp,
5434 	unsigned long of			/* File offset to write from */
5435 ) {
5436 	icmMeasurement *p = (icmMeasurement *)pp;
5437 	icc *icp = p->icp;
5438 	unsigned int len;
5439 	char *bp, *buf;		/* Buffer to write from */
5440 	int rv = 0;
5441 
5442 	/* Allocate a file write buffer */
5443 	len = p->get_size((icmBase *)p);
5444 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
5445 		sprintf(icp->err,"icmMeasurement_write malloc() failed");
5446 		return icp->errc = 2;
5447 	}
5448 	bp = buf;
5449 
5450 	/* Write type descriptor to the buffer */
5451 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
5452 		sprintf(icp->err,"icmMeasurement_write, type: write_SInt32Number() failed");
5453 		icp->al->free(icp->al, buf);
5454 		return icp->errc = rv;
5455 	}
5456 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
5457 
5458 	/* Write the encoded standard observer */
5459 	if ((rv = write_SInt32Number((int)p->observer, bp + 8)) != 0) {
5460 		sprintf(icp->err,"icmMeasurementa_write, observer: write_SInt32Number() failed");
5461 		icp->al->free(icp->al, buf);
5462 		return icp->errc = rv;
5463 	}
5464 
5465 	/* Write the XYZ values for measurement backing */
5466 	if ((rv = write_XYZNumber(&p->backing, bp+12)) != 0) {
5467 		sprintf(icp->err,"icmMeasurement, backing: write_XYZNumber error");
5468 		icp->al->free(icp->al, buf);
5469 		return icp->errc = rv;
5470 	}
5471 
5472 	/* Write the encoded measurement geometry */
5473 	if ((rv = write_SInt32Number((int)p->geometry, bp + 24)) != 0) {
5474 		sprintf(icp->err,"icmMeasurementa_write, geometry: write_SInt32Number() failed");
5475 		icp->al->free(icp->al, buf);
5476 		return icp->errc = rv;
5477 	}
5478 
5479 	/* Write the proportion of flare */
5480 	if ((rv = write_U16Fixed16Number(p->flare, bp + 28)) != 0) {
5481 		sprintf(icp->err,"icmMeasurementa_write, flare: write_U16Fixed16Number() failed");
5482 		icp->al->free(icp->al, buf);
5483 		return icp->errc = rv;
5484 	}
5485 
5486 	/* Write the encoded standard illuminant */
5487 	if ((rv = write_SInt32Number((int)p->illuminant, bp + 32)) != 0) {
5488 		sprintf(icp->err,"icmMeasurementa_write, illuminant: write_SInt32Number() failed");
5489 		icp->al->free(icp->al, buf);
5490 		return icp->errc = rv;
5491 	}
5492 
5493 	/* Write to the file */
5494 	if (   icp->fp->seek(icp->fp, of) != 0
5495 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
5496 		sprintf(icp->err,"icmMeasurement_write fseek() or fwrite() failed");
5497 		icp->al->free(icp->al, buf);
5498 		return icp->errc = 2;
5499 	}
5500 	icp->al->free(icp->al, buf);
5501 	return 0;
5502 }
5503 
5504 /* Dump a text description of the object */
icmMeasurement_dump(icmBase * pp,FILE * op,int verb)5505 static void icmMeasurement_dump(
5506 	icmBase *pp,
5507 	FILE *op,		/* Output to dump to */
5508 	int   verb		/* Verbosity level */
5509 ) {
5510 	icmMeasurement *p = (icmMeasurement *)pp;
5511 	if (verb <= 0)
5512 		return;
5513 
5514 	fprintf(op,"Measurement:\n");
5515 	fprintf(op,"  Standard Observer = %s\n", string_StandardObserver(p->observer));
5516 	fprintf(op,"  XYZ for Measurement Backing = %s\n", string_XYZNumber_and_Lab(&p->backing));
5517 	fprintf(op,"  Measurement Geometry = %s\n", string_MeasurementGeometry(p->geometry));
5518 	fprintf(op,"  Measurement Flare = %5.1f%%\n", p->flare * 100.0);
5519 	fprintf(op,"  Standard Illuminant = %s\n", string_Illuminant(p->illuminant));
5520 }
5521 
5522 /* Allocate variable sized data elements */
icmMeasurement_allocate(icmBase * pp)5523 static int icmMeasurement_allocate(
5524 	icmBase *pp
5525 ) {
5526 	/* Nothing to do */
5527 	return 0;
5528 }
5529 
5530 /* Free all storage in the object */
icmMeasurement_delete(icmBase * pp)5531 static void icmMeasurement_delete(
5532 	icmBase *pp
5533 ) {
5534 	icmMeasurement *p = (icmMeasurement *)pp;
5535 	icc *icp = p->icp;
5536 
5537 	icp->al->free(icp->al, p);
5538 }
5539 
5540 /* Create an empty object. Return null on error */
new_icmMeasurement(icc * icp)5541 static icmBase *new_icmMeasurement(
5542 	icc *icp
5543 ) {
5544 	icmMeasurement *p;
5545 	if ((p = (icmMeasurement *) icp->al->calloc(icp->al,1,sizeof(icmMeasurement))) == NULL)
5546 		return NULL;
5547 	p->ttype    = icSigMeasurementType;
5548 	p->refcount = 1;
5549 	p->get_size = icmMeasurement_get_size;
5550 	p->read     = icmMeasurement_read;
5551 	p->write    = icmMeasurement_write;
5552 	p->dump     = icmMeasurement_dump;
5553 	p->allocate = icmMeasurement_allocate;
5554 	p->del      = icmMeasurement_delete;
5555 	p->icp      = icp;
5556 
5557 	return (icmBase *)p;
5558 }
5559 
5560 /* ---------------------------------------------------------- */
5561 
5562 /* Named color structure read/write support */
read_NamedColorVal(icmNamedColorVal * p,char * bp,char * end,icColorSpaceSignature pcs,unsigned int ndc)5563 static int read_NamedColorVal(
5564 	icmNamedColorVal *p,
5565 	char *bp,
5566 	char *end,
5567 	icColorSpaceSignature pcs,		/* Header Profile Connection Space */
5568 	unsigned int ndc				/* Number of device corrds */
5569 ) {
5570 	icc *icp = p->icp;
5571 	int i;
5572 	unsigned int mxl;	/* Max possible string length */
5573 
5574 	mxl = (end - bp) < 32 ? (end - bp) : 32;
5575 	if (check_null_string(bp,mxl)) {
5576 		sprintf(icp->err,"icmNamedColorVal_read: Root name string not terminated");
5577 		return icp->errc = 1;
5578 	}
5579 	strcpy((void *)p->root, (void *)bp);
5580 	bp += strlen(p->root) + 1;
5581 	if ((bp + ndc) > end) {
5582 		sprintf(icp->err,"icmNamedColorVal_read: Data too short to read device coords");
5583 		return icp->errc = 1;
5584 	}
5585 	for (i = 0; i < ndc; i++) {
5586 		p->deviceCoords[i] = read_DCS8Number(bp);
5587 		bp += 1;
5588 	}
5589 	return 0;
5590 }
5591 
read_NamedColorVal2(icmNamedColorVal * p,char * bp,char * end,icColorSpaceSignature pcs,unsigned int ndc)5592 static int read_NamedColorVal2(
5593 	icmNamedColorVal *p,
5594 	char *bp,
5595 	char *end,
5596 	icColorSpaceSignature pcs,		/* Header Profile Connection Space */
5597 	unsigned int ndc				/* Number of device corrds */
5598 ) {
5599 	icc *icp = p->icp;
5600 	int i;
5601 	if ((bp + 32 + 6 + ndc * 2) > end) {
5602 		sprintf(icp->err,"icmNamedColorVal2_read: Data too short to read");
5603 		return icp->errc = 1;
5604 	}
5605 	if (check_null_string(bp,32)) {
5606 		sprintf(icp->err,"icmNamedColorVal2_read: Root name string not terminated");
5607 		return icp->errc = 1;
5608 	}
5609 	memcpy((void *)p->root,(void *)(bp + 0),32);
5610 	switch(pcs) {
5611 		case icSigXYZData:
5612 				p->pcsCoords[0] = read_PCSXYZ16Number(bp+32);
5613 				p->pcsCoords[1] = read_PCSXYZ16Number(bp+34);
5614 				p->pcsCoords[2] = read_PCSXYZ16Number(bp+36);
5615 			break;
5616 	   	case icSigLabData:
5617 				p->pcsCoords[0] =  read_PCSL16Number(bp+32);
5618 				p->pcsCoords[1] = read_PCSab16Number(bp+34);
5619 				p->pcsCoords[2] = read_PCSab16Number(bp+36);
5620 			break;
5621 		default:
5622 			return 1;		/* Unknown PCS */
5623 	}
5624 	for (i = 0; i < ndc; i++)
5625 		p->deviceCoords[i] = read_DCS16Number(bp + 32 + 6 + 2 * i);
5626 	return 0;
5627 }
5628 
write_NamedColorVal(icmNamedColorVal * p,char * d,icColorSpaceSignature pcs,unsigned int ndc)5629 static int write_NamedColorVal(
5630 	icmNamedColorVal *p,
5631 	char *d,
5632 	icColorSpaceSignature pcs,		/* Header Profile Connection Space */
5633 	unsigned int ndc				/* Number of device corrds */
5634 ) {
5635 	icc *icp = p->icp;
5636 	int i, rv = 0;
5637 	if (check_null_string(p->root,32) != 0) {
5638 		sprintf(icp->err,"icmNamedColorVal_write: Root string names is unterminated");
5639 		return icp->errc = 1;
5640 	}
5641 	strcpy((void *)d,(void *)p->root);
5642 	d += strlen(p->root) + 1;
5643 	for (i = 0; i < ndc; i++) {
5644 		if ((rv = write_DCS8Number(p->deviceCoords[i], d)) != 0) {
5645 			sprintf(icp->err,"icmNamedColorVal_write: write of device coord failed");
5646 			return icp->errc = 1;
5647 		}
5648 		d += 1;
5649 	}
5650 	return 0;
5651 }
5652 
write_NamedColorVal2(icmNamedColorVal * p,char * bp,icColorSpaceSignature pcs,unsigned int ndc)5653 static int write_NamedColorVal2(
5654 	icmNamedColorVal *p,
5655 	char *bp,
5656 	icColorSpaceSignature pcs,		/* Header Profile Connection Space */
5657 	unsigned int ndc				/* Number of device corrds */
5658 ) {
5659 	icc *icp = p->icp;
5660 	int i, rv = 0;
5661 	if (check_null_string(p->root,32)) {
5662 		sprintf(icp->err,"icmNamedColorVal2_write: Root string names is unterminated");
5663 		return icp->errc = 1;
5664 	}
5665 	memcpy((void *)(bp + 0),(void *)p->root,32);
5666 	switch(pcs) {
5667 		case icSigXYZData:
5668 				rv |= write_PCSXYZ16Number(p->pcsCoords[0], bp+32);
5669 				rv |= write_PCSXYZ16Number(p->pcsCoords[1], bp+34);
5670 				rv |= write_PCSXYZ16Number(p->pcsCoords[2], bp+36);
5671 			break;
5672     	case icSigLabData:
5673 				rv |=  write_PCSL16Number(p->pcsCoords[0], bp+32);
5674 				rv |= write_PCSab16Number(p->pcsCoords[1], bp+34);
5675 				rv |= write_PCSab16Number(p->pcsCoords[2], bp+36);
5676 			break;
5677 		default:
5678 			sprintf(icp->err,"icmNamedColorVal2_write: Unknown PCS");
5679 			return icp->errc = 1;
5680 	}
5681 	if (rv) {
5682 		sprintf(icp->err,"icmNamedColorVal2_write: write of PCS coord failed");
5683 		return icp->errc = 1;
5684 	}
5685 	for (i = 0; i < ndc; i++) {
5686 		if ((rv = write_DCS16Number(p->deviceCoords[i], bp + 32 + 6 + 2 * i)) != 0) {
5687 			sprintf(icp->err,"icmNamedColorVal2_write: write of device coord failed");
5688 			return icp->errc = 1;
5689 		}
5690 	}
5691 	return 0;
5692 }
5693 
5694 /* - - - - - - - - - - - */
5695 /* icmNamedColor object */
5696 
5697 /* Return the number of bytes needed to write this tag */
icmNamedColor_get_size(icmBase * pp)5698 static unsigned int icmNamedColor_get_size(
5699 	icmBase *pp
5700 ) {
5701 	icmNamedColor *p = (icmNamedColor *)pp;
5702 	unsigned int len = 0;
5703 	if (p->ttype == icSigNamedColorType) {
5704 		unsigned int i;
5705 		len += 8;			/* 8 bytes for tag and padding */
5706 		len += 4;			/* 4 for vendor specific flags */
5707 		len += 4;			/* 4 for count of named colors */
5708 		len += strlen(p->prefix) + 1; /* prefix of color names */
5709 		len += strlen(p->suffix) + 1; /* suffix of color names */
5710 		for (i = 0; i < p->count; i++) {
5711 			len += strlen(p->data[i].root) + 1; /* color names */
5712 			len += p->nDeviceCoords * 1;	/* bytes for each named color */
5713 		}
5714 	} else {	/* Named Color 2 */
5715 		len += 8;			/* 8 bytes for tag and padding */
5716 		len += 4;			/* 4 for vendor specific flags */
5717 		len += 4;			/* 4 for count of named colors */
5718 		len += 4;			/* 4 for number of device coords */
5719 		len += 32;			/* 32 for prefix of color names */
5720 		len += 32;			/* 32 for suffix of color names */
5721 		len += p->count * (32 + 6 + p->nDeviceCoords * 2);	/* bytes for each named color */
5722 	}
5723 	return len;
5724 }
5725 
5726 /* read the object, return 0 on success, error code on fail */
icmNamedColor_read(icmBase * pp,unsigned long len,unsigned long of)5727 static int icmNamedColor_read(
5728 	icmBase *pp,
5729 	unsigned long len,		/* tag length */
5730 	unsigned long of		/* start offset within file */
5731 ) {
5732 	icmNamedColor *p = (icmNamedColor *)pp;
5733 	icc *icp = p->icp;
5734 	unsigned long i;
5735 	char *bp, *buf, *end;
5736 	int rv = 0;
5737 
5738 	if (len < 4) {
5739 		sprintf(icp->err,"icmNamedColor_read: Tag too small to be legal");
5740 		return icp->errc = 1;
5741 	}
5742 
5743 	/* Allocate a file read buffer */
5744 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
5745 		sprintf(icp->err,"icmNamedColor_read: malloc() failed");
5746 		return icp->errc = 2;
5747 	}
5748 	bp = buf;
5749 	end = buf + len;
5750 
5751 	/* Read portion of file into buffer */
5752 	if (   icp->fp->seek(icp->fp, of) != 0
5753 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
5754 		sprintf(icp->err,"icmNamedColor_read: fseek() or fread() failed");
5755 		icp->al->free(icp->al, buf);
5756 		return icp->errc = 1;
5757 	}
5758 
5759 	/* Read type descriptor from the buffer */
5760 	p->ttype = (icTagTypeSignature)read_SInt32Number(bp);
5761 	if (p->ttype != icSigNamedColorType && p->ttype != icSigNamedColor2Type) {
5762 		sprintf(icp->err,"icmNamedColor_read: Wrong tag type for icmNamedColor");
5763 		icp->al->free(icp->al, buf);
5764 		return icp->errc = 1;
5765 	}
5766 
5767 	if (p->ttype == icSigNamedColorType) {
5768 		if (len < 16) {
5769 			sprintf(icp->err,"icmNamedColor_read: Tag too small to be legal");
5770 			icp->al->free(icp->al, buf);
5771 			return icp->errc = 1;
5772 		}
5773 		/* Make sure that the number of device coords in known */
5774 		p->nDeviceCoords = number_ColorSpaceSignature(icp->header->colorSpace);
5775 		if (p->nDeviceCoords > MAX_CHAN) {
5776 			sprintf(icp->err,"icmNamedColor_read: Can't handle more than %d device channels",MAX_CHAN);
5777 			icp->al->free(icp->al, buf);
5778 			return icp->errc = 1;
5779 		}
5780 
5781 	} else {	/* icmNC2 */
5782 		if (len < 84) {
5783 			sprintf(icp->err,"icmNamedColor_read: Tag too small to be legal");
5784 			icp->al->free(icp->al, buf);
5785 			return icp->errc = 1;
5786 		}
5787 	}
5788 
5789 	/* Read vendor specific flag */
5790 	p->vendorFlag = read_UInt32Number(bp+8);
5791 
5792 	/* Read count of named colors */
5793 	p->count = read_UInt32Number(bp+12);
5794 
5795 	if (p->ttype == icSigNamedColorType) {
5796 		unsigned int mxl;	/* Max possible string length */
5797 		bp = bp + 16;
5798 
5799 		/* Prefix for each color name */
5800 		mxl = (end - bp) < 32 ? (end - bp) : 32;
5801 		if (check_null_string(bp,mxl) != 0) {
5802 			sprintf(icp->err,"icmNamedColor_read: Color prefix is not null terminated");
5803 			icp->al->free(icp->al, buf);
5804 			return icp->errc = 1;
5805 		}
5806 		strcpy((void *)p->prefix, (void *)bp);
5807 		bp += strlen(p->prefix) + 1;
5808 
5809 		/* Suffix for each color name */
5810 		mxl = (end - bp) < 32 ? (end - bp) : 32;
5811 		if (check_null_string(bp,mxl) != 0) {
5812 			sprintf(icp->err,"icmNamedColor_read: Color suffix is not null terminated");
5813 			icp->al->free(icp->al, buf);
5814 			return icp->errc = 1;
5815 		}
5816 		strcpy((void *)p->suffix, (void *)bp);
5817 		bp += strlen(p->suffix) + 1;
5818 
5819 		if ((rv = p->allocate((void *)p)) != 0) {
5820 			icp->al->free(icp->al, buf);
5821 			return rv;
5822 		}
5823 
5824 		/* Read all the data from the buffer */
5825 		for (i = 0; i < p->count; i++) {
5826 			if ((rv = read_NamedColorVal(p->data+i, bp, end, icp->header->pcs, p->nDeviceCoords)) != 0) {
5827 				icp->al->free(icp->al, buf);
5828 				return rv;
5829 			}
5830 			bp += strlen(p->data[i].root) + 1;
5831 			bp += p->nDeviceCoords * 1;
5832 		}
5833 	} else {  /* icmNC2 */
5834 		/* Number of device coords per color */
5835 		p->nDeviceCoords = read_UInt32Number(bp+16);
5836 
5837 		/* Prefix for each color name */
5838 		memcpy((void *)p->prefix, (void *)(bp + 20), 32);
5839 		if (check_null_string(p->prefix,32) != 0) {
5840 			sprintf(icp->err,"icmNamedColor_read: Color prefix is not null terminated");
5841 			icp->al->free(icp->al, buf);
5842 			return icp->errc = 1;
5843 		}
5844 
5845 		/* Suffix for each color name */
5846 		memcpy((void *)p->suffix, (void *)(bp + 52), 32);
5847 		if (check_null_string(p->suffix,32) != 0) {
5848 			sprintf(icp->err,"icmNamedColor_read: Color suffix is not null terminated");
5849 			icp->al->free(icp->al, buf);
5850 			return icp->errc = 1;
5851 		}
5852 
5853 		if ((rv = p->allocate((icmBase *)p)) != 0) {
5854 			icp->al->free(icp->al, buf);
5855 			return rv;
5856 		}
5857 
5858 		/* Read all the data from the buffer */
5859 		bp = bp + 84;
5860 		for (i = 0; i < p->count; i++, bp += (32 + 6 + p->nDeviceCoords * 2)) {
5861 			if ((rv = read_NamedColorVal2(p->data+i, bp, end, icp->header->pcs, p->nDeviceCoords)) != 0) {
5862 				icp->al->free(icp->al, buf);
5863 				return rv;
5864 			}
5865 		}
5866 	}
5867 	icp->al->free(icp->al, buf);
5868 	return rv;
5869 }
5870 
5871 /* Write the contents of the object. Return 0 on sucess, error code on failure */
icmNamedColor_write(icmBase * pp,unsigned long of)5872 static int icmNamedColor_write(
5873 	icmBase *pp,
5874 	unsigned long of			/* File offset to write from */
5875 ) {
5876 	icmNamedColor *p = (icmNamedColor *)pp;
5877 	icc *icp = p->icp;
5878 	unsigned long i;
5879 	unsigned int len;
5880 	char *bp, *buf;		/* Buffer to write from */
5881 	int rv = 0;
5882 
5883 	/* Allocate a file write buffer */
5884 	len = p->get_size((icmBase *)p);
5885 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
5886 		sprintf(icp->err,"icmNamedColor_write malloc() failed");
5887 		return icp->errc = 2;
5888 	}
5889 	bp = buf;
5890 
5891 	/* Write type descriptor to the buffer */
5892 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
5893 		sprintf(icp->err,"icmNamedColor_write: write_SInt32Number() failed");
5894 		icp->al->free(icp->al, buf);
5895 		return icp->errc = rv;
5896 	}
5897 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
5898 
5899 	/* Write vendor specific flag */
5900 	if ((rv = write_UInt32Number(p->vendorFlag, bp+8)) != 0) {
5901 		sprintf(icp->err,"icmNamedColor_write: write_UInt32Number() failed");
5902 		icp->al->free(icp->al, buf);
5903 		return icp->errc = rv;
5904 	}
5905 
5906 	/* Write count of named colors */
5907 	if ((rv = write_UInt32Number(p->count, bp+12)) != 0) {
5908 		sprintf(icp->err,"icmNamedColor_write: write_UInt32Number() failed");
5909 		icp->al->free(icp->al, buf);
5910 		return icp->errc = rv;
5911 	}
5912 
5913 	if (p->ttype == icSigNamedColorType) {
5914 		bp = bp + 16;
5915 
5916 		/* Prefix for each color name */
5917 		if ((rv = check_null_string(p->prefix,32)) != 0) {
5918 			sprintf(icp->err,"icmNamedColor_write: Color prefix is not null terminated");
5919 			icp->al->free(icp->al, buf);
5920 			return icp->errc = 1;
5921 		}
5922 		strcpy((void *)bp, (void *)p->prefix);
5923 		bp += strlen(p->prefix) + 1;
5924 
5925 		/* Suffix for each color name */
5926 		if (check_null_string(p->suffix,32)) {
5927 			sprintf(icp->err,"icmNamedColor_write: Color sufix is not null terminated");
5928 			icp->al->free(icp->al, buf);
5929 			return icp->errc = 1;
5930 		}
5931 		strcpy((void *)bp, (void *)p->suffix);
5932 		bp += strlen(p->suffix) + 1;
5933 
5934 		/* Write all the data to the buffer */
5935 
5936 		for (i = 0; i < p->count; i++) {
5937 			if ((rv = write_NamedColorVal(p->data+i, bp, icp->header->pcs, p->nDeviceCoords)) != 0) {
5938 				icp->al->free(icp->al, buf);
5939 				return rv;
5940 			}
5941 			bp += strlen(p->data[i].root) + 1;
5942 			bp += p->nDeviceCoords * 1;
5943 		}
5944 	} else {	/* icmNC2 */
5945 		/* Number of device coords per color */
5946 		if ((rv = write_UInt32Number(p->nDeviceCoords, bp+16)) != 0) {
5947 			sprintf(icp->err,"icmNamedColor_write: write_UInt32Number() failed");
5948 			icp->al->free(icp->al, buf);
5949 			return icp->errc = rv;
5950 		}
5951 
5952 		/* Prefix for each color name */
5953 		if ((rv = check_null_string(p->prefix,32)) != 0) {
5954 			sprintf(icp->err,"icmNamedColor_write: Color prefix is not null terminated");
5955 			icp->al->free(icp->al, buf);
5956 			return icp->errc = 1;
5957 		}
5958 		memcpy((void *)(bp + 20), (void *)p->prefix, 32);
5959 
5960 		/* Suffix for each color name */
5961 		if (check_null_string(p->suffix,32)) {
5962 			sprintf(icp->err,"icmNamedColor_write: Color sufix is not null terminated");
5963 			icp->al->free(icp->al, buf);
5964 			return icp->errc = 1;
5965 		}
5966 		memcpy((void *)(bp + 52), (void *)p->suffix, 32);
5967 
5968 		/* Write all the data to the buffer */
5969 		bp = bp + 84;
5970 		for (i = 0; i < p->count; i++, bp += (32 + 6 + p->nDeviceCoords * 2)) {
5971 			if ((rv = write_NamedColorVal2(p->data+i, bp, icp->header->pcs, p->nDeviceCoords)) != 0) {
5972 				icp->al->free(icp->al, buf);
5973 				return rv;
5974 			}
5975 		}
5976 	}
5977 
5978 	/* Write to the file */
5979 	if (   icp->fp->seek(icp->fp, of) != 0
5980 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
5981 		sprintf(icp->err,"icmNamedColor_write fseek() or fwrite() failed");
5982 		icp->al->free(icp->al, buf);
5983 		return icp->errc = 2;
5984 	}
5985 	icp->al->free(icp->al, buf);
5986 	return 0;
5987 }
5988 
5989 /* Dump a text description of the object */
icmNamedColor_dump(icmBase * pp,FILE * op,int verb)5990 static void icmNamedColor_dump(
5991 	icmBase *pp,
5992 	FILE *op,		/* Output to dump to */
5993 	int   verb		/* Verbosity level */
5994 ) {
5995 	icmNamedColor *p = (icmNamedColor *)pp;
5996 	icc *icp = p->icp;
5997 	if (verb <= 0)
5998 		return;
5999 
6000 	if (p->ttype == icSigNamedColorType)
6001 		fprintf(op,"NamedColor:\n");
6002 	else
6003 		fprintf(op,"NamedColor2:\n");
6004 	fprintf(op,"  Vendor Flag = 0x%x\n",p->vendorFlag);
6005 	fprintf(op,"  No. colors  = %u\n",p->count);
6006 	fprintf(op,"  No. dev. coords = %u\n",p->nDeviceCoords);
6007 	fprintf(op,"  Name prefix = '%s'\n",p->prefix);
6008 	fprintf(op,"  Name suffix = '%s'\n",p->suffix);
6009 	if (verb >= 2) {
6010 		unsigned long i, n;
6011 		icmNamedColorVal *vp;
6012 		for (i = 0; i < p->count; i++) {
6013 			vp = p->data + i;
6014 			fprintf(op,"    Color %lu:\n",i);
6015 			fprintf(op,"      Name root = '%s'\n",vp->root);
6016 
6017 			if (p->ttype == icSigNamedColor2Type) {
6018 				switch(icp->header->pcs) {
6019 					case icSigXYZData:
6020 							fprintf(op,"      XYZ = %f, %f, %f'\n",
6021 							        vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]);
6022 						break;
6023 			    	case icSigLabData:
6024 							fprintf(op,"      Lab = %f, %f, %f'\n",
6025 							        vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]);
6026 						break;
6027 					default:
6028 							fprintf(op,"      Unexpected PCS\n");
6029 						break;
6030 				}
6031 			}
6032 			if (p->nDeviceCoords > 0) {
6033 				fprintf(op,"      Device Coords = ");
6034 				for (n = 0; n < p->nDeviceCoords; n++) {
6035 					if (n > 0)
6036 						printf(", ");
6037 					printf("%f",vp->deviceCoords[n]);
6038 				}
6039 				printf("\n");
6040 			}
6041 		}
6042 	}
6043 }
6044 
6045 /* Allocate variable sized data elements */
icmNamedColor_allocate(icmBase * pp)6046 static int icmNamedColor_allocate(
6047 	icmBase *pp
6048 ) {
6049 	icmNamedColor *p = (icmNamedColor *)pp;
6050 	icc *icp = p->icp;
6051 
6052 	if (p->count != p->_count) {
6053 		unsigned int i;
6054 		if (p->data != NULL)
6055 			icp->al->free(icp->al, p->data);
6056 		if ((p->data = (icmNamedColorVal *) icp->al->calloc(icp->al,p->count, sizeof(icmNamedColorVal))) == NULL) {
6057 			sprintf(icp->err,"icmNamedColor_alloc: malloc() of icmNamedColor data failed");
6058 			return icp->errc = 2;
6059 		}
6060 		for (i = 0; i < p->count; i++) {
6061 			p->data[i].icp = icp;	/* Do init */
6062 		}
6063 		p->_count = p->count;
6064 	}
6065 	return 0;
6066 }
6067 
6068 /* Free all storage in the object */
icmNamedColor_delete(icmBase * pp)6069 static void icmNamedColor_delete(
6070 	icmBase *pp
6071 ) {
6072 	icmNamedColor *p = (icmNamedColor *)pp;
6073 	icc *icp = p->icp;
6074 
6075 	if (p->data != NULL)
6076 		icp->al->free(icp->al, p->data);
6077 	icp->al->free(icp->al, p);
6078 }
6079 
6080 /* Create an empty object. Return null on error */
new_icmNamedColor(icc * icp)6081 static icmBase *new_icmNamedColor(
6082 	icc *icp
6083 ) {
6084 	icmNamedColor *p;
6085 	if ((p = (icmNamedColor *) icp->al->calloc(icp->al,1,sizeof(icmNamedColor))) == NULL)
6086 		return NULL;
6087 	p->ttype    = icSigNamedColor2Type;
6088 	p->refcount = 1;
6089 	p->get_size = icmNamedColor_get_size;
6090 	p->read     = icmNamedColor_read;
6091 	p->write    = icmNamedColor_write;
6092 	p->dump     = icmNamedColor_dump;
6093 	p->allocate = icmNamedColor_allocate;
6094 	p->del      = icmNamedColor_delete;
6095 	p->icp      = icp;
6096 
6097 	/* Default the the number of device coords appropriately for NamedColorType */
6098 	p->nDeviceCoords = number_ColorSpaceSignature(icp->header->colorSpace);
6099 
6100 	return (icmBase *)p;
6101 }
6102 
6103 /* ---------------------------------------------------------- */
6104 /* textDescription */
6105 
6106 /* Return the number of bytes needed to write this tag */
icmTextDescription_get_size(icmBase * pp)6107 static unsigned int icmTextDescription_get_size(
6108 	icmBase *pp
6109 ) {
6110 	icmTextDescription *p = (icmTextDescription *)pp;
6111 	unsigned int len = 0;
6112 	len += 8;			/* 8 bytes for tag and padding */
6113 	len += 4 + p->size;	/* Ascii string length + ascii string */
6114 	len += 8 + 2 * p->ucSize;	/* Unicode language code + length + string */
6115 	len += 3 + 67;		/* ScriptCode code, length string */
6116 	return len;
6117 }
6118 
6119 /* read the object, return 0 on success, error code on fail */
icmTextDescription_read(icmBase * pp,unsigned long len,unsigned long of)6120 static int icmTextDescription_read(
6121 	icmBase *pp,
6122 	unsigned long len,		/* tag length */
6123 	unsigned long of		/* start offset within file */
6124 ) {
6125 	icmTextDescription *p = (icmTextDescription *)pp;
6126 	icc *icp = p->icp;
6127 	int rv;
6128 	char *bp, *buf, *end;
6129 
6130 #ifdef ICM_STRICT
6131 	if (len < (8 + 4 + 8 + 3 /* + 67 */)) {
6132 #else
6133 	if (len < (8 + 4 + 8 + 3)) {
6134 #endif
6135 		sprintf(icp->err,"icmTextDescription_read: Tag too small to be legal");
6136 		return icp->errc = 1;
6137 	}
6138 
6139 	/* Allocate a file read buffer */
6140 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
6141 		sprintf(icp->err,"icmTextDescription_read: malloc() failed");
6142 		return icp->errc = 2;
6143 	}
6144 	bp = buf;
6145 	end = buf + len;
6146 
6147 	/* Read portion of file into buffer */
6148 	if (   icp->fp->seek(icp->fp, of) != 0
6149 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
6150 		sprintf(icp->err,"icmTextDescription_read: fseek() or fread() failed");
6151 		icp->al->free(icp->al, buf);
6152 		return icp->errc = 1;
6153 	}
6154 
6155 	/* Read from the buffer into the structure */
6156 	if ((rv = p->core_read(p, &bp, end)) != 0) {
6157 		icp->al->free(icp->al, buf);
6158 		return rv;
6159 	}
6160 
6161 	icp->al->free(icp->al, buf);
6162 	return 0;
6163 }
6164 
6165 /* core read the object, return 0 on success, error code on fail */
6166 static int icmTextDescription_core_read(
6167 	icmTextDescription *p,
6168 	char **bpp,				/* Pointer to buffer pointer, returns next after read */
6169 	char *end				/* Pointer to past end of read buffer */
6170 ) {
6171 	icc *icp = p->icp;
6172 	int rv = 0;
6173 	char *bp = *bpp;
6174 
6175 	if ((bp + 8) > end) {
6176 		sprintf(icp->err,"icmTextDescription_read: Data too short to type descriptor");
6177 		*bpp = bp;
6178 		return icp->errc = 1;
6179 	}
6180 
6181 	p->size = read_UInt32Number(bp);
6182 	/* Read type descriptor from the buffer */
6183 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
6184 		*bpp = bp;
6185 		sprintf(icp->err,"icmTextDescription_read: Wrong tag type ('%s') for icmTextDescription",
6186 		        tag2str((icTagTypeSignature)read_SInt32Number(bp)));
6187 		return icp->errc = 1;
6188 	}
6189 	bp = bp + 8;
6190 
6191 	/* Read the Ascii string */
6192 	if ((bp + 4) > end) {
6193 		*bpp = bp;
6194 		sprintf(icp->err,"icmTextDescription_read: Data too short to read Ascii header");
6195 		return icp->errc = 1;
6196 	}
6197 	p->size = read_UInt32Number(bp);
6198 	bp += 4;
6199 	if (p->size > 0) {
6200 		if ((bp + p->size) > end) {
6201 			*bpp = bp;
6202 			sprintf(icp->err,"icmTextDescription_read: Data to short to read Ascii string");
6203 			return icp->errc = 1;
6204 		}
6205 		if (check_null_string(bp,p->size)) {
6206 			*bpp = bp;
6207 			sprintf(icp->err,"icmTextDescription_read: ascii string is not terminated");
6208 			return icp->errc = 1;
6209 		}
6210 		if ((rv = p->allocate((icmBase *)p)) != 0) {
6211 			return rv;
6212 		}
6213 		strcpy((void *)p->desc, (void *)bp);
6214 		bp += p->size;
6215 	}
6216 
6217 	/* Read the Unicode string */
6218 	if ((bp + 8) > end) {
6219 		*bpp = bp;
6220 		sprintf(icp->err,"icmTextDescription_read: Data too short to read Unicode string");
6221 		return icp->errc = 1;
6222 	}
6223 	p->ucLangCode = read_UInt32Number(bp);
6224 	bp += 4;
6225 	p->ucSize = read_UInt32Number(bp);
6226 	bp += 4;
6227 	if (p->ucSize > 0) {
6228 		ORD16 *up;
6229 		if ((bp + 2 * p->ucSize) > end) {
6230 			*bpp = bp;
6231 			sprintf(icp->err,"icmTextDescription_read: Data too short to read Unicode string");
6232 			return icp->errc = 1;
6233 		}
6234 		if (check_null_string16(bp,p->ucSize)) {
6235 			*bpp = bp;
6236 			sprintf(icp->err,"icmTextDescription_read: Unicode string is not terminated");
6237 			return icp->errc = 1;
6238 		}
6239 		if ((rv = p->allocate((icmBase *)p)) != 0) {
6240 			return rv;
6241 		}
6242 		for(up = p->ucDesc; bp[0] != 0 || bp[1] != 0; up++, bp += 2)
6243 			*up = read_UInt16Number(bp);
6244 		*up = 0;	/* Unicode null */
6245 		bp += 2;
6246 	}
6247 
6248 	/* Read the ScriptCode string */
6249 	if ((bp + 3) > end) {
6250 		*bpp = bp;
6251 		sprintf(icp->err,"icmTextDescription_read: Data too short to read ScriptCode header");
6252 		return icp->errc = 1;
6253 	}
6254 	p->scCode = read_UInt16Number(bp);
6255 	bp += 2;
6256 	p->scSize = read_UInt8Number(bp);
6257 	bp += 1;
6258 	if (p->scSize > 0) {
6259 		if (p->scSize > 67) {
6260 			*bpp = bp;
6261 			sprintf(icp->err,"icmTextDescription_read: ScriptCode string too long");
6262 			return icp->errc = 1;
6263 		}
6264 		if ((bp + p->scSize) > end) {
6265 			*bpp = bp;
6266 			sprintf(icp->err,"icmTextDescription_read: Data too short to read ScriptCode string");
6267 			return icp->errc = 1;
6268 		}
6269 		if (check_null_string(bp,p->scSize)) {
6270 			*bpp = bp;
6271 			sprintf(icp->err,"icmTextDescription_read: ScriptCode string is not terminated");
6272 			return icp->errc = 1;
6273 		}
6274 		memcpy((void *)p->scDesc, (void *)bp, p->scSize);
6275 	} else {
6276 		memset((void *)p->scDesc, 0, 67);
6277 	}
6278 	bp += 67;
6279 
6280 	*bpp = bp;
6281 	return 0;
6282 }
6283 
6284 /* Write the contents of the object. Return 0 on sucess, error code on failure */
6285 static int icmTextDescription_write(
6286 	icmBase *pp,
6287 	unsigned long of			/* File offset to write from */
6288 ) {
6289 	icmTextDescription *p = (icmTextDescription *)pp;
6290 	icc *icp = p->icp;
6291 	unsigned int len;
6292 	char *bp, *buf;		/* Buffer to write from */
6293 	int rv = 0;
6294 
6295 	/* Allocate a file write buffer */
6296 	len = p->get_size((icmBase *)p);
6297 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
6298 		sprintf(icp->err,"icmTextDescription_write malloc() failed");
6299 		return icp->errc = 2;
6300 	}
6301 	bp = buf;
6302 
6303 	/* Write to the buffer from the structure */
6304 	if ((rv = p->core_write(p, &bp)) != 0) {
6305 		icp->al->free(icp->al, buf);
6306 		return rv;
6307 	}
6308 
6309 	/* Write to the file */
6310 	if (   icp->fp->seek(icp->fp, of) != 0
6311 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
6312 		sprintf(icp->err,"icmTextDescription_write fseek() or fwrite() failed");
6313 		icp->al->free(icp->al, buf);
6314 		return icp->errc = 2;
6315 	}
6316 	icp->al->free(icp->al, buf);
6317 	return 0;
6318 }
6319 
6320 /* Core write the contents of the object. Return 0 on sucess, error code on failure */
6321 static int icmTextDescription_core_write(
6322 	icmTextDescription *p,
6323 	char **bpp				/* Pointer to buffer pointer, returns next after read */
6324 ) {
6325 	icc *icp = p->icp;
6326 	char *bp = *bpp;
6327 	int rv = 0;
6328 
6329 	/* Write type descriptor to the buffer */
6330 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
6331 		sprintf(icp->err,"icmTextDescription_write: write_SInt32Number() failed");
6332 		*bpp = bp;
6333 		return icp->errc = rv;
6334 	}
6335 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
6336 	bp = bp + 8;
6337 
6338 	/* Write the Ascii string */
6339 	if ((rv = write_UInt32Number(p->size,bp)) != 0) {
6340 		sprintf(icp->err,"icmTextDescription_write: write_UInt32Number() failed");
6341 		*bpp = bp;
6342 		return icp->errc = rv;
6343 	}
6344 	bp += 4;
6345 	if (p->size > 0) {
6346 		if (check_null_string(p->desc,p->size)) {
6347 			*bpp = bp;
6348 			sprintf(icp->err,"icmTextDescription_write: ascii string is not terminated");
6349 			return icp->errc = 1;
6350 		}
6351 		strcpy((void *)bp, (void *)p->desc);
6352 		bp += p->size;
6353 	}
6354 
6355 	/* Write the Unicode string */
6356 	if ((rv = write_UInt32Number(p->ucLangCode, bp)) != 0) {
6357 		sprintf(icp->err,"icmTextDescription_write: write_UInt32Number() failed");
6358 		*bpp = bp;
6359 		return icp->errc = rv;
6360 	}
6361 	bp += 4;
6362 	if ((rv = write_UInt32Number(p->ucSize, bp)) != 0) {
6363 		sprintf(icp->err,"icmTextDescription_write: write_UInt32Number() failed");
6364 		*bpp = bp;
6365 		return icp->errc = rv;
6366 	}
6367 	bp += 4;
6368 	if (p->ucSize > 0) {
6369 		ORD16 *up;
6370 		if (check_null_string16((char *)p->ucDesc,p->ucSize)) {
6371 			*bpp = bp;
6372 			sprintf(icp->err,"icmTextDescription_write: Unicode string is not terminated");
6373 			return icp->errc = 1;
6374 		}
6375 		for(up = p->ucDesc; *up != 0; up++, bp += 2) {
6376 			if ((rv = write_UInt16Number(((unsigned int)*up), bp)) != 0) {
6377 				sprintf(icp->err,"icmTextDescription_write: write_UInt16Number() failed");
6378 				*bpp = bp;
6379 				return icp->errc = rv;
6380 			}
6381 		}
6382 		bp[0] = 0;	/* null */
6383 		bp[1] = 0;
6384 		bp += 2;
6385 	}
6386 
6387 	/* Write the ScriptCode string */
6388 	if ((rv = write_UInt16Number(p->scCode, bp)) != 0) {
6389 		sprintf(icp->err,"icmTextDescription_write: write_UInt16Number() failed");
6390 		*bpp = bp;
6391 		return icp->errc = rv;
6392 	}
6393 	bp += 2;
6394 	if ((rv = write_UInt8Number(p->scSize, bp)) != 0) {
6395 		sprintf(icp->err,"icmTextDescription_write: write_UInt8Number() failed");
6396 		*bpp = bp;
6397 		return icp->errc = rv;
6398 	}
6399 	bp += 1;
6400 	if (p->scSize > 0) {
6401 		if (p->scSize > 67) {
6402 			*bpp = bp;
6403 			sprintf(icp->err,"icmTextDescription_write: ScriptCode string too long");
6404 			return icp->errc = 1;
6405 		}
6406 		if (check_null_string((char *)p->scDesc,p->scSize)) {
6407 			*bpp = bp;
6408 			sprintf(icp->err,"icmTextDescription_write: ScriptCode string is not terminated");
6409 			return icp->errc = 1;
6410 		}
6411 		memcpy((void *)bp, (void *)p->scDesc, 67);
6412 	} else {
6413 		memset((void *)bp, 0, 67);
6414 	}
6415 	bp += 67;
6416 
6417 	*bpp = bp;
6418 	return 0;
6419 }
6420 
6421 /* Dump a text description of the object */
6422 static void icmTextDescription_dump(
6423 	icmBase *pp,
6424 	FILE *op,		/* Output to dump to */
6425 	int   verb		/* Verbosity level */
6426 ) {
6427 	icmTextDescription *p = (icmTextDescription *)pp;
6428 	unsigned long i, r, c;
6429 
6430 	if (verb <= 0)
6431 		return;
6432 
6433 	fprintf(op,"TextDescription:\n");
6434 
6435 	if (p->size > 0) {
6436 		unsigned long size = p->size > 0 ? p->size-1 : 0;
6437 		fprintf(op,"  ASCII data, length %lu chars:\n",p->size);
6438 
6439 		i = 0;
6440 		for (r = 1;; r++) {		/* count rows */
6441 			if (i >= size) {
6442 				fprintf(op,"\n");
6443 				break;
6444 			}
6445 			if (r > 1 && verb < 2) {
6446 				fprintf(op,"...\n");
6447 				break;			/* Print 1 row if not verbose */
6448 			}
6449 			c = 1;
6450 			fprintf(op,"    0x%04lx: ",i);
6451 			c += 10;
6452 			while (i < size && c < 75) {
6453 				if (isprint(p->desc[i])) {
6454 					fprintf(op,"%c",p->desc[i]);
6455 					c++;
6456 				} else {
6457 					fprintf(op,"\\%03o",p->desc[i]);
6458 					c += 4;
6459 				}
6460 				i++;
6461 			}
6462 			if (i < size)
6463 				fprintf(op,"\n");
6464 		}
6465 	} else {
6466 		fprintf(op,"  No ASCII data\n");
6467 	}
6468 
6469 	/* Can't dump Unicode or ScriptCode as text with portable code */
6470 	if (p->ucSize > 0) {
6471 		unsigned long size = p->ucSize;
6472 		fprintf(op,"  Unicode Data, Language code 0x%x, length %lu chars\n",
6473 		        p->ucLangCode, p->ucSize);
6474 		i = 0;
6475 		for (r = 1;; r++) {		/* count rows */
6476 			if (i >= size) {
6477 				fprintf(op,"\n");
6478 				break;
6479 			}
6480 			if (r > 1 && verb < 2) {
6481 				fprintf(op,"...\n");
6482 				break;			/* Print 1 row if not verbose */
6483 			}
6484 			c = 1;
6485 			fprintf(op,"    0x%04lx: ",i);
6486 			c += 10;
6487 			while (i < size && c < 75) {
6488 				fprintf(op,"%04x ",p->ucDesc[i]);
6489 				c += 5;
6490 				i++;
6491 			}
6492 			if (i < size)
6493 				fprintf(op,"\n");
6494 		}
6495 	} else {
6496 		fprintf(op,"  No Unicode data\n");
6497 	}
6498 	if (p->scSize > 0) {
6499 		unsigned long size = p->scSize;
6500 		fprintf(op,"  ScriptCode Data, Code 0x%x, length %lu chars\n",
6501 		        p->scCode, p->scSize);
6502 		i = 0;
6503 		for (r = 1;; r++) {		/* count rows */
6504 			if (i >= size) {
6505 				fprintf(op,"\n");
6506 				break;
6507 			}
6508 			if (r > 1 && verb < 2) {
6509 				fprintf(op,"...\n");
6510 				break;			/* Print 1 row if not verbose */
6511 			}
6512 			c = 1;
6513 			fprintf(op,"    0x%04lx: ",i);
6514 			c += 10;
6515 			while (i < size && c < 75) {
6516 				fprintf(op,"%02x ",p->scDesc[i]);
6517 				c += 3;
6518 				i++;
6519 			}
6520 			if (i < size)
6521 				fprintf(op,"\n");
6522 		}
6523 	} else {
6524 		fprintf(op,"  No ScriptCode data\n");
6525 	}
6526 }
6527 
6528 /* Allocate variable sized data elements */
6529 static int icmTextDescription_allocate(
6530 	icmBase *pp
6531 ) {
6532 	icmTextDescription *p = (icmTextDescription *)pp;
6533 	icc *icp = p->icp;
6534 
6535 	if (p->size != p->_size) {
6536 		if (p->desc != NULL)
6537 			icp->al->free(icp->al, p->desc);
6538 		if ((p->desc = (char *) icp->al->malloc(icp->al, p->size * sizeof(char))) == NULL) {
6539 			sprintf(icp->err,"icmTextDescription_alloc: malloc() of Ascii description failed");
6540 			return icp->errc = 2;
6541 		}
6542 		p->_size = p->size;
6543 	}
6544 	if (p->ucSize != p->uc_size) {
6545 		if (p->ucDesc != NULL)
6546 			icp->al->free(icp->al, p->ucDesc);
6547 		if ((p->ucDesc = (ORD16 *) icp->al->malloc(icp->al, p->ucSize * sizeof(ORD16))) == NULL) {
6548 			sprintf(icp->err,"icmTextDescription_alloc: malloc() of Unicode description failed");
6549 			return icp->errc = 2;
6550 		}
6551 		p->uc_size = p->ucSize;
6552 	}
6553 	return 0;
6554 }
6555 
6556 /* Free all variable sized elements */
6557 static void icmTextDescription_unallocate(
6558 	icmTextDescription *p
6559 ) {
6560 	icc *icp = p->icp;
6561 
6562 	if (p->desc != NULL)
6563 		icp->al->free(icp->al, p->desc);
6564 	if (p->ucDesc != NULL)
6565 		icp->al->free(icp->al, p->ucDesc);
6566 }
6567 
6568 /* Free all storage in the object */
6569 static void icmTextDescription_delete(
6570 	icmBase *pp
6571 ) {
6572 	icmTextDescription *p = (icmTextDescription *)pp;
6573 	icc *icp = p->icp;
6574 
6575 	icmTextDescription_unallocate(p);
6576 	icp->al->free(icp->al, p);
6577 }
6578 
6579 /* Initialze a named object */
6580 static void icmTextDescription_init(
6581 	icmTextDescription *p,
6582 	icc *icp
6583 ) {
6584 	memset((void *)p, 0, sizeof(icmTextDescription));	/* Imitate calloc */
6585 
6586 	p->ttype    = icSigTextDescriptionType;
6587 	p->refcount = 1;
6588 	p->get_size = icmTextDescription_get_size;
6589 	p->read     = icmTextDescription_read;
6590 	p->write    = icmTextDescription_write;
6591 	p->dump     = icmTextDescription_dump;
6592 	p->allocate = icmTextDescription_allocate;
6593 	p->del      = icmTextDescription_delete;
6594 	p->icp      = icp;
6595 
6596 	p->core_read  = icmTextDescription_core_read;
6597 	p->core_write = icmTextDescription_core_write;
6598 }
6599 
6600 /* Create an empty object. Return null on error */
6601 static icmBase *new_icmTextDescription(
6602 	icc *icp
6603 ) {
6604 	icmTextDescription *p;
6605 	if ((p = (icmTextDescription *) icp->al->calloc(icp->al,1,sizeof(icmTextDescription))) == NULL)
6606 		return NULL;
6607 
6608 	icmTextDescription_init(p,icp);
6609 	return (icmBase *)p;
6610 }
6611 
6612 /* ---------------------------------------------------------- */
6613 
6614 /* Support for icmDescStruct */
6615 
6616 /* Return the number of bytes needed to write this tag */
6617 static unsigned int icmDescStruct_get_size(
6618 	icmDescStruct *p
6619 ) {
6620 	unsigned int len = 0;
6621 	len += 20;				/* 20 bytes for header info */
6622 	len += p->device.get_size((icmBase *)&p->device);
6623 	len += p->model.get_size((icmBase *)&p->model);
6624 	return len;
6625 }
6626 
6627 /* read the object, return 0 on success, error code on fail */
6628 static int icmDescStruct_read(
6629 	icmDescStruct *p,
6630 	char **bpp,				/* Pointer to buffer pointer, returns next after read */
6631 	char *end				/* Pointer to past end of read buffer */
6632 ) {
6633 	icc *icp = p->icp;
6634 	char *bp = *bpp;
6635 	int rv = 0;
6636 
6637 	if ((bp + 20) > end) {
6638 		sprintf(icp->err,"icmDescStruct_read: Data too short read header");
6639 		*bpp = bp;
6640 		return icp->errc = 1;
6641 	}
6642 
6643     p->deviceMfg = read_SInt32Number(bp + 0);
6644     p->deviceModel = read_UInt32Number(bp + 4);
6645     read_UInt64Number(&p->attributes, bp + 8);
6646 	p->technology = read_UInt32Number(bp + 16);
6647 	*bpp = bp += 20;
6648 
6649 	/* Read the device text description */
6650 	if ((rv = p->device.core_read(&p->device, bpp, end)) != 0) {
6651 		return rv;
6652 	}
6653 
6654 	/* Read the model text description */
6655 	if ((rv = p->model.core_read(&p->model, bpp, end)) != 0) {
6656 		return rv;
6657 	}
6658 
6659 	return 0;
6660 }
6661 
6662 /* Write the contents of the object. Return 0 on sucess, error code on failure */
6663 static int icmDescStruct_write(
6664 	icmDescStruct *p,
6665 	char **bpp				/* Pointer to buffer pointer, returns next after read */
6666 ) {
6667 	icc *icp = p->icp;
6668 	char *bp = *bpp;
6669 	int rv = 0;
6670 
6671     if ((rv = write_SInt32Number(p->deviceMfg, bp + 0)) != 0) {
6672 		sprintf(icp->err,"icmDescStruct_write: write_SInt32Number() failed");
6673 		*bpp = bp;
6674 		return icp->errc = rv;
6675 	}
6676     if ((rv = write_UInt32Number(p->deviceModel, bp + 4)) != 0) {
6677 		sprintf(icp->err,"icmDescStruct_write: write_UInt32Number() failed");
6678 		*bpp = bp;
6679 		return icp->errc = rv;
6680 	}
6681     if ((rv = write_UInt64Number(&p->attributes, bp + 8)) != 0) {
6682 		sprintf(icp->err,"icmDescStruct_write: write_UInt64Number() failed");
6683 		*bpp = bp;
6684 		return icp->errc = rv;
6685 	}
6686 	if ((rv = write_UInt32Number(p->technology, bp + 16)) != 0) {
6687 		sprintf(icp->err,"icmDescStruct_write: write_UInt32Number() failed");
6688 		*bpp = bp;
6689 		return icp->errc = rv;
6690 	}
6691 	*bpp = bp += 20;
6692 
6693 	/* Write the device text description */
6694 	if ((rv = p->device.core_write(&p->device, bpp)) != 0) {
6695 		return rv;
6696 	}
6697 
6698 	/* Write the model text description */
6699 	if ((rv = p->model.core_write(&p->model, bpp)) != 0) {
6700 		return rv;
6701 	}
6702 
6703 	return 0;
6704 }
6705 
6706 /* Dump a text description of the object */
6707 static void icmDescStruct_dump(
6708 	icmDescStruct *p,
6709 	FILE *op,		/* Output to dump to */
6710 	int   verb,		/* Verbosity level */
6711 	int   index		/* Description index */
6712 ) {
6713 	if (verb <= 0)
6714 		return;
6715 
6716 	fprintf(op,"DescStruct %u:\n",index);
6717 	if (verb >= 1) {
6718 		fprintf(op,"  Dev. Mnfctr.    = %s\n",tag2str(p->deviceMfg));	/* ~~~ */
6719 		fprintf(op,"  Dev. Model      = %s\n",tag2str(p->deviceModel));	/* ~~~ */
6720 		fprintf(op,"  Dev. Attrbts    = %s\n", string_DeviceAttributes(p->attributes.l));
6721 		fprintf(op,"  Dev. Technology = %s\n", string_TechnologySignature(p->technology));
6722 		p->device.dump((icmBase *)&p->device, op,verb);
6723 		p->model.dump((icmBase *)&p->model, op,verb);
6724 		fprintf(op,"\n");
6725 	}
6726 }
6727 
6728 /* Allocate variable sized data elements (ie. descriptions) */
6729 static int icmDescStruct_allocate(
6730 	icmDescStruct *p
6731 ) {
6732 	int rv;
6733 
6734 	if ((rv = p->device.allocate((icmBase *)&p->device)) != 0) {
6735 		return rv;
6736 	}
6737 	if ((rv = p->model.allocate((icmBase *)&p->model)) != 0) {
6738 		return rv;
6739 	}
6740 	return 0;
6741 }
6742 
6743 /* Free all storage in the object */
6744 static void icmDescStruct_delete(
6745 	icmDescStruct *p
6746 ) {
6747 	icmTextDescription_unallocate(&p->device);
6748 	icmTextDescription_unallocate(&p->model);
6749 }
6750 
6751 /* Init a DescStruct object */
6752 static void icmDescStruct_init(
6753 	icmDescStruct *p,
6754 	icc *icp
6755 ) {
6756 
6757 	p->allocate = icmDescStruct_allocate;
6758 	p->icp = icp;
6759 
6760 	icmTextDescription_init(&p->device, icp);
6761 	icmTextDescription_init(&p->model, icp);
6762 }
6763 
6764 /* - - - - - - - - - - - - - - - */
6765 /* icmProfileSequenceDesc object */
6766 
6767 /* Return the number of bytes needed to write this tag */
6768 static unsigned int icmProfileSequenceDesc_get_size(
6769 	icmBase *pp
6770 ) {
6771 	icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
6772 	unsigned int len = 0;
6773 	unsigned int i;
6774 	len += 12;				/* 8 bytes for tag, padding and count */
6775 	for (i = 0; i < p->count; i++) {	/* All the description structures */
6776 		len += icmDescStruct_get_size(&p->data[i]);
6777 	}
6778 	return len;
6779 }
6780 
6781 /* read the object, return 0 on success, error code on fail */
6782 static int icmProfileSequenceDesc_read(
6783 	icmBase *pp,
6784 	unsigned long len,		/* tag length */
6785 	unsigned long of		/* start offset within file */
6786 ) {
6787 	icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
6788 	icc *icp = p->icp;
6789 	unsigned long i;
6790 	char *bp, *buf, *end;
6791 	int rv = 0;
6792 
6793 	if (len < 12) {
6794 		sprintf(icp->err,"icmProfileSequenceDesc_read: Tag too small to be legal");
6795 		return icp->errc = 1;
6796 	}
6797 
6798 	/* Allocate a file read buffer */
6799 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
6800 		sprintf(icp->err,"icmProfileSequenceDesc_read: malloc() failed");
6801 		return icp->errc = 2;
6802 	}
6803 	bp = buf;
6804 	end = buf + len;
6805 
6806 	/* Read portion of file into buffer */
6807 	if (   icp->fp->seek(icp->fp, of) != 0
6808 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
6809 		sprintf(icp->err,"icmProfileSequenceDesc_read: fseek() or fread() failed");
6810 		icp->al->free(icp->al, buf);
6811 		return icp->errc = 1;
6812 	}
6813 
6814 	/* Read type descriptor from the buffer */
6815 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
6816 		sprintf(icp->err,"icmProfileSequenceDesc_read: Wrong tag type for icmProfileSequenceDesc");
6817 		icp->al->free(icp->al, buf);
6818 		return icp->errc = 1;
6819 	}
6820 	bp += 8;	/* Skip padding */
6821 
6822 	p->count = read_UInt32Number(bp);	/* Number of sequence descriptions */
6823 	bp += 4;
6824 
6825 	/* Read all the sequence descriptions */
6826 	if ((rv = p->allocate((icmBase *)p)) != 0) {
6827 		icp->al->free(icp->al, buf);
6828 		return rv;
6829 	}
6830 	for (i = 0; i < p->count; i++) {
6831 		if ((rv = icmDescStruct_read(&p->data[i], &bp, end)) != 0) {
6832 			icp->al->free(icp->al, buf);
6833 			return rv;
6834 		}
6835 	}
6836 
6837 	icp->al->free(icp->al, buf);
6838 	return 0;
6839 }
6840 
6841 /* Write the contents of the object. Return 0 on sucess, error code on failure */
6842 static int icmProfileSequenceDesc_write(
6843 	icmBase *pp,
6844 	unsigned long of			/* File offset to write from */
6845 ) {
6846 	icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
6847 	icc *icp = p->icp;
6848 	unsigned long i;
6849 	unsigned int len;
6850 	char *bp, *buf;		/* Buffer to write from */
6851 	int rv = 0;
6852 
6853 	/* Allocate a file write buffer */
6854 	len = p->get_size((icmBase *)p);
6855 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
6856 		sprintf(icp->err,"icmProfileSequenceDesc_write malloc() failed");
6857 		return icp->errc = 2;
6858 	}
6859 	bp = buf;
6860 
6861 	/* Write type descriptor to the buffer */
6862 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
6863 		sprintf(icp->err,"icmProfileSequenceDesc_write: write_SInt32Number() failed");
6864 		icp->al->free(icp->al, buf);
6865 		return icp->errc = rv;
6866 	}
6867 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
6868 
6869 	if ((rv = write_UInt32Number(p->count,bp+8)) != 0) {
6870 		sprintf(icp->err,"icmProfileSequenceDesc_write: write_UInt32Number() failed");
6871 		icp->al->free(icp->al, buf);
6872 		return icp->errc = rv;
6873 	}
6874 	bp = bp + 12;
6875 
6876 	/* Write all the description structures */
6877 	for (i = 0; i < p->count; i++) {
6878 		if ((rv = icmDescStruct_write(&p->data[i], &bp)) != 0) {
6879 			icp->al->free(icp->al, buf);
6880 			return rv;
6881 		}
6882 	}
6883 
6884 	/* Write to the file */
6885 	if (   icp->fp->seek(icp->fp, of) != 0
6886 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
6887 		sprintf(icp->err,"icmProfileSequenceDesc_write fseek() or fwrite() failed");
6888 		icp->al->free(icp->al, buf);
6889 		return icp->errc = 2;
6890 	}
6891 	icp->al->free(icp->al, buf);
6892 	return 0;
6893 }
6894 
6895 /* Dump a text description of the object */
6896 static void icmProfileSequenceDesc_dump(
6897 	icmBase *pp,
6898 	FILE *op,		/* Output to dump to */
6899 	int   verb		/* Verbosity level */
6900 ) {
6901 	icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
6902 	if (verb <= 0)
6903 		return;
6904 
6905 	fprintf(op,"ProfileSequenceDesc:\n");
6906 	fprintf(op,"  No. elements = %u\n",p->count);
6907 	if (verb >= 2) {
6908 		unsigned long i;
6909 		for (i = 0; i < p->count; i++)
6910 			icmDescStruct_dump(&p->data[i], op, verb-1, i);
6911 	}
6912 }
6913 
6914 /* Allocate variable sized data elements (ie. count of profile descriptions) */
6915 static int icmProfileSequenceDesc_allocate(
6916 	icmBase *pp
6917 ) {
6918 	icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
6919 	icc *icp = p->icp;
6920 	unsigned int i;
6921 
6922 	if (p->count != p->_count) {
6923 		if (p->data != NULL)
6924 			icp->al->free(icp->al, p->data);
6925 		if ((p->data = (icmDescStruct *) icp->al->malloc(icp->al, p->count * sizeof(icmDescStruct))) == NULL) {
6926 			sprintf(icp->err,"icmProfileSequenceDesc_allocate Allocation of DescStruct array failed");
6927 			return icp->errc = 2;
6928 		}
6929 		/* Now init the DescStructs */
6930 		for (i = 0; i < p->count; i++) {
6931 			icmDescStruct_init(&p->data[i], icp);
6932 		}
6933 		p->_count = p->count;
6934 	}
6935 	return 0;
6936 }
6937 
6938 /* Free all storage in the object */
6939 static void icmProfileSequenceDesc_delete(
6940 	icmBase *pp
6941 ) {
6942 	icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
6943 	icc *icp = p->icp;
6944 	unsigned int i;
6945 
6946 	for (i = 0; i < p->count; i++) {
6947 		icmDescStruct_delete(&p->data[i]);	/* Free allocated contents */
6948 	}
6949 	if (p->data != NULL)
6950 		icp->al->free(icp->al, p->data);
6951 	icp->al->free(icp->al, p);
6952 }
6953 
6954 /* Create an empty object. Return null on error */
6955 static icmBase *new_icmProfileSequenceDesc(
6956 	icc *icp
6957 ) {
6958 	icmProfileSequenceDesc *p;
6959 	if ((p = (icmProfileSequenceDesc *) icp->al->calloc(icp->al,1,sizeof(icmProfileSequenceDesc))) == NULL)
6960 		return NULL;
6961 	p->ttype    = icSigProfileSequenceDescType;
6962 	p->refcount = 1;
6963 	p->get_size = icmProfileSequenceDesc_get_size;
6964 	p->read     = icmProfileSequenceDesc_read;
6965 	p->write    = icmProfileSequenceDesc_write;
6966 	p->dump     = icmProfileSequenceDesc_dump;
6967 	p->allocate = icmProfileSequenceDesc_allocate;
6968 	p->del      = icmProfileSequenceDesc_delete;
6969 	p->icp      = icp;
6970 
6971 	return (icmBase *)p;
6972 }
6973 
6974 /* ---------------------------------------------------------- */
6975 /* Signature */
6976 
6977 /* Return the number of bytes needed to write this tag */
6978 static unsigned int icmSignature_get_size(
6979 	icmBase *pp
6980 ) {
6981 	unsigned int len = 0;
6982 	len += 8;			/* 8 bytes for tag and padding */
6983 	len += 4;			/* 4 for signature */
6984 	return len;
6985 }
6986 
6987 /* read the object, return 0 on success, error code on fail */
6988 static int icmSignature_read(
6989 	icmBase *pp,
6990 	unsigned long len,		/* tag length */
6991 	unsigned long of		/* start offset within file */
6992 ) {
6993 	icmSignature *p = (icmSignature *)pp;
6994 	icc *icp = p->icp;
6995 	char *bp, *buf;
6996 
6997 	if (len < 12) {
6998 		sprintf(icp->err,"icmSignature_read: Tag too small to be legal");
6999 		return icp->errc = 1;
7000 	}
7001 
7002 	/* Allocate a file read buffer */
7003 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7004 		sprintf(icp->err,"icmSignature_read: malloc() failed");
7005 		return icp->errc = 2;
7006 	}
7007 	bp = buf;
7008 
7009 	/* Read portion of file into buffer */
7010 	if (   icp->fp->seek(icp->fp, of) != 0
7011 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
7012 		sprintf(icp->err,"icmSignature_read: fseek() or fread() failed");
7013 		icp->al->free(icp->al, buf);
7014 		return icp->errc = 1;
7015 	}
7016 
7017 	/* Read type descriptor from the buffer */
7018 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
7019 		sprintf(icp->err,"icmSignaturSignatureng tag type for icmSignature");
7020 		icp->al->free(icp->al, buf);
7021 		return icp->errc = 1;
7022 	}
7023 
7024 	/* Read the encoded measurement geometry */
7025 	p->sig = (icTechnologySignature)read_SInt32Number(bp + 8);
7026 
7027 	icp->al->free(icp->al, buf);
7028 	return 0;
7029 }
7030 
7031 /* Write the contents of the object. Return 0 on sucess, error code on failure */
7032 static int icmSignature_write(
7033 	icmBase *pp,
7034 	unsigned long of			/* File offset to write from */
7035 ) {
7036 	icmSignature *p = (icmSignature *)pp;
7037 	icc *icp = p->icp;
7038 	unsigned int len;
7039 	char *bp, *buf;		/* Buffer to write from */
7040 	int rv = 0;
7041 
7042 	/* Allocate a file write buffer */
7043 	len = p->get_size((icmBase *)p);
7044 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7045 		sprintf(icp->err,"icmSignature_write malloc() failed");
7046 		return icp->errc = 2;
7047 	}
7048 	bp = buf;
7049 
7050 	/* Write type descriptor to the buffer */
7051 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
7052 		sprintf(icp->err,"icmSignature_write: write_SInt32Number() failed");
7053 		icp->al->free(icp->al, buf);
7054 		return icp->errc = rv;
7055 	}
7056 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
7057 
7058 	/* Write the signature */
7059 	if ((rv = write_SInt32Number((int)p->sig, bp + 8)) != 0) {
7060 		sprintf(icp->err,"icmSignaturea_write: write_SInt32Number() failed");
7061 		icp->al->free(icp->al, buf);
7062 		return icp->errc = rv;
7063 	}
7064 
7065 	/* Write to the file */
7066 	if (   icp->fp->seek(icp->fp, of) != 0
7067 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
7068 		sprintf(icp->err,"icmSignature_write fseek() or fwrite() failed");
7069 		icp->al->free(icp->al, buf);
7070 		return icp->errc = 2;
7071 	}
7072 	icp->al->free(icp->al, buf);
7073 	return 0;
7074 }
7075 
7076 /* Dump a text description of the object */
7077 static void icmSignature_dump(
7078 	icmBase *pp,
7079 	FILE *op,		/* Output to dump to */
7080 	int   verb		/* Verbosity level */
7081 ) {
7082 	icmSignature *p = (icmSignature *)pp;
7083 	if (verb <= 0)
7084 		return;
7085 
7086 	fprintf(op,"Signature\n");
7087 	fprintf(op,"  Technology = %s\n", string_TechnologySignature(p->sig));
7088 }
7089 
7090 /* Allocate variable sized data elements */
7091 static int icmSignature_allocate(
7092 	icmBase *pp
7093 ) {
7094 	/* Nothing to do */
7095 	return 0;
7096 }
7097 
7098 /* Free all storage in the object */
7099 static void icmSignature_delete(
7100 	icmBase *pp
7101 ) {
7102 	icmSignature *p = (icmSignature *)pp;
7103 	icc *icp = p->icp;
7104 
7105 	icp->al->free(icp->al, p);
7106 }
7107 
7108 /* Create an empty object. Return null on error */
7109 static icmBase *new_icmSignature(
7110 	icc *icp
7111 ) {
7112 	icmSignature *p;
7113 	if ((p = (icmSignature *) icp->al->calloc(icp->al,1,sizeof(icmSignature))) == NULL)
7114 		return NULL;
7115 	p->ttype    = icSigSignatureType;
7116 	p->refcount = 1;
7117 	p->get_size = icmSignature_get_size;
7118 	p->read     = icmSignature_read;
7119 	p->write    = icmSignature_write;
7120 	p->dump     = icmSignature_dump;
7121 	p->allocate = icmSignature_allocate;
7122 	p->del      = icmSignature_delete;
7123 	p->icp      = icp;
7124 
7125 	return (icmBase *)p;
7126 }
7127 
7128 /* ---------------------------------------------------------- */
7129 
7130 /* Data conversion support functions */
7131 static int read_ScreeningData(icmScreeningData *p, char *d) {
7132 	p->frequency = read_S15Fixed16Number(d + 0);
7133 	p->angle     = read_S15Fixed16Number(d + 4);
7134 	p->spotShape = (icSpotShape)read_SInt32Number(d + 8);
7135 	return 0;
7136 }
7137 
7138 static int write_ScreeningData(icmScreeningData *p, char *d) {
7139 	int rv;
7140 	if ((rv = write_S15Fixed16Number(p->frequency, d + 0)) != 0)
7141 		return rv;
7142 	if ((rv = write_S15Fixed16Number(p->angle, d + 4)) != 0)
7143 		return rv;
7144 	if ((rv = write_SInt32Number((int)p->spotShape, d + 8)) != 0)
7145 		return rv;
7146 	return 0;
7147 }
7148 
7149 
7150 /* icmScreening object */
7151 
7152 /* Return the number of bytes needed to write this tag */
7153 static unsigned int icmScreening_get_size(
7154 	icmBase *pp
7155 ) {
7156 	icmScreening *p = (icmScreening *)pp;
7157 	unsigned int len = 0;
7158 	len += 16;				/* 16 bytes for tag, padding, flag & channeles */
7159 	len += p->channels * 12;	/* 12 bytes for each channel */
7160 	return len;
7161 }
7162 
7163 /* read the object, return 0 on success, error code on fail */
7164 static int icmScreening_read(
7165 	icmBase *pp,
7166 	unsigned long len,		/* tag length */
7167 	unsigned long of		/* start offset within file */
7168 ) {
7169 	icmScreening *p = (icmScreening *)pp;
7170 	icc *icp = p->icp;
7171 	int rv = 0;
7172 	unsigned long i;
7173 	char *bp, *buf, *end;
7174 
7175 	if (len < 12) {
7176 		sprintf(icp->err,"icmScreening_read: Tag too small to be legal");
7177 		return icp->errc = 1;
7178 	}
7179 
7180 	/* Allocate a file read buffer */
7181 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7182 		sprintf(icp->err,"icmScreening_read: malloc() failed");
7183 		return icp->errc = 2;
7184 	}
7185 	bp = buf;
7186 	end = buf + len;
7187 
7188 	/* Read portion of file into buffer */
7189 	if (   icp->fp->seek(icp->fp, of) != 0
7190 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
7191 		sprintf(icp->err,"icmScreening_read: fseek() or fread() failed");
7192 		icp->al->free(icp->al, buf);
7193 		return icp->errc = 1;
7194 	}
7195 
7196 	/* Read type descriptor from the buffer */
7197 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
7198 		sprintf(icp->err,"icmScreening_read: Wrong tag type for icmScreening");
7199 		icp->al->free(icp->al, buf);
7200 		return icp->errc = 1;
7201 	}
7202 	p->screeningFlag = read_UInt32Number(bp+8);		/* Flags */
7203 	p->channels      = read_UInt32Number(bp+12);	/* Number of channels */
7204 	bp = bp + 16;
7205 
7206 	if ((rv = p->allocate((icmBase *)p)) != 0) {
7207 		icp->al->free(icp->al, buf);
7208 		return rv;
7209 	}
7210 
7211 	/* Read all the data from the buffer */
7212 	for (i = 0; i < p->channels; i++, bp += 12) {
7213 		if ((bp + 12) > end) {
7214 			sprintf(icp->err,"icmScreening_read: Data too short to read Screening Data");
7215 			icp->al->free(icp->al, buf);
7216 			return icp->errc = 1;
7217 		}
7218 		read_ScreeningData(&p->data[i], bp);
7219 	}
7220 	icp->al->free(icp->al, buf);
7221 	return 0;
7222 }
7223 
7224 /* Write the contents of the object. Return 0 on sucess, error code on failure */
7225 static int icmScreening_write(
7226 	icmBase *pp,
7227 	unsigned long of			/* File offset to write from */
7228 ) {
7229 	icmScreening *p = (icmScreening *)pp;
7230 	icc *icp = p->icp;
7231 	unsigned long i;
7232 	unsigned int len;
7233 	char *bp, *buf;		/* Buffer to write from */
7234 	int rv = 0;
7235 
7236 	/* Allocate a file write buffer */
7237 	len = p->get_size((icmBase *)p);
7238 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7239 		sprintf(icp->err,"icmScreening_write malloc() failed");
7240 		return icp->errc = 2;
7241 	}
7242 	bp = buf;
7243 
7244 	/* Write type descriptor to the buffer */
7245 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
7246 		sprintf(icp->err,"icmScreening_write: write_SInt32Number() failed");
7247 		icp->al->free(icp->al, buf);
7248 		return icp->errc = rv;
7249 	}
7250 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
7251 
7252 	if ((rv = write_UInt32Number(p->screeningFlag,bp+8)) != 0) {
7253 			sprintf(icp->err,"icmScreening_write: write_UInt32Number() failed");
7254 			icp->al->free(icp->al, buf);
7255 			return icp->errc = rv;
7256 		}
7257 	if ((rv = write_UInt32Number(p->channels,bp+12)) != 0) {
7258 			sprintf(icp->err,"icmScreening_write: write_UInt32NumberXYZumber() failed");
7259 			icp->al->free(icp->al, buf);
7260 			return icp->errc = rv;
7261 		}
7262 	bp = bp + 16;
7263 
7264 	/* Write all the data to the buffer */
7265 	for (i = 0; i < p->channels; i++, bp += 12) {
7266 		if ((rv = write_ScreeningData(&p->data[i],bp)) != 0) {
7267 			sprintf(icp->err,"icmScreening_write: write_ScreeningData() failed");
7268 			icp->al->free(icp->al, buf);
7269 			return icp->errc = rv;
7270 		}
7271 	}
7272 
7273 	/* Write to the file */
7274 	if (   icp->fp->seek(icp->fp, of) != 0
7275 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
7276 		sprintf(icp->err,"icmScreening_write fseek() or fwrite() failed");
7277 		icp->al->free(icp->al, buf);
7278 		return icp->errc = 2;
7279 	}
7280 	icp->al->free(icp->al, buf);
7281 	return 0;
7282 }
7283 
7284 /* Dump a text description of the object */
7285 static void icmScreening_dump(
7286 	icmBase *pp,
7287 	FILE *op,		/* Output to dump to */
7288 	int   verb		/* Verbosity level */
7289 ) {
7290 	icmScreening *p = (icmScreening *)pp;
7291 	if (verb <= 0)
7292 		return;
7293 
7294 	fprintf(op,"Screening:\n");
7295 	fprintf(op,"  Flags = %s\n", string_ScreenEncodings(p->screeningFlag));
7296 	fprintf(op,"  No. channels = %u\n",p->channels);
7297 	if (verb >= 2) {
7298 		unsigned long i;
7299 		for (i = 0; i < p->channels; i++) {
7300 			fprintf(op,"    %lu:\n",i);
7301 			fprintf(op,"      Frequency:  %f\n",p->data[i].frequency);
7302 			fprintf(op,"      Angle:      %f\n",p->data[i].angle);
7303 			fprintf(op,"      Spot shape: %s\n", string_SpotShape(p->data[i].spotShape));
7304 		}
7305 	}
7306 }
7307 
7308 /* Allocate variable sized data elements */
7309 static int icmScreening_allocate(
7310 	icmBase *pp
7311 ) {
7312 	icmScreening *p = (icmScreening *)pp;
7313 	icc *icp = p->icp;
7314 
7315 	if (p->channels != p->_channels) {
7316 		if (p->data != NULL)
7317 			icp->al->free(icp->al, p->data);
7318 		if ((p->data = (icmScreeningData *) icp->al->malloc(icp->al, p->channels * sizeof(icmScreeningData))) == NULL) {
7319 			sprintf(icp->err,"icmScreening_alloc: malloc() of icmScreening data failed");
7320 			return icp->errc = 2;
7321 		}
7322 		p->_channels = p->channels;
7323 	}
7324 	return 0;
7325 }
7326 
7327 /* Free all storage in the object */
7328 static void icmScreening_delete(
7329 	icmBase *pp
7330 ) {
7331 	icmScreening *p = (icmScreening *)pp;
7332 	icc *icp = p->icp;
7333 
7334 	if (p->data != NULL)
7335 		icp->al->free(icp->al, p->data);
7336 	icp->al->free(icp->al, p);
7337 }
7338 
7339 /* Create an empty object. Return null on error */
7340 static icmBase *new_icmScreening(
7341 	icc *icp
7342 ) {
7343 	icmScreening *p;
7344 	if ((p = (icmScreening *) icp->al->calloc(icp->al,1,sizeof(icmScreening))) == NULL)
7345 		return NULL;
7346 	p->ttype    = icSigScreeningType;
7347 	p->refcount = 1;
7348 	p->get_size = icmScreening_get_size;
7349 	p->read     = icmScreening_read;
7350 	p->write    = icmScreening_write;
7351 	p->dump     = icmScreening_dump;
7352 	p->allocate = icmScreening_allocate;
7353 	p->del      = icmScreening_delete;
7354 	p->icp      = icp;
7355 
7356 	return (icmBase *)p;
7357 }
7358 
7359 /* ---------------------------------------------------------- */
7360 /* icmUcrBg object */
7361 
7362 /* Return the number of bytes needed to write this tag */
7363 static unsigned int icmUcrBg_get_size(
7364 	icmBase *pp
7365 ) {
7366 	icmUcrBg *p = (icmUcrBg *)pp;
7367 	unsigned int len = 0;
7368 	len += 8;			/* 8 bytes for tag and padding */
7369 	len += 4 + p->UCRcount * 2;	/* Undercolor Removal */
7370 	len += 4 + p->BGcount * 2;	/* Black Generation */
7371 	len += p->size;				/* Description string */
7372 	return len;
7373 }
7374 
7375 /* read the object, return 0 on success, error code on fail */
7376 static int icmUcrBg_read(
7377 	icmBase *pp,
7378 	unsigned long len,		/* tag length */
7379 	unsigned long of		/* start offset within file */
7380 ) {
7381 	icmUcrBg *p = (icmUcrBg *)pp;
7382 	icc *icp = p->icp;
7383 	unsigned long i;
7384 	int rv = 0;
7385 	char *bp, *buf, *end;
7386 
7387 	if (len < 16) {
7388 		sprintf(icp->err,"icmUcrBg_read: Tag too small to be legal");
7389 		return icp->errc = 1;
7390 	}
7391 
7392 	/* Allocate a file read buffer */
7393 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7394 		sprintf(icp->err,"icmUcrBg_read: malloc() failed");
7395 		return icp->errc = 2;
7396 	}
7397 	bp = buf;
7398 	end = buf + len;
7399 
7400 	/* Read portion of file into buffer */
7401 	if (   icp->fp->seek(icp->fp, of) != 0
7402 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
7403 		sprintf(icp->err,"icmUcrBg_read: fseek() or fread() failed");
7404 		icp->al->free(icp->al, buf);
7405 		return icp->errc = 1;
7406 	}
7407 
7408 	/* Read type descriptor from the buffer */
7409 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
7410 		sprintf(icp->err,"icmUcrBg_read: Wrong tag type for icmUcrBg");
7411 		icp->al->free(icp->al, buf);
7412 		return icp->errc = 1;
7413 	}
7414 	p->UCRcount = read_UInt32Number(bp+8);	/* First curve count */
7415 	bp = bp + 12;
7416 
7417 	if (p->UCRcount > 0) {
7418 		if ((rv = p->allocate((icmBase *)p)) != 0) {
7419 			icp->al->free(icp->al, buf);
7420 			return rv;
7421 		}
7422 		for (i = 0; i < p->UCRcount; i++, bp += 2) {
7423 			if ((bp + 2) > end) {
7424 				sprintf(icp->err,"icmUcrBg_read: Data too short to read UCR Data");
7425 				icp->al->free(icp->al, buf);
7426 				return icp->errc = 1;
7427 			}
7428 			if (p->UCRcount == 1)	/* % */
7429 				p->UCRcurve[i] = (double)read_UInt16Number(bp);
7430 			else					/* 0.0 - 1.0 */
7431 				p->UCRcurve[i] = read_DCS16Number(bp);
7432 		}
7433 	} else {
7434 		p->UCRcurve = NULL;
7435 	}
7436 
7437 	if ((bp + 4) > end) {
7438 		sprintf(icp->err,"icmData_read: Data too short to read Black Gen count");
7439 		icp->al->free(icp->al, buf);
7440 		return icp->errc = 1;
7441 	}
7442 	p->BGcount = read_UInt32Number(bp);	/* First curve count */
7443 	bp += 4;
7444 
7445 	if (p->BGcount > 0) {
7446 		if ((rv = p->allocate((icmBase *)p)) != 0) {
7447 			icp->al->free(icp->al, buf);
7448 			return rv;
7449 		}
7450 		for (i = 0; i < p->BGcount; i++, bp += 2) {
7451 			if ((bp + 2) > end) {
7452 				sprintf(icp->err,"icmUcrBg_read: Data too short to read BG Data");
7453 				icp->al->free(icp->al, buf);
7454 				return icp->errc = 1;
7455 			}
7456 			if (p->BGcount == 1)	/* % */
7457 				p->BGcurve[i] = (double)read_UInt16Number(bp);
7458 			else					/* 0.0 - 1.0 */
7459 				p->BGcurve[i] = read_DCS16Number(bp);
7460 		}
7461 	} else {
7462 		p->BGcurve = NULL;
7463 	}
7464 
7465 	p->size = end - bp;		/* Nominal string length */
7466 	if (p->size > 0) {
7467 		if (check_null_string(bp, p->size) != 0) {
7468 			sprintf(icp->err,"icmUcrBg_read: string is not null terminated");
7469 			icp->al->free(icp->al, buf);
7470 			return icp->errc = 1;
7471 		}
7472 		p->size = strlen(bp) + 1;
7473 		if ((rv = p->allocate((icmBase *)p)) != 0) {
7474 			icp->al->free(icp->al, buf);
7475 			return rv;
7476 		}
7477 		memcpy((void *)p->string, (void *)bp, p->size);
7478 		bp += p->size;
7479 	} else {
7480 		p->string = NULL;
7481 	}
7482 
7483 	icp->al->free(icp->al, buf);
7484 	return 0;
7485 }
7486 
7487 /* Write the contents of the object. Return 0 on sucess, error code on failure */
7488 static int icmUcrBg_write(
7489 	icmBase *pp,
7490 	unsigned long of			/* File offset to write from */
7491 ) {
7492 	icmUcrBg *p = (icmUcrBg *)pp;
7493 	icc *icp = p->icp;
7494 	unsigned long i;
7495 	unsigned int len;
7496 	char *bp, *buf;		/* Buffer to write from */
7497 	int rv = 0;
7498 
7499 	/* Allocate a file write buffer */
7500 	len = p->get_size((icmBase *)p);
7501 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7502 		sprintf(icp->err,"icmUcrBg_write malloc() failed");
7503 		return icp->errc = 2;
7504 	}
7505 	bp = buf;
7506 
7507 	/* Write type descriptor to the buffer */
7508 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
7509 		sprintf(icp->err,"icmUcrBg_write: write_SInt32Number() failed");
7510 		icp->al->free(icp->al, buf);
7511 		return icp->errc = rv;
7512 	}
7513 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
7514 	bp = bp + 8;
7515 
7516 	/* Write UCR curve */
7517 	if ((rv = write_UInt32Number(p->UCRcount,bp)) != 0) {
7518 		sprintf(icp->err,"icmUcrBg_write: write_UInt32Number() failed");
7519 		icp->al->free(icp->al, buf);
7520 		return icp->errc = rv;
7521 	}
7522 	bp += 4;
7523 
7524 	for (i = 0; i < p->UCRcount; i++, bp += 2) {
7525 		if (p->UCRcount == 1) { /* % */
7526 			if ((rv = write_UInt16Number((unsigned int)(p->UCRcurve[i]+0.5),bp)) != 0) {
7527 				sprintf(icp->err,"icmUcrBg_write: write_UInt16umber() failed");
7528 				icp->al->free(icp->al, buf);
7529 				return icp->errc = rv;
7530 			}
7531 		} else {
7532 			if ((rv = write_DCS16Number(p->UCRcurve[i],bp)) != 0) {
7533 				sprintf(icp->err,"icmUcrBg_write: write_DCS16umber(%f) failed",p->UCRcurve[i]);
7534 				icp->al->free(icp->al, buf);
7535 				return icp->errc = rv;
7536 			}
7537 		}
7538 	}
7539 
7540 	/* Write BG curve */
7541 	if ((rv = write_UInt32Number(p->BGcount,bp)) != 0) {
7542 		sprintf(icp->err,"icmUcrBg_write: write_UInt32Number() failed");
7543 		icp->al->free(icp->al, buf);
7544 		return icp->errc = rv;
7545 	}
7546 	bp += 4;
7547 
7548 	for (i = 0; i < p->BGcount; i++, bp += 2) {
7549 		if (p->BGcount == 1) { /* % */
7550 			if ((rv = write_UInt16Number((unsigned int)(p->BGcurve[i]+0.5),bp)) != 0) {
7551 				sprintf(icp->err,"icmUcrBg_write: write_UInt16umber() failed");
7552 				icp->al->free(icp->al, buf);
7553 				return icp->errc = rv;
7554 			}
7555 		} else {
7556 			if ((rv = write_DCS16Number(p->BGcurve[i],bp)) != 0) {
7557 				sprintf(icp->err,"icmUcrBg_write: write_DCS16umber(%f) failed",p->BGcurve[i]);
7558 				icp->al->free(icp->al, buf);
7559 				return icp->errc = rv;
7560 			}
7561 		}
7562 	}
7563 
7564 	if (p->string != NULL) {
7565 		if ((rv = check_null_string(p->string,p->size)) != 0) {
7566 			sprintf(icp->err,"icmUcrBg_write: text is not null terminated");
7567 			icp->al->free(icp->al, buf);
7568 			return icp->errc = 1;
7569 		}
7570 		memcpy((void *)bp, (void *)p->string, p->size);
7571 	}
7572 
7573 	/* Write to the file */
7574 	if (   icp->fp->seek(icp->fp, of) != 0
7575 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
7576 		sprintf(icp->err,"icmUcrBg_write fseek() or fwrite() failed");
7577 		icp->al->free(icp->al, buf);
7578 		return icp->errc = 2;
7579 	}
7580 	icp->al->free(icp->al, buf);
7581 	return 0;
7582 }
7583 
7584 /* Dump a text description of the object */
7585 static void icmUcrBg_dump(
7586 	icmBase *pp,
7587 	FILE *op,		/* Output to dump to */
7588 	int   verb		/* Verbosity level */
7589 ) {
7590 	icmUcrBg *p = (icmUcrBg *)pp;
7591 	if (verb <= 0)
7592 		return;
7593 
7594 	fprintf(op,"Undercolor Removal Curve & Black Generation:\n");
7595 
7596 	if (p->UCRcount == 0) {
7597 		fprintf(op,"  UCR: Not specified\n");
7598 	} else if (p->UCRcount == 1) {
7599 		fprintf(op,"  UCR: %f%%\n",p->UCRcurve[0]);
7600 	} else {
7601 		fprintf(op,"  UCR curve no. elements = %u\n",p->UCRcount);
7602 		if (verb >= 2) {
7603 			unsigned long i;
7604 			for (i = 0; i < p->UCRcount; i++)
7605 				fprintf(op,"  %3lu:  %f\n",i,p->UCRcurve[i]);
7606 		}
7607 	}
7608 	if (p->BGcount == 0) {
7609 		fprintf(op,"  BG: Not specified\n");
7610 	} else if (p->BGcount == 1) {
7611 		fprintf(op,"  BG: %f%%\n",p->BGcurve[0]);
7612 	} else {
7613 		fprintf(op,"  BG curve no. elements = %u\n",p->BGcount);
7614 		if (verb >= 2) {
7615 			unsigned long i;
7616 			for (i = 0; i < p->BGcount; i++)
7617 				fprintf(op,"  %3lu:  %f\n",i,p->BGcurve[i]);
7618 		}
7619 	}
7620 
7621 	{
7622 		unsigned long i, r, c, size;
7623 		fprintf(op,"  Description:\n");
7624 		fprintf(op,"    No. chars = %lu\n",p->size);
7625 
7626 		size = p->size > 0 ? p->size-1 : 0;
7627 		i = 0;
7628 		for (r = 1;; r++) {		/* count rows */
7629 			if (i >= size) {
7630 				fprintf(op,"\n");
7631 				break;
7632 			}
7633 			if (r > 1 && verb < 2) {
7634 				fprintf(op,"...\n");
7635 				break;			/* Print 1 row if not verbose */
7636 			}
7637 			c = 1;
7638 			fprintf(op,"      0x%04lx: ",i);
7639 			c += 10;
7640 			while (i < size && c < 73) {
7641 				if (isprint(p->string[i])) {
7642 					fprintf(op,"%c",p->string[i]);
7643 					c++;
7644 				} else {
7645 					fprintf(op,"\\%03o",p->string[i]);
7646 					c += 4;
7647 				}
7648 				i++;
7649 			}
7650 			if (i < size)
7651 				fprintf(op,"\n");
7652 		}
7653 	}
7654 }
7655 
7656 /* Allocate variable sized data elements */
7657 static int icmUcrBg_allocate(
7658 	icmBase *pp
7659 ) {
7660 	icmUcrBg *p = (icmUcrBg *)pp;
7661 	icc *icp = p->icp;
7662 
7663 	if (p->UCRcount != p->UCR_count) {
7664 		if (p->UCRcurve != NULL)
7665 			icp->al->free(icp->al, p->UCRcurve);
7666 		if ((p->UCRcurve = (double *) icp->al->malloc(icp->al, p->UCRcount * sizeof(double))) == NULL) {
7667 			sprintf(icp->err,"icmUcrBg_allocate: malloc() of UCR curve data failed");
7668 			return icp->errc = 2;
7669 		}
7670 		p->UCR_count = p->UCRcount;
7671 	}
7672 	if (p->BGcount != p->BG_count) {
7673 		if (p->BGcurve != NULL)
7674 			icp->al->free(icp->al, p->BGcurve);
7675 		if ((p->BGcurve = (double *) icp->al->malloc(icp->al, p->BGcount * sizeof(double))) == NULL) {
7676 			sprintf(icp->err,"icmUcrBg_allocate: malloc() of BG curve data failed");
7677 			return icp->errc = 2;
7678 		}
7679 		p->BG_count = p->BGcount;
7680 	}
7681 	if (p->size != p->_size) {
7682 		if (p->string != NULL)
7683 			icp->al->free(icp->al, p->string);
7684 		if ((p->string = (char *) icp->al->malloc(icp->al, p->size * sizeof(char))) == NULL) {
7685 			sprintf(icp->err,"icmUcrBg_allocate: malloc() of string data failed");
7686 			return icp->errc = 2;
7687 		}
7688 		p->_size = p->size;
7689 	}
7690 	return 0;
7691 }
7692 
7693 /* Free all storage in the object */
7694 static void icmUcrBg_delete(
7695 	icmBase *pp
7696 ) {
7697 	icmUcrBg *p = (icmUcrBg *)pp;
7698 	icc *icp = p->icp;
7699 
7700 	if (p->UCRcurve != NULL)
7701 		icp->al->free(icp->al, p->UCRcurve);
7702 	if (p->BGcurve != NULL)
7703 		icp->al->free(icp->al, p->BGcurve);
7704 	if (p->string != NULL)
7705 		icp->al->free(icp->al, p->string);
7706 	icp->al->free(icp->al, p);
7707 }
7708 
7709 /* Create an empty object. Return null on error */
7710 static icmBase *new_icmUcrBg(
7711 	icc *icp
7712 ) {
7713 	icmUcrBg *p;
7714 	if ((p = (icmUcrBg *) icp->al->calloc(icp->al,1,sizeof(icmUcrBg))) == NULL)
7715 		return NULL;
7716 	p->ttype    = icSigUcrBgType;
7717 	p->refcount = 1;
7718 	p->get_size = icmUcrBg_get_size;
7719 	p->read     = icmUcrBg_read;
7720 	p->write    = icmUcrBg_write;
7721 	p->dump     = icmUcrBg_dump;
7722 	p->allocate = icmUcrBg_allocate;
7723 	p->del      = icmUcrBg_delete;
7724 	p->icp      = icp;
7725 
7726 	return (icmBase *)p;
7727 }
7728 
7729 /* ---------------------------------------------------------- */
7730 /* VideoCardGamma (ColorSync 2.5 specific - c/o Neil Okamoto) */
7731 
7732 static unsigned int icmVideoCardGamma_get_size(
7733 	icmBase *pp
7734 ) {
7735 	icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
7736 	unsigned int len = 0;
7737 
7738 	len += 8;			/* 8 bytes for tag and padding */
7739 	len += 4;			/* 4 for gamma type */
7740 
7741 	/* compute size of remainder */
7742 	if (p->tagType == icmVideoCardGammaTableType) {
7743 		len += 2;       /* 2 bytes for channels */
7744 		len += 2;       /* 2 for entry count */
7745 		len += 2;       /* 2 for entry size */
7746 		len += ( p->u.table.channels *     /* compute table size */
7747 				 p->u.table.entryCount *
7748 				 p->u.table.entrySize );
7749 	}
7750 	else if (p->tagType == icmVideoCardGammaFormulaType) {
7751 		len += 12;		/* 4 bytes each for red gamma, min, & max */
7752 		len += 12;		/* 4 bytes each for green gamma, min & max */
7753 		len += 12;		/* 4 bytes each for blue gamma, min & max */
7754 	}
7755 	return len;
7756 }
7757 /* read the object, return 0 on success, error code on fail */
7758 static int icmVideoCardGamma_read(
7759 	icmBase *pp,
7760 	unsigned long len,		/* tag length */
7761 	unsigned long of		/* start offset within file */
7762 ) {
7763 	icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
7764 	icc *icp = p->icp;
7765 	int rv, c;
7766 	char *bp, *buf;
7767 	unsigned char *pchar;
7768 	unsigned short *pshort;
7769 
7770 	if (len < 18) {
7771 		sprintf(icp->err,"icmVideoCardGamma_read: Tag too small to be legal");
7772 		return icp->errc = 1;
7773 	}
7774 
7775 	/* Allocate a file read buffer */
7776 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7777 		sprintf(icp->err,"icmVideoCardGamma_read: malloc() failed");
7778 		return icp->errc = 2;
7779 	}
7780 	bp = buf;
7781 
7782 	/* Read portion of file into buffer */
7783 	if (   icp->fp->seek(icp->fp, of) != 0
7784 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
7785 		sprintf(icp->err,"icmVideoCardGamma_read: fseek() or fread() failed");
7786 		icp->al->free(icp->al, buf);
7787 		return icp->errc = 1;
7788 	}
7789 
7790 	/* Read type descriptor from the buffer */
7791 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
7792 		sprintf(icp->err,"icmVideoCardGamma_read: Wrong tag type for icmVideoCardGamma");
7793 		icp->al->free(icp->al, buf);
7794 		return icp->errc = 1;
7795 	}
7796 
7797 	/* Read gamma format (eg. table or formula) from the buffer */
7798 	p->tagType = read_UInt32Number(bp+8);
7799 
7800 	/* Read remaining gamma data based on format */
7801 	switch ((int)p->tagType) {
7802 	case icmVideoCardGammaTableType:
7803 		p->u.table.channels   = read_UInt16Number(bp+12);
7804 		p->u.table.entryCount = read_UInt16Number(bp+14);
7805 		p->u.table.entrySize  = read_UInt16Number(bp+16);
7806 		if (len-18 < p->u.table.channels*p->u.table.entryCount*p->u.table.entrySize) {
7807 			sprintf(icp->err,"icmVideoCardGamma_read: Tag too small to be legal");
7808 			return icp->errc = 1;
7809 		}
7810 		if ((rv = pp->allocate(pp)) != 0) {  /* make space for table */
7811 			icp->al->free(icp->al, buf);
7812 			return icp->errc = rv;
7813 		}
7814 		pchar = (unsigned char*)p->u.table.data;
7815 		pshort = (unsigned short*)p->u.table.data;
7816 		for (c=0, bp=bp+18; c<p->u.table.channels*p->u.table.entryCount; c++) {
7817 			switch (p->u.table.entrySize) {
7818 			case 1:
7819 				*pchar++ = read_UInt8Number(bp);
7820 				bp++;
7821 				break;
7822 			case 2:
7823 				*pshort++ = read_UInt16Number(bp);
7824 				bp+=2;
7825 				break;
7826 			default:
7827 				sprintf(icp->err,"icmVideoCardGamma_read: unsupported table entry size");
7828 				pp->del(pp);
7829 				icp->al->free(icp->al, buf);
7830 				return icp->errc = 1;
7831 			}
7832 		}
7833 		break;
7834 	case icmVideoCardGammaFormulaType:
7835 		if (len < 48) {
7836 			sprintf(icp->err,"icmVideoCardGamma_read: Tag too small to be legal");
7837 			return icp->errc = 1;
7838 		}
7839 		p->u.formula.redGamma   = read_S15Fixed16Number(bp+12);
7840 		p->u.formula.redMin     = read_S15Fixed16Number(bp+16);
7841 		p->u.formula.redMax     = read_S15Fixed16Number(bp+20);
7842 		p->u.formula.greenGamma = read_S15Fixed16Number(bp+24);
7843 		p->u.formula.greenMin   = read_S15Fixed16Number(bp+28);
7844 		p->u.formula.greenMax   = read_S15Fixed16Number(bp+32);
7845 		p->u.formula.blueGamma  = read_S15Fixed16Number(bp+36);
7846 		p->u.formula.blueMin    = read_S15Fixed16Number(bp+40);
7847 		p->u.formula.blueMax    = read_S15Fixed16Number(bp+44);
7848 		break;
7849 	default:
7850 		sprintf(icp->err,"icmVideoCardGammaTable_read: Unknown gamma format for icmVideoCardGamma");
7851 		icp->al->free(icp->al, buf);
7852 		return icp->errc = 1;
7853 	}
7854 
7855 	icp->al->free(icp->al, buf);
7856 	return 0;
7857 }
7858 
7859 /* Write the contents of the object. Return 0 on sucess, error code on failure */
7860 static int icmVideoCardGamma_write(
7861 	icmBase *pp,
7862 	unsigned long of			/* File offset to write from */
7863 ) {
7864 	icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
7865 	icc *icp = p->icp;
7866 	unsigned int len;
7867 	char *bp, *buf;		/* Buffer to write from */
7868 	int rv = 0, c;
7869 	unsigned char *pchar;
7870 	unsigned short *pshort;
7871 
7872 	/* Allocate a file write buffer */
7873 	len = p->get_size((icmBase *)p);
7874 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7875 		sprintf(icp->err,"icmViewingConditions_write malloc() failed");
7876 		return icp->errc = 2;
7877 	}
7878 	bp = buf;
7879 
7880 	/* Write type descriptor to the buffer */
7881 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
7882 		sprintf(icp->err,"icmVideoCardGamma_write: write_SInt32Number() failed");
7883 		icp->al->free(icp->al, buf);
7884 		return icp->errc = rv;
7885 	}
7886 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
7887 
7888 	/* Write gamma format (eg. table of formula) */
7889 	if ((rv = write_UInt32Number(p->tagType,bp+8)) != 0) {
7890 		sprintf(icp->err,"icmVideoCardGamma_write: write_UInt32Number() failed");
7891 		icp->al->free(icp->al, buf);
7892 		return icp->errc = rv;
7893 	}
7894 
7895 	/* Write remaining gamma data based on format */
7896 	switch ((int)p->tagType) {
7897 	case icmVideoCardGammaTableType:
7898 		if ((rv = write_UInt16Number(p->u.table.channels,bp+12)) != 0) {
7899 			sprintf(icp->err,"icmVideoCardGamma_write: write_UInt16Number() failed");
7900 			icp->al->free(icp->al, buf);
7901 			return icp->errc = rv;
7902 		}
7903 		if ((rv = write_UInt16Number(p->u.table.entryCount,bp+14)) != 0) {
7904 			sprintf(icp->err,"icmVideoCardGamma_write: write_UInt16Number() failed");
7905 			icp->al->free(icp->al, buf);
7906 			return icp->errc = rv;
7907 		}
7908 		if ((rv = write_UInt16Number(p->u.table.entrySize,bp+16)) != 0) {
7909 			sprintf(icp->err,"icmVideoCardGamma_write: write_UInt16Number() failed");
7910 			icp->al->free(icp->al, buf);
7911 			return icp->errc = rv;
7912 		}
7913 		pchar = (unsigned char*)p->u.table.data;
7914 		pshort = (unsigned short*)p->u.table.data;
7915 		for (c=0, bp=bp+18; c<p->u.table.channels*p->u.table.entryCount; c++) {
7916 			switch (p->u.table.entrySize) {
7917 			case 1:
7918 				write_UInt8Number(*pchar++,bp);
7919 				bp++;
7920 				break;
7921 			case 2:
7922 				write_UInt16Number(*pshort++,bp);
7923 				bp+=2;
7924 				break;
7925 			default:
7926 				sprintf(icp->err,"icmVideoCardGamma_write: unsupported table entry size");
7927 				icp->al->free(icp->al, buf);
7928 				return icp->errc = 1;
7929 			}
7930 		}
7931 		break;
7932 	case icmVideoCardGammaFormulaType:
7933 		if ((rv = write_S15Fixed16Number(p->u.formula.redGamma,bp+12)) != 0) {
7934 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7935 			icp->al->free(icp->al, buf);
7936 			return icp->errc = rv;
7937 		}
7938 		if ((rv = write_S15Fixed16Number(p->u.formula.redMin,bp+16)) != 0) {
7939 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7940 			icp->al->free(icp->al, buf);
7941 			return icp->errc = rv;
7942 		}
7943 		if ((rv = write_S15Fixed16Number(p->u.formula.redMax,bp+20)) != 0) {
7944 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7945 			icp->al->free(icp->al, buf);
7946 			return icp->errc = rv;
7947 		}
7948 		if ((rv = write_S15Fixed16Number(p->u.formula.greenGamma,bp+24)) != 0) {
7949 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7950 			icp->al->free(icp->al, buf);
7951 			return icp->errc = rv;
7952 		}
7953 		if ((rv = write_S15Fixed16Number(p->u.formula.greenMin,bp+28)) != 0) {
7954 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7955 			icp->al->free(icp->al, buf);
7956 			return icp->errc = rv;
7957 		}
7958 		if ((rv = write_S15Fixed16Number(p->u.formula.greenMax,bp+32)) != 0) {
7959 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7960 			icp->al->free(icp->al, buf);
7961 			return icp->errc = rv;
7962 		}
7963 		if ((rv = write_S15Fixed16Number(p->u.formula.blueGamma,bp+36)) != 0) {
7964 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7965 			icp->al->free(icp->al, buf);
7966 			return icp->errc = rv;
7967 		}
7968 		if ((rv = write_S15Fixed16Number(p->u.formula.blueMin,bp+40)) != 0) {
7969 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7970 			icp->al->free(icp->al, buf);
7971 			return icp->errc = rv;
7972 		}
7973 		if ((rv = write_S15Fixed16Number(p->u.formula.blueMax,bp+44)) != 0) {
7974 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7975 			icp->al->free(icp->al, buf);
7976 			return icp->errc = rv;
7977 		}
7978 		break;
7979 	default:
7980 		sprintf(icp->err,"icmVideoCardGammaTable_write: Unknown gamma format for icmVideoCardGamma");
7981 		icp->al->free(icp->al, buf);
7982 		return icp->errc = 1;
7983 	}
7984 
7985 	/* Write to the file */
7986 	if (   icp->fp->seek(icp->fp, of) != 0
7987 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
7988 		sprintf(icp->err,"icmViewingConditions_write fseek() or fwrite() failed");
7989 		icp->al->free(icp->al, buf);
7990 		return icp->errc = 2;
7991 	}
7992 	icp->al->free(icp->al, buf);
7993 	return 0;
7994 }
7995 
7996 /* Dump a text description of the object */
7997 static void icmVideoCardGamma_dump(
7998 	icmBase *pp,
7999 	FILE *op,		/* Output to dump to */
8000 	int   verb		/* Verbosity level */
8001 ) {
8002 	icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
8003 	int c,i;
8004 
8005 	if (verb <= 0)
8006 		return;
8007 
8008 	switch ((int)p->tagType) {
8009 	case icmVideoCardGammaTableType:
8010 		fprintf(op,"VideoCardGammaTable:\n");
8011 		fprintf(op,"  channels  = %d\n", p->u.table.channels);
8012 		fprintf(op,"  entries   = %d\n", p->u.table.entryCount);
8013 		fprintf(op,"  entrysize = %d\n", p->u.table.entrySize);
8014 		if (verb >= 2) {
8015 			/* dump array contents also */
8016 			for (c=0; c<p->u.table.channels; c++) {
8017 				fprintf(op,"  channel #%d\n",c);
8018 				for (i=0; i<p->u.table.entryCount; i++) {
8019 					if (p->u.table.entrySize == 1) {
8020 						fprintf(op,"    %d: %d\n",i,((unsigned char*)p->u.table.data)[c*p->u.table.entryCount+i]);
8021 					}
8022 					else if (p->u.table.entrySize == 2) {
8023 						fprintf(op,"    %d: %d\n",i,((unsigned short*)p->u.table.data)[c*p->u.table.entryCount+i]);
8024 					}
8025 				}
8026 			}
8027 		}
8028 		break;
8029 	case icmVideoCardGammaFormulaType:
8030 		fprintf(op,"VideoCardGammaFormula:\n");
8031 		fprintf(op,"  red gamma   = %f\n", p->u.formula.redGamma);
8032 		fprintf(op,"  red min     = %f\n", p->u.formula.redMin);
8033 		fprintf(op,"  red max     = %f\n", p->u.formula.redMax);
8034 		fprintf(op,"  green gamma = %f\n", p->u.formula.greenGamma);
8035 		fprintf(op,"  green min   = %f\n", p->u.formula.greenMin);
8036 		fprintf(op,"  green max   = %f\n", p->u.formula.greenMax);
8037 		fprintf(op,"  blue gamma  = %f\n", p->u.formula.blueGamma);
8038 		fprintf(op,"  blue min    = %f\n", p->u.formula.blueMin);
8039 		fprintf(op,"  blue max    = %f\n", p->u.formula.blueMax);
8040 		break;
8041 	default:
8042 		fprintf(op,"  Unknown tag format\n");
8043 	}
8044 }
8045 
8046 /* Allocate variable sized data elements */
8047 static int icmVideoCardGamma_allocate(
8048 	icmBase *pp
8049 ) {
8050 	icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
8051 	icc *icp = p->icp;
8052 	int size;
8053 
8054 	/* note: allocation is only relevant for table type
8055 	 * and in that case the channels, entryCount, and entrySize
8056 	 * fields must all be set prior to getting here
8057 	 */
8058 
8059 	if (p->tagType == icmVideoCardGammaTableType) {
8060 		if (p->u.table.data != NULL)
8061 			icp->al->free(icp->al, p->u.table.data);
8062 		size = (p->u.table.channels *
8063 				p->u.table.entryCount);
8064 		switch (p->u.table.entrySize) {
8065 		case 1:
8066 			size *= sizeof(unsigned char);
8067 			break;
8068 		case 2:
8069 			size *= sizeof(unsigned short);
8070 			break;
8071 		default:
8072 			sprintf(icp->err,"icmVideoCardGamma_alloc: unsupported table entry size");
8073 			return icp->errc = 1;
8074 		}
8075 		if ((p->u.table.data = (void*) icp->al->malloc(icp->al, size)) == NULL) {
8076 			sprintf(icp->err,"icmVideoCardGamma_alloc: malloc() of table data failed");
8077 			return icp->errc = 2;
8078 		}
8079 	}
8080 
8081 	return 0;
8082 }
8083 
8084 /* Free all storage in the object */
8085 static void icmVideoCardGamma_delete(
8086 	icmBase *pp
8087 ) {
8088 	icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
8089 	icc *icp = p->icp;
8090 
8091 	if (p->tagType == icmVideoCardGammaTableType && p->u.table.data != NULL)
8092 		icp->al->free(icp->al, p->u.table.data);
8093 
8094 	icp->al->free(icp->al, p);
8095 }
8096 
8097 /* Create an empty object. Return null on error */
8098 static icmBase *new_icmVideoCardGamma(
8099 	icc *icp
8100 ) {
8101 	icmVideoCardGamma *p;
8102 	if ((p = (icmVideoCardGamma *) icp->al->calloc(icp->al,1,sizeof(icmVideoCardGamma))) == NULL)
8103 		return NULL;
8104 	p->ttype    = icSigVideoCardGammaType;
8105 	p->refcount = 1;
8106 	p->get_size = icmVideoCardGamma_get_size;
8107 	p->read     = icmVideoCardGamma_read;
8108 	p->write    = icmVideoCardGamma_write;
8109 	p->dump     = icmVideoCardGamma_dump;
8110 	p->allocate = icmVideoCardGamma_allocate;
8111 	p->del      = icmVideoCardGamma_delete;
8112 	p->icp      = icp;
8113 
8114 	return (icmBase *)p;
8115 }
8116 
8117 /* ---------------------------------------------------------- */
8118 /* ViewingConditions */
8119 
8120 /* Return the number of bytes needed to write this tag */
8121 static unsigned int icmViewingConditions_get_size(
8122 	icmBase *pp
8123 ) {
8124 	unsigned int len = 0;
8125 	len += 8;			/* 8 bytes for tag and padding */
8126 	len += 12;			/* 12 for XYZ of illuminant */
8127 	len += 12;			/* 12 for XYZ of surround */
8128 	len += 4;			/* 4 for illuminant type */
8129 	return len;
8130 }
8131 
8132 /* read the object, return 0 on success, error code on fail */
8133 static int icmViewingConditions_read(
8134 	icmBase *pp,
8135 	unsigned long len,		/* tag length */
8136 	unsigned long of		/* start offset within file */
8137 ) {
8138 	icmViewingConditions *p = (icmViewingConditions *)pp;
8139 	icc *icp = p->icp;
8140 	int rv;
8141 	char *bp, *buf;
8142 
8143 	if (len < 36) {
8144 		sprintf(icp->err,"icmViewingConditions_read: Tag too small to be legal");
8145 		return icp->errc = 1;
8146 	}
8147 
8148 	/* Allocate a file read buffer */
8149 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
8150 		sprintf(icp->err,"icmViewingConditions_read: malloc() failed");
8151 		return icp->errc = 2;
8152 	}
8153 	bp = buf;
8154 
8155 	/* Read portion of file into buffer */
8156 	if (   icp->fp->seek(icp->fp, of) != 0
8157 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
8158 		sprintf(icp->err,"icmViewingConditions_read: fseek() or fread() failed");
8159 		icp->al->free(icp->al, buf);
8160 		return icp->errc = 1;
8161 	}
8162 
8163 	/* Read type descriptor from the buffer */
8164 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
8165 		sprintf(icp->err,"icmViewingConditions_read: Wrong tag type for icmViewingConditions");
8166 		icp->al->free(icp->al, buf);
8167 		return icp->errc = 1;
8168 	}
8169 
8170 	/* Read the XYZ values for the illuminant */
8171 	if ((rv = read_XYZNumber(&p->illuminant, bp+8)) != 0) {
8172 		sprintf(icp->err,"icmViewingConditions: read_XYZNumber error");
8173 		icp->al->free(icp->al, buf);
8174 		return icp->errc = rv;
8175 	}
8176 
8177 	/* Read the XYZ values for the surround */
8178 	if ((rv = read_XYZNumber(&p->surround, bp+20)) != 0) {
8179 		sprintf(icp->err,"icmViewingConditions: read_XYZNumber error");
8180 		icp->al->free(icp->al, buf);
8181 		return icp->errc = rv;
8182 	}
8183 
8184 	/* Read the encoded standard illuminant */
8185 	p->stdIlluminant = (icIlluminant)read_SInt32Number(bp + 32);
8186 
8187 	icp->al->free(icp->al, buf);
8188 	return 0;
8189 }
8190 
8191 /* Write the contents of the object. Return 0 on sucess, error code on failure */
8192 static int icmViewingConditions_write(
8193 	icmBase *pp,
8194 	unsigned long of			/* File offset to write from */
8195 ) {
8196 	icmViewingConditions *p = (icmViewingConditions *)pp;
8197 	icc *icp = p->icp;
8198 	unsigned int len;
8199 	char *bp, *buf;		/* Buffer to write from */
8200 	int rv = 0;
8201 
8202 	/* Allocate a file write buffer */
8203 	len = p->get_size((icmBase *)p);
8204 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
8205 		sprintf(icp->err,"icmViewingConditions_write malloc() failed");
8206 		return icp->errc = 2;
8207 	}
8208 	bp = buf;
8209 
8210 	/* Write type descriptor to the buffer */
8211 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
8212 		sprintf(icp->err,"icmViewingConditions_write: write_SInt32Number() failed");
8213 		icp->al->free(icp->al, buf);
8214 		return icp->errc = rv;
8215 	}
8216 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
8217 
8218 	/* Write the XYZ values for the illuminant */
8219 	if ((rv = write_XYZNumber(&p->illuminant, bp+8)) != 0) {
8220 		sprintf(icp->err,"icmViewingConditions: write_XYZNumber error");
8221 		icp->al->free(icp->al, buf);
8222 		return icp->errc = rv;
8223 	}
8224 
8225 	/* Write the XYZ values for the surround */
8226 	if ((rv = write_XYZNumber(&p->surround, bp+20)) != 0) {
8227 		sprintf(icp->err,"icmViewingConditions: write_XYZNumber error");
8228 		icp->al->free(icp->al, buf);
8229 		return icp->errc = rv;
8230 	}
8231 
8232 	/* Write the encoded standard illuminant */
8233 	if ((rv = write_SInt32Number((int)p->stdIlluminant, bp + 32)) != 0) {
8234 		sprintf(icp->err,"icmViewingConditionsa_write: write_SInt32Number() failed");
8235 		icp->al->free(icp->al, buf);
8236 		return icp->errc = rv;
8237 	}
8238 
8239 	/* Write to the file */
8240 	if (   icp->fp->seek(icp->fp, of) != 0
8241 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
8242 		sprintf(icp->err,"icmViewingConditions_write fseek() or fwrite() failed");
8243 		icp->al->free(icp->al, buf);
8244 		return icp->errc = 2;
8245 	}
8246 	icp->al->free(icp->al, buf);
8247 	return 0;
8248 }
8249 
8250 /* Dump a text description of the object */
8251 static void icmViewingConditions_dump(
8252 	icmBase *pp,
8253 	FILE *op,		/* Output to dump to */
8254 	int   verb		/* Verbosity level */
8255 ) {
8256 	icmViewingConditions *p = (icmViewingConditions *)pp;
8257 	if (verb <= 0)
8258 		return;
8259 
8260 	fprintf(op,"Viewing Conditions:\n");
8261 	fprintf(op,"  XYZ value of illuminant in cd/m^2 = %s\n", string_XYZNumber(&p->illuminant));
8262 	fprintf(op,"  XYZ value of surround in cd/m^2   = %s\n", string_XYZNumber(&p->surround));
8263 	fprintf(op,"  Illuminant type = %s\n", string_Illuminant(p->stdIlluminant));
8264 }
8265 
8266 /* Allocate variable sized data elements */
8267 static int icmViewingConditions_allocate(
8268 	icmBase *pp
8269 ) {
8270 	/* Nothing to do */
8271 	return 0;
8272 }
8273 
8274 /* Free all storage in the object */
8275 static void icmViewingConditions_delete(
8276 	icmBase *pp
8277 ) {
8278 	icmViewingConditions *p = (icmViewingConditions *)pp;
8279 	icc *icp = p->icp;
8280 
8281 	icp->al->free(icp->al, p);
8282 }
8283 
8284 /* Create an empty object. Return null on error */
8285 static icmBase *new_icmViewingConditions(
8286 	icc *icp
8287 ) {
8288 	icmViewingConditions *p;
8289 	if ((p = (icmViewingConditions *) icp->al->calloc(icp->al,1,sizeof(icmViewingConditions))) == NULL)
8290 		return NULL;
8291 	p->ttype    = icSigViewingConditionsType;
8292 	p->refcount = 1;
8293 	p->get_size = icmViewingConditions_get_size;
8294 	p->read     = icmViewingConditions_read;
8295 	p->write    = icmViewingConditions_write;
8296 	p->dump     = icmViewingConditions_dump;
8297 	p->allocate = icmViewingConditions_allocate;
8298 	p->del      = icmViewingConditions_delete;
8299 	p->icp      = icp;
8300 
8301 	return (icmBase *)p;
8302 }
8303 
8304 /* ---------------------------------------------------------- */
8305 /* icmCrdInfo object */
8306 
8307 /* Return the number of bytes needed to write this tag */
8308 static unsigned int icmCrdInfo_get_size(
8309 	icmBase *pp
8310 ) {
8311 	icmCrdInfo *p = (icmCrdInfo *)pp;
8312 	unsigned int len = 0, t;
8313 	len += 8;				/* 8 bytes for tag and padding */
8314 	len += 4 + p->ppsize;	/* Postscript product name */
8315 	for (t = 0; t < 4; t++) {	/* For all 4 intents */
8316 		len += 4 + p->crdsize[t];	/* crd names */
8317 	}
8318 	return len;
8319 }
8320 
8321 /* read the object, return 0 on success, error code on fail */
8322 static int icmCrdInfo_read(
8323 	icmBase *pp,
8324 	unsigned long len,		/* tag length */
8325 	unsigned long of		/* start offset within file */
8326 ) {
8327 	icmCrdInfo *p = (icmCrdInfo *)pp;
8328 	icc *icp = p->icp;
8329 	unsigned long t;
8330 	int rv = 0;
8331 	char *bp, *buf, *end;
8332 
8333 	if (len < 28) {
8334 		sprintf(icp->err,"icmCrdInfo_read: Tag too small to be legal");
8335 		return icp->errc = 1;
8336 	}
8337 
8338 	/* Allocate a file read buffer */
8339 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
8340 		sprintf(icp->err,"icmCrdInfo_read: malloc() failed");
8341 		return icp->errc = 2;
8342 	}
8343 	bp = buf;
8344 	end = buf + len;
8345 
8346 	/* Read portion of file into buffer */
8347 	if (   icp->fp->seek(icp->fp, of) != 0
8348 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
8349 		sprintf(icp->err,"icmCrdInfo_read: fseek() or fread() failed");
8350 		icp->al->free(icp->al, buf);
8351 		return icp->errc = 1;
8352 	}
8353 
8354 	/* Read type descriptor from the buffer */
8355 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
8356 		sprintf(icp->err,"icmCrdInfo_read: Wrong tag type for icmCrdInfo");
8357 		icp->al->free(icp->al, buf);
8358 		return icp->errc = 1;
8359 	}
8360 	bp = bp + 8;
8361 
8362 	/* Postscript product name */
8363 	if ((bp + 4) > end) {
8364 		sprintf(icp->err,"icmCrdInfo_read: Data too short to read Postscript product name");
8365 		icp->al->free(icp->al, buf);
8366 		return icp->errc = 1;
8367 	}
8368 	p->ppsize = read_UInt32Number(bp);
8369 	bp += 4;
8370 	if (p->ppsize > 0) {
8371 		if ((bp + p->ppsize) > end) {
8372 			sprintf(icp->err,"icmCrdInfo_read: Data to short to read Postscript product string");
8373 			icp->al->free(icp->al, buf);
8374 			return icp->errc = 1;
8375 		}
8376 		if (check_null_string(bp,p->ppsize)) {
8377 			sprintf(icp->err,"icmCrdInfo_read: Postscript product name is not terminated");
8378 			icp->al->free(icp->al, buf);
8379 			return icp->errc = 1;
8380 		}
8381 		if ((rv = p->allocate((icmBase *)p)) != 0) {
8382 			icp->al->free(icp->al, buf);
8383 			return rv;
8384 		}
8385 		memcpy((void *)p->ppname, (void *)bp, p->ppsize);
8386 		bp += p->ppsize;
8387 	}
8388 
8389 	/* CRD names for the four rendering intents */
8390 	for (t = 0; t < 4; t++) {	/* For all 4 intents */
8391 		if ((bp + 4) > end) {
8392 			sprintf(icp->err,"icmCrdInfo_read: Data too short to read CRD%ld name",t);
8393 			icp->al->free(icp->al, buf);
8394 			return icp->errc = 1;
8395 		}
8396 		p->crdsize[t] = read_UInt32Number(bp);
8397 		bp += 4;
8398 		if (p->crdsize[t] > 0) {
8399 			if ((bp + p->crdsize[t]) > end) {
8400 				sprintf(icp->err,"icmCrdInfo_read: Data to short to read CRD%ld string",t);
8401 				icp->al->free(icp->al, buf);
8402 				return icp->errc = 1;
8403 			}
8404 			if (check_null_string(bp,p->crdsize[t])) {
8405 				sprintf(icp->err,"icmCrdInfo_read: CRD%ld name is not terminated",t);
8406 				icp->al->free(icp->al, buf);
8407 				return icp->errc = 1;
8408 			}
8409 			if ((rv = p->allocate((icmBase *)p)) != 0) {
8410 				icp->al->free(icp->al, buf);
8411 				return rv;
8412 			}
8413 			memcpy((void *)p->crdname[t], (void *)bp, p->crdsize[t]);
8414 			bp += p->crdsize[t];
8415 		}
8416 	}
8417 
8418 	icp->al->free(icp->al, buf);
8419 	return 0;
8420 }
8421 
8422 /* Write the contents of the object. Return 0 on sucess, error code on failure */
8423 static int icmCrdInfo_write(
8424 	icmBase *pp,
8425 	unsigned long of			/* File offset to write from */
8426 ) {
8427 	icmCrdInfo *p = (icmCrdInfo *)pp;
8428 	icc *icp = p->icp;
8429 	unsigned long t;
8430 	unsigned int len;
8431 	char *bp, *buf;		/* Buffer to write from */
8432 	int rv = 0;
8433 
8434 	/* Allocate a file write buffer */
8435 	len = p->get_size((icmBase *)p);
8436 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
8437 		sprintf(icp->err,"icmCrdInfo_write malloc() failed");
8438 		return icp->errc = 2;
8439 	}
8440 	bp = buf;
8441 
8442 	/* Write type descriptor to the buffer */
8443 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
8444 		sprintf(icp->err,"icmCrdInfo_write: write_SInt32Number() failed");
8445 		icp->al->free(icp->al, buf);
8446 		return icp->errc = rv;
8447 	}
8448 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
8449 	bp = bp + 8;
8450 
8451 	/* Postscript product name */
8452 	if ((rv = write_UInt32Number(p->ppsize,bp)) != 0) {
8453 		sprintf(icp->err,"icmCrdInfo_write: write_UInt32Number() failed");
8454 		icp->al->free(icp->al, buf);
8455 		return icp->errc = rv;
8456 	}
8457 	bp += 4;
8458 	if (p->ppsize > 0) {
8459 		if ((rv = check_null_string(p->ppname,p->ppsize)) != 0) {
8460 			sprintf(icp->err,"icmCrdInfo_write: Postscript product name is not terminated");
8461 			icp->al->free(icp->al, buf);
8462 			return icp->errc = 1;
8463 		}
8464 		memcpy((void *)bp, (void *)p->ppname, p->ppsize);
8465 		bp += p->ppsize;
8466 	}
8467 
8468 	/* CRD names for the four rendering intents */
8469 	for (t = 0; t < 4; t++) {	/* For all 4 intents */
8470 		if ((rv = write_UInt32Number(p->crdsize[t],bp)) != 0) {
8471 			sprintf(icp->err,"icmCrdInfo_write: write_UInt32Number() failed");
8472 			icp->al->free(icp->al, buf);
8473 			return icp->errc = rv;
8474 		}
8475 		bp += 4;
8476 		if (p->ppsize > 0) {
8477 			if ((rv = check_null_string(p->crdname[t],p->crdsize[t])) != 0) {
8478 				sprintf(icp->err,"icmCrdInfo_write: CRD%ld name is not terminated",t);
8479 				icp->al->free(icp->al, buf);
8480 				return icp->errc = 1;
8481 			}
8482 			memcpy((void *)bp, (void *)p->crdname[t], p->crdsize[t]);
8483 			bp += p->crdsize[t];
8484 		}
8485 	}
8486 
8487 	/* Write to the file */
8488 	if (   icp->fp->seek(icp->fp, of) != 0
8489 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
8490 		sprintf(icp->err,"icmCrdInfo_write fseek() or fwrite() failed");
8491 		icp->al->free(icp->al, buf);
8492 		return icp->errc = 2;
8493 	}
8494 	icp->al->free(icp->al, buf);
8495 	return 0;
8496 }
8497 
8498 /* Dump a text description of the object */
8499 static void icmCrdInfo_dump(
8500 	icmBase *pp,
8501 	FILE *op,		/* Output to dump to */
8502 	int   verb		/* Verbosity level */
8503 ) {
8504 	icmCrdInfo *p = (icmCrdInfo *)pp;
8505 	unsigned long i, r, c, size, t;
8506 
8507 	if (verb <= 0)
8508 		return;
8509 
8510 	fprintf(op,"PostScript Product name and CRD names:\n");
8511 
8512 	fprintf(op,"  Product name:\n");
8513 	fprintf(op,"    No. chars = %lu\n",p->ppsize);
8514 
8515 	size = p->ppsize > 0 ? p->ppsize-1 : 0;
8516 	i = 0;
8517 	for (r = 1;; r++) {		/* count rows */
8518 		if (i >= size) {
8519 			fprintf(op,"\n");
8520 			break;
8521 		}
8522 		if (r > 1 && verb < 2) {
8523 			fprintf(op,"...\n");
8524 			break;			/* Print 1 row if not verbose */
8525 		}
8526 		c = 1;
8527 		fprintf(op,"      0x%04lx: ",i);
8528 		c += 10;
8529 		while (i < size && c < 73) {
8530 			if (isprint(p->ppname[i])) {
8531 				fprintf(op,"%c",p->ppname[i]);
8532 				c++;
8533 			} else {
8534 				fprintf(op,"\\%03o",p->ppname[i]);
8535 				c += 4;
8536 			}
8537 			i++;
8538 		}
8539 		if (i < size)
8540 			fprintf(op,"\n");
8541 	}
8542 
8543 	for (t = 0; t < 4; t++) {	/* For all 4 intents */
8544 		fprintf(op,"  CRD%ld name:\n",t);
8545 		fprintf(op,"    No. chars = %lu\n",p->crdsize[t]);
8546 
8547 		size = p->crdsize[t] > 0 ? p->crdsize[t]-1 : 0;
8548 		i = 0;
8549 		for (r = 1;; r++) {		/* count rows */
8550 			if (i >= size) {
8551 				fprintf(op,"\n");
8552 				break;
8553 			}
8554 			if (r > 1 && verb < 2) {
8555 				fprintf(op,"...\n");
8556 				break;			/* Print 1 row if not verbose */
8557 			}
8558 			c = 1;
8559 			fprintf(op,"      0x%04lx: ",i);
8560 			c += 10;
8561 			while (i < size && c < 73) {
8562 				if (isprint(p->crdname[t][i])) {
8563 					fprintf(op,"%c",p->crdname[t][i]);
8564 					c++;
8565 				} else {
8566 					fprintf(op,"\\%03o",p->crdname[t][i]);
8567 					c += 4;
8568 				}
8569 				i++;
8570 			}
8571 			if (i < size)
8572 				fprintf(op,"\n");
8573 		}
8574 	}
8575 }
8576 
8577 /* Allocate variable sized data elements */
8578 static int icmCrdInfo_allocate(
8579 	icmBase *pp
8580 ) {
8581 	icmCrdInfo *p = (icmCrdInfo *)pp;
8582 	icc *icp = p->icp;
8583 	unsigned int t;
8584 
8585 	if (p->ppsize != p->_ppsize) {
8586 		if (p->ppname != NULL)
8587 			icp->al->free(icp->al, p->ppname);
8588 		if ((p->ppname = (char *) icp->al->malloc(icp->al, p->ppsize * sizeof(char))) == NULL) {
8589 			sprintf(icp->err,"icmCrdInfo_alloc: malloc() of string data failed");
8590 			return icp->errc = 2;
8591 		}
8592 		p->_ppsize = p->ppsize;
8593 	}
8594 	for (t = 0; t < 4; t++) {	/* For all 4 intents */
8595 		if (p->crdsize[t] != p->_crdsize[t]) {
8596 			if (p->crdname[t] != NULL)
8597 				icp->al->free(icp->al, p->crdname[t]);
8598 			if ((p->crdname[t] = (char *) icp->al->malloc(icp->al, p->crdsize[t] * sizeof(char))) == NULL) {
8599 				sprintf(icp->err,"icmCrdInfo_alloc: malloc() of CRD%d name string failed",t);
8600 				return icp->errc = 2;
8601 			}
8602 			p->_crdsize[t] = p->crdsize[t];
8603 		}
8604 	}
8605 	return 0;
8606 }
8607 
8608 /* Free all storage in the object */
8609 static void icmCrdInfo_delete(
8610 	icmBase *pp
8611 ) {
8612 	icmCrdInfo *p = (icmCrdInfo *)pp;
8613 	icc *icp = p->icp;
8614 	unsigned int t;
8615 
8616 	if (p->ppname != NULL)
8617 		icp->al->free(icp->al, p->ppname);
8618 	for (t = 0; t < 4; t++) {	/* For all 4 intents */
8619 		if (p->crdname[t] != NULL)
8620 			icp->al->free(icp->al, p->crdname[t]);
8621 	}
8622 	icp->al->free(icp->al, p);
8623 }
8624 
8625 /* Create an empty object. Return null on error */
8626 static icmBase *new_icmCrdInfo(
8627 	icc *icp
8628 ) {
8629 	icmCrdInfo *p;
8630 	if ((p = (icmCrdInfo *) icp->al->calloc(icp->al,1,sizeof(icmCrdInfo))) == NULL)
8631 		return NULL;
8632 	p->ttype    = icSigCrdInfoType;
8633 	p->refcount = 1;
8634 	p->get_size = icmCrdInfo_get_size;
8635 	p->read     = icmCrdInfo_read;
8636 	p->write    = icmCrdInfo_write;
8637 	p->dump     = icmCrdInfo_dump;
8638 	p->allocate = icmCrdInfo_allocate;
8639 	p->del      = icmCrdInfo_delete;
8640 	p->icp      = icp;
8641 
8642 	return (icmBase *)p;
8643 }
8644 
8645 /* ========================================================== */
8646 /* icmHeader object */
8647 /* ========================================================== */
8648 
8649 /* Return the number of bytes needed to write this tag */
8650 static unsigned int icmHeader_get_size(
8651 	icmHeader *p
8652 ) {
8653 	return 128;		/* By definition */
8654 }
8655 
8656 /* read the object, return 0 on success, error code on fail */
8657 static int icmHeader_read(
8658 	icmHeader *p,
8659 	unsigned long len,		/* tag length */
8660 	unsigned long of		/* start offset within file */
8661 ) {
8662 	icc *icp = p->icp;
8663 	char *buf;
8664 	unsigned int tt;
8665 	int rv = 0;
8666 
8667 	if (len != 128) {
8668 		sprintf(icp->err,"icmHeader_read: Length expected to be 128");
8669 		return icp->errc = 1;
8670 	}
8671 
8672 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
8673 		sprintf(icp->err,"icmHeader_read: malloc() failed");
8674 		return icp->errc = 2;
8675 	}
8676 	if (   icp->fp->seek(icp->fp, of) != 0
8677 	    || icp->fp->read(icp->fp, buf, 1, len) != len) {
8678 		sprintf(icp->err,"icmHeader_read: fseek() or fread() failed");
8679 		icp->al->free(icp->al, buf);
8680 		return icp->errc = 1;
8681 	}
8682 
8683 	/* Fill in the in-memory structure */
8684 	p->size  = read_UInt32Number(buf + 0);	/* Profile size in bytes */
8685     p->cmmId = read_SInt32Number(buf + 4);	/* CMM for profile */
8686 	tt       = read_UInt8Number(buf + 8);	/* Raw major version number */
8687     p->majv  = (tt >> 4) * 10 + (tt & 0xf);	/* Integer major version number */
8688 	tt       = read_UInt8Number(buf + 9);	/* Raw minor/bug fix version numbers */
8689     p->minv  = (tt >> 4);					/* Integer minor version number */
8690     p->bfv   = (tt & 0xf);					/* Integer bug fix version number */
8691 	p->deviceClass = (icProfileClassSignature)
8692 	           read_SInt32Number(buf + 12);	/* Type of profile */
8693     p->colorSpace = (icColorSpaceSignature)
8694 	           read_SInt32Number(buf + 16);	/* Color space of data */
8695     p->pcs = (icColorSpaceSignature)
8696 	           read_SInt32Number(buf + 20);	/* PCS: XYZ or Lab */
8697 	if ((rv = read_DateTimeNumber(&p->date, buf + 24)) != 0) {	/* Creation Date */
8698 		sprintf(icp->err,"icmHeader_read: read_DateTimeNumber corrupted");
8699 		icp->al->free(icp->al, buf);
8700 		return icp->errc = rv;
8701 	}
8702 	tt = read_SInt32Number(buf+36);
8703 	if (tt != icMagicNumber) {				/* Check magic number */
8704 		sprintf(icp->err,"icmHeader_read: wrong magic number 0x%x",tt);
8705 		icp->al->free(icp->al, buf);
8706 		return icp->errc = 1;
8707 	}
8708     p->platform = (icPlatformSignature)
8709 	           read_SInt32Number(buf + 40);			/* Primary platform */
8710     p->flags = read_UInt32Number(buf + 44);			/* Various bits */
8711     p->manufacturer = read_SInt32Number(buf + 48); /* Dev manufacturer */
8712     p->model = read_SInt32Number(buf + 52);			/* Dev model */
8713     read_UInt64Number(&p->attributes, buf + 56);	/* Device attributes */
8714 	p->renderingIntent = (icRenderingIntent)
8715 	           read_SInt32Number(buf + 64);	/* Rendering intent */
8716 	if ((rv = read_XYZNumber(&p->illuminant, buf + 68)) != 0) {	/* Profile illuminant */
8717 		sprintf(icp->err,"icmHeader_read: read_XYZNumber error");
8718 		icp->al->free(icp->al, buf);
8719 		return icp->errc = rv;
8720 	}
8721     p->creator = read_SInt32Number(buf + 80);	/* Profile creator */
8722 
8723 	icp->al->free(icp->al, buf);
8724 	return 0;
8725 }
8726 
8727 /* Write the contents of the object. Return 0 on sucess, error code on failure */
8728 static int icmHeader_write(
8729 	icmHeader *p,
8730 	unsigned long of			/* File offset to write from */
8731 ) {
8732 	icc *icp = p->icp;
8733 	char *buf;		/* Buffer to write from */
8734 	unsigned int len;
8735 	unsigned int tt;
8736 	int rv = 0;
8737 
8738 	len = p->get_size(p);
8739 	if ((buf = (char *) icp->al->calloc(icp->al,1,len)) == NULL) {			/* Zero it - some CMS are fussy */
8740 		sprintf(icp->err,"icmHeader_write calloc() failed");
8741 		return icp->errc = 2;
8742 	}
8743 
8744 	/* Fill in the write buffer */
8745 	if ((rv = write_UInt32Number(p->size, buf + 0)) != 0) {	/* Profile size in bytes */
8746 		sprintf(icp->err,"icmHeader_write: profile size");
8747 		icp->al->free(icp->al, buf);
8748 		return icp->errc = rv;
8749 	}
8750 
8751     if ((rv = write_SInt32Number(p->cmmId, buf + 4)) != 0) {	/* CMM for profile */
8752 		sprintf(icp->err,"icmHeader_write: cmmId");
8753 		icp->al->free(icp->al, buf);
8754 		return icp->errc = rv;
8755 	}
8756 	if (p->majv < 0 || p->majv > 99			/* Sanity check version numbers */
8757 	 || p->minv < 0 || p->minv > 9
8758 	 || p->bfv  < 0 || p->bfv  > 9) {
8759 		sprintf(icp->err,"icmHeader_write: version number");
8760 		icp->al->free(icp->al, buf);
8761 		return icp->errc = 1;
8762 	}
8763 	tt = ((p->majv/10) << 4) + (p->majv % 10);
8764 	if ((rv = write_UInt8Number(tt, buf + 8)) != 0) {	/* Raw major version number */
8765 		sprintf(icp->err,"icmHeader_write: Uint8Number major version");
8766 		icp->al->free(icp->al, buf);
8767 		return icp->errc = rv;
8768 	}
8769     tt = (p->minv << 4) + p->bfv;
8770 	if ((rv = write_UInt8Number(tt, buf + 9)) != 0) {	/* Raw minor/bug fix version numbers */
8771 		sprintf(icp->err,"icmHeader_write: Uint8Number minor/bug fix");
8772 		icp->al->free(icp->al, buf);
8773 		return icp->errc = rv;
8774 	}
8775 	if ((rv = write_SInt32Number((int)p->deviceClass, buf + 12)) != 0) {	/* Type of profile */
8776 		sprintf(icp->err,"icmHeader_write: SInt32Number deviceClass");
8777 		icp->al->free(icp->al, buf);
8778 		return icp->errc = rv;
8779 	}
8780 	if ((rv = write_SInt32Number((int)p->colorSpace, buf + 16)) != 0) {	/* Color space of data */
8781 		sprintf(icp->err,"icmHeader_write: SInt32Number data color space");
8782 		icp->al->free(icp->al, buf);
8783 		return icp->errc = rv;
8784 	}
8785 	if ((rv = write_SInt32Number((int)p->pcs, buf + 20)) != 0) {		/* PCS: XYZ or Lab */
8786 		sprintf(icp->err,"icmHeader_write: SInt32Number PCS");
8787 		icp->al->free(icp->al, buf);
8788 		return icp->errc = rv;
8789 	}
8790 	if ((rv = write_DateTimeNumber(&p->date, buf + 24)) != 0) {		/* Creation Date */
8791 		sprintf(icp->err,"icmHeader_write: DateTimeNumber creation");
8792 		icp->al->free(icp->al, buf);
8793 		return icp->errc = rv;
8794 	}
8795 	if ((rv = write_SInt32Number(icMagicNumber, buf+36)) != 0) {		/* Magic number */
8796 		sprintf(icp->err,"icmHeader_write: SInt32Number magic");
8797 		icp->al->free(icp->al, buf);
8798 		return icp->errc = rv;
8799 	}
8800 	if ((rv = write_SInt32Number((int)p->platform, buf + 40)) != 0) {	/* Primary platform */
8801 		sprintf(icp->err,"icmHeader_write: SInt32Number platform");
8802 		icp->al->free(icp->al, buf);
8803 		return icp->errc = rv;
8804 	}
8805     if ((rv = write_UInt32Number(p->flags, buf + 44)) != 0) {			/* Various bits */
8806 		sprintf(icp->err,"icmHeader_write: UInt32Number flags");
8807 		icp->al->free(icp->al, buf);
8808 		return icp->errc = rv;
8809 	}
8810     if ((rv = write_SInt32Number(p->manufacturer, buf + 48)) != 0) { /* Dev manufacturer */
8811 		sprintf(icp->err,"icmHeader_write: SInt32Number manufaturer");
8812 		icp->al->free(icp->al, buf);
8813 		return icp->errc = rv;
8814 	}
8815     if ((write_SInt32Number(p->model, buf + 52)) != 0) {				/* Dev model */
8816 		sprintf(icp->err,"icmHeader_write: SInt32Number model");
8817 		icp->al->free(icp->al, buf);
8818 		return icp->errc = rv;
8819 	}
8820     if ((rv = write_UInt64Number(&p->attributes, buf + 56)) != 0) {	/* Device attributes */
8821 		sprintf(icp->err,"icmHeader_write: UInt64Number attributes");
8822 		icp->al->free(icp->al, buf);
8823 		return icp->errc = rv;
8824 	}
8825 	if ((rv = write_SInt32Number((int)p->renderingIntent, buf + 64)) != 0) { /* Rendering intent */
8826 		sprintf(icp->err,"icmHeader_write: SInt32Number rendering intent");
8827 		icp->al->free(icp->al, buf);
8828 		return icp->errc = rv;
8829 	}
8830 	if ((rv = write_XYZNumber(&p->illuminant, buf + 68)) != 0) {		/* Profile illuminant */
8831 		sprintf(icp->err,"icmHeader_write: XYZNumber illuminant");
8832 		icp->al->free(icp->al, buf);
8833 		return icp->errc = rv;
8834 	}
8835     if ((rv = write_SInt32Number(p->creator, buf + 80)) != 0) {		/* Profile creator */
8836 		sprintf(icp->err,"icmHeader_write: SInt32Number creator");
8837 		icp->al->free(icp->al, buf);
8838 		return icp->errc = rv;
8839 	}
8840 
8841 	if (   icp->fp->seek(icp->fp, of) != 0
8842 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
8843 		sprintf(icp->err,"icmHeader_write fseek() or fwrite() failed");
8844 		icp->al->free(icp->al, buf);
8845 		return icp->errc = 2;
8846 	}
8847 
8848 	icp->al->free(icp->al, buf);
8849 	return rv;
8850 }
8851 
8852 static void icmHeader_dump(
8853 	icmHeader *p,
8854 	FILE *op,		/* Output to dump to */
8855 	int   verb		/* Verbosity level */
8856 ) {
8857 	if (verb <= 0)
8858 		return;
8859 
8860 	fprintf(op,"Header:\n");
8861 	fprintf(op,"  size         = %d bytes\n",p->size);
8862 	fprintf(op,"  CMM          = %s\n",tag2str(p->cmmId));
8863 	fprintf(op,"  Version      = %d.%d.%d\n",p->majv, p->minv, p->bfv);
8864 	fprintf(op,"  Device Class = %s\n", string_ProfileClassSignature(p->deviceClass));
8865 	fprintf(op,"  Color Space  = %s\n", string_ColorSpaceSignature(p->colorSpace));
8866 	fprintf(op,"  Conn. Space  = %s\n", string_ColorSpaceSignature(p->pcs));
8867 	fprintf(op,"  Date, Time   = %s\n", string_DateTimeNumber(&p->date));
8868 	fprintf(op,"  Platform     = %s\n", string_PlatformSignature(p->platform));
8869 	fprintf(op,"  Flags        = %s\n", string_ProfileHeaderFlags(p->flags));
8870 	fprintf(op,"  Dev. Mnfctr. = %s\n",tag2str(p->manufacturer));	/* ~~~ */
8871 	fprintf(op,"  Dev. Model   = %s\n",tag2str(p->model));	/* ~~~ */
8872 	fprintf(op,"  Dev. Attrbts = %s\n", string_DeviceAttributes(p->attributes.l));
8873 	fprintf(op,"  Rndrng Intnt = %s\n", string_RenderingIntent(p->renderingIntent));
8874 	fprintf(op,"  Illuminant   = %s\n", string_XYZNumber_and_Lab(&p->illuminant));
8875 	fprintf(op,"  Creator      = %s\n",tag2str(p->creator));	/* ~~~ */
8876 	fprintf(op,"\n");
8877 }
8878 
8879 static void icmHeader_delete(
8880 	icmHeader *p
8881 ) {
8882 	icc *icp = p->icp;
8883 
8884 	icp->al->free(icp->al, p);
8885 }
8886 
8887 /* Create an empty object. Return null on error */
8888 static icmHeader *new_icmHeader(
8889 	icc *icp
8890 ) {
8891 	icmHeader *p;
8892 	if ((p = (icmHeader *) icp->al->calloc(icp->al,1,sizeof(icmHeader))) == NULL)
8893 		return NULL;
8894 	p->icp      = icp;
8895 	p->get_size = icmHeader_get_size;
8896 	p->read     = icmHeader_read;
8897 	p->write    = icmHeader_write;
8898 	p->dump     = icmHeader_dump;
8899 	p->del      = icmHeader_delete;
8900 
8901 	return p;
8902 }
8903 
8904 /* ---------------------------------------------------------- */
8905 /* Type vector table. Match the Tag type against the object creator */
8906 static struct {
8907 	icTagTypeSignature  ttype;			/* The tag type signature */
8908 	icmBase *              (*new_obj)(icc *icp);
8909 } typetable[] = {
8910 	{icSigCrdInfoType,             new_icmCrdInfo},
8911 	{icSigCurveType,               new_icmCurve},
8912 	{icSigDataType,                new_icmData},
8913 	{icSigDateTimeType,            new_icmDateTimeNumber},
8914 	{icSigLut16Type,               new_icmLut},
8915 	{icSigLut8Type,                new_icmLut},
8916 	{icSigMeasurementType,         new_icmMeasurement},
8917 	{icSigNamedColorType,          new_icmNamedColor},
8918 	{icSigNamedColor2Type,         new_icmNamedColor},
8919 	{icSigProfileSequenceDescType, new_icmProfileSequenceDesc},
8920 	{icSigS15Fixed16ArrayType,     new_icmS15Fixed16Array},
8921 	{icSigScreeningType,           new_icmScreening},
8922 	{icSigSignatureType,           new_icmSignature},
8923 	{icSigTextDescriptionType,     new_icmTextDescription},
8924 	{icSigTextType,                new_icmText},
8925 	{icSigU16Fixed16ArrayType,     new_icmU16Fixed16Array},
8926 	{icSigUcrBgType,               new_icmUcrBg},
8927 	{icSigVideoCardGammaType,      new_icmVideoCardGamma},
8928 	{icSigUInt16ArrayType,         new_icmUInt16Array},
8929 	{icSigUInt32ArrayType,         new_icmUInt32Array},
8930 	{icSigUInt64ArrayType,         new_icmUInt64Array},
8931 	{icSigUInt8ArrayType,          new_icmUInt8Array},
8932 	{icSigViewingConditionsType,   new_icmViewingConditions},
8933 	{icSigXYZArrayType,            new_icmXYZArray},
8934 	{icMaxEnumType,                NULL}
8935 };
8936 
8937 /* Table that lists the legal Types for each Tag Signature */
8938 static struct {
8939 	icTagSignature      sig;
8940 	icTagTypeSignature  ttypes[4];			/* Arbitrary max of 4 */
8941 } sigtypetable[] = {
8942 	{icSigAToB0Tag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8943 	{icSigAToB1Tag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8944 	{icSigAToB2Tag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8945 	{icSigBlueColorantTag,			{icSigXYZType,icMaxEnumType}},
8946 	{icSigBlueTRCTag,				{icSigCurveType,icMaxEnumType}},
8947 	{icSigBToA0Tag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8948 	{icSigBToA1Tag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8949 	{icSigBToA2Tag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8950 	{icSigCalibrationDateTimeTag,	{icSigDateTimeType,icMaxEnumType}},
8951 	{icSigCharTargetTag,			{icSigTextType,icMaxEnumType}},
8952 	{icSigCopyrightTag,				{icSigTextType,icMaxEnumType}},
8953 	{icSigCrdInfoTag,				{icSigCrdInfoType,icMaxEnumType}},
8954 	{icSigDeviceMfgDescTag,			{icSigTextDescriptionType,icMaxEnumType}},
8955 	{icSigDeviceModelDescTag,		{icSigTextDescriptionType,icMaxEnumType}},
8956 	{icSigGamutTag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8957 	{icSigGrayTRCTag,				{icSigCurveType,icMaxEnumType}},
8958 	{icSigGreenColorantTag,			{icSigXYZType,icMaxEnumType}},
8959 	{icSigGreenTRCTag,				{icSigCurveType,icMaxEnumType}},
8960 	{icSigLuminanceTag,				{icSigXYZType,icMaxEnumType}},
8961 	{icSigMeasurementTag,			{icSigMeasurementType,icMaxEnumType}},
8962 	{icSigMediaBlackPointTag,		{icSigXYZType,icMaxEnumType}},
8963 	{icSigMediaWhitePointTag,		{icSigXYZType,icMaxEnumType}},
8964 	{icSigNamedColorTag,			{icSigNamedColorType,icMaxEnumType}},
8965 	{icSigNamedColor2Tag,			{icSigNamedColor2Type,icMaxEnumType}},
8966 	{icSigPreview0Tag,				{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8967 	{icSigPreview1Tag,				{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8968 	{icSigPreview2Tag,				{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8969 	{icSigProfileDescriptionTag,	{icSigTextDescriptionType,icMaxEnumType}},
8970 	{icSigProfileSequenceDescTag,	{icSigProfileSequenceDescType,icMaxEnumType}},
8971 	{icSigPs2CRD0Tag,				{icSigDataType,icMaxEnumType}},
8972 	{icSigPs2CRD1Tag,				{icSigDataType,icMaxEnumType}},
8973 	{icSigPs2CRD2Tag,				{icSigDataType,icMaxEnumType}},
8974 	{icSigPs2CRD3Tag,				{icSigDataType,icMaxEnumType}},
8975 	{icSigPs2CSATag,				{icSigDataType,icMaxEnumType}},
8976 	{icSigPs2RenderingIntentTag,	{icSigDataType,icMaxEnumType}},
8977 	{icSigRedColorantTag,			{icSigXYZType,icMaxEnumType}},
8978 	{icSigRedTRCTag,				{icSigCurveType,icMaxEnumType}},
8979 	{icSigScreeningDescTag,			{icSigTextDescriptionType,icMaxEnumType}},
8980 	{icSigScreeningTag,				{icSigScreeningType,icMaxEnumType}},
8981 	{icSigTechnologyTag,			{icSigSignatureType,icMaxEnumType}},
8982 	{icSigUcrBgTag,					{icSigUcrBgType,icMaxEnumType}},
8983 	{icSigVideoCardGammaTag,		{icSigVideoCardGammaType,icMaxEnumType}},
8984 	{icSigViewingCondDescTag,		{icSigTextDescriptionType,icMaxEnumType}},
8985 	{icSigViewingConditionsTag,		{icSigViewingConditionsType,icMaxEnumType}},
8986 	{icMaxEnumType,					{icMaxEnumType}}
8987 };
8988 
8989 /* Fake color tag for specifying PCS */
8990 #define icSigPCSData  ((icColorSpaceSignature) 0x50435320L)
8991 
8992 /* Table that lists the required tags for various profiles */
8993 static struct {
8994 	icProfileClassSignature sig;		/* Profile signature */
8995 	int      			    chans;		/* Data Color channels, -ve for match but try next, */
8996 										/*          -100 for ignore, -200 for ignore and try next */
8997 	icColorSpaceSignature   colsig;		/* Data Color space signature, icMaxEnumData for ignore, */
8998 										/*                           icSigPCSData for XYZ of Lab */
8999 	icColorSpaceSignature   pcssig;		/* PCS Color space signature, icMaxEnumData for ignore, */
9000 										/*                          icSigPCSData for XYZ or Lab */
9001 	icTagSignature          tags[12];	/* Arbitrary max of 12 */
9002 } tagchecktable[] = {
9003     {icSigInputClass,      -1, icMaxEnumData, icSigPCSData,
9004 	 	{icSigProfileDescriptionTag,
9005 		 icSigGrayTRCTag,
9006 		 icSigMediaWhitePointTag,
9007 		 icSigCopyrightTag, icMaxEnumType}},
9008 
9009     {icSigInputClass,      -3, icMaxEnumData, icSigXYZData,
9010 	 	{icSigProfileDescriptionTag,
9011 		 icSigRedColorantTag,
9012 		 icSigGreenColorantTag,
9013 		 icSigBlueColorantTag,
9014 		 icSigRedTRCTag,
9015 		 icSigGreenTRCTag,
9016 		 icSigBlueTRCTag,
9017 		 icSigMediaWhitePointTag,
9018 		 icSigCopyrightTag, icMaxEnumType}},
9019 
9020     {icSigInputClass,     -100, icMaxEnumData, icSigPCSData,
9021 	 	{icSigProfileDescriptionTag,
9022 		 icSigAToB0Tag,
9023 		 icSigMediaWhitePointTag,
9024 		 icSigCopyrightTag, icMaxEnumType}},
9025 
9026     {icSigDisplayClass,     -1, icMaxEnumData, icSigPCSData,
9027 	 	{icSigProfileDescriptionTag,
9028 		 icSigGrayTRCTag,
9029 		 icSigMediaWhitePointTag,
9030 		 icSigCopyrightTag, icMaxEnumType}},
9031 
9032     {icSigDisplayClass,     -3, icSigRgbData, icSigXYZData,	/* Rgb or any 3 component space ?? */
9033 	 	{icSigProfileDescriptionTag,
9034 		 icSigRedColorantTag,
9035 		 icSigGreenColorantTag,
9036 		 icSigBlueColorantTag,
9037 		 icSigRedTRCTag,
9038 		 icSigGreenTRCTag,
9039 		 icSigBlueTRCTag,
9040 		 icSigMediaWhitePointTag,
9041 		 icSigCopyrightTag, icMaxEnumType}},
9042 
9043 	/* Non-3 component Display device */
9044     {icSigDisplayClass,     -100, icMaxEnumData, icSigPCSData,
9045 	 	{icSigProfileDescriptionTag,
9046 		 icSigAToB0Tag,					/* BToA doesn't seem to be required, which is strange... */
9047 		 icSigMediaWhitePointTag,
9048 		 icSigCopyrightTag, icMaxEnumType}},
9049 
9050     {icSigOutputClass,     -1, icMaxEnumData, icSigPCSData,
9051 	 	{icSigProfileDescriptionTag,
9052 		 icSigGrayTRCTag,
9053 		 icSigMediaWhitePointTag,
9054 		 icSigCopyrightTag, icMaxEnumType}},
9055 
9056     {icSigOutputClass,     -1, icMaxEnumData, icSigPCSData,
9057 	 	{icSigProfileDescriptionTag,
9058 		 icSigAToB0Tag,
9059 		 icSigBToA0Tag,
9060 		 icSigGamutTag,
9061 		 icSigAToB1Tag,
9062 		 icSigBToA1Tag,
9063 		 icSigAToB2Tag,
9064 		 icSigBToA2Tag,
9065 		 icSigMediaWhitePointTag,
9066 		 icSigCopyrightTag, icMaxEnumType}},
9067 
9068     {icSigOutputClass,     -2, icMaxEnumData, icSigPCSData,
9069 	 	{icSigProfileDescriptionTag,
9070 		 icSigAToB0Tag,
9071 		 icSigBToA0Tag,
9072 		 icSigGamutTag,
9073 		 icSigAToB1Tag,
9074 		 icSigBToA1Tag,
9075 		 icSigAToB2Tag,
9076 		 icSigBToA2Tag,
9077 		 icSigMediaWhitePointTag,
9078 		 icSigCopyrightTag, icMaxEnumType}},
9079 
9080     {icSigOutputClass,     -3, icMaxEnumData, icSigPCSData,
9081 	 	{icSigProfileDescriptionTag,
9082 		 icSigAToB0Tag,
9083 		 icSigBToA0Tag,
9084 		 icSigGamutTag,
9085 		 icSigAToB1Tag,
9086 		 icSigBToA1Tag,
9087 		 icSigAToB2Tag,
9088 		 icSigBToA2Tag,
9089 		 icSigMediaWhitePointTag,
9090 		 icSigCopyrightTag, icMaxEnumType}},
9091 
9092     {icSigOutputClass,     -4, icMaxEnumData, icSigPCSData,
9093 	 	{icSigProfileDescriptionTag,
9094 		 icSigAToB0Tag,
9095 		 icSigBToA0Tag,
9096 		 icSigGamutTag,
9097 		 icSigAToB1Tag,
9098 		 icSigBToA1Tag,
9099 		 icSigAToB2Tag,
9100 		 icSigBToA2Tag,
9101 		 icSigMediaWhitePointTag,
9102 		 icSigCopyrightTag, icMaxEnumType}},
9103 
9104     {icSigOutputClass,     -100, icMaxEnumData, icSigPCSData,	/* Assumed from Hexachrome examples */
9105 	 	{icSigProfileDescriptionTag,
9106 		 icSigBToA0Tag,
9107 		 icSigBToA1Tag,
9108 		 icSigBToA2Tag,
9109 		 icSigMediaWhitePointTag,
9110 		 icSigCopyrightTag, icMaxEnumType}},
9111 
9112     {icSigLinkClass,      -100, icMaxEnumData, icMaxEnumData,
9113 	 	{icSigProfileDescriptionTag,
9114 		 icSigAToB0Tag,
9115 		 icSigProfileSequenceDescTag,
9116 		 icSigCopyrightTag, icMaxEnumType}},
9117 
9118     {icSigColorSpaceClass,       -100, icMaxEnumData, icSigPCSData,
9119 	 	{icSigProfileDescriptionTag,
9120 		 icSigBToA0Tag,
9121 		 icSigAToB0Tag,
9122 		 icSigMediaWhitePointTag,
9123 		 icSigCopyrightTag, icMaxEnumType}},
9124 
9125     {icSigAbstractClass,      -100, icSigPCSData, icSigPCSData,
9126 	 	{icSigProfileDescriptionTag,
9127 		 icSigAToB0Tag,
9128 		 icSigMediaWhitePointTag,
9129 		 icSigCopyrightTag, icMaxEnumType}},
9130 
9131     {icSigNamedColorClass,        -200, icMaxEnumData, icMaxEnumData,
9132 	 	{icSigProfileDescriptionTag,
9133 		 icSigNamedColor2Tag,
9134 		 icSigMediaWhitePointTag,
9135 		 icSigCopyrightTag, icMaxEnumType}},
9136 
9137     {icSigNamedColorClass,        -100, icMaxEnumData, icMaxEnumData,
9138 	 	{icSigProfileDescriptionTag,
9139 		 icSigNamedColorTag,				/* Not strictly V3.4 */
9140 		 icSigMediaWhitePointTag,
9141 		 icSigCopyrightTag, icMaxEnumType}},
9142 
9143 	{icMaxEnumType,-1,icMaxEnumData, icMaxEnumData,		{icMaxEnumType}}
9144 };
9145 
9146 /* ------------------------------------------------------------- */
9147 /* Check that the ICC profile looks like it will be legal. */
9148 /* Return non-zero and set error string if not */
9149 static int check_icc_legal(
9150 	icc *p
9151 ) {
9152 	int i, j;
9153 	icProfileClassSignature  sig;
9154 	icColorSpaceSignature colsig;
9155 	icColorSpaceSignature pcssig;
9156 	int      	          dchans;
9157 
9158 	if (p->header == NULL) {
9159 		sprintf(p->err,"icc_check_legal: Header is missing");
9160 		return p->errc = 1;
9161 	}
9162 
9163 	sig = p->header->deviceClass;
9164 	colsig = p->header->colorSpace;
9165 	dchans = number_ColorSpaceSignature(colsig);
9166 	pcssig = p->header->pcs;
9167 
9168 	/* Find a matching table entry */
9169 	for (i = 0; tagchecktable[i].sig != icMaxEnumType; i++) {
9170 		if (    tagchecktable[i].sig   == sig
9171 		 && (   tagchecktable[i].chans == dchans	/* Exactly matches */
9172 		     || tagchecktable[i].chans == -dchans	/* Exactly matches, but can try next table */
9173 		     || tagchecktable[i].chans < -99)		/* Doesn't have to match or try next table */
9174 		 && (   tagchecktable[i].colsig == colsig
9175 		     || (tagchecktable[i].colsig == icSigPCSData
9176 		         && (colsig == icSigXYZData || colsig == icSigLabData))
9177 		     || tagchecktable[i].colsig == icMaxEnumData)
9178 		 && (   tagchecktable[i].pcssig == pcssig
9179 		     || (tagchecktable[i].pcssig == icSigPCSData
9180 		         && (pcssig == icSigXYZData || pcssig == icSigLabData))
9181 		     || tagchecktable[i].pcssig == icMaxEnumData)) {
9182 
9183 			/* Found entry, so now check that all the required tags are present. */
9184 			for (j = 0; tagchecktable[i].tags[j] != icMaxEnumType; j++) {
9185 				if (p->find_tag(p, tagchecktable[i].tags[j]) != 0) {	/* Not present! */
9186 					if (tagchecktable[i].chans == -200
9187 					 || tagchecktable[i].chans == -dchans) {	/* But can try next table */
9188 						break;
9189 					}
9190 					sprintf(p->err,"icc_check_legal: deviceClass %s is missing required tag %s",
9191 					               tag2str(sig), tag2str(tagchecktable[i].tags[j]));
9192 					return p->errc = 1;
9193 				}
9194 			}
9195 			if (tagchecktable[i].tags[j] == icMaxEnumType) {
9196 				break;		/* Fount all required tags */
9197 			}
9198 		}
9199 	}
9200 
9201 	/* According to the spec. if the deviceClass is:
9202 		an Abstract Class: both in and out color spaces should be PCS
9203 		an Link Class: both in and out color spaces can be any, and should
9204 		    be the input space of the first profile in the link, and the
9205 		    input space of the last profile in the link respectively.
9206 		a Named Class: in and out color spaces are not defined in the spec.
9207 		Input, Display, Output and ColorSpace Classes, input color
9208 		    space can be any, and the output space must be PCS.
9209 		~~ should check this here ???
9210 	*/
9211 
9212 	return 0;	/* Assume anything is ok */
9213 }
9214 
9215 
9216 /* read the object, return 0 on success, error code on fail */
9217 /* NOTE: this doesn't read the tag types, they should be read on demand. */
9218 static int icc_read(
9219 	icc *p,
9220 	icmFile *fp,			/* File to read from */
9221 	unsigned long of		/* File offset to read from */
9222 ) {
9223 	char tcbuf[4];			/* Tag count read buffer */
9224 	int i;
9225 	unsigned int len;
9226 	int er = 0;				/* Error code */
9227 
9228 	p->fp = fp;
9229 	p->of = of;
9230 	if (p->header == NULL) {
9231 		sprintf(p->err,"icc_read: No header defined");
9232 		return p->errc = 1;
9233 	}
9234 
9235 	/* Read the header */
9236 	if (p->header->read(p->header, 128, of)) {
9237 		return 1;
9238 	}
9239 
9240 	/* Read the tag count */
9241 	if (   p->fp->seek(p->fp, of + 128) != 0
9242 	    || p->fp->read(p->fp, tcbuf, 1, 4) != 4) {
9243 		sprintf(p->err,"icc_read: fseek() or fread() failed on tag count");
9244 		return p->errc = 1;
9245 	}
9246 
9247 	p->count = read_UInt32Number(tcbuf);		/* Tag count */
9248 	if (p->count > 0) {
9249 		char *bp, *buf;
9250 		if ((p->data = (icmTag *) p->al->malloc(p->al, p->count * sizeof(icmTag))) == NULL) {
9251 			sprintf(p->err,"icc_read: Tag table malloc() failed");
9252 			return p->errc = 2;
9253 		}
9254 
9255 		len = 4 + p->count * 12;
9256 		if ((buf = (char *) p->al->malloc(p->al, len)) == NULL) {
9257 			sprintf(p->err,"icc_read: Tag table read buffer malloc() failed");
9258 			p->al->free(p->al, p->data);
9259 			p->data = NULL;
9260 			return p->errc = 2;
9261 		}
9262 		if (   p->fp->seek(p->fp, of + 128) != 0
9263 		    || p->fp->read(p->fp, buf, 1, len) != len) {
9264 			sprintf(p->err,"icc_read: fseek() or fread() failed on tag table");
9265 			p->al->free(p->al, p->data);
9266 			p->data = NULL;
9267 			p->al->free(p->al, buf);
9268 			return p->errc = 1;
9269 		}
9270 
9271 		/* Fill in the tag table structure */
9272 		bp = buf+4;
9273 		for (i = 0; i < p->count; i++, bp += 12) {
9274 	    	p->data[i].sig = (icTagSignature)read_SInt32Number(bp + 0);
9275 	    	p->data[i].offset = read_UInt32Number(bp + 4);
9276 	    	p->data[i].size = read_UInt32Number(bp + 8);
9277 			if (   p->fp->seek(p->fp, of + p->data[i].offset) != 0
9278 			    || p->fp->read(p->fp, tcbuf, 1, 4) != 4) {
9279 				sprintf(p->err,"icc_read: fseek() or fread() failed on tag headers");
9280 				p->al->free(p->al, p->data);
9281 				p->data = NULL;
9282 				p->al->free(p->al, buf);
9283 				return p->errc = 1;
9284 			}
9285 	    	p->data[i].ttype = read_SInt32Number(tcbuf);		/* Tag type */
9286 	    	p->data[i].objp = NULL;							/* Read on demand */
9287 		}
9288 		p->al->free(p->al, buf);
9289 	}	/* p->count > 0 */
9290 
9291 	return er;
9292 }
9293 
9294 #define DO_ALIGN(val) (((val) + 3) & ~3)
9295 
9296 /* Return the total size needed for the profile */
9297 /* Return 0 on error. */
9298 static unsigned int icc_get_size(
9299 	icc *p
9300 ) {
9301 	unsigned int size = 0;
9302 	int i;
9303 
9304 	/* Check that the right tags etc. are present for a legal ICC profile */
9305 	if (check_icc_legal(p) != 0) {
9306 		return 0;
9307 	}
9308 
9309 	/* Compute the total size and tag element data offsets */
9310 	if (p->header == NULL) {
9311 		sprintf(p->err,"icc_get_size: No header defined");
9312 		p->errc = 1;
9313 		return 0;
9314 	}
9315 
9316 	size += p->header->get_size(p->header);
9317 
9318 	size = DO_ALIGN(size);
9319 	size += 4 + p->count * 12;	/* Tag table length */
9320 
9321 	/* Reset touched flag for each tag type */
9322 	for (i = 0; i < p->count; i++) {
9323 		if (p->data[i].objp == NULL) {
9324 			sprintf(p->err,"icc_get_size: Internal error - NULL tag element");
9325 			p->errc = 1;
9326 			return 0;
9327 		}
9328 		p->data[i].objp->touched = 0;
9329 	}
9330 	/* Get size for each tag type, skipping links */
9331 	for (i = 0; i < p->count; i++) {
9332 		if (p->data[i].objp->touched == 0) { /* Not alllowed for previously */
9333 			size = DO_ALIGN(size);
9334 			size += p->data[i].objp->get_size(p->data[i].objp);
9335 			p->data[i].objp->touched = 1;	/* Don't account for this again */
9336 		}
9337 	}
9338 
9339 	return size;	/* Total size needed */
9340 }
9341 
9342 /* Write the contents of the object. Return 0 on sucess, error code on failure */
9343 static int icc_write(
9344 	icc *p,
9345 	icmFile *fp,		/* File to write to */
9346 	unsigned long of	/* File offset to write to */
9347 ) {
9348 	char *bp, *buf;		/* Buffer to write to */
9349 	unsigned int len;
9350 	int rv = 0;
9351 	int i;
9352 	unsigned int size = 0;
9353 
9354 	/* Check that the right tags etc. are present for a legal ICC profile */
9355 	if ((rv = check_icc_legal(p)) != 0) {
9356 		return rv;
9357 	}
9358 
9359 	p->fp = fp;			/* Open file pointer */
9360 	p->of = of;			/* Offset of ICC profile */
9361 
9362 	/* Compute the total size and tag element data offsets */
9363 	if (p->header == NULL) {
9364 		sprintf(p->err,"icc_write: No header defined");
9365 		return p->errc = 1;
9366 	}
9367 
9368 	size += p->header->get_size(p->header);
9369 
9370 	len = 4 + p->count * 12;	/* Tag table length */
9371 	size = DO_ALIGN(size);
9372 	size += len;
9373 
9374 	/* Allocate memory buffer for tag table */
9375 	if ((buf = (char *) p->al->malloc(p->al, len)) == NULL) {
9376 		sprintf(p->err,"icc_write malloc() failed");
9377 		return p->errc = 2;
9378 	}
9379 	bp = buf;
9380 
9381     if ((rv = write_UInt32Number(p->count, bp)) != 0) {		/* Tag count */
9382 		sprintf(p->err,"icc_write: write_UInt32Number() failed on tag count");
9383 		p->al->free(p->al, buf);
9384 		return p->errc = rv;
9385 	}
9386 	bp += 4;
9387 	/* Reset touched flag for each tag type */
9388 	for (i = 0; i < p->count; i++) {
9389 		if (p->data[i].objp == NULL) {
9390 			sprintf(p->err,"icc_write: Internal error - NULL tag element");
9391 			p->al->free(p->al, buf);
9392 			return p->errc = 1;
9393 		}
9394 		p->data[i].objp->touched = 0;
9395 	}
9396 	/* Set the offset and size for each tag type */
9397 	for (i = 0; i < p->count; i++) {
9398 		if (p->data[i].objp->touched == 0) {	/* Allocate space for tag type */
9399 			size = DO_ALIGN(size);
9400 			p->data[i].offset = size;			/* Profile relative target */
9401 			p->data[i].size = p->data[i].objp->get_size(p->data[i].objp);
9402 			size += p->data[i].size;
9403 			p->data[i].objp->touched = 1;	/* Allocated space for it */
9404 		} else { /* must be linked - copy allocation */
9405 			int k;
9406 			for (k = 0; k < p->count; k++) {	/* Find linked tag */
9407 				if (p->data[k].objp == p->data[i].objp)
9408 					break;
9409 			}
9410 			if (k == p->count) {
9411 				sprintf(p->err,"icc_write: corrupted link");
9412 				return p->errc = 2;
9413 			}
9414 		    p->data[i].offset = p->data[k].offset;
9415 		    p->data[i].size   = p->data[k].size;
9416 		}
9417 		/* Write tag table entry for this tag */
9418 		if ((rv = write_SInt32Number((int)p->data[i].sig,bp + 0)) != 0) {
9419 			sprintf(p->err,"icc_write: write_SInt32Number() failed on tag signature");
9420 			p->al->free(p->al, buf);
9421 			return p->errc = rv;
9422 		}
9423 		if ((rv = write_UInt32Number(p->data[i].offset, bp + 4)) != 0) {
9424 			sprintf(p->err,"icc_write: write_UInt32Number() failed on tag offset");
9425 			p->al->free(p->al, buf);
9426 			return p->errc = rv;
9427 		}
9428 		if ((rv = write_UInt32Number(p->data[i].size, bp + 8)) != 0) {
9429 			sprintf(p->err,"icc_write: write_UInt32Number() failed on tag size");
9430 			p->al->free(p->al, buf);
9431 			return p->errc = rv;
9432 		}
9433 		bp += 12;
9434 	}
9435 	p->header->size = size;		/* Record total icc size */
9436 
9437 	/* Write the header */
9438 	if ((rv = p->header->write(p->header, of)) != 0) {
9439 		p->al->free(p->al, buf);
9440 		return rv;
9441 	}
9442 
9443 	/* Write the tag table */
9444 	if (   p->fp->seek(p->fp, of + 128) != 0
9445 	    || p->fp->write(p->fp, buf, 1, len) != len) {
9446 		sprintf(p->err,"icc_write: fseek() or fwrite() failed");
9447 		p->al->free(p->al, buf);
9448 		return p->errc = 1;
9449 	}
9450 	p->al->free(p->al, buf);
9451 
9452 	/* Write all the tag element data */
9453 	for (i = 0; i < p->count; i++) {	/* For all the tag element data */
9454 		if (p->data[i].objp->touched == 0)
9455 			continue;		/* Must be linked, and we've already written it */
9456 		if ((rv = p->data[i].objp->write(p->data[i].objp, of + p->data[i].offset)) != 0) {
9457 			return rv;
9458 		}
9459 		p->data[i].objp->touched = 0;	/* Written it */
9460 	}
9461 
9462 	if (p->fp->flush(p->fp) != 0) {
9463 		sprintf(p->err,"icc_write flush() failed");
9464 		p->al->free(p->al, buf);
9465 		return p->errc = 2;
9466 	}
9467 	return rv;
9468 }
9469 #undef DO_ALIGN
9470 
9471 /* Create and add a tag with the given signature. */
9472 /* Returns a pointer to the element object */
9473 /* Returns NULL if error - icc->errc will contain */
9474 /* 2 on system error, */
9475 /* 3 if unknown tag */
9476 /* NOTE: that we prevent tag duplication */
9477 static icmBase *icc_add_tag(
9478 	icc *p,
9479     icTagSignature sig,			/* Tag signature - may be unknown */
9480 	icTagTypeSignature  ttype	/* Tag type */
9481 ) {
9482 	icmBase *tp;
9483 	icmBase *nob;
9484 	int i, j, ok = 1;
9485 
9486 	/* Check that a known signature has an acceptable type */
9487 	for (i = 0; sigtypetable[i].sig != icMaxEnumType; i++) {
9488 		if (sigtypetable[i].sig == sig) {	/* recognized signature */
9489 			ok = 0;
9490 			for (j = 0; sigtypetable[i].ttypes[j] != icMaxEnumType; j++) {
9491 				if (sigtypetable[i].ttypes[j] == ttype)	/* recognized type */
9492 					ok = 1;
9493 			}
9494 			break;
9495 		}
9496 	}
9497 	if (!ok) {
9498 		sprintf(p->err,"icc_add_tag: wrong tag type for signature");
9499 		p->errc = 1;
9500 		return NULL;
9501 	}
9502 
9503 	/* Check that we know how to handle this type */
9504 	for (i = 0; typetable[i].ttype != icMaxEnumType; i++) {
9505 		if (typetable[i].ttype == ttype)
9506 			break;
9507 	}
9508 	if (typetable[i].ttype == icMaxEnumType) {
9509 		sprintf(p->err,"icc_add_tag: unsupported tag type");
9510 		p->errc = 1;
9511 		return NULL;
9512 	}
9513 
9514 	/* Check that this tag doesn't already exits */
9515 	/* (Perhaps we should simply replace it, rather than erroring ?) */
9516 	for (j = 0; j < p->count; j++) {
9517 		if (p->data[j].sig == sig) {
9518 			sprintf(p->err,"icc_add_tag: Already have tag '%s' in profile",tag2str(p->data[j].sig));
9519 			p->errc = 1;
9520 			return NULL;
9521 		}
9522 	}
9523 
9524 	/* Make space in tag table for new tag item */
9525 	if (p->data == NULL)
9526 		tp = p->al->malloc(p->al, (p->count+1) * sizeof(icmTag));
9527 	else
9528 		tp = p->al->realloc(p->al, (void *)p->data, (p->count+1) * sizeof(icmTag));
9529 	if (tp == NULL) {
9530 		sprintf(p->err,"icc_add_tag: Tag table realloc() failed");
9531 		p->errc = 2;
9532 		return NULL;
9533 	}
9534 	p->data = (icmTag *)tp;
9535 
9536 	/* Allocate the empty object */
9537 	if ((nob = typetable[i].new_obj(p)) == NULL)
9538 		return NULL;
9539 
9540 	/* Fill out our tag table entry */
9541     p->data[p->count].sig = sig;		/* The tag signature */
9542 	p->data[p->count].ttype = nob->ttype = ttype;	/* The tag type signature */
9543     p->data[p->count].offset = 0;		/* Unknown offset yet */
9544     p->data[p->count].size = 0;			/* Unknown size yet */
9545     p->data[p->count].objp = nob;		/* Empty object */
9546 	p->count++;
9547 
9548 	return nob;
9549 }
9550 
9551 /* Create and add a tag which is a link to an existing tag. */
9552 /* Returns a pointer to the element object */
9553 /* Returns NULL if error - icc->errc will contain */
9554 /* 3 if incompatible tag */
9555 /* NOTE: that we prevent tag duplication */
9556 static icmBase *icc_link_tag(
9557 	icc *p,
9558     icTagSignature sig,			/* Tag signature - may be unknown */
9559     icTagSignature ex_sig		/* Tag signature of tag to link to */
9560 ) {
9561 	icmBase *tp;
9562 	int i, j, exi, ok = 1;
9563 
9564 	/* Search for existing signature */
9565 	for (exi = 0; exi < p->count; exi++) {
9566 		if (p->data[exi].sig == ex_sig)		/* Found it */
9567 			break;
9568 	}
9569 	if (exi == p->count) {
9570 		sprintf(p->err,"icc_link_tag: Can't find existing tag '%s'",tag2str(ex_sig));
9571 		p->errc = 1;
9572 		return NULL;
9573 	}
9574 
9575     if (p->data[exi].objp == NULL) {
9576 		sprintf(p->err,"icc_link_tag: Existing tag '%s' isn't loaded",tag2str(ex_sig));
9577 		p->errc = 1;
9578 		return NULL;
9579 	}
9580 
9581 	/* Check that a known signature has an acceptable type */
9582 	for (i = 0; sigtypetable[i].sig != icMaxEnumType; i++) {
9583 		if (sigtypetable[i].sig == sig) {	/* recognized signature */
9584 			ok = 0;
9585 			for (j = 0; sigtypetable[i].ttypes[j] != icMaxEnumType; j++) {
9586 				if (sigtypetable[i].ttypes[j] == p->data[exi].ttype)	/* recognized type */
9587 					ok = 1;
9588 			}
9589 			break;
9590 		}
9591 	}
9592 	if (!ok) {
9593 		sprintf(p->err,"icc_link_tag: wrong tag type for signature");
9594 		p->errc = 1;
9595 		return NULL;
9596 	}
9597 
9598 	/* Check that this tag doesn't already exits */
9599 	for (j = 0; j < p->count; j++) {
9600 		if (p->data[j].sig == sig) {
9601 			sprintf(p->err,"icc_link_tag: Already have tag '%s' in profile",tag2str(p->data[j].sig));
9602 			p->errc = 1;
9603 			return NULL;
9604 		}
9605 	}
9606 
9607 	/* Make space in tag table for new tag item */
9608 	if (p->data == NULL)
9609 		tp = p->al->malloc(p->al, (p->count+1) * sizeof(icmTag));
9610 	else
9611 		tp = p->al->realloc(p->al, (void *)p->data, (p->count+1) * sizeof(icmTag));
9612 	if (tp == NULL) {
9613 		sprintf(p->err,"icc_link_tag: Tag table realloc() failed");
9614 		p->errc = 2;
9615 		return NULL;
9616 	}
9617 	p->data = (icmTag *)tp;
9618 
9619 	/* Fill out our tag table entry */
9620     p->data[p->count].sig  = sig;		/* The tag signature */
9621 	p->data[p->count].ttype  = p->data[exi].ttype;	/* The tag type signature */
9622     p->data[p->count].offset = p->data[exi].offset;	/* Same offset (may not be allocated yet) */
9623     p->data[p->count].size = p->data[exi].size;		/* Same size (may not be allocated yet) */
9624     p->data[p->count].objp = p->data[exi].objp;		/* Shared object */
9625 	p->data[exi].objp->refcount++;					/* Bump reference count on tag type */
9626 	p->count++;
9627 
9628 	return p->data[exi].objp;
9629 }
9630 
9631 /* Search for tag signature */
9632 /* return: */
9633 /* 0 if found */
9634 /* 1 if found but not handled type */
9635 /* 2 if not found */
9636 /* NOTE: doesn't set icc->errc or icc->err[] */
9637 /* NOTE: we don't handle tag duplication - you'll always get the first in the file. */
9638 static int icc_find_tag(
9639 	icc *p,
9640     icTagSignature sig			/* Tag signature - may be unknown */
9641 ) {
9642 	int i,j;
9643 
9644 	/* Search for signature */
9645 	for (i = 0; i < p->count; i++) {
9646 		if (p->data[i].sig == sig)		/* Found it */
9647 			break;
9648 	}
9649 	if (i == p->count)
9650 		return 2;
9651 
9652 	/* See if we can handle this type */
9653 	for (j = 0; typetable[j].ttype != icMaxEnumType; j++) {
9654 		if (typetable[j].ttype == p->data[i].ttype)
9655 			break;
9656 	}
9657 	if (typetable[j].ttype == icMaxEnumType)
9658 		return 1;
9659 
9660 	return 0;
9661 }
9662 
9663 /* Read the tag element data, and return a pointer to the object */
9664 /**
9665  * Returns NULL if error - icc->errc will contain:
9666  * 1 if found but not handled type
9667  * 2 if not found
9668  **/
9669 /* NOTE: we don't handle tag duplication - you'll always get the first in the file */
9670 static icmBase *icc_read_tag(
9671 	icc *p,
9672     icTagSignature sig			/* Tag signature - may be unknown */
9673 ) {
9674 	icmBase *nob;
9675 	int i,j,k;
9676 
9677 	/* Search for signature */
9678 	for (i = 0; i < p->count; i++) {
9679 		if (p->data[i].sig == sig)		/* Found it */
9680 			break;
9681 	}
9682 	if (i >= p->count) {
9683 		sprintf(p->err,"icc_read_tag: Tag '%s' not found",string_TagSignature(sig));
9684 		p->errc = 2;
9685 		return NULL;
9686 	}
9687 
9688 	/* See if it's already been read */
9689     if (p->data[i].objp != NULL) {
9690 		return p->data[i].objp;		/* Just return it */
9691 	}
9692 
9693 	/* See if this should be a link */
9694 	for (k = 0; k < p->count; k++) {
9695 		if (i == k)
9696 			continue;
9697 	    if (p->data[i].ttype  == p->data[k].ttype	/* Exact match and already read */
9698 	     && p->data[i].offset == p->data[k].offset
9699 	     && p->data[i].size   == p->data[k].size
9700 	     && p->data[k].objp != NULL)
9701 			break;
9702 	}
9703 	if (k < p->count) {		/* Make this a link */
9704 		p->data[i].objp = p->data[k].objp;
9705 		p->data[k].objp->refcount++;	/* Bump reference count */
9706 		return p->data[k].objp;			/* Done */
9707 	}
9708 
9709 	/* See if we can handle this type */
9710 	for (j = 0; typetable[j].ttype != icMaxEnumType; j++) {
9711 		if (typetable[j].ttype == p->data[i].ttype)
9712 			break;
9713 	}
9714 	if (typetable[j].ttype == icMaxEnumType) {
9715 		sprintf(p->err,"icc_read_tag: Unhandled tag type '%s'",string_TypeSignature(p->data[i].ttype));
9716 		p->errc = 1;
9717 		return NULL;
9718 	}
9719 
9720 	/* Creat and read in the object */
9721 	if ((nob = typetable[j].new_obj(p)) == NULL)
9722 		return NULL;
9723 	if ((nob->read(nob, p->data[i].size, p->of + p->data[i].offset)) != 0) {
9724 		nob->del(nob);		/* Failed, so destroy it */
9725 		return NULL;
9726 	}
9727     p->data[i].objp = nob;
9728 	return nob;
9729 }
9730 
9731 /* Rename a tag signature */
9732 static int icc_rename_tag(
9733 	icc *p,
9734     icTagSignature sig,			/* Existing Tag signature - may be unknown */
9735     icTagSignature sigNew		/* New Tag signature - may be unknown */
9736 ) {
9737 	int i, j, k, ok = 1;
9738 
9739 	/* Search for signature */
9740 	for (k = 0; k < p->count; k++) {
9741 		if (p->data[k].sig == sig)		/* Found it */
9742 			break;
9743 	}
9744 	if (k >= p->count) {
9745 		sprintf(p->err,"icc_rename_tag: Tag '%s' not found",string_TagSignature(sig));
9746 		return p->errc = 2;
9747 	}
9748 
9749 	/* Check that a known new signature has an acceptable type */
9750 	for (i = 0; sigtypetable[i].sig != icMaxEnumType; i++) {
9751 		if (sigtypetable[i].sig == sigNew) {	/* recognized signature */
9752 			ok = 0;
9753 			for (j = 0; sigtypetable[i].ttypes[j] != icMaxEnumType; j++) {
9754 				if (sigtypetable[i].ttypes[j] == p->data[k].ttype)	/* recognized type */
9755 					ok = 1;
9756 			}
9757 			break;
9758 		}
9759 	}
9760 
9761 	if (!ok) {
9762 		sprintf(p->err,"icc_rename_tag: wrong signature for tag type");
9763 		p->errc = 1;
9764 		return p->errc;
9765 	}
9766 
9767 	/* change its signature */
9768 	p->data[k].sig = sigNew;
9769 
9770 	return 0;
9771 }
9772 
9773 /* Unread the tag, and free the underlying tag type */
9774 /* if this was the last reference to it. */
9775 /* Returns non-zero on error: */
9776 /* tag not found - icc->errc will contain 2 */
9777 /* tag not read - icc->errc will contain 2 */
9778 static int icc_unread_tag(
9779 	icc *p,
9780     icTagSignature sig		/* Tag signature - may be unknown */
9781 ) {
9782 	int i;
9783 
9784 	/* Search for signature */
9785 	for (i = 0; i < p->count; i++) {
9786 		if (p->data[i].sig == sig)		/* Found it */
9787 			break;
9788 	}
9789 	if (i >= p->count) {
9790 		sprintf(p->err,"icc_unread_tag: Tag '%s' not found",string_TagSignature(sig));
9791 		return p->errc = 2;
9792 	}
9793 
9794 	/* See if it's been read */
9795     if (p->data[i].objp == NULL) {
9796 		sprintf(p->err,"icc_unread_tag: Tag '%s' not currently loaded",string_TagSignature(sig));
9797 		return p->errc = 2;
9798 	}
9799 
9800 	if (--(p->data[i].objp->refcount) == 0)			/* decrement reference count */
9801 			(p->data[i].objp->del)(p->data[i].objp);	/* Last reference */
9802   	p->data[i].objp = NULL;
9803 
9804 	return 0;
9805 }
9806 
9807 /* Delete the tag, and free the underlying tag type */
9808 /* if this was the last reference to it. */
9809 /* Returns non-zero on error: */
9810 /* tag not found - icc->errc will contain 2 */
9811 static int icc_delete_tag(
9812 	icc *p,
9813     icTagSignature sig		/* Tag signature - may be unknown */
9814 ) {
9815 	int i;
9816 
9817 	/* Search for signature */
9818 	for (i = 0; i < p->count; i++) {
9819 		if (p->data[i].sig == sig)		/* Found it */
9820 			break;
9821 	}
9822 	if (i >= p->count) {
9823 		sprintf(p->err,"icc_delete_tag: Tag '%s' not found",string_TagSignature(sig));
9824 		return p->errc = 2;
9825 	}
9826 
9827 	/* If it's been read into memory, decrement the reference count */
9828     if (p->data[i].objp != NULL) {
9829 		if (--(p->data[i].objp->refcount) == 0)			/* decrement reference count */
9830 			(p->data[i].objp->del)(p->data[i].objp);	/* Last reference */
9831   		p->data[i].objp = NULL;
9832 	}
9833 
9834 	/* Now remove it from the tag list */
9835 	for (; i < (p->count-1); i++)
9836 		p->data[i] = p->data[i+1];	/* Copy the structure down */
9837 
9838 	p->count--;		/* One less tag in list */
9839 
9840 	return 0;
9841 }
9842 
9843 
9844 /* Read all the tags into memory. */
9845 /* Returns non-zero on error. */
9846 static int icc_read_all_tags(
9847 	icc *p
9848 ) {
9849 	int i;
9850 
9851 	for (i = 0; i < p->count; i++) {	/* For all the tag element data */
9852 		icmBase *ob;
9853 		if ((ob = p->read_tag(p, p->data[i].sig)) == NULL) {
9854 			return p->errc;
9855 		}
9856 	}
9857 	return 0;
9858 }
9859 
9860 
9861 static void icc_dump(
9862 	icc *p,
9863 	FILE *op,		/* Output to dump to */
9864 	int   verb		/* Verbosity level */
9865 ) {
9866 	int i;
9867 	if (verb <= 0)
9868 		return;
9869 
9870 	fprintf(op,"icc:\n");
9871 
9872 	/* Dump the header */
9873 	if (p->header != NULL)
9874 		p->header->dump(p->header,op,verb);
9875 
9876 	/* Dump all the tag elements */
9877 	for (i = 0; i < p->count; i++) {	/* For all the tag element data */
9878 		icmBase *ob;
9879 		int tr;
9880 		fprintf(op,"tag %d:\n",i);
9881 		fprintf(op,"  sig      %s\n",tag2str(p->data[i].sig));
9882 		fprintf(op,"  type     %s\n",tag2str(p->data[i].ttype));
9883 		fprintf(op,"  offset   %d\n", p->data[i].offset);
9884 		fprintf(op,"  size     %d\n", p->data[i].size);
9885 		tr = 0;
9886 		if ((ob = p->data[i].objp) == NULL) {
9887 			/* The object is not loaded, so load it then free it */
9888 			if ((ob = p->read_tag(p, p->data[i].sig)) == NULL) {
9889 				fprintf(op,"Unable to read: %d, %s\n",p->errc,p->err);
9890 			}
9891 			tr = 1;
9892 		}
9893 		if (ob != NULL) {
9894 			/* fprintf(op,"  refcount %d\n", ob->refcount); */
9895 			ob->dump(ob,op,verb-1);
9896 
9897 			if (tr != 0) {		/* Cleanup if temporary */
9898 				ob->refcount--;
9899 				(ob->del)(ob);
9900 				p->data[i].objp = NULL;
9901 			}
9902 		}
9903 		fprintf(op,"\n");
9904 	}
9905 }
9906 
9907 static void icc_delete(
9908 	icc *p
9909 ) {
9910 	int i;
9911 	icmAlloc *al = p->al;
9912 	int del_al   = p->del_al;
9913 
9914 	/* Free up the header */
9915 	if (p->header != NULL)
9916 		(p->header->del)(p->header);
9917 
9918 	/* Free up the tag data objects */
9919 	for (i = 0; i < p->count; i++) {
9920 		if (p->data[i].objp != NULL) {
9921 			if (--(p->data[i].objp->refcount) == 0)	/* decrement reference count */
9922 				(p->data[i].objp->del)(p->data[i].objp);	/* Last reference */
9923 	  	  	p->data[i].objp = NULL;
9924 		}
9925 	}
9926 
9927 	/* Free tag table */
9928 	al->free(al, p->data);
9929 
9930 	/* This object */
9931 	al->free(al, p);
9932 
9933 	if (del_al)			/* We are responsible for deleting allocator */
9934 		al->del(al);
9935 }
9936 
9937 /* ================================================== */
9938 /* Lut Color normalizing and de-normalizing functions */
9939 
9940 /* As a rule, I am representing Lut in memory as values in machine form as real */
9941 /* numbers in the range 0.0 - 1.0. For many color spaces (ie. RGB, Gray, */
9942 /* hsv, hls, cmyk and other device coords), this is entirely appropriate. */
9943 /* For the PCS though, this is not correct, since (I assume!) the binary */
9944 /* representation will be consistent with the encoding in Annex A, page 74 */
9945 /* of the standard. Note that the standard doesn't specify the encoding of */
9946 /* many color spaces (ie. Yuv, Yxy etc.), and is unclear about PCS. */
9947 
9948 /* The following functions convert to and from the PCS spaces (XYZ or Lab) */
9949 /* and the real Lut input/output values. These are used to convert real color */
9950 /* space values into/out of the raw lut 0.0-1.0 representation. */
9951 
9952 /* This is used internally to support the Lut->lookup() function, */
9953 /* and can also be used by someone writing a Lut based profile to determine */
9954 /* the colorspace range that the input lut indexes cover, as well */
9955 /* as processing the output luts values into normalized form ready */
9956 /* for writing. */
9957 
9958 /* These functions should be accessed by calling icc.getNormFuncs() */
9959 
9960 /* - - - - - - - - - - - - - - - - */
9961 /* According to 6.5.5 and 6.5.6 of the spec., */
9962 /* XYZ index values are represented the same as their table */
9963 /* values, ie. as a u1.15 representation, with a value */
9964 /* range from 0.0 ->  1.999969482422 */
9965 
9966 /* Convert Lut index/value to XYZ coord. */
9967 static void Lut_Lut2XYZ(double *out, double *in) {
9968 	out[0] = in[0] * (1.0 + 32767.0/32768); /* X */
9969 	out[1] = in[1] * (1.0 + 32767.0/32768); /* Y */
9970 	out[2] = in[2] * (1.0 + 32767.0/32768); /* Z */
9971 }
9972 
9973 /* Convert XYZ coord to Lut index/value. */
9974 static void Lut_XYZ2Lut(double *out, double *in) {
9975 	out[0] = in[0] * (1.0/(1.0 + 32767.0/32768));
9976 	out[1] = in[1] * (1.0/(1.0 + 32767.0/32768));
9977 	out[2] = in[2] * (1.0/(1.0 + 32767.0/32768));
9978 }
9979 
9980 /* - - - - - - - - - - - - - - - - */
9981 /* Convert Lab to Lut numbers */
9982 /* Annex A specifies 8 and 16 bit encoding, but is */
9983 /* silent on the Lut index normalization. */
9984 /* Following Michael Bourgoin's 1998 SIGGRAPH course comment on this, */
9985 /* we assume here that the index encoding is the same as the */
9986 /* value encoding. */
9987 
9988 /* Convert Lut16 table index/value to Lab */
9989 static void Lut_Lut2Lab16(double *out, double *in) {
9990 	out[0] = in[0] * (100.0 * 65535.0)/65280.0;			/* L */
9991 	out[1] = (in[1] * (255.0 * 65535.0)/65280) - 128.0;	/* a */
9992 	out[2] = (in[2] * (255.0 * 65535.0)/65280) - 128.0;	/* b */
9993 }
9994 
9995 /* Convert Lab to Lut16 table index/value */
9996 static void Lut_Lab2Lut16(double *out, double *in) {
9997 	out[0] = in[0] * 65280.0/(100.0 * 65535.0);				/* L */
9998 	out[1] = (in[1] + 128.0) * 65280.0/(255.0 * 65535.0);	/* a */
9999 	out[2] = (in[2] + 128.0) * 65280.0/(255.0 * 65535.0);	/* b */
10000 }
10001 
10002 /* Convert Lut8 table index/value to Lab */
10003 static void Lut_Lut2Lab8(double *out, double *in) {
10004 	out[0] = in[0] * 100.0;				/* L */
10005 	out[1] = (in[1] * 255.0) - 128.0;	/* a */
10006 	out[2] = (in[2] * 255.0) - 128.0;	/* b */
10007 }
10008 
10009 /* Convert Lab to Lut8 table index/value */
10010 static void Lut_Lab2Lut8(double *out, double *in) {
10011 	out[0] = in[0] * 1.0/100.0;				/* L */
10012 	out[1] = (in[1] + 128.0) * 1.0/255.0;	/* a */
10013 	out[2] = (in[2] + 128.0) * 1.0/255.0;	/* b */
10014 }
10015 
10016 /* - - - - - - - - - - - - - - - - */
10017 /* Convert Luv to Lut number */
10018 /* This data normalization is taken from Apples */
10019 /* Colorsync specification. */
10020 /* As per other color spaces, we assume that the index */
10021 /* normalization is the same as the data normalization. */
10022 
10023 /* Convert Lut table index/value to Luv */
10024 static void Lut_Lut2Luv(double *out, double *in) {
10025 	out[0] = in[0] * 100.0;						/* L */
10026 	out[1] = (in[1] * 65535.0/256.0) - 128.0;	/* u */
10027 	out[2] = (in[2] * 65535.0/256.0) - 128.0;	/* v */
10028 }
10029 
10030 /* Convert Luv to Lut table index/value */
10031 static void Lut_Luv2Lut(double *out, double *in) {
10032 	out[0] = in[0] * 1.0/100.0;					/* L */
10033 	out[1] = (in[1] + 128.0) * 256.0/65535.0;	/* u */
10034 	out[2] = (in[2] + 128.0) * 256.0/65535.0;	/* v */
10035 }
10036 
10037 /* - - - - - - - - - - - - - - - - */
10038 /* Default N component conversions */
10039 static void Lut_N(double *out, double *in, int nc) {
10040 	for (--nc; nc >= 0; nc--)
10041 		out[nc] = in[nc];
10042 }
10043 
10044 /* 1 */
10045 static void Lut_1(double *out, double *in) {
10046 	out[0] = in[0];
10047 }
10048 
10049 /* 2 */
10050 static void Lut_2(double *out, double *in) {
10051 	out[0] = in[0];
10052 	out[1] = in[1];
10053 }
10054 
10055 /* 3 */
10056 static void Lut_3(double *out, double *in) {
10057 	out[0] = in[0];
10058 	out[1] = in[1];
10059 	out[2] = in[2];
10060 }
10061 
10062 /* 4 */
10063 static void Lut_4(double *out, double *in) {
10064 	out[0] = in[0];
10065 	out[1] = in[1];
10066 	out[2] = in[2];
10067 	out[3] = in[3];
10068 }
10069 
10070 /* 5 */
10071 static void Lut_5(double *out, double *in) {
10072 	out[0] = in[0];
10073 	out[1] = in[1];
10074 	out[2] = in[2];
10075 	out[3] = in[3];
10076 	out[4] = in[4];
10077 }
10078 
10079 /* 6 */
10080 static void Lut_6(double *out, double *in) {
10081 	out[0] = in[0];
10082 	out[1] = in[1];
10083 	out[2] = in[2];
10084 	out[3] = in[3];
10085 	out[4] = in[4];
10086 	out[5] = in[5];
10087 }
10088 
10089 /* 7 */
10090 static void Lut_7(double *out, double *in) {
10091 	Lut_N(out, in, 7);
10092 }
10093 
10094 /* 8 */
10095 static void Lut_8(double *out, double *in) {
10096 	Lut_N(out, in, 8);
10097 }
10098 
10099 /* 9 */
10100 static void Lut_9(double *out, double *in) {
10101 	Lut_N(out, in, 9);
10102 }
10103 
10104 /* 10 */
10105 static void Lut_10(double *out, double *in) {
10106 	Lut_N(out, in, 10);
10107 }
10108 
10109 /* 11 */
10110 static void Lut_11(double *out, double *in) {
10111 	Lut_N(out, in, 11);
10112 }
10113 
10114 /* 12 */
10115 static void Lut_12(double *out, double *in) {
10116 	Lut_N(out, in, 12);
10117 }
10118 
10119 /* 13 */
10120 static void Lut_13(double *out, double *in) {
10121 	Lut_N(out, in, 13);
10122 }
10123 
10124 /* 14 */
10125 static void Lut_14(double *out, double *in) {
10126 	Lut_N(out, in, 14);
10127 }
10128 
10129 /* 15 */
10130 static void Lut_15(double *out, double *in) {
10131 	Lut_N(out, in, 15);
10132 }
10133 
10134 /* Function table - match conversions to color spaces. */
10135 /* Anything not here, we don't know how to convert. */
10136 /* (ie. YCbCr) */
10137 static struct {
10138 	icColorSpaceSignature csig;
10139 	void (*fromLut8)(double *out, double *in);		/* 8  bit from Lut index/entry */
10140 	void (*fromLut16)(double *out, double *in);		/* 16 bit from Lut index/entry */
10141 	void (*toLut8)(double *out, double *in);		/* 8  bit to Lut index/entry */
10142 	void (*toLut16)(double *out, double *in);		/* 16 bit to Lut index/entry */
10143 } colnormtable[] = {
10144 	{icSigXYZData,     NULL,         Lut_Lut2XYZ,   NULL,         Lut_XYZ2Lut },
10145 	{icSigLabData,     Lut_Lut2Lab8, Lut_Lut2Lab16, Lut_Lab2Lut8, Lut_Lab2Lut16 },
10146 	{icSigLuvData,     Lut_Lut2Luv,  Lut_Lut2Luv,   Lut_Luv2Lut,  Lut_Luv2Lut },
10147 	{icSigYxyData,     Lut_3,        Lut_3,         Lut_3,        Lut_3 },
10148 	{icSigRgbData,     Lut_3,        Lut_3,         Lut_3,        Lut_3 },
10149 	{icSigGrayData,    Lut_1,        Lut_1,         Lut_1,        Lut_1 },
10150 	{icSigHsvData,     Lut_3,        Lut_3,         Lut_3,        Lut_3 },
10151 	{icSigHlsData,     Lut_3,        Lut_3,         Lut_3,        Lut_3 },
10152 	{icSigCmykData,    Lut_4,        Lut_4,         Lut_4,        Lut_4 },
10153 	{icSigCmyData,     Lut_3,        Lut_3,         Lut_3,        Lut_3 },
10154 	{icSigMch6Data,    Lut_6,        Lut_6,         Lut_6,        Lut_6 },
10155 	{icSig2colorData,  Lut_2,        Lut_2,         Lut_2,        Lut_2 },
10156 	{icSig3colorData,  Lut_3,        Lut_3,         Lut_3,        Lut_3 },
10157 	{icSig4colorData,  Lut_4,        Lut_4,         Lut_4,        Lut_4 },
10158 	{icSig5colorData,  Lut_5,        Lut_5,         Lut_5,        Lut_5 },
10159 	{icSig6colorData,  Lut_6,        Lut_6,         Lut_6,        Lut_6 },
10160 	{icSig7colorData,  Lut_7,        Lut_7,         Lut_7,        Lut_7 },
10161 	{icSig8colorData,  Lut_8,        Lut_8,         Lut_8,        Lut_8 },
10162 	{icSig9colorData,  Lut_9,        Lut_9,         Lut_9,        Lut_9 },
10163 	{icSig10colorData, Lut_10,       Lut_10,        Lut_10,       Lut_10 },
10164 	{icSig11colorData, Lut_11,       Lut_11,        Lut_11,       Lut_11 },
10165 	{icSig12colorData, Lut_12,       Lut_12,        Lut_12,       Lut_12 },
10166 	{icSig13colorData, Lut_13,       Lut_13,        Lut_13,       Lut_13 },
10167 	{icSig14colorData, Lut_14,       Lut_14,        Lut_14,       Lut_14 },
10168 	{icSig15colorData, Lut_15,       Lut_15,        Lut_15,       Lut_15 },
10169 	{icMaxEnumData,    NULL,         NULL,          NULL,         NULL   }
10170 };
10171 
10172 /* Find appropriate conversion functions */
10173 /* given the color space and Lut type */
10174 /* Return 0 on success, 1 on match failure */
10175 /* NOTE: doesn't set error value, message etc.! */
10176 static int getNormFunc(
10177 	icColorSpaceSignature csig,
10178 	icTagTypeSignature    tagSig,
10179 	icmNormFlag           flag,
10180 	void               (**nfunc)(double *out, double *in)
10181 ) {
10182 	int i;
10183 	for (i = 0; colnormtable[i].csig != icMaxEnumData; i++) {
10184 		if (colnormtable[i].csig == csig)
10185 			break;	/* Found it */
10186 	}
10187 	if (colnormtable[i].csig == icMaxEnumData) {	/* Oops */
10188 		*nfunc   = NULL;
10189 		return 1;
10190 	}
10191 
10192 	if (flag == icmFromLuti || flag == icmFromLutv) {	/* Table index/value decoding functions */
10193 		if (tagSig == icSigLut8Type) {
10194 			*nfunc = colnormtable[i].fromLut8;
10195 			return 0;
10196 		} else if (tagSig == icSigLut16Type) {
10197 			*nfunc = colnormtable[i].fromLut16;
10198 			return 0;
10199 		} else {
10200 			*nfunc   = NULL;
10201 			return 1;
10202 		}
10203 	} else if (flag == icmToLuti || flag == icmToLutv) {	/* Table index/value encoding functions */
10204 		if (tagSig == icSigLut8Type) {
10205 			*nfunc = colnormtable[i].toLut8;
10206 			return 0;
10207 		} else if (tagSig == icSigLut16Type) {
10208 			*nfunc = colnormtable[i].toLut16;
10209 			return 0;
10210 		} else {
10211 			*nfunc   = NULL;
10212 			return 1;
10213 		}
10214 	} else {
10215 		*nfunc   = NULL;
10216 		return 1;
10217 	}
10218 	return 0;
10219 }
10220 
10221 /* - - - - - - - - - - - - - - - - - - - - - - - - */
10222 /* Colorspace ranges - used instead of norm/denorm by Mono & Matrix */
10223 
10224 /* Function table - match ranges to color spaces. */
10225 /* Anything not here, we don't know how to convert. */
10226 /* (ie. YCbCr) */
10227 static struct {
10228 	icColorSpaceSignature csig;
10229 	int same;				/* Non zero if first entry applies to all channels */
10230 	double min[15];			/* Minimum value for this colorspace */
10231 	double max[15];			/* Maximum value for this colorspace */
10232 } colorrangetable[] = {
10233 	{icSigXYZData,     1, { 0.0 } , { 1.0 + 32767.0/32768.0 } },
10234 	{icSigLabData,     0, { 0.0, -128.0, -128.0 },
10235 	                      { 100.0 + 25500.0/65280.0, 127.0 + 255.0/256.0, 127.0 + 255.0/256.0 } },
10236 	{icSigLuvData,     0, { 0.0, -128.0, -128.0 },
10237 	                      { 100.0, 127.0 + 255.0/256.0, 127.0 + 255.0/256.0 } },
10238 	{icSigYxyData,     1, { 0.0 }, { 1.0 } },
10239 	{icSigRgbData,     1, { 0.0 }, { 1.0 } },
10240 	{icSigGrayData,    1, { 0.0 }, { 1.0 } },
10241 	{icSigHsvData,     1, { 0.0 }, { 1.0 } },
10242 	{icSigHlsData,     1, { 0.0 }, { 1.0 } },
10243 	{icSigCmykData,    1, { 0.0 }, { 1.0 } },
10244 	{icSigCmyData,     1, { 0.0 }, { 1.0 } },
10245 	{icSigMch6Data,    1, { 0.0 }, { 1.0 } },
10246 	{icSig2colorData,  1, { 0.0 }, { 1.0 } },
10247 	{icSig3colorData,  1, { 0.0 }, { 1.0 } },
10248 	{icSig4colorData,  1, { 0.0 }, { 1.0 } },
10249 	{icSig5colorData,  1, { 0.0 }, { 1.0 } },
10250 	{icSig6colorData,  1, { 0.0 }, { 1.0 } },
10251 	{icSig7colorData,  1, { 0.0 }, { 1.0 } },
10252 	{icSig8colorData,  1, { 0.0 }, { 1.0 } },
10253 	{icSig9colorData,  1, { 0.0 }, { 1.0 } },
10254 	{icSig10colorData, 1, { 0.0 }, { 1.0 } },
10255 	{icSig11colorData, 1, { 0.0 }, { 1.0 } },
10256 	{icSig12colorData, 1, { 0.0 }, { 1.0 } },
10257 	{icSig13colorData, 1, { 0.0 }, { 1.0 } },
10258 	{icSig14colorData, 1, { 0.0 }, { 1.0 } },
10259 	{icSig15colorData, 1, { 0.0 }, { 1.0 } },
10260 	{icMaxEnumData     }
10261 };
10262 
10263 /* Find appropriate typical encoding ranges for a */
10264 /* colorspace given the color space. */
10265 /* Return 0 on success, 1 on match failure */
10266 static int getRange(
10267 	icColorSpaceSignature csig,
10268 	double *min, double *max
10269 ) {
10270 	int i, e, ee;
10271 	for (i = 0; colorrangetable[i].csig != icMaxEnumData; i++) {
10272 		if (colorrangetable[i].csig == csig)
10273 			break;	/* Found it */
10274 	}
10275 	if (colorrangetable[i].csig == icMaxEnumData) {	/* Oops */
10276 		return 1;
10277 	}
10278 	ee = number_ColorSpaceSignature(csig);		/* Get number of components */
10279 
10280 	if (colorrangetable[i].same) {		/* All channels are the same */
10281 		for (e = 0; e < ee; e++) {
10282 			if (min != NULL)
10283 				min[e] = colorrangetable[i].min[0];
10284 			if (max != NULL)
10285 				max[e] = colorrangetable[i].max[0];
10286 		}
10287 	} else {
10288 		for (e = 0; e < ee; e++) {
10289 			if (min != NULL)
10290 				min[e] = colorrangetable[i].min[e];
10291 			if (max != NULL)
10292 				max[e] = colorrangetable[i].max[e];
10293 		}
10294 	}
10295 	return 0;
10296 }
10297 
10298 /* - - - - - - - - - - - - - - - - - - - - - - - - */
10299 
10300 /*
10301 	Matrix Inversion
10302 	by Richard Carling
10303 	from "Graphics Gems", Academic Press, 1990
10304 */
10305 
10306 /*
10307  *   adjoint( original_matrix, inverse_matrix )
10308  *
10309  *     calculate the adjoint of a 3x3 matrix
10310  *
10311  *      Let  a   denote the minor determinant of matrix A obtained by
10312  *           ij
10313  *
10314  *      deleting the ith row and jth column from A.
10315  *
10316  *                    i+j
10317  *     Let  b   = (-1)    a
10318  *          ij            ji
10319  *
10320  *    The matrix B = (b  ) is the adjoint of A
10321  *                     ij
10322  */
10323 
10324 #define det2x2(a, b, c, d) (a * d - b * c)
10325 
10326 static void adjoint(
10327 double out[3][3],
10328 double in[3][3]
10329 ) {
10330     double a1, a2, a3, b1, b2, b3, c1, c2, c3;
10331 
10332     /* assign to individual variable names to aid  */
10333     /* selecting correct values  */
10334 
10335 	a1 = in[0][0]; b1 = in[0][1]; c1 = in[0][2];
10336 	a2 = in[1][0]; b2 = in[1][1]; c2 = in[1][2];
10337 	a3 = in[2][0]; b3 = in[2][1]; c3 = in[2][2];
10338 
10339     /* row column labeling reversed since we transpose rows & columns */
10340 
10341     out[0][0]  =   det2x2(b2, b3, c2, c3);
10342     out[1][0]  = - det2x2(a2, a3, c2, c3);
10343     out[2][0]  =   det2x2(a2, a3, b2, b3);
10344 
10345     out[0][1]  = - det2x2(b1, b3, c1, c3);
10346     out[1][1]  =   det2x2(a1, a3, c1, c3);
10347     out[2][1]  = - det2x2(a1, a3, b1, b3);
10348 
10349     out[0][2]  =   det2x2(b1, b2, c1, c2);
10350     out[1][2]  = - det2x2(a1, a2, c1, c2);
10351     out[2][2]  =   det2x2(a1, a2, b1, b2);
10352 }
10353 
10354 /*
10355  * double = det3x3(  a1, a2, a3, b1, b2, b3, c1, c2, c3 )
10356  *
10357  * calculate the determinant of a 3x3 matrix
10358  * in the form
10359  *
10360  *     | a1,  b1,  c1 |
10361  *     | a2,  b2,  c2 |
10362  *     | a3,  b3,  c3 |
10363  */
10364 
10365 static double det3x3(double in[3][3]) {
10366     double a1, a2, a3, b1, b2, b3, c1, c2, c3;
10367     double ans;
10368 
10369 	a1 = in[0][0]; b1 = in[0][1]; c1 = in[0][2];
10370 	a2 = in[1][0]; b2 = in[1][1]; c2 = in[1][2];
10371 	a3 = in[2][0]; b3 = in[2][1]; c3 = in[2][2];
10372 
10373     ans = a1 * det2x2(b2, b3, c2, c3)
10374         - b1 * det2x2(a2, a3, c2, c3)
10375         + c1 * det2x2(a2, a3, b2, b3);
10376     return ans;
10377 }
10378 
10379 #define SMALL_NUMBER	1.e-8
10380 /*
10381  *   inverse( original_matrix, inverse_matrix )
10382  *
10383  *    calculate the inverse of a 4x4 matrix
10384  *
10385  *     -1
10386  *     A  = ___1__ adjoint A
10387  *         det A
10388  */
10389 
10390 /* Return non-zero if not invertable */
10391 static int inverse3x3(
10392 double out[3][3],
10393 double in[3][3]
10394 ) {
10395     int i, j;
10396     double det;
10397 
10398     /*  calculate the 3x3 determinant
10399      *  if the determinant is zero,
10400      *  then the inverse matrix is not unique.
10401      */
10402     det = det3x3(in);
10403 
10404     if ( fabs(det) < SMALL_NUMBER)
10405         return 1;
10406 
10407     /* calculate the adjoint matrix */
10408     adjoint(out, in);
10409 
10410     /* scale the adjoint matrix to get the inverse */
10411     for (i = 0; i < 3; i++)
10412         for(j = 0; j < 3; j++)
10413 		    out[i][j] /= det;
10414 	return 0;
10415 }
10416 
10417 /* - - - - - - - - - - - - - - - - - - - - - - - - */
10418 
10419 /* Multuply XYZ array by 3x3 transform matrix */
10420 static void icmMulBy3x3(double out[3], double mat[3][3], double in[3]) {
10421 	double tt[3];
10422 
10423 	tt[0] = mat[0][0] * in[0] + mat[0][1] * in[1] + mat[0][2] * in[2];
10424 	tt[1] = mat[1][0] * in[0] + mat[1][1] * in[1] + mat[1][2] * in[2];
10425 	tt[2] = mat[2][0] * in[0] + mat[2][1] * in[1] + mat[2][2] * in[2];
10426 
10427 	out[0] = tt[0];
10428 	out[1] = tt[1];
10429 	out[2] = tt[2];
10430 }
10431 
10432 /* - - - - - - - - - - - - - - - - - - - - - - - - */
10433 /* CIE XYZ to perceptual Lab */
10434 void
10435 icmXYZ2Lab(icmXYZNumber *w, double *out, double *in) {
10436 	double X = in[0], Y = in[1], Z = in[2];
10437 	double x,y,z,fx,fy,fz;
10438 	double L;
10439 
10440 	x = X/w->X;
10441 	if (x > 0.008856451586)
10442 		fx = pow(x,1.0/3.0);
10443 	else
10444 		fx = 7.787036979 * x + 16.0/116.0;
10445 
10446 	y = Y/w->Y;
10447 	if (y > 0.008856451586) {
10448 		fy = pow(y,1.0/3.0);
10449 		L = 116.0 * fy - 16.0;
10450 	} else {
10451 		fy = 7.787036979 * y + 16.0/116.0;
10452 		L = 903.2963058 * y;
10453 	}
10454 
10455 	z = Z/w->Z;
10456 	if (z > 0.008856451586)
10457 		fz = pow(z,1.0/3.0);
10458 	else
10459 		fz = 7.787036979 * z + 16.0/116.0;
10460 
10461 	out[0] = L;
10462 	out[1] = 500.0 * (fx - fy);
10463 	out[2] = 200.0 * (fy - fz);
10464 }
10465 
10466 /* Perceptual Lab to CIE XYZ */
10467 void
10468 icmLab2XYZ(icmXYZNumber *w, double *out, double *in) {
10469 	double L = in[0], a = in[1], b = in[2];
10470 	double x,y,z,fx,fy,fz;
10471 
10472 	if (L > 8.0) {
10473 		fy = (L + 16.0)/116.0;
10474 		y = pow(fy,3.0);
10475 	} else {
10476 		y = L/903.2963058;
10477 		fy = 7.787036979 * y + 16.0/116.0;
10478 	}
10479 
10480 	fx = a/500.0 + fy;
10481 	if (fx > 24.0/116.0)
10482 		x = pow(fx,3.0);
10483 	else
10484 		x = (fx - 16.0/116.0)/7.787036979;
10485 
10486 	fz = fy - b/200.0;
10487 	if (fz > 24.0/116.0)
10488 		z = pow(fz,3.0);
10489 	else
10490 		z = (fz - 16.0/116.0)/7.787036979;
10491 
10492 	out[0] = x * w->X;
10493 	out[1] = y * w->Y;
10494 	out[2] = z * w->Z;
10495 }
10496 
10497 /* available D50 Illuminant */
10498 icmXYZNumber icmD50 = { 		/* Profile illuminant - D50 */
10499     0.9642, 1.0000, 0.8249
10500 };
10501 
10502 /* available D65 Illuminant */
10503 icmXYZNumber icmD65 = { 		/* Profile illuminant - D65 */
10504 	0.9505, 1.0000, 1.0890
10505 };
10506 
10507 /* Default black point */
10508 icmXYZNumber icmBlack = {
10509     0.0000, 0.0000, 0.0000
10510 };
10511 
10512 /* Return the normal Delta E given two Lab values */
10513 double icmLabDE(double *Lab1, double *Lab2) {
10514 	double rv = 0.0, tt;
10515 
10516 	tt = Lab1[0] - Lab2[0];
10517 	rv += tt * tt;
10518 	tt = Lab1[1] - Lab2[1];
10519 	rv += tt * tt;
10520 	tt = Lab1[2] - Lab2[2];
10521 	rv += tt * tt;
10522 
10523 	return sqrt(rv);
10524 }
10525 
10526 /* Return the normal Delta E squared, given two Lab values */
10527 double icmLabDEsq(double *Lab1, double *Lab2) {
10528 	double rv = 0.0, tt;
10529 
10530 	tt = Lab1[0] - Lab2[0];
10531 	rv += tt * tt;
10532 	tt = Lab1[1] - Lab2[1];
10533 	rv += tt * tt;
10534 	tt = Lab1[2] - Lab2[2];
10535 	rv += tt * tt;
10536 
10537 	return rv;
10538 }
10539 
10540 /* Return the CIE94 Delta E color difference measure */
10541 double icmCIE94(double Lab1[3], double Lab2[3]) {
10542 	double desq, dhsq;
10543 	double dlsq, dcsq;
10544 	double c12;
10545 
10546 	{
10547 		double dl, da, db;
10548 		dl = Lab1[0] - Lab2[0];
10549 		dlsq = dl * dl;		/* dl squared */
10550 		da = Lab1[1] - Lab2[1];
10551 		db = Lab1[2] - Lab2[2];
10552 
10553 		/* Compute normal Lab delta E squared */
10554 		desq = dlsq + da * da + db * db;
10555 	}
10556 
10557 	{
10558 		double c1, c2, dc;
10559 
10560 		/* Compute chromanance for the two colors */
10561 		c1 = sqrt(Lab1[1] * Lab1[1] + Lab1[2] * Lab1[2]);
10562 		c2 = sqrt(Lab2[1] * Lab2[1] + Lab2[2] * Lab2[2]);
10563 		c12 = sqrt(c1 * c2);	/* Symetric chromanance */
10564 
10565 		/* delta chromanance squared */
10566 		dc = c2 - c1;
10567 		dcsq = dc * dc;
10568 	}
10569 
10570 	/* Compute delta hue squared */
10571 	if ((dhsq = desq - dlsq - dcsq) < 0.0)
10572 		dhsq = 0.0;
10573 
10574 	{
10575 		double sc, sh;
10576 
10577 		/* Weighting factors for delta chromanance & delta hue */
10578 		sc = 1.0 + 0.048 * c12;
10579 		sh = 1.0 + 0.014 * c12;
10580 
10581 		return sqrt(dlsq + dcsq/(sc * sc) + dhsq/(sh * sh));
10582 	}
10583 }
10584 
10585 /* Return the CIE94 Delta E color difference measure, squared */
10586 double icmCIE94sq(double Lab1[3], double Lab2[3]) {
10587 	double desq, dhsq;
10588 	double dlsq, dcsq;
10589 	double c12;
10590 
10591 	{
10592 		double dl, da, db;
10593 		dl = Lab1[0] - Lab2[0];
10594 		dlsq = dl * dl;		/* dl squared */
10595 		da = Lab1[1] - Lab2[1];
10596 		db = Lab1[2] - Lab2[2];
10597 
10598 		/* Compute normal Lab delta E squared */
10599 		desq = dlsq + da * da + db * db;
10600 	}
10601 
10602 	{
10603 		double c1, c2, dc;
10604 
10605 		/* Compute chromanance for the two colors */
10606 		c1 = sqrt(Lab1[1] * Lab1[1] + Lab1[2] * Lab1[2]);
10607 		c2 = sqrt(Lab2[1] * Lab2[1] + Lab2[2] * Lab2[2]);
10608 		c12 = sqrt(c1 * c2);	/* Symetric chromanance */
10609 
10610 		/* delta chromanance squared */
10611 		dc = c2 - c1;
10612 		dcsq = dc * dc;
10613 	}
10614 
10615 	/* Compute delta hue squared */
10616 	if ((dhsq = desq - dlsq - dcsq) < 0.0)
10617 		dhsq = 0.0;
10618 
10619 	{
10620 		double sc, sh;
10621 
10622 		/* Weighting factors for delta chromanance & delta hue */
10623 		sc = 1.0 + 0.048 * c12;
10624 		sh = 1.0 + 0.014 * c12;
10625 
10626 		return dlsq + dcsq/(sc * sc) + dhsq/(sh * sh);
10627 	}
10628 }
10629 
10630 /* - - - - - - - - - - - - - - - - - - - - - - - - */
10631 
10632 /* Multiply one 3x3 with another */
10633 static void mul3x3(double dst[3][3], double src[3][3]) {
10634 	int i, j, k;
10635 	double td[3][3];		/* Temporary dest */
10636 
10637 	for (j = 0; j < 3; j++) {
10638 		for (i = 0; i < 3; i++) {
10639 			double tt = 0.0;
10640 			for (k = 0; k < 3; k++)
10641 				tt += src[j][k] * dst[k][i];
10642 			td[j][i] = tt;
10643 		}
10644 	}
10645 
10646 	/* Copy result out */
10647 	for (j = 0; j < 3; j++)
10648 		for (i = 0; i < 3; i++)
10649 			dst[j][i] = td[j][i];
10650 }
10651 
10652 /* Chrmatic Adaption transform utility */
10653 /* Return a 3x3 chromatic adaption matrix */
10654 void icmChromAdaptMatrix(
10655 	int flags,
10656 	icmXYZNumber d_wp,		/* Destination white point */
10657 	icmXYZNumber s_wp,		/* Source white point */
10658 	double mat[3][3]		/* Destination matrix */
10659 ) {
10660 	double dst[3], src[3];			/* Source & destination white points */
10661 	double vkmat[3][3];				/* Von Kries matrix */
10662 	double bradford[3][3] = {		/* Bradford cone space matrix */
10663 		{  0.8951,  0.2664, -0.1614 },
10664 		{ -0.7502,  1.7135,  0.0367 },
10665 		{  0.0389, -0.0685,  1.0296 }
10666 	};
10667 	double ibradford[3][3];			/* Inverse Bradford */
10668 
10669 	/* Set initial matrix to unity */
10670 	if (!(flags & ICM_CAM_MULMATRIX)) {
10671 		mat[0][0] = mat[1][1] = mat[2][2] = 1.0;
10672 		mat[0][1] = mat[0][2] = 0.0;
10673 		mat[1][0] = mat[1][2] = 0.0;
10674 		mat[2][0] = mat[2][1] = 0.0;
10675 	}
10676 
10677 	icmXYZ2Ary(src, s_wp);
10678 	icmXYZ2Ary(dst, d_wp);
10679 
10680 	if (flags & ICM_CAM_BRADFORD) {
10681 		icmMulBy3x3(src, bradford, src);
10682 		icmMulBy3x3(dst, bradford, dst);
10683 	}
10684 
10685 	/* Setup the Von Kries white point adaption matrix */
10686 	vkmat[0][0] = dst[0]/src[0];
10687 	vkmat[1][1] = dst[1]/src[1];
10688 	vkmat[2][2] = dst[2]/src[2];
10689 	vkmat[0][1] = vkmat[0][2] = 0.0;
10690 	vkmat[1][0] = vkmat[1][2] = 0.0;
10691 	vkmat[2][0] = vkmat[2][1] = 0.0;
10692 
10693 	/* Transform to Bradford space if requested */
10694 	if (flags & ICM_CAM_BRADFORD) {
10695 		mul3x3(mat, bradford);
10696 	}
10697 
10698 	/* Apply chromatic adaption */
10699 	mul3x3(mat, vkmat);
10700 
10701 	/* Transform from Bradford space */
10702 	if (flags & ICM_CAM_BRADFORD) {
10703 		inverse3x3(ibradford, bradford);
10704 		mul3x3(mat, ibradford);
10705 	}
10706 
10707 	/* We're done */
10708 }
10709 
10710 /* - - - - - - - - - - - - - - - - - - - - - - - - */
10711 
10712 /* Return information about the native lut in/out colorspaces. */
10713 /* Any pointer may be NULL if value is not to be returned */
10714 static void
10715 icmLutSpaces(
10716 	struct _icmLuBase *p,			/* This */
10717 	icColorSpaceSignature *ins,		/* Return Native input color space */
10718 	int *inn,						/* Return number of input components */
10719 	icColorSpaceSignature *outs,	/* Return Native output color space */
10720 	int *outn						/* Return number of output components */
10721 ) {
10722 	if (ins != NULL)
10723 		*ins = p->inSpace;
10724 	if (inn != NULL)
10725 		*inn = (int)number_ColorSpaceSignature(p->inSpace);
10726 
10727 	if (outs != NULL)
10728 		*outs = p->outSpace;
10729 	if (outn != NULL)
10730 		*outn = (int)number_ColorSpaceSignature(p->outSpace);
10731 }
10732 
10733 /* Return information about the effective lookup in/out colorspaces, */
10734 /* including allowance for PCS overide. */
10735 /* Any pointer may be NULL if value is not to be returned */
10736 static void
10737 icmLuSpaces(
10738 	struct _icmLuBase *p,			/* This */
10739 	icColorSpaceSignature *ins,		/* Return effective input color space */
10740 	int *inn,						/* Return number of input components */
10741 	icColorSpaceSignature *outs,	/* Return effective output color space */
10742 	int *outn,						/* Return number of output components */
10743 	icmLuAlgType *alg,				/* Return type of lookup algorithm used */
10744     icRenderingIntent *intt,		/* Return the intent being implented */
10745     icmLookupFunc *fnc,				/* Return the profile function being implemented */
10746 	icColorSpaceSignature *pcs		/* Return the profile effective PCS */
10747 ) {
10748 	if (ins != NULL)
10749 		*ins = p->e_inSpace;
10750 	if (inn != NULL)
10751 		*inn = (int)number_ColorSpaceSignature(p->e_inSpace);
10752 
10753 	if (outs != NULL)
10754 		*outs = p->e_outSpace;
10755 	if (outn != NULL)
10756 		*outn = (int)number_ColorSpaceSignature(p->e_outSpace);
10757 
10758 	if (alg != NULL)
10759 		*alg = p->ttype;
10760 
10761     if (intt != NULL)
10762 		*intt = p->intent;
10763 
10764 	if (fnc != NULL)
10765 		*fnc = p->function;
10766 
10767 	if (pcs != NULL)
10768 		*pcs = p->e_pcs;
10769 }
10770 
10771 /* Return the media white and black points in XYZ space. */
10772 /* Note that if not in the icc, the black point will be returned as 0, 0, 0 */
10773 /* Any pointer may be NULL if value is not to be returned */
10774 static void icmLuWh_bk_points(
10775 struct _icmLuBase *p,
10776 icmXYZNumber *wht,
10777 icmXYZNumber *blk
10778 ) {
10779 	if (wht != NULL)
10780 		*wht = p->whitePoint;	/* Structure copy */
10781 
10782 	if (blk != NULL)
10783 		*blk = p->blackPoint;	/* Structure copy */
10784 }
10785 
10786 /* Get the effective (externally visible) ranges for the Monochrome or Matrix profile */
10787 /* Arguments may be NULL */
10788 static void
10789 icmLu_get_ranges (
10790 	struct _icmLuBase *p,
10791 	double *inmin, double *inmax,		/* Return maximum range of inspace values */
10792 	double *outmin, double *outmax		/* Return maximum range of outspace values */
10793 ) {
10794 	/* Hmm. we have no way of handlin an error from getRange. */
10795 	/* It shouldn't ever return one unless there is a mismatch between */
10796 	/* getRange and Lu creation... */
10797 	getRange(p->e_inSpace, inmin, inmax);
10798 	getRange(p->e_outSpace, outmin, outmax);
10799 }
10800 
10801 /* - - - - - - - - - - - - - - - - - - - - - - - - */
10802 /* Forward and Backward Monochrome type conversion */
10803 /* Return 0 on success, 1 if clipping occured, 2 on other error */
10804 
10805 /* Individual components of Fwd conversion: */
10806 
10807 /* Actual device to linearised device */
10808 static int
10809 icmLuMonoFwd_curve (
10810 icmLuMono *p,		/* This */
10811 double *out,		/* Vector of output values */
10812 double *in			/* Vector of input values */
10813 ) {
10814 	icc *icp = p->icp;
10815 	int rv = 0;
10816 
10817 	/* Translate from device to PCS scale */
10818 	if ((rv |= p->grayCurve->lookup_fwd(p->grayCurve,&out[0],&in[0])) > 1) {
10819 		sprintf(icp->err,"icc_lookup: Curve->lookup_fwd() failed");
10820 		icp->errc = rv;
10821 		return 2;
10822 	}
10823 
10824 	return rv;
10825 }
10826 
10827 /* Linearised device to relative PCS */
10828 static int
10829 icmLuMonoFwd_map (
10830 icmLuMono *p,		/* This */
10831 double *out,		/* Vector of output values (native space) */
10832 double *in			/* Vector of input values (native space) */
10833 ) {
10834 	int rv = 0;
10835 	double Y = in[0];		/* In case out == in */
10836 
10837 	out[0] = p->pcswht.X;
10838 	out[1] = p->pcswht.Y;
10839 	out[2] = p->pcswht.Z;
10840 	if (p->pcs == icSigLabData)
10841 		icmXYZ2Lab(&p->pcswht, out, out);	/* in Lab */
10842 
10843 	/* Scale linearized device level to PCS white */
10844 	out[0] *= Y;
10845 	out[1] *= Y;
10846 	out[2] *= Y;
10847 
10848 	return rv;
10849 }
10850 
10851 /* relative PCS to absolute PCS (if required) */
10852 static int
10853 icmLuMonoFwd_abs (	/* Abs comes last in Fwd conversion */
10854 icmLuMono *p,		/* This */
10855 double *out,		/* Vector of output values in Effective PCS */
10856 double *in			/* Vector of input values in Native PCS */
10857 ) {
10858 	int rv = 0;
10859 
10860 	if (out != in) {
10861 		int i;
10862 		for (i = 0; i < 3; i++)		/* Don't alter input values */
10863 			out[i] = in[i];
10864 	}
10865 
10866 	/* Do absolute conversion */
10867 	if (p->intent == icAbsoluteColorimetric) {
10868 
10869 		if (p->pcs == icSigLabData) 	/* Convert L to Y */
10870 			icmLab2XYZ(&p->pcswht, out, out);
10871 
10872 		/* Convert from Relative to Absolute colorometric */
10873 		icmMulBy3x3(out, p->toAbs, out);
10874 
10875 		if (p->e_pcs == icSigLabData)
10876 			icmXYZ2Lab(&p->pcswht, out, out);
10877 
10878 	} else {
10879 
10880 		/* Convert from Native to Effective output space */
10881 		if (p->pcs == icSigLabData && p->e_pcs == icSigXYZData)
10882 			icmLab2XYZ(&p->pcswht, out, out);
10883 		else if (p->pcs == icSigXYZData && p->e_pcs == icSigLabData)
10884 			icmXYZ2Lab(&p->pcswht, out, out);
10885 	}
10886 
10887 	return rv;
10888 }
10889 
10890 
10891 /* Overall Fwd conversion routine */
10892 static int
10893 icmLuMonoFwd_lookup (
10894 icmLuBase *pp,		/* This */
10895 double *out,		/* Vector of output values */
10896 double *in			/* Input value */
10897 ) {
10898 	int rv = 0;
10899 	icmLuMono *p = (icmLuMono *)pp;
10900 	rv |= icmLuMonoFwd_curve(p, out, in);
10901 	rv |= icmLuMonoFwd_map(p, out, out);
10902 	rv |= icmLuMonoFwd_abs(p, out, out);
10903 	return rv;
10904 }
10905 
10906 
10907 /* Individual components of Bwd conversion: */
10908 
10909 /* Convert from relative PCS to absolute PCS (if required) */
10910 static int
10911 icmLuMonoBwd_abs (	/* Abs comes first in Bwd conversion */
10912 icmLuMono *p,		/* This */
10913 double *out,		/* Vector of output values in Native PCS */
10914 double *in			/* Vector of input values in Effective PCS */
10915 ) {
10916 	int rv = 0;
10917 
10918 	if (out != in) {
10919 		int i;
10920 		for (i = 0; i < 3; i++)		/* Don't alter input values */
10921 			out[i] = in[i];
10922 	}
10923 
10924 	/* Force to monochrome locus in correct space */
10925 	if (p->e_pcs == icSigLabData) {
10926 		double wp[3];
10927 
10928 		if (p->intent == icAbsoluteColorimetric) {
10929 			wp[0] = p->whitePoint.X;
10930 			wp[1] = p->whitePoint.Y;
10931 			wp[2] = p->whitePoint.Z;
10932 		} else {
10933 			wp[0] = p->pcswht.X;
10934 			wp[1] = p->pcswht.Y;
10935 			wp[2] = p->pcswht.Z;
10936 		}
10937 		icmXYZ2Lab(&p->pcswht, wp, wp);	/* Convert to Lab white point */
10938 		out[1] = out[0]/wp[0] * wp[1];
10939 		out[2] = out[0]/wp[0] * wp[2];
10940 
10941 	} else {
10942 		if (p->intent == icAbsoluteColorimetric) {
10943 			out[0] = out[1]/p->whitePoint.Y * p->whitePoint.X;
10944 			out[2] = out[1]/p->whitePoint.Y * p->whitePoint.Z;
10945 		} else {
10946 			out[0] = out[1]/p->pcswht.Y * p->pcswht.X;
10947 			out[2] = out[1]/p->pcswht.Y * p->pcswht.Z;
10948 		}
10949 	}
10950 
10951 	/* Do absolute conversion to */
10952 	if (p->intent == icAbsoluteColorimetric) {
10953 
10954 		if (p->e_pcs == icSigLabData)
10955 			icmLab2XYZ(&p->pcswht, out, out);
10956 
10957 		icmMulBy3x3(out, p->fromAbs, out);
10958 
10959 		/* Convert from Effective to Native input space */
10960 		if (p->pcs == icSigLabData)
10961 			icmXYZ2Lab(&p->pcswht, out, out);
10962 
10963 	} else {
10964 
10965 		/* Convert from Effective to Native input space */
10966 		if (p->e_pcs == icSigLabData && p->pcs == icSigXYZData)
10967 			icmLab2XYZ(&p->pcswht, out, out);
10968 		else if (p->e_pcs == icSigXYZData && p->pcs == icSigLabData)
10969 			icmXYZ2Lab(&p->pcswht, out, out);
10970 	}
10971 
10972 	return rv;
10973 }
10974 
10975 /* Map from relative PCS to linearised device */
10976 static int
10977 icmLuMonoBwd_map (
10978 icmLuMono *p,		/* This */
10979 double *out,		/* Output value */
10980 double *in			/* Vector of input values (native space) */
10981 ) {
10982 	int rv = 0;
10983 	double pcsw[3];
10984 
10985 	pcsw[0] = p->pcswht.X;
10986 	pcsw[1] = p->pcswht.Y;
10987 	pcsw[2] = p->pcswht.Z;
10988 	if (p->pcs == icSigLabData)
10989 		icmXYZ2Lab(&p->pcswht, pcsw, pcsw);	/* in Lab (should be 100.0!) */
10990 
10991 	/* Divide linearized device level into PCS white luminence */
10992 	if (p->pcs == icSigLabData)
10993 		out[0] = in[0]/pcsw[0];
10994 	else
10995 		out[0] = in[1]/pcsw[1];
10996 
10997 	return rv;
10998 }
10999 
11000 /* Map from linearised device to actual device */
11001 static int
11002 icmLuMonoBwd_curve (
11003 icmLuMono *p,		/* This */
11004 double *out,		/* Output value */
11005 double *in			/* Input value */
11006 ) {
11007 	icc *icp = p->icp;
11008 	int rv = 0;
11009 
11010 	/* Convert to device value through curve */
11011 	if ((rv = p->grayCurve->lookup_bwd(p->grayCurve,&out[0],&in[0])) > 1) {
11012 		sprintf(icp->err,"icc_lookup: Curve->lookup_bwd() failed");
11013 		icp->errc = rv;
11014 		return 2;
11015 	}
11016 
11017 	return rv;
11018 }
11019 
11020 /* Overall Bwd conversion routine */
11021 static int
11022 icmLuMonoBwd_lookup (
11023 icmLuBase *pp,		/* This */
11024 double *out,		/* Output value */
11025 double *in			/* Vector of input values */
11026 ) {
11027 	double temp[3];
11028 	int rv = 0;
11029 	icmLuMono *p = (icmLuMono *)pp;
11030 	rv |= icmLuMonoBwd_abs(p, temp, in);
11031 	rv |= icmLuMonoBwd_map(p, out, temp);
11032 	rv |= icmLuMonoBwd_curve(p, out, out);
11033 	return rv;
11034 }
11035 
11036 static void
11037 icmLuMono_delete(
11038 icmLuBase *p
11039 ) {
11040 	icc *icp = p->icp;
11041 
11042 	icp->al->free(icp->al, p);
11043 }
11044 
11045 static icmLuBase *
11046 new_icmLuMono(
11047 	struct _icc          *icp,
11048     icColorSpaceSignature inSpace,		/* Native Input color space */
11049     icColorSpaceSignature outSpace,		/* Native Output color space */
11050     icColorSpaceSignature pcs,			/* Native PCS */
11051     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
11052     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
11053     icColorSpaceSignature e_pcs,		/* Effective PCS */
11054 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
11055 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
11056 	icRenderingIntent     intent,		/* Rendering intent */
11057 	icmLookupFunc         func,			/* Functionality requested */
11058 	int dir								/* 0 = fwd, 1 = bwd */
11059 ) {
11060 	icmLuMono *p;
11061 
11062 	if ((p = (icmLuMono *) icp->al->calloc(icp->al,1,sizeof(icmLuMono))) == NULL)
11063 		return NULL;
11064 	p->icp      = icp;
11065 	p->del      = icmLuMono_delete;
11066 	p->lutspaces= icmLutSpaces;
11067 	p->spaces   = icmLuSpaces;
11068 	p->get_ranges = icmLu_get_ranges;
11069 	p->wh_bk_points = icmLuWh_bk_points;
11070 	p->fwd_lookup = icmLuMonoFwd_lookup;
11071 	p->fwd_curve  = icmLuMonoFwd_curve;
11072 	p->fwd_map    = icmLuMonoFwd_map;
11073 	p->fwd_abs    = icmLuMonoFwd_abs;
11074 	p->bwd_lookup = icmLuMonoBwd_lookup;
11075 	p->bwd_abs    = icmLuMonoFwd_abs;
11076 	p->bwd_map    = icmLuMonoFwd_map;
11077 	p->bwd_curve  = icmLuMonoFwd_curve;
11078 	if (dir) {
11079 		p->ttype      = icmMonoBwdType;
11080 		p->lookup     = icmLuMonoBwd_lookup;
11081 	} else {
11082 		p->ttype      = icmMonoFwdType;
11083 		p->lookup     = icmLuMonoFwd_lookup;
11084 	}
11085 
11086 	/* See if the color spaces are appropriate for the mono type */
11087 	if (number_ColorSpaceSignature(icp->header->colorSpace) != 1
11088 	  || ( icp->header->pcs != icSigXYZData && icp->header->pcs != icSigLabData)) {
11089 		p->del((icmLuBase *)p);
11090 		return NULL;
11091 	}
11092 
11093 	/* Find the appropriate tags */
11094 	if ((p->grayCurve = (icmCurve *)icp->read_tag(icp, icSigGrayTRCTag)) == NULL
11095          || p->grayCurve->ttype != icSigCurveType) {
11096 		p->del((icmLuBase *)p);
11097 		return NULL;
11098 	}
11099 
11100 	p->pcswht = icp->header->illuminant;
11101 	p->whitePoint = whitePoint;
11102 	p->blackPoint = blackPoint;
11103 	p->intent   = intent;
11104 	p->function = func;
11105 	p->inSpace  = inSpace;
11106 	p->outSpace = outSpace;
11107 	p->pcs      = pcs;
11108 	p->e_inSpace  = e_inSpace;
11109 	p->e_outSpace = e_outSpace;
11110 	p->e_pcs      = e_pcs;
11111 
11112 	/* Create absolute <-> relative conversion matricies */
11113 	icmChromAdaptMatrix(ICM_CAM_BRADFORD, whitePoint, icmD50, p->toAbs);
11114 	icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, whitePoint,  p->fromAbs);
11115 
11116 	return (icmLuBase *)p;
11117 }
11118 
11119 static icmLuBase *
11120 new_icmLuMonoFwd(
11121 	struct _icc          *icp,
11122     icColorSpaceSignature inSpace,		/* Native Input color space */
11123     icColorSpaceSignature outSpace,		/* Native Output color space */
11124     icColorSpaceSignature pcs,			/* Native PCS */
11125     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
11126     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
11127     icColorSpaceSignature e_pcs,		/* Effective PCS */
11128 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
11129 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
11130 	icRenderingIntent     intent,		/* Rendering intent */
11131 	icmLookupFunc         func			/* Functionality requested */
11132 ) {
11133 	return new_icmLuMono(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
11134 	                     whitePoint, blackPoint, intent, func, 0);
11135 }
11136 
11137 
11138 static icmLuBase *
11139 new_icmLuMonoBwd(
11140 	struct _icc          *icp,
11141     icColorSpaceSignature inSpace,		/* Native Input color space */
11142     icColorSpaceSignature outSpace,		/* Native Output color space */
11143     icColorSpaceSignature pcs,			/* Native PCS */
11144     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
11145     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
11146     icColorSpaceSignature e_pcs,		/* Effective PCS */
11147 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
11148 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
11149 	icRenderingIntent     intent,		/* Rendering intent */
11150 	icmLookupFunc         func			/* Functionality requested */
11151 ) {
11152 	return new_icmLuMono(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
11153 	                     whitePoint, blackPoint, intent, func, 1);
11154 }
11155 
11156 /* - - - - - - - - - - - - - - - - - - - - - - - */
11157 /* Forward and Backward Matrix type conversion */
11158 /* Return 0 on success, 1 if clipping occured, 2 on other error */
11159 
11160 /* Individual components of Fwd conversion: */
11161 static int
11162 icmLuMatrixFwd_curve (
11163 icmLuMatrix *p,		/* This */
11164 double *out,		/* Vector of output values */
11165 double *in			/* Vector of input values */
11166 ) {
11167 	icc *icp = p->icp;
11168 	int rv = 0;
11169 
11170 	/* Curve lookups */
11171 	if ((rv |= p->redCurve->lookup_fwd(  p->redCurve,  &out[0],&in[0])) > 1
11172 	 || (rv |= p->greenCurve->lookup_fwd(p->greenCurve,&out[1],&in[1])) > 1
11173 	 || (rv |= p->blueCurve->lookup_fwd( p->blueCurve, &out[2],&in[2])) > 1) {
11174 		sprintf(icp->err,"icc_lookup: Curve->lookup_fwd() failed");
11175 		icp->errc = rv;
11176 		return 2;
11177 	}
11178 
11179 	return rv;
11180 }
11181 
11182 static int
11183 icmLuMatrixFwd_matrix (
11184 icmLuMatrix *p,		/* This */
11185 double *out,		/* Vector of output values */
11186 double *in			/* Vector of input values */
11187 ) {
11188 	int rv = 0;
11189 	double tt[3];
11190 
11191 	/* Matrix */
11192 	tt[0] = p->mx[0][0] * in[0] + p->mx[0][1] * in[1] + p->mx[0][2] * in[2];
11193 	tt[1] = p->mx[1][0] * in[0] + p->mx[1][1] * in[1] + p->mx[1][2] * in[2];
11194 	tt[2] = p->mx[2][0] * in[0] + p->mx[2][1] * in[1] + p->mx[2][2] * in[2];
11195 
11196 	out[0] = tt[0];
11197 	out[1] = tt[1];
11198 	out[2] = tt[2];
11199 
11200 	return rv;
11201 }
11202 
11203 static int
11204 icmLuMatrixFwd_abs (/* Abs comes last in Fwd conversion */
11205 icmLuMatrix *p,		/* This */
11206 double *out,		/* Vector of output values */
11207 double *in			/* Vector of input values */
11208 ) {
11209 	int rv = 0;
11210 
11211 	if (out != in) {
11212 		int i;
11213 		for (i = 0; i < 3; i++)		/* Don't alter input values */
11214 			out[i] = in[i];
11215 	}
11216 
11217 	/* If required, convert from Relative to Absolute colorometric */
11218 	if (p->intent == icAbsoluteColorimetric) {
11219 		icmMulBy3x3(out, p->toAbs, out);
11220 	}
11221 
11222 	/* If e_pcs is Lab, then convert XYZ to Lab */
11223 	if (p->e_pcs == icSigLabData)
11224 		icmXYZ2Lab(&p->pcswht, out, out);
11225 
11226 	return rv;
11227 }
11228 
11229 
11230 /* Overall Fwd conversion */
11231 static int
11232 icmLuMatrixFwd_lookup (
11233 icmLuBase *pp,		/* This */
11234 double *out,		/* Vector of output values */
11235 double *in			/* Vector of input values */
11236 ) {
11237 	int rv = 0;
11238 	icmLuMatrix *p = (icmLuMatrix *)pp;
11239 	rv |= icmLuMatrixFwd_curve(p, out, in);
11240 	rv |= icmLuMatrixFwd_matrix(p, out, out);
11241 	rv |= icmLuMatrixFwd_abs(p, out, out);
11242 	return rv;
11243 }
11244 
11245 /* Individual components of Bwd conversion: */
11246 
11247 static int
11248 icmLuMatrixBwd_abs (/* Abs comes first in Bwd conversion */
11249 icmLuMatrix *p,		/* This */
11250 double *out,		/* Vector of output values */
11251 double *in			/* Vector of input values */
11252 ) {
11253 	int rv = 0;
11254 
11255 	if (out != in) {
11256 		int i;
11257 		for (i = 0; i < 3; i++)		/* Don't alter input values */
11258 			out[i] = in[i];
11259 	}
11260 
11261 	/* If e_pcs is Lab, then convert Lab to XYZ */
11262 	if (p->e_pcs == icSigLabData)
11263 		icmLab2XYZ(&p->pcswht, out, out);
11264 
11265 	/* If required, convert from Absolute to Relative colorometric */
11266 	if (p->intent == icAbsoluteColorimetric) {
11267 		icmMulBy3x3(out, p->fromAbs, out);
11268 	}
11269 
11270 	return rv;
11271 }
11272 
11273 static int
11274 icmLuMatrixBwd_matrix (
11275 icmLuMatrix *p,		/* This */
11276 double *out,		/* Vector of output values */
11277 double *in			/* Vector of input values */
11278 ) {
11279 	int rv = 0;
11280 	double tt[3];
11281 
11282 	/* Matrix */
11283 	tt[0] = p->bmx[0][0] * in[0] + p->bmx[0][1] * in[1] + p->bmx[0][2] * in[2];
11284 	tt[1] = p->bmx[1][0] * in[0] + p->bmx[1][1] * in[1] + p->bmx[1][2] * in[2];
11285 	tt[2] = p->bmx[2][0] * in[0] + p->bmx[2][1] * in[1] + p->bmx[2][2] * in[2];
11286 
11287 	out[0] = tt[0];
11288 	out[1] = tt[1];
11289 	out[2] = tt[2];
11290 
11291 	return rv;
11292 }
11293 
11294 static int
11295 icmLuMatrixBwd_curve (
11296 icmLuMatrix *p,		/* This */
11297 double *out,		/* Vector of output values */
11298 double *in			/* Vector of input values */
11299 ) {
11300 	icc *icp = p->icp;
11301 	int rv = 0;
11302 
11303 	/* Curves */
11304 	if ((rv |= p->redCurve->lookup_bwd(p->redCurve,&out[0],&out[0])) > 1
11305 	 ||	(rv |= p->greenCurve->lookup_bwd(p->greenCurve,&out[1],&out[1])) > 1
11306 	 || (rv |= p->blueCurve->lookup_bwd(p->blueCurve,&out[2],&out[2])) > 1) {
11307 		sprintf(icp->err,"icc_lookup: Curve->lookup_bwd() failed");
11308 		icp->errc = rv;
11309 		return 2;
11310 	}
11311 	return rv;
11312 }
11313 
11314 /* Overall Bwd conversion */
11315 static int
11316 icmLuMatrixBwd_lookup (
11317 icmLuBase *pp,		/* This */
11318 double *out,		/* Vector of output values */
11319 double *in			/* Vector of input values */
11320 ) {
11321 	int rv = 0;
11322 	icmLuMatrix *p = (icmLuMatrix *)pp;
11323 	rv |= icmLuMatrixBwd_abs(p, out, in);
11324 	rv |= icmLuMatrixBwd_matrix(p, out, out);
11325 	rv |= icmLuMatrixBwd_curve(p, out, out);
11326 	return rv;
11327 }
11328 
11329 static void
11330 icmLuMatrix_delete(
11331 icmLuBase *p
11332 ) {
11333 	icc *icp = p->icp;
11334 
11335 	icp->al->free(icp->al, p);
11336 }
11337 
11338 /* We setup valid fwd and bwd component conversions, */
11339 /* but setup only the asked for overal conversion. */
11340 static icmLuBase *
11341 new_icmLuMatrix(
11342 	struct _icc          *icp,
11343     icColorSpaceSignature inSpace,		/* Native Input color space */
11344     icColorSpaceSignature outSpace,		/* Native Output color space */
11345     icColorSpaceSignature pcs,			/* Native PCS */
11346     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
11347     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
11348     icColorSpaceSignature e_pcs,		/* Effective PCS */
11349 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
11350 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
11351 	icRenderingIntent     intent,		/* Rendering intent */
11352 	icmLookupFunc         func,			/* Functionality requested */
11353 	int dir								/* 0 = fwd, 1 = bwd */
11354 ) {
11355 	icmLuMatrix *p;
11356 
11357 	if ((p = (icmLuMatrix *) icp->al->calloc(icp->al,1,sizeof(icmLuMatrix))) == NULL)
11358 		return NULL;
11359 	p->icp      = icp;
11360 	p->del      = icmLuMatrix_delete;
11361 	p->lutspaces= icmLutSpaces;
11362 	p->spaces   = icmLuSpaces;
11363 	p->get_ranges = icmLu_get_ranges;
11364 	p->wh_bk_points = icmLuWh_bk_points;
11365 	p->fwd_lookup = icmLuMatrixFwd_lookup;
11366 	p->fwd_curve  = icmLuMatrixFwd_curve;
11367 	p->fwd_matrix = icmLuMatrixFwd_matrix;
11368 	p->fwd_abs    = icmLuMatrixFwd_abs;
11369 	p->bwd_lookup = icmLuMatrixBwd_lookup;
11370 	p->bwd_abs    = icmLuMatrixBwd_abs;
11371 	p->bwd_matrix = icmLuMatrixBwd_matrix;
11372 	p->bwd_curve  = icmLuMatrixBwd_curve;
11373 	if (dir) {
11374 		p->ttype      = icmMatrixBwdType;
11375 		p->lookup     = icmLuMatrixBwd_lookup;
11376 	} else {
11377 		p->ttype      = icmMatrixFwdType;
11378 		p->lookup     = icmLuMatrixFwd_lookup;
11379 	}
11380 
11381 	/* Note that we can use matrix type even if PCS is Lab, */
11382 	/* by simply converting it. */
11383 
11384 	/* Find the appropriate tags */
11385 	if ((p->redCurve = (icmCurve *)icp->read_tag(icp, icSigRedTRCTag)) == NULL
11386      || p->redCurve->ttype != icSigCurveType
11387 	 || (p->greenCurve = (icmCurve *)icp->read_tag(icp, icSigGreenTRCTag)) == NULL
11388      || p->greenCurve->ttype != icSigCurveType
11389 	 || (p->blueCurve = (icmCurve *)icp->read_tag(icp, icSigBlueTRCTag)) == NULL
11390      || p->blueCurve->ttype != icSigCurveType
11391 	 || (p->redColrnt = (icmXYZArray *)icp->read_tag(icp, icSigRedColorantTag)) == NULL
11392      || p->redColrnt->ttype != icSigXYZType || p->redColrnt->size < 1
11393 	 || (p->greenColrnt = (icmXYZArray *)icp->read_tag(icp, icSigGreenColorantTag)) == NULL
11394      || p->greenColrnt->ttype != icSigXYZType || p->greenColrnt->size < 1
11395 	 || (p->blueColrnt = (icmXYZArray *)icp->read_tag(icp, icSigBlueColorantTag)) == NULL
11396      || p->blueColrnt->ttype != icSigXYZType || p->blueColrnt->size < 1) {
11397 		p->del((icmLuBase *)p);
11398 		return NULL;
11399 	}
11400 
11401 	/* Setup the matrix */
11402 	p->mx[0][0] = p->redColrnt->data[0].X;
11403 	p->mx[0][1] = p->greenColrnt->data[0].X;
11404 	p->mx[0][2] = p->blueColrnt->data[0].X;
11405 	p->mx[1][1] = p->greenColrnt->data[0].Y;
11406 	p->mx[1][0] = p->redColrnt->data[0].Y;
11407 	p->mx[1][2] = p->blueColrnt->data[0].Y;
11408 	p->mx[2][1] = p->greenColrnt->data[0].Z;
11409 	p->mx[2][0] = p->redColrnt->data[0].Z;
11410 	p->mx[2][2] = p->blueColrnt->data[0].Z;
11411 
11412 	if (inverse3x3(p->bmx, p->mx) != 0) {	/* Compute inverse */
11413 		sprintf(icp->err,"icc_new_iccLuMatrix: Matrix wasn't invertable");
11414 		icp->errc = 2;
11415 		p->del((icmLuBase *)p);
11416 		return NULL;
11417 	}
11418 
11419 	p->pcswht = icp->header->illuminant;
11420 	p->whitePoint = whitePoint;
11421 	p->blackPoint = blackPoint;
11422 	p->intent   = intent;
11423 	p->function = func;
11424 	p->inSpace  = inSpace;
11425 	p->outSpace = outSpace;
11426 	p->pcs      = pcs;
11427 	p->e_inSpace  = e_inSpace;
11428 	p->e_outSpace = e_outSpace;
11429 	p->e_pcs      = e_pcs;
11430 
11431 	/* Create absolute <-> relative conversion matricies */
11432 	icmChromAdaptMatrix(ICM_CAM_BRADFORD, whitePoint, icmD50, p->toAbs);
11433 	icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, whitePoint,  p->fromAbs);
11434 
11435 	return (icmLuBase *)p;
11436 }
11437 
11438 static icmLuBase *
11439 new_icmLuMatrixFwd(
11440 	struct _icc          *icp,
11441     icColorSpaceSignature inSpace,		/* Native Input color space */
11442     icColorSpaceSignature outSpace,		/* Native Output color space */
11443     icColorSpaceSignature pcs,			/* Native PCS */
11444     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
11445     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
11446     icColorSpaceSignature e_pcs,		/* Effective PCS */
11447 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
11448 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
11449 	icRenderingIntent     intent,		/* Rendering intent */
11450 	icmLookupFunc         func			/* Functionality requested */
11451 ) {
11452 	return new_icmLuMatrix(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
11453 	                       whitePoint, blackPoint, intent, func, 0);
11454 }
11455 
11456 
11457 static icmLuBase *
11458 new_icmLuMatrixBwd(
11459 	struct _icc          *icp,
11460     icColorSpaceSignature inSpace,		/* Native Input color space */
11461     icColorSpaceSignature outSpace,		/* Native Output color space */
11462     icColorSpaceSignature pcs,			/* Native PCS */
11463     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
11464     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
11465     icColorSpaceSignature e_pcs,		/* Effective PCS */
11466 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
11467 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
11468 	icRenderingIntent     intent,		/* Rendering intent */
11469 	icmLookupFunc         func			/* Functionality requested */
11470 ) {
11471 	return new_icmLuMatrix(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
11472 	                       whitePoint, blackPoint, intent, func, 1);
11473 }
11474 
11475 /* - - - - - - - - - - - - - - - - - - - - - - - */
11476 /* Forward and Backward Multi-Dimensional Interpolation type conversion */
11477 /* Return 0 on success, 1 if clipping occured, 2 on other error */
11478 
11479 /* Components of overall lookup, in order */
11480 static int icmLuLut_in_abs(icmLuLut *p, double *out, double *in) {
11481 	icmLut *lut = p->lut;
11482 	int rv = 0;
11483 
11484 	if (out != in) {
11485 		int i;
11486 		for (i = 0; i < lut->inputChan; i++)		/* Don't alter input values */
11487 			out[i] = in[i];
11488 	}
11489 
11490 	/* If Bwd Lut, take care of Absolute color space and effective input space */
11491 	if ((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
11492 		&& p->intent == icAbsoluteColorimetric) {
11493 
11494 		if (p->e_inSpace == icSigLabData)
11495 			icmLab2XYZ(&p->pcswht, out, out);
11496 
11497 		/* Convert from Absolute to Relative colorometric */
11498 		icmMulBy3x3(out, p->fromAbs, out);
11499 
11500 		if (p->inSpace == icSigLabData)
11501 			icmXYZ2Lab(&p->pcswht, out, out);
11502 
11503 	} else {
11504 
11505 		/* Convert from Effective to Native input space */
11506 		if (p->e_inSpace == icSigLabData && p->inSpace == icSigXYZData)
11507 			icmLab2XYZ(&p->pcswht, out, out);
11508 		else if (p->e_inSpace == icSigXYZData && p->inSpace == icSigLabData)
11509 			icmXYZ2Lab(&p->pcswht, out, out);
11510 	}
11511 
11512 	return rv;
11513 }
11514 
11515 /* Possible matrix lookup */
11516 static int icmLuLut_matrix(icmLuLut *p, double *out, double *in) {
11517 	icmLut *lut = p->lut;
11518 	int rv = 0;
11519 
11520 	if (p->usematrix)
11521 		rv |= lut->lookup_matrix(lut,out,in);
11522 	else if (out != in) {
11523 		int i;
11524 		for (i = 0; i < lut->inputChan; i++)
11525 			out[i] = in[i];
11526 	}
11527 	return rv;
11528 }
11529 
11530 /* Do input -> input' lookup */
11531 static int icmLuLut_input(icmLuLut *p, double *out, double *in) {
11532 	icmLut *lut = p->lut;
11533 	int rv = 0;
11534 
11535 	p->in_normf(out, in); 						/* Normalize from input color space */
11536 	rv |= lut->lookup_input(lut,out,out);		/* Lookup though input tables */
11537 	p->in_denormf(out,out);						/* De-normalize to input color space */
11538 	return rv;
11539 }
11540 
11541 /* Do input'->output' lookup */
11542 static int icmLuLut_clut(icmLuLut *p, double *out, double *in) {
11543 	icmLut *lut = p->lut;
11544 	double temp[MAX_CHAN];
11545 	int rv = 0;
11546 
11547 	p->in_normf(temp, in); 						/* Normalize from input color space */
11548 	rv |= p->lookup_clut(lut,out,temp);			/* Lookup though clut tables */
11549 	p->out_denormf(out,out);					/* De-normalize to output color space */
11550 	return rv;
11551 }
11552 
11553 /* Do output'->output lookup */
11554 static int icmLuLut_output(icmLuLut *p, double *out, double *in) {
11555 	icmLut *lut = p->lut;
11556 	int rv = 0;
11557 
11558 	p->out_normf(out,in);						/* Normalize from output color space */
11559 	rv |= lut->lookup_output(lut,out,out);		/* Lookup though output tables */
11560 	p->out_denormf(out, out);					/* De-normalize to output color space */
11561 	return rv;
11562 }
11563 
11564 static int icmLuLut_out_abs(icmLuLut *p, double *out, double *in) {
11565 	icmLut *lut = p->lut;
11566 	int rv = 0;
11567 
11568 	if (out != in) {
11569 		int i;
11570 		for (i = 0; i < lut->inputChan; i++)		/* Don't alter input values */
11571 			out[i] = in[i];
11572 	}
11573 
11574 	/* If Fwd Lut, take care of Absolute color space */
11575 	/* and convert from native to effective out PCS */
11576 	if ((p->function == icmFwd || p->function == icmPreview)
11577 		&& p->intent == icAbsoluteColorimetric) {
11578 
11579 		if (p->outSpace == icSigLabData)
11580 			icmLab2XYZ(&p->pcswht, out, out);
11581 
11582 		/* Convert from Relative to Absolute colorometric XYZ */
11583 		icmMulBy3x3(out, p->toAbs, out);
11584 
11585 		if (p->e_outSpace == icSigLabData)
11586 			icmXYZ2Lab(&p->pcswht, out, out);
11587 	} else {
11588 
11589 		/* Convert from Native to Effective output space */
11590 		if (p->outSpace == icSigLabData && p->e_outSpace == icSigXYZData)
11591 			icmLab2XYZ(&p->pcswht, out, out);
11592 		else if (p->outSpace == icSigXYZData && p->e_outSpace == icSigLabData)
11593 			icmXYZ2Lab(&p->pcswht, out, out);
11594 	}
11595 	return rv;
11596 }
11597 
11598 
11599 /* Overall lookup */
11600 static int
11601 icmLuLut_lookup (
11602 icmLuBase *pp,		/* This */
11603 double *out,		/* Vector of output values */
11604 double *in			/* Vector of input values */
11605 ) {
11606 	int rv = 0;
11607 	icmLuLut *p = (icmLuLut *)pp;
11608 	icmLut *lut = p->lut;
11609 	double temp[MAX_CHAN];
11610 
11611 	rv |= p->in_abs(p,temp,in);						/* Possible absolute conversion */
11612 	if (p->usematrix)
11613 		rv |= lut->lookup_matrix(lut,temp,temp);/* If XYZ, multiply by non-unity matrix */
11614 	p->in_normf(temp, temp);					/* Normalize for input color space */
11615 	rv |= lut->lookup_input(lut,temp,temp);		/* Lookup though input tables */
11616 	rv |= p->lookup_clut(lut,out,temp);			/* Lookup though clut tables */
11617 	rv |= lut->lookup_output(lut,out,out);		/* Lookup though output tables */
11618 	p->out_denormf(out,out);					/* Normalize for output color space */
11619 	rv |= p->out_abs(p,out,out);				/* Possible absolute conversion */
11620 
11621 	return rv;
11622 }
11623 
11624 #ifdef NEVER	/* The following should be identical in effect to the above. */
11625 
11626 /* Overall lookup */
11627 static int
11628 icmLuLut_lookup (
11629 icmLuBase *pp,		/* This */
11630 double *out,		/* Vector of output values */
11631 double *in			/* Vector of input values */
11632 ) {
11633 	int i, rv = 0;
11634 	icmLuLut *p = (icmLuLut *)pp;
11635 	icmLut *lut = p->lut;
11636 	double temp[MAX_CHAN];
11637 
11638 	rv |= p->in_abs(p,temp,in);
11639 	rv |= p->matrix(p,temp,temp);
11640 	rv |= p->input(p,temp,temp);
11641 	rv |= p->clut(p,out,temp);
11642 	rv |= p->output(p,out,out);
11643 	rv |= p->out_abs(p,out,out);
11644 
11645 	return rv;
11646 }
11647 #endif	/* NEVER */
11648 /* - - - - - - - - - - - - - - - - - - - - - - - - - - */
11649 /* Some components of inverse lookup, in order */
11650 /* ~~ should these be in icmLut (like all the fwd transforms)? */
11651 
11652 static int icmLuLut_inv_out_abs(icmLuLut *p, double *out, double *in) {
11653 	icmLut *lut = p->lut;
11654 	int rv = 0;
11655 
11656 	if (out != in) {
11657 		int i;
11658 		for (i = 0; i < lut->inputChan; i++)		/* Don't alter input values */
11659 			out[i] = in[i];
11660 	}
11661 
11662 	/* If Fwd Lut, take care of Absolute color space */
11663 	/* and convert from effective to native inverse output PCS */
11664 	/* OutSpace must be PCS: XYZ or Lab */
11665 	if ((p->function == icmFwd || p->function == icmPreview)
11666 		&& p->intent == icAbsoluteColorimetric) {
11667 
11668 		if (p->e_outSpace == icSigLabData)
11669 			icmLab2XYZ(&p->pcswht, out, out);
11670 
11671 		/* Convert from Absolute to Relative colorometric */
11672 		icmMulBy3x3(out, p->fromAbs, out);
11673 
11674 		if (p->outSpace == icSigLabData)
11675 			icmXYZ2Lab(&p->pcswht, out, out);
11676 
11677 	} else {
11678 
11679 		/* Convert from Effective to Native output space */
11680 		if (p->e_outSpace == icSigLabData && p->outSpace == icSigXYZData)
11681 			icmLab2XYZ(&p->pcswht, out, out);
11682 		else if (p->e_outSpace == icSigXYZData && p->outSpace == icSigLabData)
11683 			icmXYZ2Lab(&p->pcswht, out, out);
11684 	}
11685 	return rv;
11686 }
11687 
11688 /* Do output->output' inverse lookup */
11689 static int icmLuLut_inv_output(icmLuLut *p, double *out, double *in) {
11690 	icc *icp = p->icp;
11691 	icmLut *lut = p->lut;
11692 	int rv = 0;
11693 
11694 	if (lut->rot.inited == 0) {
11695 		rv = icmTable_setup_bwd(icp, &lut->rot, lut->outputEnt, lut->outputTable);
11696 		if (rv != 0) {
11697 			sprintf(icp->err,"icc_Lut_inv_input: Malloc failure in inverse lookup init.");
11698 			return icp->errc = rv;
11699 		}
11700 	}
11701 
11702 	p->out_normf(out,in);						/* Normalize from output color space */
11703 	rv |= icmTable_lookup_bwd(&lut->rot, out, out); /* Reverse lookup though output tables */
11704 	p->out_denormf(out, out);					/* De-normalize to output color space */
11705 	return rv;
11706 }
11707 
11708 /* No output' -> input inverse lookup. */
11709 /* This is non-trivial ! */
11710 
11711 /* Do input' -> input inverse lookup */
11712 static int icmLuLut_inv_input(icmLuLut *p, double *out, double *in) {
11713 	icc *icp = p->icp;
11714 	icmLut *lut = p->lut;
11715 	int rv = 0;
11716 
11717 	if (lut->rit.inited == 0) {
11718 		rv = icmTable_setup_bwd(icp, &lut->rit, lut->inputEnt, lut->inputTable);
11719 		if (rv != 0) {
11720 			sprintf(icp->err,"icc_Lut_inv_input: Malloc failure in inverse lookup init.");
11721 			return icp->errc = rv;
11722 		}
11723 	}
11724 
11725 	p->in_normf(out, in); 						/* Normalize from input color space */
11726 	rv |= icmTable_lookup_bwd(&lut->rit, out, out); /* Reverse lookup though input tables */
11727 	p->in_denormf(out,out);						/* De-normalize to input color space */
11728 	return rv;
11729 }
11730 
11731 /* Possible inverse matrix lookup */
11732 static int icmLuLut_inv_matrix(icmLuLut *p, double *out, double *in) {
11733 	icc *icp = p->icp;
11734 	icmLut *lut = p->lut;
11735 	int rv = 0;
11736 
11737 	if (p->usematrix) {
11738 		double tt[3];
11739 		if (p->imx_valid == 0) {
11740 			if (inverse3x3(p->imx, lut->e) != 0) {	/* Compute inverse */
11741 				sprintf(icp->err,"icc_new_iccLuMatrix: Matrix wasn't invertable");
11742 				icp->errc = 2;
11743 				return 2;
11744 			}
11745 			p->imx_valid = 1;
11746 		}
11747 		/* Matrix multiply */
11748 		tt[0] = p->imx[0][0] * in[0] + p->imx[0][1] * in[1] + p->imx[0][2] * in[2];
11749 		tt[1] = p->imx[1][0] * in[0] + p->imx[1][1] * in[1] + p->imx[1][2] * in[2];
11750 		tt[2] = p->imx[2][0] * in[0] + p->imx[2][1] * in[1] + p->imx[2][2] * in[2];
11751 		out[0] = tt[0], out[1] = tt[1], out[2] = tt[2];
11752 	} else if (out != in) {
11753 		int i;
11754 		for (i = 0; i < lut->inputChan; i++)
11755 			out[i] = in[i];
11756 	}
11757 	return rv;
11758 }
11759 
11760 static int icmLuLut_inv_in_abs(icmLuLut *p, double *out, double *in) {
11761 	icmLut *lut = p->lut;
11762 	int rv = 0;
11763 
11764 	if (out != in) {
11765 		int i;
11766 		for (i = 0; i < lut->inputChan; i++)		/* Don't alter input values */
11767 			out[i] = in[i];
11768 	}
11769 
11770 	/* If Bwd Lut, take care of Absolute color space, and */
11771 	/* convert from native to effective input space */
11772 	if ((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
11773 		&& p->intent == icAbsoluteColorimetric) {
11774 
11775 		if (p->inSpace == icSigLabData)
11776 			icmLab2XYZ(&p->pcswht, out, out);
11777 
11778 		/* Convert from Relative to Absolute colorometric XYZ */
11779 		icmMulBy3x3(out, p->toAbs, out);
11780 
11781 		if (p->e_inSpace == icSigLabData)
11782 			icmXYZ2Lab(&p->pcswht, out, out);
11783 	} else {
11784 
11785 		/* Convert from Native to Effective input space */
11786 		if (p->inSpace == icSigLabData && p->e_inSpace == icSigXYZData)
11787 			icmLab2XYZ(&p->pcswht, out, out);
11788 		else if (p->inSpace == icSigXYZData && p->e_inSpace == icSigLabData)
11789 			icmXYZ2Lab(&p->pcswht, out, out);
11790 	}
11791 	return rv;
11792 }
11793 
11794 /* - - - - - - - - - - - - - - - - - - - - - - - - - - */
11795 
11796 /* Return LuLut information */
11797 static void icmLuLut_get_info(
11798 	icmLuLut     *p,		/* this */
11799 	icmLut       **lutp,	/* Pointer to icc lut type */
11800 	icmXYZNumber *pcswhtp,	/* Pointer to profile PCS white point */
11801 	icmXYZNumber *whitep,	/* Pointer to profile absolute white point */
11802 	icmXYZNumber *blackp	/* Pointer to profile absolute black point */
11803 ) {
11804 	if (lutp != NULL)
11805 		*lutp = p->lut;
11806 	if (pcswhtp != NULL)
11807 		*pcswhtp = p->pcswht;
11808 	if (whitep != NULL)
11809 		*whitep = p->whitePoint;
11810 	if (blackp != NULL)
11811 		*blackp = p->blackPoint;
11812 }
11813 
11814 /* Get the native ranges for the LuLut */
11815 static void
11816 icmLuLut_get_lutranges (
11817 	struct _icmLuLut *p,
11818 	double *inmin, double *inmax,		/* Return maximum range of inspace values */
11819 	double *outmin, double *outmax		/* Return maximum range of outspace values */
11820 ) {
11821 	int i;
11822 
11823 	for (i = 0; i < p->lut->inputChan; i++) {
11824 		inmin[i] = 0.0;	/* Normalized range of input space values */
11825 		inmax[i] = 1.0;
11826 	}
11827 	p->in_denormf(inmin,inmin);	/* Convert to real colorspace range */
11828 	p->in_denormf(inmax,inmax);
11829 
11830 	/* Make sure min and max are so. */
11831 	for (i = 0; i < p->lut->inputChan; i++) {
11832 		if (inmin[i] > inmax[i]) {
11833 			double tt;
11834 			tt = inmin[i];
11835 			inmin[i] = inmax[i];
11836 			inmax[i] = tt;
11837 		}
11838 	}
11839 
11840 	for (i = 0; i < p->lut->outputChan; i++) {
11841 		outmin[i] = 0.0;	/* Normalized range of output space values */
11842 		outmax[i] = 1.0;
11843 	}
11844 	p->out_denormf(outmin,outmin);	/* Convert to real colorspace range */
11845 	p->out_denormf(outmax,outmax);
11846 
11847 	/* Make sure min and max are so. */
11848 	for (i = 0; i < p->lut->outputChan; i++) {
11849 		if (outmin[i] > outmax[i]) {
11850 			double tt;
11851 			tt = outmin[i];
11852 			outmin[i] = outmax[i];
11853 			outmax[i] = tt;
11854 		}
11855 	}
11856 }
11857 
11858 /* Get the effective (externally visible) ranges for the LuLut */
11859 /* Arguments may be NULL */
11860 static void
11861 icmLuLut_get_ranges (
11862 	struct _icmLuBase *pp,
11863 	double *inmin, double *inmax,		/* Return maximum range of inspace values */
11864 	double *outmin, double *outmax		/* Return maximum range of outspace values */
11865 ) {
11866 	icmLuLut *p = (icmLuLut *)pp;
11867 	double tinmin[MAX_CHAN], tinmax[MAX_CHAN], toutmin[MAX_CHAN], toutmax[MAX_CHAN];
11868 	int i;
11869 
11870 	/* fudge NULL arguments so that they don't bomb */
11871 	if (inmin == NULL)
11872 		inmin = tinmin;
11873 	if (inmax == NULL)
11874 		inmax = tinmax;
11875 	if (outmin == NULL)
11876 		outmin = toutmin;
11877 	if (outmax == NULL)
11878 		outmax = toutmax;
11879 
11880 	for (i = 0; i < p->lut->inputChan; i++) {
11881 		inmin[i] = 0.0;	/* Normalized range of input space values */
11882 		inmax[i] = 1.0;
11883 	}
11884 	p->e_in_denormf(inmin,inmin);	/* Convert to real colorspace range */
11885 	p->e_in_denormf(inmax,inmax);
11886 
11887 	/* Make sure min and max are so. */
11888 	for (i = 0; i < p->lut->inputChan; i++) {
11889 		if (inmin[i] > inmax[i]) {
11890 			double tt;
11891 			tt = inmin[i];
11892 			inmin[i] = inmax[i];
11893 			inmax[i] = tt;
11894 		}
11895 	}
11896 
11897 	for (i = 0; i < p->lut->outputChan; i++) {
11898 		outmin[i] = 0.0;	/* Normalized range of output space values */
11899 		outmax[i] = 1.0;
11900 	}
11901 	p->e_out_denormf(outmin,outmin);	/* Convert to real colorspace range */
11902 	p->e_out_denormf(outmax,outmax);
11903 
11904 	/* Make sure min and max are so. */
11905 	for (i = 0; i < p->lut->outputChan; i++) {
11906 		if (outmin[i] > outmax[i]) {
11907 			double tt;
11908 			tt = outmin[i];
11909 			outmin[i] = outmax[i];
11910 			outmax[i] = tt;
11911 		}
11912 	}
11913 }
11914 
11915 /* Return the underlying Lut matrix */
11916 static void
11917 icmLuLut_get_matrix (
11918 	struct _icmLuLut *p,
11919 	double m[3][3]
11920 ) {
11921 	int i, j;
11922 	icmLut *lut = p->lut;
11923 
11924 	if (p->usematrix) {
11925 		for (i = 0; i < 3; i++)
11926 			for (j = 0; j < 3; j++)
11927 				m[i][j] = lut->e[i][j];	/* Copy from Lut */
11928 
11929 	} else {							/* return unity matrix */
11930 		for (i = 0; i < 3; i++) {
11931 			for (j = 0; j < 3; j++) {
11932 				if (i == j)
11933 					m[i][j] = 1.0;
11934 				else
11935 					m[i][j] = 0.0;
11936 			}
11937 		}
11938 	}
11939 }
11940 
11941 
11942 static void
11943 icmLuLut_delete(
11944 icmLuBase *p
11945 ) {
11946 	icc *icp = p->icp;
11947 
11948 	icp->al->free(icp->al, p);
11949 }
11950 
11951 static icmLuBase *
11952 new_icmLuLut(
11953 	icc                   *icp,
11954 	icTagSignature        ttag,			/* Target Lut tag */
11955     icColorSpaceSignature inSpace,		/* Native Input color space */
11956     icColorSpaceSignature outSpace,		/* Native Output color space */
11957     icColorSpaceSignature pcs,			/* Native PCS */
11958     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
11959     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
11960     icColorSpaceSignature e_pcs,		/* Effective PCS */
11961 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
11962 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
11963 	icRenderingIntent     intent,		/* Rendering intent */
11964 	icmLookupFunc         func			/* Functionality requested */
11965 ) {
11966 	icmLuLut *p;
11967 
11968 	if ((p = (icmLuLut *) icp->al->calloc(icp->al,1,sizeof(icmLuLut))) == NULL)
11969 		return NULL;
11970 	p->ttype    = icmLutType;
11971 	p->icp      = icp;
11972 	p->del      = icmLuLut_delete;
11973 	p->lutspaces= icmLutSpaces;
11974 	p->spaces   = icmLuSpaces;
11975 	p->wh_bk_points = icmLuWh_bk_points;
11976 
11977 	p->lookup   = icmLuLut_lookup;
11978 	p->in_abs   = icmLuLut_in_abs;
11979 	p->matrix   = icmLuLut_matrix;
11980 	p->input    = icmLuLut_input;
11981 	p->clut     = icmLuLut_clut;
11982 	p->output   = icmLuLut_output;
11983 	p->out_abs  = icmLuLut_out_abs;
11984 
11985 	p->inv_in_abs   = icmLuLut_inv_in_abs;
11986 	p->inv_matrix   = icmLuLut_inv_matrix;
11987 	p->inv_input    = icmLuLut_inv_input;
11988 	p->inv_output   = icmLuLut_inv_output;
11989 	p->inv_out_abs  = icmLuLut_inv_out_abs;
11990 
11991 	p->pcswht   = icp->header->illuminant;
11992 	p->whitePoint = whitePoint;
11993 	p->blackPoint = blackPoint;
11994 	p->intent   = intent;
11995 	p->function = func;
11996 	p->inSpace  = inSpace;
11997 	p->outSpace = outSpace;
11998 	p->pcs      = pcs;
11999 	p->e_inSpace  = e_inSpace;
12000 	p->e_outSpace = e_outSpace;
12001 	p->e_pcs      = e_pcs;
12002 	p->get_info = icmLuLut_get_info;
12003 	p->get_lutranges = icmLuLut_get_lutranges;
12004 	p->get_ranges = icmLuLut_get_ranges;
12005 	p->get_matrix = icmLuLut_get_matrix;
12006 
12007 	/* Create absolute <-> relative conversion matricies */
12008 	icmChromAdaptMatrix(ICM_CAM_BRADFORD, whitePoint, icmD50, p->toAbs);
12009 	icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, whitePoint,  p->fromAbs);
12010 
12011 	/* Get the Lut tag, & check that it is expected type */
12012 	if ((p->lut = (icmLut *)icp->read_tag(icp, ttag)) == NULL
12013 	 || (p->lut->ttype != icSigLut8Type && p->lut->ttype != icSigLut16Type)) {
12014 		p->del((icmLuBase *)p);
12015 		return NULL;
12016 	}
12017 
12018 	/* Check if matrix should be used */
12019 	if (inSpace == icSigXYZData && p->lut->nu_matrix(p->lut))
12020 		p->usematrix = 1;
12021 	else
12022 		p->usematrix = 0;
12023 
12024 	/* Lookup input color space to normalized index function */
12025 	if (getNormFunc(inSpace, p->lut->ttype, icmToLuti, &p->in_normf)) {
12026 		sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
12027 		icp->errc = 1;
12028 		p->del((icmLuBase *)p);
12029 		return NULL;
12030 	}
12031 
12032 	/* Lookup normalized index to input color space function */
12033 	if (getNormFunc(inSpace, p->lut->ttype, icmFromLuti, &p->in_denormf)) {
12034 		sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
12035 		icp->errc = 1;
12036 		p->del((icmLuBase *)p);
12037 		return NULL;
12038 	}
12039 
12040 	/* Lookup output color space to normalized Lut entry value function */
12041 	if (getNormFunc(outSpace, p->lut->ttype, icmToLutv, &p->out_normf)) {
12042 		sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
12043 		icp->errc = 1;
12044 		p->del((icmLuBase *)p);
12045 		return NULL;
12046 	}
12047 
12048 	/* Lookup normalized Lut entry value to output color space function */
12049 	if (getNormFunc(outSpace, p->lut->ttype, icmFromLutv, &p->out_denormf)) {
12050 		sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
12051 		icp->errc = 1;
12052 		p->del((icmLuBase *)p);
12053 		return NULL;
12054 	}
12055 
12056 	/* Lookup normalized index to effective input color space function */
12057 	if (getNormFunc(e_inSpace, p->lut->ttype, icmFromLuti, &p->e_in_denormf)) {
12058 		sprintf(icp->err,"icc_get_luobj: Unknown effective colorspace");
12059 		icp->errc = 1;
12060 		p->del((icmLuBase *)p);
12061 		return NULL;
12062 	}
12063 
12064 	/* Lookup normalized Lut entry value to effective output color space function */
12065 	if (getNormFunc(e_outSpace, p->lut->ttype, icmFromLutv, &p->e_out_denormf)) {
12066 		sprintf(icp->err,"icc_get_luobj: Unknown effective colorspace");
12067 		icp->errc = 1;
12068 		p->del((icmLuBase *)p);
12069 		return NULL;
12070 	}
12071 
12072 	/* Determine appropriate clut lookup algorithm */
12073 	{
12074 		int use_sx;				/* -1 = undecided, 0 = N-linear, 1 = Simplex lookup */
12075 		icColorSpaceSignature ins, outs;	/* In and out Lut color spaces */
12076 		int inn, outn;		/* in and out number of Lut components */
12077 
12078 		p->lutspaces((icmLuBase *)p, &ins, &inn, &outs, &outn);
12079 
12080 		/* Determine if the input space is "Device" like, */
12081 		/* ie. luminance will be expected to vary most strongly */
12082 		/* with the diagonal change in input coordinates. */
12083 		switch(ins) {
12084 
12085 			/* Luminence is carried by the sum of all the output channels, */
12086 			/* so output luminence will dominantly be in diagonal direction. */
12087 			case icSigRgbData:
12088 			case icSigGrayData:
12089 			case icSigCmykData:
12090 			case icSigCmyData:
12091 			case icSigMch6Data:
12092 				use_sx = 1;		/* Simplex interpolation is appropriate */
12093 				break;
12094 
12095 			/* A single channel carries the luminence information */
12096 			case icSigLabData:
12097 			case icSigLuvData:
12098 			case icSigYCbCrData:
12099 			case icSigYxyData:
12100 			case icSigXYZData:
12101 			case icSigHlsData:
12102 			case icSigHsvData:
12103 				use_sx = 0;		/* N-linear interpolation is appropriate */
12104 				break;
12105 			default:
12106 				use_sx = -1;		/* undecided */
12107 			    	break;
12108 		}
12109 
12110 		/* If we couldn't figure it out from the input space, */
12111 		/* check output luminance variation with a diagonal input */
12112 		/* change. */
12113 		if (use_sx == -1) {
12114 			int lc;		/* Luminance channel */
12115 
12116 			/* Determine where the luminence is carried in the output */
12117 			switch(outs) {
12118 
12119 				/* Luminence is carried by the sum of all the output channels */
12120 				case icSigRgbData:
12121 				case icSigGrayData:
12122 				case icSigCmykData:
12123 				case icSigCmyData:
12124 				case icSigMch6Data:
12125 					lc = -1;		/* Average all channels */
12126 					break;
12127 
12128 				/* A single channel carries the luminence information */
12129 				case icSigLabData:
12130 				case icSigLuvData:
12131 				case icSigYCbCrData:
12132 				case icSigYxyData:
12133 					lc = 0;
12134 					break;
12135 
12136 				case icSigXYZData:
12137 				case icSigHlsData:
12138 					lc = 1;
12139 					break;
12140 
12141 				case icSigHsvData:
12142 					lc = 2;
12143 					break;
12144 
12145 				/* default means give up and use N-linear type lookup */
12146 				default:
12147 					lc = -2;
12148 					break;
12149 			}
12150 
12151 			/* If we know how luminance is represented in output space */
12152 			if (lc != -2) {
12153 				double tout1[MAX_CHAN];		/* Test output values */
12154 				double tout2[MAX_CHAN];
12155 				double tt, diag;
12156 				int n;
12157 
12158 				/* Determine input space location of min and max of */
12159 				/* given output channel (chan = -1 means average of all) */
12160 				p->lut->min_max(p->lut, tout1, tout2, lc);
12161 
12162 				/* Convert to vector and then calculate normalized */
12163 				/* dot product with diagonal vector (1,1,1...) */
12164 				for (tt = 0.0, n = 0; n < inn; n++) {
12165 					tout1[n] = tout2[n] - tout1[n];
12166 					tt += tout1[n] * tout1[n];
12167 				}
12168 				if (tt > 0.0)
12169 					tt = sqrt(tt);			/* normalizing factor for maximum delta */
12170 				else
12171 					tt = 1.0;				/* Hmm. */
12172 				tt *= sqrt((double)inn);	/* Normalizing factor for diagonal vector */
12173 				for (diag = 0.0, n = 0; n < outn; n++)
12174 					diag += tout1[n] / tt;
12175 				diag = fabs(diag);
12176 
12177 				/* I'm not really convinced that this is a reliable */
12178 				/* indicator of whether simplex interpolation should be used ... */
12179 				/* It does seem to do the right thing with YCC space though. */
12180 				if (diag > 0.8)	/* Diagonal is dominant ? */
12181 					use_sx = 1;
12182 
12183 				/* If we couldn't figure it out, use N-linear interpolation */
12184 				if (use_sx == -1)
12185 					use_sx = 0;
12186 			}
12187 		}
12188 
12189 		if (use_sx) {
12190 			p->lookup_clut = p->lut->lookup_clut_sx;
12191 		} else
12192 			p->lookup_clut = p->lut->lookup_clut_nl;
12193 	}
12194 	return (icmLuBase *)p;
12195 }
12196 
12197 /* - - - - - - - - - - - - - - - - - - - - - - - */
12198 
12199 /* Return an appropriate lookup object */
12200 /* Return NULL on error, and detailed error in icc */
12201 static icmLuBase* icc_get_luobj (
12202 	icc *p,						/* ICC */
12203 	icmLookupFunc func,			/* Conversion functionality */
12204 	icRenderingIntent intent,	/* Rendering intent, including icmAbsoluteColorimetricXYZ */
12205 	icColorSpaceSignature pcsor,/* PCS overide (0 = def) */
12206 	icmLookupOrder order		/* Conversion representation search Order */
12207 ) {
12208 	int rv;
12209 	icmLuBase *luobj = NULL;	/* Lookup object to return */
12210 	icmXYZNumber whitePoint, blackPoint;
12211 	icColorSpaceSignature pcs, e_pcs;	/* PCS and effective PCS */
12212 
12213 	/* Check that the profile is legal, since we depend on it */
12214 	if ((rv = check_icc_legal(p)) != 0)
12215 		return NULL;
12216 
12217 	/* Figure out the native and effective PCS */
12218 	e_pcs = pcs = p->header->pcs;
12219 	if (pcsor != icmSigDefaultData)
12220 		e_pcs = pcsor;			/* Overide */
12221 
12222 	/* Get White and Black points from the profile */
12223 	{
12224 		icmXYZArray *whitePointTag, *blackPointTag;
12225 
12226 		if ((whitePointTag = (icmXYZArray *)p->read_tag(p, icSigMediaWhitePointTag)) == NULL
12227          || whitePointTag->ttype != icSigXYZType || whitePointTag->size < 1) {
12228 			if (intent == icAbsoluteColorimetric) {
12229 				sprintf(p->err,"icc_lookup: Profile is missing Media White Point Tag");
12230 				p->errc = 1;
12231 				return NULL;
12232 			}
12233 			whitePoint = icmD50;						/* safe value */
12234 		} else
12235 			whitePoint = whitePointTag->data[0];	/* Copy structure */
12236 
12237 		if ((blackPointTag = (icmXYZArray *)p->read_tag(p, icSigMediaBlackPointTag)) == NULL
12238          || blackPointTag->ttype != icSigXYZType || blackPointTag->size < 1) {
12239 			blackPoint = icmBlack;						/* default */
12240 		} else
12241 			blackPoint = blackPointTag->data[0];	/* Copy structure */
12242 	}
12243 
12244 	/* How we expect to execute the request depends firstly on the type of profile */
12245 	switch (p->header->deviceClass) {
12246     	case icSigInputClass:
12247     	case icSigDisplayClass:
12248 			/* Look for AToB0 based profile + optional BToA0 reverse */
12249 			/* or three component matrix profile (reversable) */
12250 			/* or momochrome table profile (reversable) */
12251 			/* No intent */
12252 			/* Device <-> PCS */
12253 			/* Determine the algorithm and set its parameters */
12254 
12255 			if (intent != icmDefaultIntent
12256 			 && intent != icAbsoluteColorimetric) {
12257 				sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Input/Display profile");
12258 				p->errc = 1;
12259 				return NULL;
12260 			}
12261 
12262 			switch (func) {
12263 		    	case icmFwd:	/* Device to PCS */
12264 					if (order != icmLuOrdRev) {
12265 						/* Try Lut type lookup first */
12266 						if ((luobj = new_icmLuLut(p, icSigAToB0Tag,
12267 						     p->header->colorSpace, pcs, pcs,
12268 						     p->header->colorSpace, e_pcs, e_pcs,
12269 						     whitePoint, blackPoint, intent, func)) != NULL)
12270 							break;
12271 
12272 						/* See if it could be a matrix lookup */
12273 						if ((luobj = new_icmLuMatrixFwd(p,
12274 						     p->header->colorSpace, pcs, pcs,
12275 						     p->header->colorSpace, e_pcs, e_pcs,
12276 						     whitePoint, blackPoint, intent, func)) != NULL)
12277 							break;
12278 
12279 						/* See if it could be a monochrome lookup */
12280 						if ((luobj = new_icmLuMonoFwd(p,
12281 						     p->header->colorSpace, pcs, pcs,
12282 						     p->header->colorSpace, e_pcs, e_pcs,
12283 						     whitePoint, blackPoint, intent, func)) != NULL)
12284 							break;
12285 
12286 					} else {
12287 						/* See if it could be a monochrome lookup */
12288 						if ((luobj = new_icmLuMonoFwd(p,
12289 						     p->header->colorSpace, pcs, pcs,
12290 						     p->header->colorSpace, e_pcs, e_pcs,
12291 						     whitePoint, blackPoint, intent, func)) != NULL)
12292 							break;
12293 
12294 						/* See if it could be a matrix lookup */
12295 						if ((luobj = new_icmLuMatrixFwd(p,
12296 						     p->header->colorSpace, pcs, pcs,
12297 						     p->header->colorSpace, e_pcs, e_pcs,
12298 						     whitePoint, blackPoint, intent, func)) != NULL)
12299 							break;
12300 
12301 						/* Try Lut type lookup last */
12302 						if ((luobj = new_icmLuLut(p, icSigAToB0Tag,
12303 						     p->header->colorSpace, pcs, pcs,
12304 						     p->header->colorSpace, e_pcs, e_pcs,
12305 						     whitePoint, blackPoint, intent, func)) != NULL)
12306 							break;
12307 					}
12308 					break;
12309 
12310 		    	case icmBwd:	/* PCS to Device */
12311 					if (order != icmLuOrdRev) {
12312 						/* Try Lut type lookup first */
12313 						if ((luobj = new_icmLuLut(p, icSigBToA0Tag,
12314 						     pcs, p->header->colorSpace, pcs,
12315 						     e_pcs, p->header->colorSpace, e_pcs,
12316 						     whitePoint, blackPoint, intent, func)) != NULL)
12317 							break;
12318 
12319 						/* See if it could be a matrix lookup */
12320 						if ((luobj = new_icmLuMatrixBwd(p,
12321 						     pcs, p->header->colorSpace, pcs,
12322 						     e_pcs, p->header->colorSpace, e_pcs,
12323 						     whitePoint, blackPoint, intent, func)) != NULL)
12324 							break;
12325 
12326 						/* See if it could be a monochrome lookup */
12327 						if ((luobj = new_icmLuMonoBwd(p,
12328 						     pcs, p->header->colorSpace, pcs,
12329 						     e_pcs, p->header->colorSpace, e_pcs,
12330 						     whitePoint, blackPoint, intent, func)) != NULL)
12331 							break;
12332 					} else {
12333 						/* See if it could be a monochrome lookup */
12334 						if ((luobj = new_icmLuMonoBwd(p,
12335 						     pcs, p->header->colorSpace, pcs,
12336 						     e_pcs, p->header->colorSpace, e_pcs,
12337 						     whitePoint, blackPoint, intent, func)) != NULL)
12338 							break;
12339 
12340 						/* See if it could be a matrix lookup */
12341 						if ((luobj = new_icmLuMatrixBwd(p,
12342 						     pcs, p->header->colorSpace, pcs,
12343 						     e_pcs, p->header->colorSpace, e_pcs,
12344 						     whitePoint, blackPoint, intent, func)) != NULL)
12345 							break;
12346 
12347 						/* Try Lut type lookup last */
12348 						if ((luobj = new_icmLuLut(p, icSigBToA0Tag,
12349 						     pcs, p->header->colorSpace, pcs,
12350 						     e_pcs, p->header->colorSpace, e_pcs,
12351 						     whitePoint, blackPoint, intent, func)) != NULL)
12352 							break;
12353 					}
12354 					break;
12355 
12356 				default:
12357 					sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
12358 					p->errc = 1;
12359 					return NULL;
12360 				}
12361 			break;
12362 
12363     	case icSigOutputClass:
12364 			/* Expect BToA Lut and optional AToB Lut, All intents, expect gamut */
12365 			/* or momochrome table profile (reversable) */
12366 			/* Device <-> PCS */
12367 			/* Gamut Lut - no intent */
12368 			/* Optional preview links PCS <-> PCS */
12369 
12370 			/* Determine the algorithm and set its parameters */
12371 			switch (func) {
12372 				icTagSignature ttag;
12373 
12374 		    	case icmFwd:	/* Device to PCS */
12375 
12376 					if (intent == icmDefaultIntent)
12377 						intent = icRelativeColorimetric;	/* Make this the default */
12378 
12379 					switch (intent) {
12380 		    			case icRelativeColorimetric:
12381 		    			case icAbsoluteColorimetric:
12382 								ttag = icSigAToB1Tag;
12383 							break;
12384 		    			case icPerceptual:
12385 								ttag = icSigAToB0Tag;
12386 							break;
12387 		    			case icSaturation:
12388 								ttag = icSigAToB2Tag;
12389 							break;
12390 						default:
12391 							sprintf(p->err,"icc_get_luobj: Unknown intent");
12392 							p->errc = 1;
12393 							return NULL;
12394 					}
12395 
12396 					if (order != icmLuOrdRev) {
12397 						/* Try Lut type lookup first */
12398 						if ((luobj = new_icmLuLut(p, ttag,
12399 						     p->header->colorSpace, pcs, pcs,
12400 						     p->header->colorSpace, e_pcs, e_pcs,
12401 						     whitePoint, blackPoint, intent, func)) != NULL)
12402 							break;
12403 
12404 						/* See if it could be a matrix lookup */
12405 						if ((luobj = new_icmLuMatrixFwd(p,
12406 						     p->header->colorSpace, pcs, pcs,
12407 						     p->header->colorSpace, e_pcs, e_pcs,
12408 						     whitePoint, blackPoint, intent, func)) != NULL)
12409 							break;
12410 
12411 						/* See if it could be a monochrome lookup */
12412 						if ((luobj = new_icmLuMonoFwd(p,
12413 						     p->header->colorSpace, pcs, pcs,
12414 						     p->header->colorSpace, e_pcs, e_pcs,
12415 						     whitePoint, blackPoint, intent, func)) != NULL)
12416 							break;
12417 					} else {
12418 						/* See if it could be a monochrome lookup */
12419 						if ((luobj = new_icmLuMonoFwd(p,
12420 						     p->header->colorSpace, pcs, pcs,
12421 						     p->header->colorSpace, e_pcs, e_pcs,
12422 						     whitePoint, blackPoint, intent, func)) != NULL)
12423 							break;
12424 
12425 						/* See if it could be a matrix lookup */
12426 						if ((luobj = new_icmLuMatrixFwd(p,
12427 						     p->header->colorSpace, pcs, pcs,
12428 						     p->header->colorSpace, e_pcs, e_pcs,
12429 						     whitePoint, blackPoint, intent, func)) != NULL)
12430 							break;
12431 
12432 						/* Try Lut type lookup last */
12433 						if ((luobj = new_icmLuLut(p, ttag,
12434 						     p->header->colorSpace, pcs, pcs,
12435 						     p->header->colorSpace, e_pcs, e_pcs,
12436 						     whitePoint, blackPoint, intent, func)) != NULL)
12437 							break;
12438 					}
12439 					break;
12440 
12441 		    	case icmBwd:	/* PCS to Device */
12442 
12443 					if (intent == icmDefaultIntent)
12444 						intent = icRelativeColorimetric;	/* Make this the default */
12445 
12446 					switch (intent) {
12447 		    			case icRelativeColorimetric:
12448 		    			case icAbsoluteColorimetric:
12449 								ttag = icSigBToA1Tag;
12450 							break;
12451 		    			case icPerceptual:
12452 								ttag = icSigBToA0Tag;
12453 							break;
12454 		    			case icSaturation:
12455 								ttag = icSigBToA2Tag;
12456 							break;
12457 						default:
12458 							sprintf(p->err,"icc_get_luobj: Unknown intent");
12459 							p->errc = 1;
12460 							return NULL;
12461 					}
12462 
12463 					if (order != icmLuOrdRev) {
12464 						/* Try Lut type lookup first */
12465 						if ((luobj = new_icmLuLut(p, ttag,
12466 						     pcs, p->header->colorSpace, pcs,
12467 						     e_pcs, p->header->colorSpace, e_pcs,
12468 						     whitePoint, blackPoint, intent, func)) != NULL)
12469 							break;
12470 
12471 						/* See if it could be a matrix lookup */
12472 						if ((luobj = new_icmLuMatrixBwd(p,
12473 						     pcs, p->header->colorSpace, pcs,
12474 						     e_pcs, p->header->colorSpace, e_pcs,
12475 						     whitePoint, blackPoint, intent, func)) != NULL)
12476 							break;
12477 
12478 						/* See if it could be a monochrome lookup */
12479 						if ((luobj = new_icmLuMonoBwd(p,
12480 						     pcs, p->header->colorSpace, pcs,
12481 						     e_pcs, p->header->colorSpace, e_pcs,
12482 						     whitePoint, blackPoint, intent, func)) != NULL)
12483 							break;
12484 					} else {
12485 						/* See if it could be a monochrome lookup */
12486 						if ((luobj = new_icmLuMonoBwd(p,
12487 						     pcs, p->header->colorSpace, pcs,
12488 						     e_pcs, p->header->colorSpace, e_pcs,
12489 						     whitePoint, blackPoint, intent, func)) != NULL)
12490 							break;
12491 
12492 						/* See if it could be a matrix lookup */
12493 						if ((luobj = new_icmLuMatrixBwd(p,
12494 						     pcs, p->header->colorSpace, pcs,
12495 						     e_pcs, p->header->colorSpace, e_pcs,
12496 						     whitePoint, blackPoint, intent, func)) != NULL)
12497 							break;
12498 
12499 						/* Try Lut type lookup last */
12500 						if ((luobj = new_icmLuLut(p, ttag,
12501 						     pcs, p->header->colorSpace, pcs,
12502 						     e_pcs, p->header->colorSpace, e_pcs,
12503 						     whitePoint, blackPoint, intent, func)) != NULL)
12504 							break;
12505 					}
12506 					break;
12507 
12508 		    	case icmGamut:	/* PCS to 1D */
12509 
12510 					if (intent != icmDefaultIntent) {
12511 						sprintf(p->err,"icc_get_luobj: Intent is inappropriate for type of function");
12512 						p->errc = 1;
12513 						return NULL;
12514 					}
12515 
12516 					/* If the target tag exists, and it is a Lut */
12517 					luobj = new_icmLuLut(p, icSigGamutTag,
12518 					     pcs, icSigGrayData, pcs,
12519 					     e_pcs, icSigGrayData, e_pcs,
12520 					     whitePoint, blackPoint, intent, func);
12521 					break;
12522 
12523 		    	case icmPreview:	/* PCS to PCS */
12524 
12525 					switch (intent)  {
12526 		    			case icRelativeColorimetric:
12527 								ttag = icSigPreview1Tag;
12528 							break;
12529 		    			case icPerceptual:
12530 								ttag = icSigPreview0Tag;
12531 							break;
12532 		    			case icSaturation:
12533 								ttag = icSigPreview2Tag;
12534 							break;
12535 		    			case icAbsoluteColorimetric:
12536 							sprintf(p->err,"icc_get_luobj: Intent is inappropriate for type of function");
12537 							p->errc = 1;
12538 							return NULL;
12539 						default:
12540 							sprintf(p->err,"icc_get_luobj: Unknown intent");
12541 							p->errc = 1;
12542 							return NULL;
12543 					}
12544 
12545 					/* If the target tag exists, and it is a Lut */
12546 					luobj = new_icmLuLut(p, ttag,
12547 					     pcs, pcs, pcs,
12548 					     e_pcs, e_pcs, e_pcs,
12549 					     whitePoint, blackPoint, intent, func);
12550 					break;
12551 
12552 				default:
12553 					sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
12554 					p->errc = 1;
12555 					return NULL;
12556 			}
12557 			break;
12558 
12559     	case icSigLinkClass:
12560 			/* Expect AToB0 Lut and optional BToA0 Lut, One intent in header */
12561 			/* Device <-> Device */
12562 
12563 			if (intent != p->header->renderingIntent
12564 			 && intent != icmDefaultIntent) {
12565 				sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Link profile");
12566 				p->errc = 1;
12567 				return NULL;
12568 			}
12569 			intent = p->header->renderingIntent;
12570 
12571 			/* Determine the algorithm and set its parameters */
12572 			switch (func) {
12573 		    	case icmFwd:	/* Device to PCS (== Device) */
12574 
12575 					luobj = new_icmLuLut(p, icSigAToB0Tag,
12576 					     p->header->colorSpace, pcs, pcs,
12577 					     p->header->colorSpace, pcs, pcs,
12578 					     whitePoint, blackPoint, intent, func);
12579 					break;
12580 
12581 		    	case icmBwd:	/* PCS (== Device) to Device */
12582 
12583 					luobj = new_icmLuLut(p, icSigBToA0Tag,
12584 					     pcs, p->header->colorSpace, pcs,
12585 					     pcs, p->header->colorSpace, pcs,
12586 					     whitePoint, blackPoint, intent, func);
12587 					break;
12588 
12589 				default:
12590 					sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
12591 					p->errc = 1;
12592 					return NULL;
12593 			}
12594 			break;
12595 
12596     	case icSigAbstractClass:
12597 			/* Expect AToB0 Lut and BToA Lut, no intents */
12598 			/* PCS <-> PCS */
12599 			/* Determine the algorithm and set its parameters */
12600 
12601 			if (intent != icmDefaultIntent) {
12602 				sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Abstract profile");
12603 				p->errc = 1;
12604 				return NULL;
12605 			}
12606 
12607 			switch (func) {
12608 		    	case icmFwd:	/* PCS (== Device) to PCS */
12609 
12610 					luobj = new_icmLuLut(p, icSigAToB0Tag,
12611 					     p->header->colorSpace, pcs, pcs,
12612 					     e_pcs, e_pcs, e_pcs,
12613 					     whitePoint, blackPoint, intent, func);
12614 					break;
12615 
12616 		    	case icmBwd:	/* PCS to PCS (== Device) */
12617 
12618 					luobj = new_icmLuLut(p, icSigBToA0Tag,
12619 					     pcs, p->header->colorSpace, pcs,
12620 					     e_pcs, e_pcs, e_pcs,
12621 					     whitePoint, blackPoint, intent, func);
12622 					break;
12623 
12624 				default:
12625 					sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
12626 					p->errc = 1;
12627 					return NULL;
12628 			}
12629 			break;
12630 
12631     	case icSigColorSpaceClass:
12632 			/* Expect AToB0 Lut and BToA0 Lut, no intents, */
12633 			/* Device <-> PCS */
12634 
12635 			if (intent != icmDefaultIntent) {
12636 				sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Colorspace profile");
12637 				p->errc = 1;
12638 				return NULL;
12639 			}
12640 
12641 			/* Determine the algorithm and set its parameters */
12642 			switch (func) {
12643 		    	case icmFwd:	/* Device to PCS */
12644 
12645 					luobj = new_icmLuLut(p, icSigAToB0Tag,
12646 					     p->header->colorSpace, pcs, pcs,
12647 					     p->header->colorSpace, e_pcs, e_pcs,
12648 					     whitePoint, blackPoint, intent, func);
12649 					break;
12650 
12651 		    	case icmBwd:	/* PCS to Device */
12652 
12653 					luobj = new_icmLuLut(p, icSigBToA0Tag,
12654 					     pcs, p->header->colorSpace, pcs,
12655 					     e_pcs, p->header->colorSpace, e_pcs,
12656 					     whitePoint, blackPoint, intent, func);
12657 					break;
12658 
12659 				default:
12660 					sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
12661 					p->errc = 1;
12662 					return NULL;
12663 			}
12664 			break;
12665 
12666     	case icSigNamedColorClass:
12667 			/* Expect Name -> Device, Optional PCS */
12668 			/* and a reverse lookup would be useful */
12669 			/* (ie. PCS or Device coords to closest named color) */
12670 			/* ~~ to be implemented ~~ */
12671 
12672 			/* ~~ Absolute intent is valid for processing of */
12673 			/* PCS from named Colors. Also allow for e_pcs */
12674 			if (intent != icmDefaultIntent) {
12675 				sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Named Color profile");
12676 				p->errc = 1;
12677 				return NULL;
12678 			}
12679 
12680 			sprintf(p->err,"icc_get_luobj: Named Colors not handled yet");
12681 			p->errc = 1;
12682 			return NULL;
12683 
12684     	default:
12685 			sprintf(p->err,"icc_get_luobj: Unknown profile class");
12686 			p->errc = 1;
12687 			return NULL;
12688 	}
12689 
12690 	return luobj;
12691 }
12692 
12693 /* - - - - - - - - - - - - - - - - - - - - - - - - */
12694 
12695 /* Create an empty object. Return null on error */
12696 icc *new_icc_a(
12697 icmAlloc *al			/* Optional memory allocator. NULL for default */
12698 ) {
12699 	icc *p;
12700 	int del_al = 0;
12701 
12702 	if (al == NULL) {	/* None provided, create default */
12703 		if ((al = new_icmAllocStd()) == NULL)
12704 			return NULL;
12705 		del_al = 1;		/* We need to delete it */
12706 	}
12707 
12708 	if ((p = (icc *) al->calloc(al, 1,sizeof(icc))) == NULL)
12709 		return NULL;
12710 	p->al = al;				/* Heap allocator */
12711 	p->del_al = del_al;		/* Flag noting whether we delete it */
12712 
12713 	p->get_size      = icc_get_size;
12714 	p->read          = icc_read;
12715 	p->write         = icc_write;
12716 	p->dump          = icc_dump;
12717 	p->del           = icc_delete;
12718 	p->add_tag       = icc_add_tag;
12719 	p->link_tag      = icc_link_tag;
12720 	p->find_tag      = icc_find_tag;
12721 	p->read_tag      = icc_read_tag;
12722 	p->rename_tag    = icc_rename_tag;
12723 	p->unread_tag    = icc_unread_tag;
12724 	p->read_all_tags = icc_read_all_tags;
12725 	p->delete_tag    = icc_delete_tag;
12726 
12727 	p->get_luobj  = icc_get_luobj;
12728 
12729 #if defined(__IBMC__) && defined(_M_IX86)
12730 	_control87(EM_UNDERFLOW, EM_UNDERFLOW);
12731 #endif
12732 
12733 	/* Allocate a header object */
12734 	if ((p->header = new_icmHeader(p)) == NULL) {
12735 		al->free(al, p);
12736 		if (del_al)
12737 			al->del(al);
12738 		return NULL;
12739 	}
12740 
12741 	/* Values that must be set before writing */
12742 	p->header->deviceClass = icMaxEnumClass;/* Type of profile - must be set! */
12743     p->header->colorSpace = icMaxEnumData;	/* Clr space of data - must be set! */
12744     p->header->pcs = icMaxEnumData;			/* PCS: XYZ or Lab - must be set! */
12745     p->header->renderingIntent = icMaxEnumIntent;	/* Rendering intent - must be set ! */
12746 
12747 	/* Values that should be set before writing */
12748 	p->header->manufacturer = -1;			/* Dev manufacturer - should be set ! */
12749     p->header->model = -1;					/* Dev model number - should be set ! */
12750     p->header->attributes.l = 0;			/* ICC Device attributes - should set ! */
12751     p->header->flags = 0;					/* Embedding flags - should be set ! */
12752 
12753 	/* Values that may be set before writing */
12754     p->header->attributes.h = 0;			/* Dev Device attributes - may be set ! */
12755     p->header->creator = str2tag("argl");	/* Profile creator - Argyll - may be set ! */
12756 
12757 	/* Init default values in header */
12758 	p->header->cmmId = str2tag("argl");		/* CMM for profile - Argyll CMM */
12759     p->header->majv = 2;					/* Current version 2.1.0 */
12760 	p->header->minv = 1;
12761 	p->header->bfv  = 0;
12762 	setcur_DateTimeNumber(&p->header->date);/* Creation Date */
12763     p->header->platform = icSigMicrosoft;	/* Primary Platform */
12764     p->header->illuminant = icmD50;			/* Profile illuminant - D50 */
12765 
12766 	return p;
12767 }
12768 
12769 
12770 /* For backwards compatibility - a NULL allocator version */
12771 icc *new_icc(void) {
12772 	return new_icc_a(NULL);
12773 }
12774 
12775 
12776 /* ---------------------------------------------------------- */
12777