xref: /plan9/sys/src/cmd/gs/icclib/icc.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
1 
2 /*
3  * International Color Consortium Format Library (icclib)
4  * For ICC profile version 3.4
5  *
6  * Author:  Graeme W. Gill
7  * Date:    2001/02/08
8  * Version: 2.00
9  *
10  * Copyright 1997, 1998, 1999, 2000, 2001 Graeme W. Gill
11  * See Licence.txt file for conditions of use.
12  */
13 
14 /*
15  * TTBD:
16  *
17  *      NameColor Dump doesn't handle device space correctly -
18  *	    should use appropriate interpretation in case device is Lab etc.
19  *
20  *      Should recognise & honour unicode 0xFFFE endian marker.
21  *      Should generate it on writing too ?
22  *
23  *		Should fix all write_number failure errors to indicate failed value.
24  *		(Partially implimented - need to check all write_number functions)
25  *
26  *		Make write fail error messages be specific on which element failed.
27  *
28  *		Should add named color space lookup function support.
29  *
30  *		Should probably reject reading or writing profiles with majv != 2 ?
31  *
32  *      Would be nice to add generic ability to add new tag type handling,
33  *      so that the base library doesn't need to be modified (ie. VideoCardGamma) ?
34  *
35  *		Need to add DeviceSettings and OutputResponse tags to bring up to
36  *		ICC.1:1998-09 [started]
37  *
38  */
39 
40 /* Trial: Make the default grid points of the Lab clut be symetrical about */
41 /*        a/b 0.0, and also make L = 100.0 fall on a grid point. */
42 /*        This seems a good idea. */
43 
44 #define SYMETRICAL_DEFAULT_LAB_RANGE
45 
46 /*
47  * Change History:
48  *
49  * 2.00
50  *      Change absolute conversion to be white point only, and use
51  *      Bradford transform by default. (ie. we are now ignoring the
52  *      comment in section 6.4.22 of the 1998 spec. about the
53  *      media black point being used for absolute colorimetry,
54  *      ignoring the recommendation on page 118 in section E.5,
55  *      and are taking up the recommendation on page 124 in section
56  *      E.16 that a more sophisticated chromatic adaption model be used.)
57  *
58  *      This is for better compatibility with other CMM's, and to
59  *      improve the results when using simple links between
60  *      profiles with non-D50 white points. Standard profiles
61  *      like sRGB will also be more accurate when interpreted
62  *      with absolute colorimetric intent.
63  *      This will cause some slight incompatibilty with previous
64  *      versions of icclib.
65  *
66  *      Added ColorSync 2.5 specific VideoCardGamma tag support
67  *      (from Neil Okamoto)
68  *
69  * 1.31
70  *      Added file I/O class to allow substitution of alternative ICC profile
71  *      file access. Provide standard file class instance, and memory image
72  *      instance of file I/O class as default and example.
73  *      Added an optional new_icc_a() object creator, that takes a memory
74  *      allocator class instance. This allows an alternate memory heap to
75  *      be used with the icc class.
76  *      Renamed object free() methods to del() for more consistency with new().
77  *
78  * 1.30
79  *      Added workaround for reading some Adobe profiles with confused header DateTime.
80  *      Enhanced tag allocate() methods so that they can resize allocations.
81  *      Enhanced icmLut_set_tables() to access grid points in a cache friendly order.
82  *      Fixed bug in check_icc_legal() that caused bogus errors, removed
83  *      uneccessary static declarations in icc.h, and fixed a bug in
84  *      icmTable_lookup_bwd() that effected both accuracy and speed. (Thanks to Andrei Frolov)
85  *      Removed icmAbsoluteColorimetricXYZ intent, and replaced it with
86  *      a PCS overide capability. This adds a new parameter to get_luobj()
87  *      Added Lab translations of some XYZ "dump" strings.
88  *      Fix memory leak after failed tag read + rename_tag function
89  *      + shared library support changes. (Thanks to Carles Llopis).
90  *		Changed all the public 2str utility routines to a single function
91  *      that can be used to interpret an enumeration or tag in a human
92  *      readable form.
93  *
94  * 1.23
95  *      Fixed important bug in Lut read/write. The matrix values had their
96  *      rows and columns switched. Not many profiles exercise this code.
97  *      Thanks to David Gillman for discovering this problem.
98  *      Fixup compiler complains about illegal enum values for icmCurveStyle,
99  *      and icmDataStyle. Malloc memory icmLut_lookup_clut_nl for gw[], so that
100  *      it is more friendly to systems with a limited stack. (Thanks to Dave White)
101  *
102  * 1.22	99/11/11 Snapshot of current code.
103  *      Added more hooks to support inherited implimentation of
104  *      color conversion, used in Argyll to support reversing
105  *      multi-dimentional table lookups.
106  *      Cleaned up color conversion code to make it easier to follow.
107  *      Adding simplex interpolation for non-Lab style input space interpolation.
108  *      Fix Sun misalignment and realloc problems (Thanks to Allan N. Hessenflow)
109  *      Fixed endian problem with Unicode on read and write.
110  *      Expanded icmTextDescription_dump() to do hex dump of Unicode and ScriptCode.
111  *      Changed over to ICC.1:1998-09 .h file.
112  *      Started implimenting ICC.1:1998-09, but not complete yet!
113  *
114  * 1.21	99/2/14
115  *     	After re-reading Michael Bourgoin's 1998 SIGGRAPH notes,
116  *      I have consolidated the Lut input index, and table value encodings.
117  *      The default set_tables() scaling has been adjusted appropriately
118  *      for this correction of Lab encoding.
119  *      Trying to create an 8 bit XYZ Lut will now fail if icclib helper
120  *      functions are used to create it.
121  *
122  * 1.20	99/2/7
123  *      Added profile color lookup functon.
124  *      Added set_tables() support.
125  *      Various bug fixes and enhancements.
126  */
127 
128 #include <stdio.h>
129 #include <stdlib.h>
130 #include <stdarg.h>
131 #include <sys/types.h>
132 #include <string.h>
133 #include <ctype.h>
134 #include <math.h>
135 #include <time.h>
136 #ifdef __sun
137 #include <unistd.h>
138 #endif
139 #if defined(__IBMC__) && defined(_M_IX86)
140 #include <float.h>
141 #endif
142 #include "icc.h"
143 
144 /* ========================================================== */
145 /* Default system interface object implimentations */
146 
147 /* Standard Stream file I/O icmFile compatible class */
148 /* Note that this uses malloc, so replace class if */
149 /* you need a different memory allocator. */
150 
151 /* Set current position to offset. Return 0 on success, nz on failure. */
152 static int icmFileStd_seek(
153 icmFile *pp,
154 long int offset
155 ) {
156 	icmFileStd *p = (icmFileStd *)pp;
157 
158 	return fseek(p->fp, offset, SEEK_SET);
159 }
160 
161 /* Read count items of size length. Return number of items successfully read. */
162 static size_t icmFileStd_read(
163 icmFile *pp,
164 void *buffer,
165 size_t size,
166 size_t count
167 ) {
168 	icmFileStd *p = (icmFileStd *)pp;
169 
170 	return fread(buffer, size, count, p->fp);
171 }
172 
173 /* write count items of size length. Return number of items successfully written. */
174 static size_t icmFileStd_write(
175 icmFile *pp,
176 void *buffer,
177 size_t size,
178 size_t count
179 ) {
180 	icmFileStd *p = (icmFileStd *)pp;
181 
182 	return fwrite(buffer, size, count, p->fp);
183 }
184 
185 
186 /* flush all write data out to secondary storage. Return nz on failure. */
187 static int icmFileStd_flush(
188 icmFile *pp
189 ) {
190 	icmFileStd *p = (icmFileStd *)pp;
191 
192 	return fflush(p->fp);
193 }
194 
195 /* we're done with the file object, return nz on failure */
196 static int icmFileStd_delete(
197 icmFile *pp
198 ) {
199 	icmFileStd *p = (icmFileStd *)pp;
200 
201 	if (p->doclose != 0) {
202 		if (fclose(p->fp) != 0)
203 			return 2;
204 	}
205 
206 	free(p);
207 	return 0;
208 }
209 
210 /* Create icmFile given a (binary) FILE* */
211 icmFile *new_icmFileStd_fp(
212 FILE *fp
213 ) {
214 	icmFileStd *p;
215 	if ((p = (icmFileStd *) calloc(1,sizeof(icmFileStd))) == NULL)
216 		return NULL;
217 	p->seek  = icmFileStd_seek;
218 	p->read  = icmFileStd_read;
219 	p->write = icmFileStd_write;
220 	p->flush = icmFileStd_flush;
221 	p->del   = icmFileStd_delete;
222 
223 	p->fp = fp;
224 	p->doclose = 0;
225 
226 	return (icmFile *)p;
227 }
228 
229 /* Create icmFile given a file name */
230 icmFile *new_icmFileStd_name(
231 char *name,
232 char *mode
233 ) {
234 	FILE *fp;
235 	icmFile *p;
236 #if defined(O_BINARY)
237 	char nmode[50];
238 #endif
239 
240 	if ((fp = fopen(name,mode)) == NULL)
241 		return NULL;
242 
243 #if defined(O_BINARY)
244 	strcpy(nmode, mode);
245 	strcat(nmode, "b");
246 	if ((fp = freopen(name, nmode, fp)) == NULL)
247 		return NULL;
248 #endif
249 
250 	p = new_icmFileStd_fp(fp);
251 
252 	if (p != NULL) {
253 		icmFileStd *pp = (icmFileStd *)p;
254 		pp->doclose = 1;
255 	}
256 	return p;
257 }
258 
259 /* ------------------------------------------------- */
260 /* Memory image icmFile compatible class */
261 /* Note that this uses malloc, so replace class if */
262 /* you need a different memory allocator. */
263 
264 /* Set current position to offset. Return 0 on success, nz on failure. */
265 static int icmFileMem_seek(
266 icmFile *pp,
267 long int offset
268 ) {
269 	icmFileMem *p = (icmFileMem *)pp;
270 	unsigned char *np;
271 
272 	np = p->start + offset;
273 	if (np < p->start || np >= p->end)
274 		return 1;
275 	p->cur = np;
276 	return 0;
277 }
278 
279 /* Read count items of size length. Return number of items successfully read. */
280 static size_t icmFileMem_read(
281 icmFile *pp,
282 void *buffer,
283 size_t size,
284 size_t count
285 ) {
286 	icmFileMem *p = (icmFileMem *)pp;
287 	size_t len;
288 
289 	len = size * count;
290 	if ((p->cur + len) >= p->end) {		/* Too much */
291 		if (size > 0)
292 			count = (p->end - p->cur)/size;
293 		else
294 			count = 0;
295 	}
296 	len = size * count;
297 	if (len > 0)
298 		memcpy (buffer, p->cur, len);
299 	p->cur += len;
300 	return count;
301 }
302 
303 /* write count items of size length. Return number of items successfully written. */
304 static size_t icmFileMem_write(
305 icmFile *pp,
306 void *buffer,
307 size_t size,
308 size_t count
309 ) {
310 	icmFileMem *p = (icmFileMem *)pp;
311 	size_t len;
312 
313 	len = size * count;
314 	if ((p->cur + len) >= p->end) {		/* Too much */
315 		if (size > 0)
316 			count = (p->end - p->cur)/size;
317 		else
318 			count = 0;
319 	}
320 	len = size * count;
321 	if (len > 0)
322 		memcpy (p->cur, buffer, len);
323 	p->cur += len;
324 	return count;
325 }
326 
327 
328 /* flush all write data out to secondary storage. Return nz on failure. */
329 static int icmFileMem_flush(
330 icmFile *pp
331 ) {
332 	icmFileMem *p = (icmFileMem *)pp;
333 
334 	return 0;
335 }
336 
337 /* we're done with the file object, return nz on failure */
338 static int icmFileMem_delete(
339 icmFile *pp
340 ) {
341 	icmFileMem *p = (icmFileMem *)pp;
342 
343 	free(p);
344 	return 0;
345 }
346 
347 /* Create a memory image file access class */
348 icmFile *new_icmFileMem(
349 void *base,				/* Pointer to base of memory buffer */
350 size_t length			/* Number of bytes in buffer */
351 ) {
352 	icmFileMem *p;
353 	if ((p = (icmFileMem *) calloc(1,sizeof(icmFileMem))) == NULL)
354 		return NULL;
355 	p->seek  = icmFileMem_seek;
356 	p->read  = icmFileMem_read;
357 	p->write = icmFileMem_write;
358 	p->flush = icmFileMem_flush;
359 	p->del   = icmFileMem_delete;
360 
361 	p->cur = p->start = base;
362 	p->end = p->start + length;
363 
364 	return (icmFile *)p;
365 }
366 
367 /* ------------------------------------------------- */
368 /* Standard Heap allocator icmAlloc compatible class */
369 /* Just call the standard system function */
370 
371 static void *icmAllocStd_malloc(
372 struct _icmAlloc *pp,
373 size_t size
374 ) {
375 	icmAllocStd *p = (icmAllocStd *)pp;
376 
377 	return malloc(size);
378 }
379 
380 static void *icmAllocStd_calloc(
381 struct _icmAlloc *pp,
382 size_t num,
383 size_t size
384 ) {
385 	icmAllocStd *p = (icmAllocStd *)pp;
386 
387 	return calloc(num, size);
388 }
389 
390 void *icmAllocStd_realloc(
391 struct _icmAlloc *pp,
392 void *ptr,
393 size_t size
394 ) {
395 	icmAllocStd *p = (icmAllocStd *)pp;
396 
397 	return realloc(ptr, size);
398 }
399 
400 
401 static void icmAllocStd_free(
402 struct _icmAlloc *pp,
403 void *ptr
404 ) {
405 	icmAllocStd *p = (icmAllocStd *)pp;
406 
407 	free(ptr);
408 }
409 
410 /* we're done with the AllocStd object */
411 static void icmAllocStd_delete(
412 icmAlloc *pp
413 ) {
414 	icmAllocStd *p = (icmAllocStd *)pp;
415 
416 	free(p);
417 }
418 
419 /* Create icmAllocStd */
420 icmAlloc *new_icmAllocStd() {
421 	icmAllocStd *p;
422 	if ((p = (icmAllocStd *) calloc(1,sizeof(icmAllocStd))) == NULL)
423 		return NULL;
424 	p->malloc  = icmAllocStd_malloc;
425 	p->calloc  = icmAllocStd_calloc;
426 	p->realloc = icmAllocStd_realloc;
427 	p->free    = icmAllocStd_free;
428 	p->del     = icmAllocStd_delete;
429 
430 	return (icmAlloc *)p;
431 }
432 
433 /* ========================================================== */
434 /* Conversion support functions */
435 /* Convert between ICC storage types and native C types */
436 /* Write routine return non-zero if numbers can't be represented */
437 
438 /* Unsigned */
439 static unsigned int read_UInt8Number(char *p) {
440 	unsigned int rv;
441 	rv = (unsigned int)((ORD8 *)p)[0];
442 	return rv;
443 }
444 
445 static int write_UInt8Number(unsigned int d, char *p) {
446 	if (d > 255)
447 		return 1;
448 	((ORD8 *)p)[0] = (ORD8)d;
449 	return 0;
450 }
451 
452 static unsigned int read_UInt16Number(char *p) {
453 	unsigned int rv;
454 	rv = 256 * (unsigned int)((ORD8 *)p)[0]
455 	   +       (unsigned int)((ORD8 *)p)[1];
456 	return rv;
457 }
458 
459 static int write_UInt16Number(unsigned int d, char *p) {
460 	if (d > 65535)
461 		return 1;
462 	((ORD8 *)p)[0] = (ORD8)(d >> 8);
463 	((ORD8 *)p)[1] = (ORD8)(d);
464 	return 0;
465 }
466 
467 static unsigned int read_UInt32Number(char *p) {
468 	unsigned int rv;
469 	rv = 16777216 * (unsigned int)((ORD8 *)p)[0]
470 	   +    65536 * (unsigned int)((ORD8 *)p)[1]
471 	   +      256 * (unsigned int)((ORD8 *)p)[2]
472 	   +            (unsigned int)((ORD8 *)p)[3];
473 	return rv;
474 }
475 
476 static int write_UInt32Number(unsigned int d, char *p) {
477 	((ORD8 *)p)[0] = (ORD8)(d >> 24);
478 	((ORD8 *)p)[1] = (ORD8)(d >> 16);
479 	((ORD8 *)p)[2] = (ORD8)(d >> 8);
480 	((ORD8 *)p)[3] = (ORD8)(d);
481 	return 0;
482 }
483 
484 static void read_UInt64Number(uint64 *d, char *p) {
485 	d->h = 16777216 * (unsigned int)((ORD8 *)p)[0]
486 	     +    65536 * (unsigned int)((ORD8 *)p)[1]
487 	     +      256 * (unsigned int)((ORD8 *)p)[2]
488 	     +            (unsigned int)((ORD8 *)p)[3];
489 	d->l = 16777216 * (unsigned int)((ORD8 *)p)[4]
490 	     +    65536 * (unsigned int)((ORD8 *)p)[5]
491 	     +      256 * (unsigned int)((ORD8 *)p)[6]
492 	     +            (unsigned int)((ORD8 *)p)[7];
493 }
494 
495 static int write_UInt64Number(uint64 *d, char *p) {
496 	((ORD8 *)p)[0] = (ORD8)(d->h >> 24);
497 	((ORD8 *)p)[1] = (ORD8)(d->h >> 16);
498 	((ORD8 *)p)[2] = (ORD8)(d->h >> 8);
499 	((ORD8 *)p)[3] = (ORD8)(d->h);
500 	((ORD8 *)p)[4] = (ORD8)(d->l >> 24);
501 	((ORD8 *)p)[5] = (ORD8)(d->l >> 16);
502 	((ORD8 *)p)[6] = (ORD8)(d->l >> 8);
503 	((ORD8 *)p)[7] = (ORD8)(d->l);
504 	return 0;
505 }
506 
507 static double read_U8Fixed8Number(char *p) {
508 	ORD32 o32;
509 	o32 = 256 * (ORD32)((ORD8 *)p)[0]		/* Read big endian 16 bit unsigned */
510         +       (ORD32)((ORD8 *)p)[1];
511 	return (double)o32/256.0;
512 }
513 
514 static int write_U8Fixed8Number(double d, char *p) {
515 	ORD32 o32;
516 	d = d * 256.0 + 0.5;
517 	if (d >= 65536.0)
518 		return 1;
519 	if (d < 0.0)
520 		return 1;
521 	o32 = (ORD32)d;
522 	((ORD8 *)p)[0] = (ORD8)((o32) >> 8);
523 	((ORD8 *)p)[1] = (ORD8)((o32));
524 	return 0;
525 }
526 
527 static double read_U16Fixed16Number(char *p) {
528 	ORD32 o32;
529 	o32 = 16777216 * (ORD32)((ORD8 *)p)[0]		/* Read big endian 32 bit unsigned */
530         +    65536 * (ORD32)((ORD8 *)p)[1]
531 	    +      256 * (ORD32)((ORD8 *)p)[2]
532 	    +            (ORD32)((ORD8 *)p)[3];
533 	return (double)o32/65536.0;
534 }
535 
536 static int write_U16Fixed16Number(double d, char *p) {
537 	ORD32 o32;
538 	d = d * 65536.0 + 0.5;
539 	if (d >= 4294967296.0)
540 		return 1;
541 	if (d < 0.0)
542 		return 1;
543 	o32 = (ORD32)d;
544 	((ORD8 *)p)[0] = (ORD8)((o32) >> 24);
545 	((ORD8 *)p)[1] = (ORD8)((o32) >> 16);
546 	((ORD8 *)p)[2] = (ORD8)((o32) >> 8);
547 	((ORD8 *)p)[3] = (ORD8)((o32));
548 	return 0;
549 }
550 
551 
552 /* Signed numbers */
553 static int read_SInt8Number(char *p) {
554 	int rv;
555 	rv = (int)((INT8 *)p)[0];
556 	return rv;
557 }
558 
559 static int write_SInt8Number(int d, char *p) {
560 	if (d > 127)
561 		return 1;
562 	else if (d < -128)
563 		return 1;
564 	((INT8 *)p)[0] = (INT8)d;
565 	return 0;
566 }
567 
568 static int read_SInt16Number(char *p) {
569 	int rv;
570 	rv = 256 * (int)((INT8 *)p)[0]
571 	   +       (int)((ORD8 *)p)[1];
572 	return rv;
573 }
574 
575 static int write_SInt16Number(int d, char *p) {
576 	if (d > 32767)
577 		return 1;
578 	else if (d < -32768)
579 		return 1;
580 	((INT8 *)p)[0] = (INT8)(d >> 8);
581 	((ORD8 *)p)[1] = (ORD8)(d);
582 	return 0;
583 }
584 
585 static int read_SInt32Number(char *p) {
586 	int rv;
587 	rv = 16777216 * (int)((INT8 *)p)[0]
588 	   +    65536 * (int)((ORD8 *)p)[1]
589 	   +      256 * (int)((ORD8 *)p)[2]
590 	   +            (int)((ORD8 *)p)[3];
591 	return rv;
592 }
593 
594 static int write_SInt32Number(int d, char *p) {
595 	((INT8 *)p)[0] = (INT8)(d >> 24);
596 	((ORD8 *)p)[1] = (ORD8)(d >> 16);
597 	((ORD8 *)p)[2] = (ORD8)(d >> 8);
598 	((ORD8 *)p)[3] = (ORD8)(d);
599 	return 0;
600 }
601 
602 static void read_SInt64Number(int64 *d, char *p) {
603 	d->h = 16777216 * (int)((INT8 *)p)[0]
604 	     +    65536 * (int)((ORD8 *)p)[1]
605 	     +      256 * (int)((ORD8 *)p)[2]
606 	     +            (int)((ORD8 *)p)[3];
607 	d->l = 16777216 * (unsigned int)((ORD8 *)p)[4]
608 	     +    65536 * (unsigned int)((ORD8 *)p)[5]
609 	     +      256 * (unsigned int)((ORD8 *)p)[6]
610 	     +            (unsigned int)((ORD8 *)p)[7];
611 }
612 
613 static int write_SInt64Number(int64 *d, char *p) {
614 	((INT8 *)p)[0] = (INT8)(d->h >> 24);
615 	((ORD8 *)p)[1] = (ORD8)(d->h >> 16);
616 	((ORD8 *)p)[2] = (ORD8)(d->h >> 8);
617 	((ORD8 *)p)[3] = (ORD8)(d->h);
618 	((ORD8 *)p)[4] = (ORD8)(d->l >> 24);
619 	((ORD8 *)p)[5] = (ORD8)(d->l >> 16);
620 	((ORD8 *)p)[6] = (ORD8)(d->l >> 8);
621 	((ORD8 *)p)[7] = (ORD8)(d->l);
622 	return 0;
623 }
624 
625 static double read_S15Fixed16Number(char *p) {
626 	INT32 i32;
627 	i32 = 16777216 * (INT32)((INT8 *)p)[0]		/* Read big endian 32 bit signed */
628         +    65536 * (INT32)((ORD8 *)p)[1]
629 	    +      256 * (INT32)((ORD8 *)p)[2]
630 	    +            (INT32)((ORD8 *)p)[3];
631 	return (double)i32/65536.0;
632 }
633 
634 static int write_S15Fixed16Number(double d, char *p) {
635 	INT32 i32;
636 	d = ceil(d * 65536.0);		/* Beware! (int)(d + 0.5) doesn't work! */
637 	if (d >= 2147483648.0)
638 		return 1;
639 	if (d < -2147483648.0)
640 		return 1;
641 	i32 = (INT32)d;
642 	((INT8 *)p)[0] = (INT8)((i32) >> 24);		/* Write big endian 32 bit signed */
643 	((ORD8 *)p)[1] = (ORD8)((i32) >> 16);
644 	((ORD8 *)p)[2] = (ORD8)((i32) >> 8);
645 	((ORD8 *)p)[3] = (ORD8)((i32));
646 	return 0;
647 }
648 
649 /* PCS encoded numbers */
650 
651 /* 16 bit XYZ  - value range 0.0 - 1.9997 */
652 static double read_PCSXYZ16Number(char *p) {
653 	ORD32 o32;
654 	o32 = 256 * (ORD32)((ORD8 *)p)[0]		/* Read big endian 16 bit unsigned */
655         +       (ORD32)((ORD8 *)p)[1];
656 	return (double)o32/32768.0;
657 }
658 
659 static int write_PCSXYZ16Number(double d, char *p) {
660 	ORD32 o32;
661 	d = d * 32768.0 + 0.5;
662 	if (d >= 65536.0)
663 		return 1;
664 	if (d < 0.0)
665 		return 1;
666 	o32 = (ORD32)d;
667 	((ORD8 *)p)[0] = (ORD8)((o32) >> 8);
668 	((ORD8 *)p)[1] = (ORD8)((o32));
669 	return 0;
670 }
671 
672 /* L part of 8 bit Lab - value range 0.0 - 100.0 */
673 static double read_PCSL8Number(char *p) {
674 	ORD32 o32;
675 	o32 = (ORD32)((ORD8 *)p)[0];		/* Read big endian 8 bit unsigned */
676 	return (double)o32/2.550;
677 }
678 
679 static int write_PCSL8Number(double d, char *p) {
680 	ORD32 o32;
681 	d = d * 2.550 + 0.5;
682 	if (d >= 256.0)
683 		return 1;
684 	if (d < 0.0)
685 		return 1;
686 	o32 = (ORD32)d;
687 	((ORD8 *)p)[0] = (ORD8)((o32));
688 	return 0;
689 }
690 
691 /* ab part of 8 bit Lab - value range -128.0 - 127.0 */
692 static double read_PCSab8Number(char *p) {
693 	ORD32 o32;
694 	o32 = (ORD32)((ORD8 *)p)[0];	/* Read big endian 8 bit unsigned */
695 	return (double)o32-128.0;
696 }
697 
698 static int write_PCSab8Number(double d, char *p) {
699 	ORD32 o32;
700 	d = (d+128.0) + 0.5;
701 	if (d >= 256.0)
702 		return 1;
703 	if (d < 0.0)
704 		return 1;
705 	o32 = (ORD32)d;
706 	((ORD8 *)p)[0] = (ORD8)((o32));
707 	return 0;
708 }
709 
710 /* L part of 16 bit Lab - value range 0.0 - 100.0 */
711 static double read_PCSL16Number(char *p) {
712 	ORD32 o32;
713 	o32 = 256 * (ORD32)((ORD8 *)p)[0]		/* Read big endian 16 bit unsigned */
714         +       (ORD32)((ORD8 *)p)[1];
715 	return (double)o32/652.800;				/* 0xff00/100.0 */
716 }
717 
718 static int write_PCSL16Number(double d, char *p) {
719 	ORD32 o32;
720 	d = d * 652.800 + 0.5;
721 	if (d >= 65536.0)
722 		return 1;
723 	if (d < 0.0)
724 		return 1;
725 	o32 = (ORD32)d;
726 	((ORD8 *)p)[0] = (ORD8)((o32) >> 8);
727 	((ORD8 *)p)[1] = (ORD8)((o32));
728 	return 0;
729 }
730 
731 /* ab part of 16 bit Lab - value range -128.0 - 127.9961 */
732 static double read_PCSab16Number(char *p) {
733 	ORD32 o32;
734 	o32 = 256 * (ORD32)((ORD8 *)p)[0]		/* Read big endian 16 bit unsigned */
735         +       (ORD32)((ORD8 *)p)[1];
736 	return ((double)o32/256.0)-128.0;
737 }
738 
739 static int write_PCSab16Number(double d, char *p) {
740 	ORD32 o32;
741 	d = (d+128.0) * 256.0 + 0.5;
742 	if (d >= 65536.0)
743 		return 1;
744 	if (d < 0.0)
745 		return 1;
746 	o32 = (ORD32)d;
747 	((ORD8 *)p)[0] = (ORD8)((o32) >> 8);
748 	((ORD8 *)p)[1] = (ORD8)((o32));
749 	return 0;
750 }
751 
752 /* Device coordinate as 8 bit value range 0.0 - 1.0 */
753 static double read_DCS8Number(char *p) {
754 	unsigned int rv;
755 	rv =   (unsigned int)((ORD8 *)p)[0];
756 	return (double)rv/255.0;
757 }
758 
759 static int write_DCS8Number(double d, char *p) {
760 	ORD32 o32;
761 	d = d * 255.0 + 0.5;
762 	if (d >= 256.0)
763 		return 1;
764 	if (d < 0.0)
765 		return 1;
766 	o32 = (ORD32)d;
767 	((ORD8 *)p)[0] = (ORD8)(o32);
768 	return 0;
769 }
770 
771 /* Device coordinate as 16 bit value range 0.0 - 1.0 */
772 static double read_DCS16Number(char *p) {
773 	unsigned int rv;
774 	rv = 256 * (unsigned int)((ORD8 *)p)[0]
775 	   +       (unsigned int)((ORD8 *)p)[1];
776 	return (double)rv/65535.0;
777 }
778 
779 static int write_DCS16Number(double d, char *p) {
780 	ORD32 o32;
781 	d = d * 65535.0 + 0.5;
782 	if (d >= 65536.0)
783 		return 1;
784 	if (d < 0.0)
785 		return 1;
786 	o32 = (ORD32)d;
787 	((ORD8 *)p)[0] = (ORD8)(o32 >> 8);
788 	((ORD8 *)p)[1] = (ORD8)(o32);
789 	return 0;
790 }
791 
792 /* ---------------------------------------------------------- */
793 /* Auiliary function - return a string that represents a tag */
794 /* Note - returned buffers are static, can only be used 5 */
795 /* times before buffers get reused. */
796 char *tag2str(
797 	int tag
798 ) {
799 	int i;
800 	static int si = 0;			/* String buffer index */
801 	static char buf[5][20];		/* String buffers */
802 	char *bp;
803 	unsigned char c[4];
804 
805 	bp = buf[si++];
806 	si %= 5;				/* Rotate through buffers */
807 
808 	c[0] = 0xff & (tag >> 24);
809 	c[1] = 0xff & (tag >> 16);
810 	c[2] = 0xff & (tag >> 8);
811 	c[3] = 0xff & (tag >> 0);
812 	for (i = 0; i < 4; i++) {	/* Can we represent it as a string ? */
813 		if (!isprint(c[i]))
814 			break;
815 	}
816 	if (i < 4) {	/* Not printable - use hex */
817 		sprintf(bp,"0x%x",tag);
818 	} else {		/* Printable */
819 		sprintf(bp,"'%c%c%c%c'",c[0],c[1],c[2],c[3]);
820 	}
821 	return bp;
822 }
823 
824 /* Auiliary function - return a tag created from a string */
825 int str2tag(
826 	char *str
827 ) {
828 	unsigned long tag;
829 	tag = (((unsigned long)str[0]) << 24)
830 	    + (((unsigned long)str[1]) << 16)
831 	    + (((unsigned long)str[2]) << 8)
832 	    + (((unsigned long)str[3]));
833 	return (int)tag;
834 }
835 
836 /* helper - return 1 if the string doesn't have a */
837 /*  null terminator, return 0 if it does. */
838 /* Note: will return 1 if len == 0 */
839 static int check_null_string(char *cp, int len) {
840 	for (; len > 0; len--) {
841 		if (*cp++ == '\000')
842 			break;
843 	}
844 	if (len == 0)
845 		return 1;
846 	return 0;
847 }
848 
849 /* helper - return 1 if the string doesn't have a */
850 /*  null terminator, return 0 if it does. */
851 /* Note: will return 1 if len == 0 */
852 /* Unicode version */
853 static int check_null_string16(char *cp, int len) {
854 	for (; len > 0; len--) {	/* Length is in characters */
855 		if (cp[0] == 0 && cp[1] == 0)
856 			break;
857 		cp += 2;
858 	}
859 	if (len == 0)
860 		return 1;
861 	return 0;
862 }
863 
864 /* Color Space to number of component conversion */
865 /* Return 0 on error */
866 static unsigned int number_ColorSpaceSignature(icColorSpaceSignature sig) {
867 	switch(sig) {
868 		case icSigXYZData:
869 			return 3;
870 		case icSigLabData:
871 			return 3;
872 		case icSigLuvData:
873 			return 3;
874 		case icSigYCbCrData:
875 			return 3;
876 		case icSigYxyData:
877 			return 3;
878 		case icSigRgbData:
879 			return 3;
880 		case icSigGrayData:
881 			return 1;
882 		case icSigHsvData:
883 			return 3;
884 		case icSigHlsData:
885 			return 3;
886 		case icSigCmykData:
887 			return 4;
888 		case icSigCmyData:
889 			return 3;
890 		case icSig2colorData:
891 			return 2;
892 		case icSig3colorData:
893 			return 3;
894 		case icSig4colorData:
895 			return 4;
896 		case icSig5colorData:
897 		case icSigMch5Data:
898 			return 5;
899 		case icSig6colorData:
900 		case icSigMch6Data:
901 			return 6;
902 		case icSig7colorData:
903 		case icSigMch7Data:
904 			return 7;
905 		case icSig8colorData:
906 		case icSigMch8Data:
907 			return 8;
908 		case icSig9colorData:
909 			return 9;
910 		case icSig10colorData:
911 			return 10;
912 		case icSig11colorData:
913 			return 11;
914 		case icSig12colorData:
915 			return 12;
916 		case icSig13colorData:
917 			return 13;
918 		case icSig14colorData:
919 			return 14;
920 		case icSig15colorData:
921 			return 15;
922 	}
923 	return 0;
924 }
925 
926 /* ------------------------------------------------------- */
927 /* Flag dump functions */
928 /* Note - returned buffers are static, can only be used 5 */
929 /* times before buffers get reused. */
930 
931 /* Screening Encodings */
932 static char *string_ScreenEncodings(unsigned long flags) {
933 	static int si = 0;			/* String buffer index */
934 	static char buf[5][80];		/* String buffers */
935 	char *bp, *cp;
936 
937 	cp = bp = buf[si++];
938 	si %= 5;				/* Rotate through buffers */
939 
940 	if (flags & icPrtrDefaultScreensTrue) {
941 		sprintf(cp,"Default Screen");
942 	} else {
943 		sprintf(cp,"No Default Screen");
944 	}
945 	cp = cp + strlen(cp);
946 	if (flags & icLinesPerInch) {
947 		sprintf(cp,", Lines Per Inch");
948 	} else {
949 		sprintf(cp,", Lines Per cm");
950 	}
951 	cp = cp + strlen(cp);
952 
953 	return bp;
954 }
955 
956 /* Device attributes */
957 static char *string_DeviceAttributes(unsigned long flags) {
958 	static int si = 0;			/* String buffer index */
959 	static char buf[5][80];		/* String buffers */
960 	char *bp, *cp;
961 
962 	cp = bp = buf[si++];
963 	si %= 5;				/* Rotate through buffers */
964 
965 	if (flags & icTransparency) {
966 		sprintf(cp,"Transparency");
967 	} else {
968 		sprintf(cp,"Reflective");
969 	}
970 	cp = cp + strlen(cp);
971 	if (flags & icMatte) {
972 		sprintf(cp,", Matte");
973 	} else {
974 		sprintf(cp,", Glossy");
975 	}
976 	cp = cp + strlen(cp);
977 
978 	return bp;
979 }
980 
981 /* Profile header flags */
982 static char *string_ProfileHeaderFlags(unsigned long flags) {
983 	static int si = 0;			/* String buffer index */
984 	static char buf[5][80];		/* String buffers */
985 	char *bp, *cp;
986 
987 	cp = bp = buf[si++];
988 	si %= 5;				/* Rotate through buffers */
989 
990 	if (flags & icEmbeddedProfileTrue) {
991 		sprintf(cp,"Embedded Profile");
992 	} else {
993 		sprintf(cp,"Not Embedded Profile");
994 	}
995 	cp = cp + strlen(cp);
996 	if (flags & icUseWithEmbeddedDataOnly) {
997 		sprintf(cp,", Use with embedded data only");
998 	} else {
999 		sprintf(cp,", Use anywhere");
1000 	}
1001 	cp = cp + strlen(cp);
1002 
1003 	return bp;
1004 }
1005 
1006 
1007 static char *string_AsciiOrBinaryData(unsigned long flags) {
1008 	static int si = 0;			/* String buffer index */
1009 	static char buf[5][80];		/* String buffers */
1010 	char *bp, *cp;
1011 
1012 	cp = bp = buf[si++];
1013 	si %= 5;				/* Rotate through buffers */
1014 
1015 	if (flags & icBinaryData) {
1016 		sprintf(cp,"Binary");
1017 	} else {
1018 		sprintf(cp,"Ascii");
1019 	}
1020 	cp = cp + strlen(cp);
1021 
1022 	return bp;
1023 }
1024 
1025 /* ------------------------------------------------------------ */
1026 /* Enumeration dump functions */
1027 /* Note - returned buffers are static, can only be used once */
1028 /* before buffers get reused if type is unknown. */
1029 
1030 /* public tags and sizes */
1031 static char *string_TagSignature(icTagSignature sig) {
1032 	static char buf[80];
1033 	switch(sig) {
1034 		case icSigAToB0Tag:
1035 			return "AToB0 Multidimentional Transform";
1036 		case icSigAToB1Tag:
1037 			return "AToB1 Multidimentional Transform";
1038 		case icSigAToB2Tag:
1039 			return "AToB2 Multidimentional Transform";
1040 		case icSigBlueColorantTag:
1041 			return "Blue Colorant";
1042 		case icSigBlueTRCTag:
1043 			return "Blue Tone Reproduction Curve";
1044 		case icSigBToA0Tag:
1045 			return "BToA0 Multidimentional Transform";
1046 		case icSigBToA1Tag:
1047 			return "BToA1 Multidimentional Transform";
1048 		case icSigBToA2Tag:
1049 			return "BToA2 Multidimentional Transform";
1050 		case icSigCalibrationDateTimeTag:
1051 			return "Calibration Date & Time";
1052 		case icSigCharTargetTag:
1053 			return "Characterization Target";
1054 		case icSigCopyrightTag:
1055 			return "Copyright";
1056 		case icSigCrdInfoTag:
1057 			return "CRD Info";
1058 		case icSigDeviceMfgDescTag:
1059 			return "Device Manufacturer Description";
1060 		case icSigDeviceModelDescTag:
1061 			return "Device Model Description";
1062 		case icSigGamutTag:
1063 			return "Gamut";
1064 		case icSigGrayTRCTag:
1065 			return "Gray Tone Reproduction Curve";
1066 		case icSigGreenColorantTag:
1067 			return "Green Colorant";
1068 		case icSigGreenTRCTag:
1069 			return "Green Tone Reproduction Curve";
1070 		case icSigLuminanceTag:
1071 			return "Luminance";
1072 		case icSigMeasurementTag:
1073 			return "Measurement";
1074 		case icSigMediaBlackPointTag:
1075 			return "Media Black Point";
1076 		case icSigMediaWhitePointTag:
1077 			return "Media White Point";
1078 		case icSigNamedColorTag:
1079 			return "Named Color";
1080 		case icSigNamedColor2Tag:
1081 			return "Named Color 2";
1082 		case icSigPreview0Tag:
1083 			return "Preview0";
1084 		case icSigPreview1Tag:
1085 			return "Preview1";
1086 		case icSigPreview2Tag:
1087 			return "Preview2";
1088 		case icSigProfileDescriptionTag:
1089 			return "Profile Description";
1090 		case icSigProfileSequenceDescTag:
1091 			return "Profile Sequence";
1092 		case icSigPs2CRD0Tag:
1093 			return "PS Level 2 CRD perceptual";
1094 		case icSigPs2CRD1Tag:
1095 			return "PS Level 2 CRD colorimetric";
1096 		case icSigPs2CRD2Tag:
1097 			return "PS Level 2 CRD saturation";
1098 		case icSigPs2CRD3Tag:
1099 			return "PS Level 2 CRD absolute";
1100 		case icSigPs2CSATag:
1101 			return "PS Level 2 color space array";
1102 		case icSigPs2RenderingIntentTag:
1103 			return "PS Level 2 Rendering Intent";
1104 		case icSigRedColorantTag:
1105 			return "Red Colorant";
1106 		case icSigRedTRCTag:
1107 			return "Red Tone Reproduction Curve";
1108 		case icSigScreeningDescTag:
1109 			return "Screening Description";
1110 		case icSigScreeningTag:
1111 			return "Screening Attributes";
1112 		case icSigTechnologyTag:
1113 			return "Device Technology";
1114 		case icSigUcrBgTag:
1115 			return "Under Color Removal & Black Generation";
1116 		case icSigVideoCardGammaTag:
1117 			return "Video Card Gamma Curve";
1118 		case icSigViewingCondDescTag:
1119 			return "Viewing Condition Description";
1120 		case icSigViewingConditionsTag:
1121 			return "Viewing Condition Paramaters";
1122 	}
1123 	sprintf(buf,"Unrecognized - %s",tag2str(sig));
1124 	return buf;
1125 }
1126 
1127 /* technology signature descriptions */
1128 static char *string_TechnologySignature(icTechnologySignature sig) {
1129 	static char buf[80];
1130 	switch(sig) {
1131 		case icSigDigitalCamera:
1132 			return "Digital Camera";
1133 		case icSigFilmScanner:
1134 			return "Film Scanner";
1135 		case icSigReflectiveScanner:
1136 			return "Reflective Scanner";
1137 		case icSigInkJetPrinter:
1138 			return "InkJet Printer";
1139 		case icSigThermalWaxPrinter:
1140 			return "Thermal WaxPrinter";
1141 		case icSigElectrophotographicPrinter:
1142 			return "Electrophotographic Printer";
1143 		case icSigElectrostaticPrinter:
1144 			return "Electrostatic Printer";
1145 		case icSigDyeSublimationPrinter:
1146 			return "DyeSublimation Printer";
1147 		case icSigPhotographicPaperPrinter:
1148 			return "Photographic Paper Printer";
1149 		case icSigFilmWriter:
1150 			return "Film Writer";
1151 		case icSigVideoMonitor:
1152 			return "Video Monitor";
1153 		case icSigVideoCamera:
1154 			return "Video Camera";
1155 		case icSigProjectionTelevision:
1156 			return "Projection Television";
1157 		case icSigCRTDisplay:
1158 			return "Cathode Ray Tube Display";
1159 		case icSigPMDisplay:
1160 			return "Passive Matrix Display";
1161 		case icSigAMDisplay:
1162 			return "Active Matrix Display";
1163 		case icSigPhotoCD:
1164 			return "Photo CD";
1165 		case icSigPhotoImageSetter:
1166 			return "Photo ImageSetter";
1167 		case icSigGravure:
1168 			return "Gravure";
1169 		case icSigOffsetLithography:
1170 			return "Offset Lithography";
1171 		case icSigSilkscreen:
1172 			return "Silkscreen";
1173 		case icSigFlexography:
1174 			return "Flexography";
1175 	}
1176 	sprintf(buf,"Unrecognized - %s",tag2str(sig));
1177 	return buf;
1178 }
1179 
1180 /* type signatures */
1181 static char *string_TypeSignature(icTagTypeSignature sig) {
1182 	static char buf[80];
1183 	switch(sig) {
1184 		case icSigCurveType:
1185 			return "Curve";
1186 		case icSigDataType:
1187 			return "Data";
1188 		case icSigDateTimeType:
1189 			return "DateTime";
1190 		case icSigLut16Type:
1191 			return "Lut16";
1192 		case icSigLut8Type:
1193 			return "Lut8";
1194 		case icSigMeasurementType:
1195 			return "Measurement";
1196 		case icSigNamedColorType:
1197 			return "Named Color";
1198 		case icSigProfileSequenceDescType:
1199 			return "Profile Sequence Desc";
1200 		case icSigS15Fixed16ArrayType:
1201 			return "S15Fixed16 Array";
1202 		case icSigScreeningType:
1203 			return "Screening";
1204 		case icSigSignatureType:
1205 			return "Signature";
1206 		case icSigTextType:
1207 			return "Text";
1208 		case icSigTextDescriptionType:
1209 			return "Text Description";
1210 		case icSigU16Fixed16ArrayType:
1211 			return "U16Fixed16 Array";
1212 		case icSigUcrBgType:
1213 			return "Under Color Removal & Black Generation";
1214 		case icSigUInt16ArrayType:
1215 			return "UInt16 Array";
1216 		case icSigUInt32ArrayType:
1217 			return "UInt32 Array";
1218 		case icSigUInt64ArrayType:
1219 			return "UInt64 Array";
1220 		case icSigUInt8ArrayType:
1221 			return "UInt8 Array";
1222 		case icSigVideoCardGammaType:
1223 			return "Video Card Gamma";
1224 		case icSigViewingConditionsType:
1225 			return "Viewing Conditions";
1226 		case icSigXYZType:
1227 			return "XYZ (Array?)";
1228 		case icSigNamedColor2Type:
1229 			return "Named Color 2";
1230 		case icSigCrdInfoType:
1231 			return "CRD Info";
1232 	}
1233 	sprintf(buf,"Unrecognized - %s",tag2str(sig));
1234 	return buf;
1235 }
1236 
1237 /* Color Space Signatures */
1238 static char *string_ColorSpaceSignature(icColorSpaceSignature sig) {
1239 	static char buf[80];
1240 	switch(sig) {
1241 		case icSigXYZData:
1242 			return "XYZ";
1243 		case icSigLabData:
1244 			return "Lab";
1245 		case icSigLuvData:
1246 			return "Luv";
1247 		case icSigYCbCrData:
1248 			return "YCbCr";
1249 		case icSigYxyData:
1250 			return "Yxy";
1251 		case icSigRgbData:
1252 			return "RGB";
1253 		case icSigGrayData:
1254 			return "Gray";
1255 		case icSigHsvData:
1256 			return "HSV";
1257 		case icSigHlsData:
1258 			return "HLS";
1259 		case icSigCmykData:
1260 			return "CMYK";
1261 		case icSigCmyData:
1262 			return "CMY";
1263 		case icSig2colorData:
1264 			return "2 Color";
1265 		case icSig3colorData:
1266 			return "3 Color";
1267 		case icSig4colorData:
1268 			return "4 Color";
1269 		case icSig5colorData:
1270 		case icSigMch5Data:
1271 			return "5 Color";
1272 		case icSig6colorData:
1273 		case icSigMch6Data:
1274 			return "6 Color";
1275 		case icSig7colorData:
1276 		case icSigMch7Data:
1277 			return "7 Color";
1278 		case icSig8colorData:
1279 		case icSigMch8Data:
1280 			return "8 Color";
1281 		case icSig9colorData:
1282 			return "9 Color";
1283 		case icSig10colorData:
1284 			return "10 Color";
1285 		case icSig11colorData:
1286 			return "11 Color";
1287 		case icSig12colorData:
1288 			return "12 Color";
1289 		case icSig13colorData:
1290 			return "13 Color";
1291 		case icSig14colorData:
1292 			return "14 Color";
1293 		case icSig15colorData:
1294 			return "15 Color";
1295 	}
1296 	sprintf(buf,"Unrecognized - %s",tag2str(sig));
1297 	return buf;
1298 }
1299 
1300 #ifdef NEVER
1301 /* Public version of above */
1302 char *ColorSpaceSignature2str(icColorSpaceSignature sig) {
1303 	return string_ColorSpaceSignature(sig);
1304 }
1305 #endif
1306 
1307 
1308 /* profileClass enumerations */
1309 static char *string_ProfileClassSignature(icProfileClassSignature sig) {
1310 	static char buf[80];
1311 	switch(sig) {
1312 		case icSigInputClass:
1313 			return "Input";
1314 		case icSigDisplayClass:
1315 			return "Display";
1316 		case icSigOutputClass:
1317 			return "Output";
1318 		case icSigLinkClass:
1319 			return "Link";
1320 		case icSigAbstractClass:
1321 			return "Abstract";
1322 		case icSigColorSpaceClass:
1323 			return "Color Space";
1324 		case icSigNamedColorClass:
1325 			return "Named Color";
1326 	}
1327 	sprintf(buf,"Unrecognized - %s",tag2str(sig));
1328 	return buf;
1329 }
1330 
1331 /* Platform Signatures */
1332 static char *string_PlatformSignature(icPlatformSignature sig) {
1333 	static char buf[80];
1334 	switch(sig) {
1335 		case icSigMacintosh:
1336 			return "Macintosh";
1337 		case icSigMicrosoft:
1338 			return "Microsoft";
1339 		case icSigSolaris:
1340 			return "Solaris";
1341 		case icSigSGI:
1342 			return "SGI";
1343 		case icSigTaligent:
1344 			return "Taligent";
1345 	}
1346 	sprintf(buf,"Unrecognized - %s",tag2str(sig));
1347 	return buf;
1348 }
1349 
1350 /* Measurement Geometry, used in the measurmentType tag */
1351 static char *string_MeasurementGeometry(icMeasurementGeometry sig) {
1352 	static char buf[30];
1353 	switch(sig) {
1354 		case icGeometryUnknown:
1355 			return "Unknown";
1356 		case icGeometry045or450:
1357 			return "0/45 or 45/0";
1358 		case icGeometry0dord0:
1359 			return "0/d or d/0";
1360 	}
1361 	sprintf(buf,"Unrecognized - 0x%x",sig);
1362 	return buf;
1363 }
1364 
1365 /* Rendering Intents, used in the profile header */
1366 static char *string_RenderingIntent(icRenderingIntent sig) {
1367 	static char buf[30];
1368 	switch(sig) {
1369 		case icPerceptual:
1370 			return "Perceptual";
1371 	    case icRelativeColorimetric:
1372 	    	return "Relative Colorimetric";
1373 	    case icSaturation:
1374 	    	return "Saturation";
1375 	    case icAbsoluteColorimetric:
1376 	    	return "Absolute Colorimetric";
1377 	}
1378 	sprintf(buf,"Unrecognized - 0x%x",sig);
1379 	return buf;
1380 }
1381 
1382 /* Different Spot Shapes currently defined, used for screeningType */
1383 static char *string_SpotShape(icSpotShape sig) {
1384 	static char buf[30];
1385 	switch(sig) {
1386 		case icSpotShapeUnknown:
1387 			return "Unknown";
1388 		case icSpotShapePrinterDefault:
1389 			return "Printer Default";
1390 		case icSpotShapeRound:
1391 			return "Round";
1392 		case icSpotShapeDiamond:
1393 			return "Diamond";
1394 		case icSpotShapeEllipse:
1395 			return "Ellipse";
1396 		case icSpotShapeLine:
1397 			return "Line";
1398 		case icSpotShapeSquare:
1399 			return "Square";
1400 		case icSpotShapeCross:
1401 			return "Cross";
1402 	}
1403 	sprintf(buf,"Unrecognized - 0x%x",sig);
1404 	return buf;
1405 }
1406 
1407 /* Standard Observer, used in the measurmentType tag */
1408 static char *string_StandardObserver(icStandardObserver sig) {
1409 	static char buf[30];
1410 	switch(sig) {
1411 		case icStdObsUnknown:
1412 			return "Unknown";
1413 		case icStdObs1931TwoDegrees:
1414 			return "1931 Two Degrees";
1415 		case icStdObs1964TenDegrees:
1416 			return "1964 Ten Degrees";
1417 	}
1418 	sprintf(buf,"Unrecognized - 0x%x",sig);
1419 	return buf;
1420 }
1421 
1422 /* Pre-defined illuminants, used in measurement and viewing conditions type */
1423 static char *string_Illuminant(icIlluminant sig) {
1424 	static char buf[30];
1425 	switch(sig) {
1426 		case icIlluminantUnknown:
1427 			return "Unknown";
1428 		case icIlluminantD50:
1429 			return "D50";
1430 		case icIlluminantD65:
1431 			return "D65";
1432 		case icIlluminantD93:
1433 			return "D93";
1434 		case icIlluminantF2:
1435 			return "F2";
1436 		case icIlluminantD55:
1437 			return "D55";
1438 		case icIlluminantA:
1439 			return "A";
1440 		case icIlluminantEquiPowerE:
1441 			return "Equi-Power(E)";
1442 		case icIlluminantF8:
1443 			return "F8";
1444 	}
1445 	sprintf(buf,"Unrecognized - 0x%x",sig);
1446 	return buf;
1447 }
1448 
1449 /* Return a text abreviation of a color lookup algorithm */
1450 static char *string_LuAlg(icmLuAlgType alg) {
1451 	static char buf[80];
1452 
1453 	switch(alg) {
1454     	case icmMonoFwdType:
1455 			return "MonoFwd";
1456     	case icmMonoBwdType:
1457 			return "MonoBwd";
1458     	case icmMatrixFwdType:
1459 			return "MatrixFwd";
1460     	case icmMatrixBwdType:
1461 			return "MatrixBwd";
1462     	case icmLutType:
1463 			return "Lut";
1464 	}
1465 	sprintf(buf,"Unrecognized - %d",alg);
1466 	return buf;
1467 }
1468 
1469 /* Return a string description of the given enumeration value */
1470 /* Public: */
1471 char *icm2str(icmEnumType etype, int enumval) {
1472 
1473 	switch(etype) {
1474 	    case icmScreenEncodings:
1475 			return string_ScreenEncodings((unsigned long) enumval);
1476 	    case icmDeviceAttributes:
1477 			return string_DeviceAttributes((unsigned long) enumval);
1478 		case icmProfileHeaderFlags:
1479 			return string_ProfileHeaderFlags((unsigned long) enumval);
1480 		case icmAsciiOrBinaryData:
1481 			return string_AsciiOrBinaryData((unsigned long) enumval);
1482 		case icmTagSignature:
1483 			return string_TagSignature((icTagSignature) enumval);
1484 		case icmTechnologySignature:
1485 			return string_TechnologySignature((icTechnologySignature) enumval);
1486 		case icmTypeSignature:
1487 			return string_TypeSignature((icTagTypeSignature) enumval);
1488 		case icmColorSpaceSignature:
1489 			return string_ColorSpaceSignature((icColorSpaceSignature) enumval);
1490 		case icmProfileClassSignaure:
1491 			return string_ProfileClassSignature((icProfileClassSignature) enumval);
1492 		case icmPlatformSignature:
1493 			return string_PlatformSignature((icPlatformSignature) enumval);
1494 		case icmMeasurementGeometry:
1495 			return string_MeasurementGeometry((icMeasurementGeometry) enumval);
1496 		case icmRenderingIntent:
1497 			return string_RenderingIntent((icRenderingIntent) enumval);
1498 		case icmSpotShape:
1499 			return string_SpotShape((icSpotShape) enumval);
1500 		case icmStandardObserver:
1501 			return string_StandardObserver((icStandardObserver) enumval);
1502 		case icmIlluminant:
1503 			return string_Illuminant((icIlluminant) enumval);
1504 		case icmLuAlg:
1505 			return string_LuAlg((icmLuAlgType) enumval);
1506 	}
1507 	return "enum2str got unknown type";
1508 }
1509 
1510 /* ========================================================== */
1511 /* Object I/O routines                                        */
1512 /* ========================================================== */
1513 /* icmUInt8Array object */
1514 
1515 /* Return the number of bytes needed to write this tag */
1516 static unsigned int icmUInt8Array_get_size(
1517 	icmBase *pp
1518 ) {
1519 	icmUInt8Array *p = (icmUInt8Array *)pp;
1520 	unsigned int len = 0;
1521 	len += 8;			/* 8 bytes for tag and padding */
1522 	len += p->size * 1;	/* 1 byte for each UInt8 */
1523 	return len;
1524 }
1525 
1526 /* read the object, return 0 on success, error code on fail */
1527 static int icmUInt8Array_read(
1528 	icmBase *pp,
1529 	unsigned long len,		/* tag length */
1530 	unsigned long of		/* start offset within file */
1531 ) {
1532 	icmUInt8Array *p = (icmUInt8Array *)pp;
1533 	icc *icp = p->icp;
1534 	int rv = 0;
1535 	unsigned long i, size;
1536 	char *bp, *buf;
1537 
1538 	if (len < 8) {
1539 		sprintf(icp->err,"icmUInt8Array_read: Tag too small to be legal");
1540 		return icp->errc = 1;
1541 	}
1542 
1543 	/* Allocate a file read buffer */
1544 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
1545 		sprintf(icp->err,"icmUInt8Array_read: malloc() failed");
1546 		return icp->errc = 2;
1547 	}
1548 	bp = buf;
1549 
1550 	/* Read portion of file into buffer */
1551 	if (   icp->fp->seek(icp->fp, of) != 0
1552 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
1553 		sprintf(icp->err,"icmUInt8Array_read: fseek() or fread() failed");
1554 		icp->al->free(icp->al, buf);
1555 		return icp->errc = 1;
1556 	}
1557 	p->size = size = (len - 8)/1;		/* Number of elements in the array */
1558 
1559 	if ((rv = p->allocate((icmBase *)p)) != 0) {
1560 		icp->al->free(icp->al, buf);
1561 		return rv;
1562 	}
1563 
1564 	/* Read type descriptor from the buffer */
1565 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
1566 		icp->al->free(icp->al, buf);
1567 		sprintf(icp->err,"icmUInt8Array_read: Wrong tag type for icmUInt8Array");
1568 		return icp->errc = 1;
1569 	}
1570 	bp += 8;	/* Skip padding */
1571 
1572 	/* Read all the data from the buffer */
1573 	for (i = 0; i < size; i++, bp += 1) {
1574 		p->data[i] = read_UInt8Number(bp);
1575 	}
1576 	icp->al->free(p->icp->al, buf);
1577 	return 0;
1578 }
1579 
1580 /* Write the contents of the object. Return 0 on sucess, error code on failure */
1581 static int icmUInt8Array_write(
1582 	icmBase *pp,
1583 	unsigned long of			/* File offset to write from */
1584 ) {
1585 	icmUInt8Array *p = (icmUInt8Array *)pp;
1586 	icc *icp = p->icp;
1587 	unsigned long i;
1588 	unsigned int len;
1589 	char *bp, *buf;		/* Buffer to write from */
1590 	int rv = 0;
1591 
1592 	/* Allocate a file write buffer */
1593 	len = p->get_size((icmBase *)p);
1594 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
1595 		sprintf(icp->err,"icmUInt8Array_write malloc() failed");
1596 		return icp->errc = 2;
1597 	}
1598 	bp = buf;
1599 
1600 	/* Write type descriptor to the buffer */
1601 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
1602 		sprintf(icp->err,"icmUInt8Array_write: write_SInt32Number() failed");
1603 		icp->al->free(icp->al, buf);
1604 		return icp->errc = rv;
1605 	}
1606 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
1607 	bp += 8;	/* Skip padding */
1608 
1609 	/* Write all the data to the buffer */
1610 	for (i = 0; i < p->size; i++, bp += 1) {
1611 		if ((rv = write_UInt8Number(p->data[i],bp)) != 0) {
1612 			sprintf(icp->err,"icmUInt8Array_write: write_UInt8umber() failed");
1613 			icp->al->free(icp->al, buf);
1614 			return icp->errc = rv;
1615 		}
1616 	}
1617 
1618 	/* Write to the file */
1619 	if (   icp->fp->seek(icp->fp, of) != 0
1620 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
1621 		sprintf(icp->err,"icmUInt8Array_write fseek() or fwrite() failed");
1622 		icp->al->free(icp->al, buf);
1623 		return icp->errc = 2;
1624 	}
1625 	icp->al->free(icp->al, buf);
1626 	return 0;
1627 }
1628 
1629 /* Dump a text description of the object */
1630 static void icmUInt8Array_dump(
1631 	icmBase *pp,
1632 	FILE *op,		/* Output to dump to */
1633 	int   verb		/* Verbosity level */
1634 ) {
1635 	icmUInt8Array *p = (icmUInt8Array *)pp;
1636 	if (verb <= 0)
1637 		return;
1638 
1639 	fprintf(op,"UInt8Array:\n");
1640 	fprintf(op,"  No. elements = %u\n",p->size);
1641 	if (verb >= 2) {
1642 		unsigned long i;
1643 		for (i = 0; i < p->size; i++)
1644 			fprintf(op,"    %u:  %u\n",i,p->data[i]);
1645 	}
1646 }
1647 
1648 /* Allocate variable sized data elements */
1649 static int icmUInt8Array_allocate(
1650 	icmBase *pp
1651 ) {
1652 	icmUInt8Array *p = (icmUInt8Array *)pp;
1653 	icc *icp = p->icp;
1654 
1655 	if (p->size != p->_size) {
1656 		if (p->data != NULL)
1657 			icp->al->free(icp->al, p->data);
1658 		if ((p->data = (unsigned int *) icp->al->malloc(icp->al, p->size * sizeof(unsigned int))) == NULL) {
1659 			sprintf(icp->err,"icmUInt8Array_alloc: malloc() of icmUInt8Array data failed");
1660 			return icp->errc = 2;
1661 		}
1662 		p->_size = p->size;
1663 	}
1664 	return 0;
1665 }
1666 
1667 /* Free all storage in the object */
1668 static void icmUInt8Array_delete(
1669 	icmBase *pp
1670 ) {
1671 	icmUInt8Array *p = (icmUInt8Array *)pp;
1672 	icc *icp = p->icp;
1673 
1674 	if (p->data != NULL)
1675 		icp->al->free(icp->al, p->data);
1676 	icp->al->free(icp->al, p);
1677 }
1678 
1679 /* Create an empty object. Return null on error */
1680 static icmBase *new_icmUInt8Array(
1681 	icc *icp
1682 ) {
1683 	icmUInt8Array *p;
1684 	if ((p = (icmUInt8Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt8Array))) == NULL)
1685 		return NULL;
1686 	p->ttype    = icSigUInt8ArrayType;
1687 	p->refcount = 1;
1688 	p->get_size = icmUInt8Array_get_size;
1689 	p->read     = icmUInt8Array_read;
1690 	p->write    = icmUInt8Array_write;
1691 	p->dump     = icmUInt8Array_dump;
1692 	p->allocate = icmUInt8Array_allocate;
1693 	p->del      = icmUInt8Array_delete;
1694 	p->icp      = icp;
1695 
1696 	return (icmBase *)p;
1697 }
1698 
1699 /* ---------------------------------------------------------- */
1700 /* icmUInt16Array object */
1701 
1702 /* Return the number of bytes needed to write this tag */
1703 static unsigned int icmUInt16Array_get_size(
1704 	icmBase *pp
1705 ) {
1706 	icmUInt16Array *p = (icmUInt16Array *)pp;
1707 	unsigned int len = 0;
1708 	len += 8;			/* 8 bytes for tag and padding */
1709 	len += p->size * 2;	/* 2 bytes for each UInt16 */
1710 	return len;
1711 }
1712 
1713 /* read the object, return 0 on success, error code on fail */
1714 static int icmUInt16Array_read(
1715 	icmBase *pp,
1716 	unsigned long len,		/* tag length */
1717 	unsigned long of		/* start offset within file */
1718 ) {
1719 	icmUInt16Array *p = (icmUInt16Array *)pp;
1720 	icc *icp = p->icp;
1721 	int rv = 0;
1722 	unsigned long i, size;
1723 	char *bp, *buf;
1724 
1725 	if (len < 8) {
1726 		sprintf(icp->err,"icmUInt16Array_read: Tag too small to be legal");
1727 		return icp->errc = 1;
1728 	}
1729 
1730 	/* Allocate a file read buffer */
1731 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
1732 		sprintf(icp->err,"icmUInt16Array_read: malloc() failed");
1733 		return icp->errc = 2;
1734 	}
1735 	bp = buf;
1736 
1737 	/* Read portion of file into buffer */
1738 	if (   icp->fp->seek(icp->fp, of) != 0
1739 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
1740 		sprintf(icp->err,"icmUInt16Array_read: fseek() or fread() failed");
1741 		icp->al->free(icp->al, buf);
1742 		return icp->errc = 1;
1743 	}
1744 	p->size = size = (len - 8)/2;		/* Number of elements in the array */
1745 
1746 	if ((rv = p->allocate((icmBase *)p)) != 0) {
1747 		icp->al->free(icp->al, buf);
1748 		return rv;
1749 	}
1750 
1751 	/* Read type descriptor from the buffer */
1752 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
1753 		sprintf(icp->err,"icmUInt16Array_read: Wrong tag type for icmUInt16Array");
1754 		icp->al->free(icp->al, buf);
1755 		return icp->errc = 1;
1756 	}
1757 	bp += 8;	/* Skip padding */
1758 
1759 	/* Read all the data from the buffer */
1760 	for (i = 0; i < size; i++, bp += 2) {
1761 		p->data[i] = read_UInt16Number(bp);
1762 	}
1763 	icp->al->free(icp->al, buf);
1764 	return 0;
1765 }
1766 
1767 /* Write the contents of the object. Return 0 on sucess, error code on failure */
1768 static int icmUInt16Array_write(
1769 	icmBase *pp,
1770 	unsigned long of			/* File offset to write from */
1771 ) {
1772 	icmUInt16Array *p = (icmUInt16Array *)pp;
1773 	icc *icp = p->icp;
1774 	unsigned long i;
1775 	unsigned int len;
1776 	char *bp, *buf;		/* Buffer to write from */
1777 	int rv = 0;
1778 
1779 	/* Allocate a file write buffer */
1780 	len = p->get_size((icmBase *)p);
1781 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
1782 		sprintf(icp->err,"icmUInt16Array_write malloc() failed");
1783 		return icp->errc = 2;
1784 	}
1785 	bp = buf;
1786 
1787 	/* Write type descriptor to the buffer */
1788 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
1789 		sprintf(icp->err,"icmUInt16Array_write: write_SInt32Number() failed");
1790 		icp->al->free(icp->al, buf);
1791 		return icp->errc = rv;
1792 	}
1793 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
1794 
1795 	/* Write all the data to the buffer */
1796 	bp += 8;	/* Skip padding */
1797 	for (i = 0; i < p->size; i++, bp += 2) {
1798 		if ((rv = write_UInt16Number(p->data[i],bp)) != 0) {
1799 			sprintf(icp->err,"icmUInt16Array_write: write_UInt16umber() failed");
1800 			icp->al->free(icp->al, buf);
1801 			return icp->errc = rv;
1802 		}
1803 	}
1804 
1805 	/* Write to the file */
1806 	if (   icp->fp->seek(icp->fp, of) != 0
1807 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
1808 		sprintf(icp->err,"icmUInt16Array_write fseek() or fwrite() failed");
1809 		icp->al->free(icp->al, buf);
1810 		return icp->errc = 2;
1811 	}
1812 	icp->al->free(icp->al, buf);
1813 	return 0;
1814 }
1815 
1816 /* Dump a text description of the object */
1817 static void icmUInt16Array_dump(
1818 	icmBase *pp,
1819 	FILE *op,		/* Output to dump to */
1820 	int   verb		/* Verbosity level */
1821 ) {
1822 	icmUInt16Array *p = (icmUInt16Array *)pp;
1823 	if (verb <= 0)
1824 		return;
1825 
1826 	fprintf(op,"UInt16Array:\n");
1827 	fprintf(op,"  No. elements = %u\n",p->size);
1828 	if (verb >= 2) {
1829 		unsigned long i;
1830 		for (i = 0; i < p->size; i++)
1831 			fprintf(op,"    %u:  %u\n",i,p->data[i]);
1832 	}
1833 }
1834 
1835 /* Allocate variable sized data elements */
1836 static int icmUInt16Array_allocate(
1837 	icmBase *pp
1838 ) {
1839 	icmUInt16Array *p = (icmUInt16Array *)pp;
1840 	icc *icp = p->icp;
1841 
1842 	if (p->size != p->_size) {
1843 		if (p->data != NULL)
1844 			icp->al->free(icp->al, p->data);
1845 		if ((p->data = (unsigned int *) icp->al->malloc(icp->al, p->size * sizeof(unsigned int))) == NULL) {
1846 			sprintf(icp->err,"icmUInt16Array_alloc: malloc() of icmUInt16Array data failed");
1847 			return icp->errc = 2;
1848 		}
1849 		p->_size = p->size;
1850 	}
1851 	return 0;
1852 }
1853 
1854 /* Free all storage in the object */
1855 static void icmUInt16Array_delete(
1856 	icmBase *pp
1857 ) {
1858 	icmUInt16Array *p = (icmUInt16Array *)pp;
1859 	icc *icp = p->icp;
1860 
1861 	if (p->data != NULL)
1862 		icp->al->free(icp->al, p->data);
1863 	icp->al->free(icp->al, p);
1864 }
1865 
1866 /* Create an empty object. Return null on error */
1867 static icmBase *new_icmUInt16Array(
1868 	icc *icp
1869 ) {
1870 	icmUInt16Array *p;
1871 	if ((p = (icmUInt16Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt16Array))) == NULL)
1872 		return NULL;
1873 	p->ttype    = icSigUInt16ArrayType;
1874 	p->refcount = 1;
1875 	p->get_size = icmUInt16Array_get_size;
1876 	p->read     = icmUInt16Array_read;
1877 	p->write    = icmUInt16Array_write;
1878 	p->dump     = icmUInt16Array_dump;
1879 	p->allocate = icmUInt16Array_allocate;
1880 	p->del      = icmUInt16Array_delete;
1881 	p->icp      = icp;
1882 
1883 	return (icmBase *)p;
1884 }
1885 
1886 /* ---------------------------------------------------------- */
1887 /* icmUInt32Array object */
1888 
1889 /* Return the number of bytes needed to write this tag */
1890 static unsigned int icmUInt32Array_get_size(
1891 	icmBase *pp
1892 ) {
1893 	icmUInt32Array *p = (icmUInt32Array *)pp;
1894 	unsigned int len = 0;
1895 	len += 8;			/* 8 bytes for tag and padding */
1896 	len += p->size * 4;	/* 4 bytes for each UInt32 */
1897 	return len;
1898 }
1899 
1900 /* read the object, return 0 on success, error code on fail */
1901 static int icmUInt32Array_read(
1902 	icmBase *pp,
1903 	unsigned long len,		/* tag length */
1904 	unsigned long of		/* start offset within file */
1905 ) {
1906 	icmUInt32Array *p = (icmUInt32Array *)pp;
1907 	icc *icp = p->icp;
1908 	int rv = 0;
1909 	unsigned long i, size;
1910 	char *bp, *buf;
1911 
1912 	if (len < 8) {
1913 		sprintf(icp->err,"icmUInt32Array_read: Tag too small to be legal");
1914 		return icp->errc = 1;
1915 	}
1916 
1917 	/* Allocate a file read buffer */
1918 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
1919 		sprintf(icp->err,"icmUInt32Array_read: malloc() failed");
1920 		return icp->errc = 2;
1921 	}
1922 	bp = buf;
1923 
1924 	/* Read portion of file into buffer */
1925 	if (   icp->fp->seek(icp->fp, of) != 0
1926 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
1927 		sprintf(icp->err,"icmUInt32Array_read: fseek() or fread() failed");
1928 		icp->al->free(icp->al, buf);
1929 		return icp->errc = 1;
1930 	}
1931 	p->size = size = (len - 8)/4;		/* Number of elements in the array */
1932 
1933 	if ((rv = p->allocate((icmBase *)p)) != 0) {
1934 		icp->al->free(icp->al, buf);
1935 		return rv;
1936 	}
1937 
1938 	/* Read type descriptor from the buffer */
1939 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
1940 		sprintf(icp->err,"icmUInt32Array_read: Wrong tag type for icmUInt32Array");
1941 		icp->al->free(icp->al, buf);
1942 		return icp->errc = 1;
1943 	}
1944 	bp += 8;	/* Skip padding */
1945 
1946 	/* Read all the data from the buffer */
1947 	for (i = 0; i < size; i++, bp += 4) {
1948 		p->data[i] = read_UInt32Number(bp);
1949 	}
1950 	icp->al->free(icp->al, buf);
1951 	return 0;
1952 }
1953 
1954 /* Write the contents of the object. Return 0 on sucess, error code on failure */
1955 static int icmUInt32Array_write(
1956 	icmBase *pp,
1957 	unsigned long of			/* File offset to write from */
1958 ) {
1959 	icmUInt32Array *p = (icmUInt32Array *)pp;
1960 	icc *icp = p->icp;
1961 	unsigned long i;
1962 	unsigned int len;
1963 	char *bp, *buf;		/* Buffer to write from */
1964 	int rv = 0;
1965 
1966 	/* Allocate a file write buffer */
1967 	len = p->get_size((icmBase *)p);
1968 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
1969 		sprintf(icp->err,"icmUInt32Array_write malloc() failed");
1970 		return icp->errc = 2;
1971 	}
1972 	bp = buf;
1973 
1974 	/* Write type descriptor to the buffer */
1975 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
1976 		sprintf(icp->err,"icmUInt32Array_write: write_SInt32Number() failed");
1977 		icp->al->free(icp->al, buf);
1978 		return icp->errc = rv;
1979 	}
1980 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
1981 
1982 	/* Write all the data to the buffer */
1983 	bp += 8;	/* Skip padding */
1984 	for (i = 0; i < p->size; i++, bp += 4) {
1985 		if ((rv = write_UInt32Number(p->data[i],bp)) != 0) {
1986 			sprintf(icp->err,"icmUInt32Array_write: write_UInt32umber() failed");
1987 			icp->al->free(icp->al, buf);
1988 			return icp->errc = rv;
1989 		}
1990 	}
1991 
1992 	/* Write to the file */
1993 	if (   icp->fp->seek(icp->fp, of) != 0
1994 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
1995 		sprintf(icp->err,"icmUInt32Array_write fseek() or fwrite() failed");
1996 		icp->al->free(icp->al, buf);
1997 		return icp->errc = 2;
1998 	}
1999 	icp->al->free(icp->al, buf);
2000 	return 0;
2001 }
2002 
2003 /* Dump a text description of the object */
2004 static void icmUInt32Array_dump(
2005 	icmBase *pp,
2006 	FILE *op,		/* Output to dump to */
2007 	int   verb		/* Verbosity level */
2008 ) {
2009 	icmUInt32Array *p = (icmUInt32Array *)pp;
2010 	if (verb <= 0)
2011 		return;
2012 
2013 	fprintf(op,"UInt32Array:\n");
2014 	fprintf(op,"  No. elements = %u\n",p->size);
2015 	if (verb >= 2) {
2016 		unsigned long i;
2017 		for (i = 0; i < p->size; i++)
2018 			fprintf(op,"    %u:  %u\n",i,p->data[i]);
2019 	}
2020 }
2021 
2022 /* Allocate variable sized data elements */
2023 static int icmUInt32Array_allocate(
2024 	icmBase *pp
2025 ) {
2026 	icmUInt32Array *p = (icmUInt32Array *)pp;
2027 	icc *icp = p->icp;
2028 
2029 	if (p->size != p->_size) {
2030 		if (p->data != NULL)
2031 			icp->al->free(icp->al, p->data);
2032 		if ((p->data = (unsigned int *) icp->al->malloc(icp->al, p->size * sizeof(unsigned int))) == NULL) {
2033 			sprintf(icp->err,"icmUInt32Array_alloc: malloc() of icmUInt32Array data failed");
2034 			return icp->errc = 2;
2035 		}
2036 		p->_size = p->size;
2037 	}
2038 	return 0;
2039 }
2040 
2041 /* Free all storage in the object */
2042 static void icmUInt32Array_delete(
2043 	icmBase *pp
2044 ) {
2045 	icmUInt32Array *p = (icmUInt32Array *)pp;
2046 	icc *icp = p->icp;
2047 
2048 	if (p->data != NULL)
2049 		icp->al->free(icp->al, p->data);
2050 	icp->al->free(icp->al, p);
2051 }
2052 
2053 /* Create an empty object. Return null on error */
2054 static icmBase *new_icmUInt32Array(
2055 	icc *icp
2056 ) {
2057 	icmUInt32Array *p;
2058 	if ((p = (icmUInt32Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt32Array))) == NULL)
2059 		return NULL;
2060 	p->ttype    = icSigUInt32ArrayType;
2061 	p->refcount = 1;
2062 	p->get_size = icmUInt32Array_get_size;
2063 	p->read     = icmUInt32Array_read;
2064 	p->write    = icmUInt32Array_write;
2065 	p->dump     = icmUInt32Array_dump;
2066 	p->allocate = icmUInt32Array_allocate;
2067 	p->del      = icmUInt32Array_delete;
2068 	p->icp      = icp;
2069 
2070 	return (icmBase *)p;
2071 }
2072 
2073 /* ---------------------------------------------------------- */
2074 /* icmUInt64Array object */
2075 
2076 /* Return the number of bytes needed to write this tag */
2077 static unsigned int icmUInt64Array_get_size(
2078 	icmBase *pp
2079 ) {
2080 	icmUInt64Array *p = (icmUInt64Array *)pp;
2081 	unsigned int len = 0;
2082 	len += 8;			/* 8 bytes for tag and padding */
2083 	len += p->size * 8;	/* 8 bytes for each UInt64 */
2084 	return len;
2085 }
2086 
2087 /* read the object, return 0 on success, error code on fail */
2088 static int icmUInt64Array_read(
2089 	icmBase *pp,
2090 	unsigned long len,		/* tag length */
2091 	unsigned long of		/* start offset within file */
2092 ) {
2093 	icmUInt64Array *p = (icmUInt64Array *)pp;
2094 	icc *icp = p->icp;
2095 	int rv = 0;
2096 	unsigned long i, size;
2097 	char *bp, *buf;
2098 
2099 	if (len < 8) {
2100 		sprintf(icp->err,"icmUInt64Array_read: Tag too small to be legal");
2101 		return icp->errc = 1;
2102 	}
2103 
2104 	/* Allocate a file read buffer */
2105 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2106 		sprintf(icp->err,"icmUInt64Array_read: malloc() failed");
2107 		return icp->errc = 2;
2108 	}
2109 	bp = buf;
2110 
2111 	/* Read portion of file into buffer */
2112 	if (   icp->fp->seek(icp->fp, of) != 0
2113 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
2114 		sprintf(icp->err,"icmUInt64Array_read: fseek() or fread() failed");
2115 		icp->al->free(icp->al, buf);
2116 		return icp->errc = 1;
2117 	}
2118 	p->size = size = (len - 8)/8;		/* Number of elements in the array */
2119 
2120 	if ((rv = p->allocate((icmBase *)p)) != 0) {
2121 		icp->al->free(icp->al, buf);
2122 		return rv;
2123 	}
2124 
2125 	/* Read type descriptor from the buffer */
2126 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
2127 		sprintf(icp->err,"icmUInt64Array_read: Wrong tag type for icmUInt64Array");
2128 		icp->al->free(icp->al, buf);
2129 		return icp->errc = 1;
2130 	}
2131 	bp += 8;	/* Skip padding */
2132 
2133 	/* Read all the data from the buffer */
2134 	for (i = 0; i < size; i++, bp += 8) {
2135 		read_UInt64Number(&p->data[i], bp);
2136 	}
2137 	icp->al->free(icp->al, buf);
2138 	return 0;
2139 }
2140 
2141 /* Write the contents of the object. Return 0 on sucess, error code on failure */
2142 static int icmUInt64Array_write(
2143 	icmBase *pp,
2144 	unsigned long of			/* File offset to write from */
2145 ) {
2146 	icmUInt64Array *p = (icmUInt64Array *)pp;
2147 	icc *icp = p->icp;
2148 	unsigned long i;
2149 	unsigned int len;
2150 	char *bp, *buf;		/* Buffer to write from */
2151 	int rv = 0;
2152 
2153 	/* Allocate a file write buffer */
2154 	len = p->get_size((icmBase *)p);
2155 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2156 		sprintf(icp->err,"icmUInt64Array_write malloc() failed");
2157 		return icp->errc = 2;
2158 	}
2159 	bp = buf;
2160 
2161 	/* Write type descriptor to the buffer */
2162 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
2163 		sprintf(icp->err,"icmUInt64Array_write: write_SInt32Number() failed");
2164 		icp->al->free(icp->al, buf);
2165 		return icp->errc = rv;
2166 	}
2167 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
2168 
2169 	/* Write all the data to the buffer */
2170 	bp += 8;	/* Skip padding */
2171 	for (i = 0; i < p->size; i++, bp += 8) {
2172 		if ((rv = write_UInt64Number(&p->data[i],bp)) != 0) {
2173 			sprintf(icp->err,"icmUInt64Array_write: write_UInt64umber() failed");
2174 			icp->al->free(icp->al, buf);
2175 			return icp->errc = rv;
2176 		}
2177 	}
2178 
2179 	/* Write to the file */
2180 	if (   icp->fp->seek(icp->fp, of) != 0
2181 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
2182 		sprintf(icp->err,"icmUInt64Array_write fseek() or fwrite() failed");
2183 		icp->al->free(icp->al, buf);
2184 		return icp->errc = 2;
2185 	}
2186 	icp->al->free(icp->al, buf);
2187 	return 0;
2188 }
2189 
2190 /* Dump a text description of the object */
2191 static void icmUInt64Array_dump(
2192 	icmBase *pp,
2193 	FILE *op,		/* Output to dump to */
2194 	int   verb		/* Verbosity level */
2195 ) {
2196 	icmUInt64Array *p = (icmUInt64Array *)pp;
2197 	if (verb <= 0)
2198 		return;
2199 
2200 	fprintf(op,"UInt64Array:\n");
2201 	fprintf(op,"  No. elements = %u\n",p->size);
2202 	if (verb >= 2) {
2203 		unsigned long i;
2204 		for (i = 0; i < p->size; i++)
2205 			fprintf(op,"    %u:  h=%u, l=%u\n",i,p->data[i].h,p->data[i].l);
2206 	}
2207 }
2208 
2209 /* Allocate variable sized data elements */
2210 static int icmUInt64Array_allocate(
2211 	icmBase *pp
2212 ) {
2213 	icmUInt64Array *p = (icmUInt64Array *)pp;
2214 	icc *icp = p->icp;
2215 
2216 	if (p->size != p->_size) {
2217 		if (p->data != NULL)
2218 			icp->al->free(icp->al, p->data);
2219 		if ((p->data = (uint64 *) icp->al->malloc(icp->al, p->size * sizeof(uint64))) == NULL) {
2220 			sprintf(icp->err,"icmUInt64Array_alloc: malloc() of icmUInt64Array data failed");
2221 			return icp->errc = 2;
2222 		}
2223 		p->_size = p->size;
2224 	}
2225 	return 0;
2226 }
2227 
2228 /* Free all storage in the object */
2229 static void icmUInt64Array_delete(
2230 	icmBase *pp
2231 ) {
2232 	icmUInt64Array *p = (icmUInt64Array *)pp;
2233 	icc *icp = p->icp;
2234 
2235 	if (p->data != NULL)
2236 		icp->al->free(icp->al, p->data);
2237 	icp->al->free(icp->al, p);
2238 }
2239 
2240 /* Create an empty object. Return null on error */
2241 static icmBase *new_icmUInt64Array(
2242 	icc *icp
2243 ) {
2244 	icmUInt64Array *p;
2245 	if ((p = (icmUInt64Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt64Array))) == NULL)
2246 		return NULL;
2247 	p->ttype    = icSigUInt64ArrayType;
2248 	p->refcount = 1;
2249 	p->get_size = icmUInt64Array_get_size;
2250 	p->read     = icmUInt64Array_read;
2251 	p->write    = icmUInt64Array_write;
2252 	p->dump     = icmUInt64Array_dump;
2253 	p->allocate = icmUInt64Array_allocate;
2254 	p->del      = icmUInt64Array_delete;
2255 	p->icp      = icp;
2256 
2257 	return (icmBase *)p;
2258 }
2259 
2260 /* ---------------------------------------------------------- */
2261 /* icmU16Fixed16Array object */
2262 
2263 /* Return the number of bytes needed to write this tag */
2264 static unsigned int icmU16Fixed16Array_get_size(
2265 	icmBase *pp
2266 ) {
2267 	icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
2268 	unsigned int len = 0;
2269 	len += 8;			/* 8 bytes for tag and padding */
2270 	len += p->size * 4;	/* 4 byte for each U16Fixed16 */
2271 	return len;
2272 }
2273 
2274 /* read the object, return 0 on success, error code on fail */
2275 static int icmU16Fixed16Array_read(
2276 	icmBase *pp,
2277 	unsigned long len,		/* tag length */
2278 	unsigned long of		/* start offset within file */
2279 ) {
2280 	icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
2281 	icc *icp = p->icp;
2282 	int rv = 0;
2283 	unsigned long i, size;
2284 	char *bp, *buf;
2285 
2286 	if (len < 8) {
2287 		sprintf(icp->err,"icmU16Fixed16Array_read: Tag too small to be legal");
2288 		return icp->errc = 1;
2289 	}
2290 
2291 	/* Allocate a file read buffer */
2292 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2293 		sprintf(icp->err,"icmU16Fixed16Array_read: malloc() failed");
2294 		return icp->errc = 2;
2295 	}
2296 	bp = buf;
2297 
2298 	/* Read portion of file into buffer */
2299 	if (   icp->fp->seek(icp->fp, of) != 0
2300 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
2301 		sprintf(icp->err,"icmU16Fixed16Array_read: fseek() or fread() failed");
2302 		icp->al->free(icp->al, buf);
2303 		return icp->errc = 1;
2304 	}
2305 	p->size = size = (len - 8)/4;		/* Number of elements in the array */
2306 
2307 	if ((rv = p->allocate((icmBase *)p)) != 0) {
2308 		icp->al->free(icp->al, buf);
2309 		return rv;
2310 	}
2311 
2312 	/* Read type descriptor from the buffer */
2313 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
2314 		sprintf(icp->err,"icmU16Fixed16Array_read: Wrong tag type for icmU16Fixed16Array");
2315 		icp->al->free(icp->al, buf);
2316 		return icp->errc = 1;
2317 	}
2318 	bp += 8;	/* Skip padding */
2319 
2320 	/* Read all the data from the buffer */
2321 	for (i = 0; i < size; i++, bp += 4) {
2322 		p->data[i] = read_U16Fixed16Number(bp);
2323 	}
2324 	icp->al->free(icp->al, buf);
2325 	return 0;
2326 }
2327 
2328 /* Write the contents of the object. Return 0 on sucess, error code on failure */
2329 static int icmU16Fixed16Array_write(
2330 	icmBase *pp,
2331 	unsigned long of			/* File offset to write from */
2332 ) {
2333 	icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
2334 	icc *icp = p->icp;
2335 	unsigned long i;
2336 	unsigned int len;
2337 	char *bp, *buf;		/* Buffer to write from */
2338 	int rv = 0;
2339 
2340 	/* Allocate a file write buffer */
2341 	len = p->get_size((icmBase *)p);
2342 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2343 		sprintf(icp->err,"icmU16Fixed16Array_write malloc() failed");
2344 		return icp->errc = 2;
2345 	}
2346 	bp = buf;
2347 
2348 	/* Write type descriptor to the buffer */
2349 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
2350 		sprintf(icp->err,"icmU16Fixed16Array_write: write_SInt32Number() failed");
2351 		icp->al->free(icp->al, buf);
2352 		return icp->errc = rv;
2353 	}
2354 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
2355 
2356 	/* Write all the data to the buffer */
2357 	bp += 8;	/* Skip padding */
2358 	for (i = 0; i < p->size; i++, bp += 4) {
2359 		if ((rv = write_U16Fixed16Number(p->data[i],bp)) != 0) {
2360 			sprintf(icp->err,"icmU16Fixed16Array_write: write_U16Fixed16umber() failed");
2361 			icp->al->free(icp->al, buf);
2362 			return icp->errc = rv;
2363 		}
2364 	}
2365 
2366 	/* Write to the file */
2367 	if (   icp->fp->seek(icp->fp, of) != 0
2368 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
2369 		sprintf(icp->err,"icmU16Fixed16Array_write fseek() or fwrite() failed");
2370 		icp->al->free(icp->al, buf);
2371 		return icp->errc = 2;
2372 	}
2373 	icp->al->free(icp->al, buf);
2374 	return 0;
2375 }
2376 
2377 /* Dump a text description of the object */
2378 static void icmU16Fixed16Array_dump(
2379 	icmBase *pp,
2380 	FILE *op,		/* Output to dump to */
2381 	int   verb		/* Verbosity level */
2382 ) {
2383 	icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
2384 	if (verb <= 0)
2385 		return;
2386 
2387 	fprintf(op,"U16Fixed16Array:\n");
2388 	fprintf(op,"  No. elements = %u\n",p->size);
2389 	if (verb >= 2) {
2390 		unsigned long i;
2391 		for (i = 0; i < p->size; i++)
2392 			fprintf(op,"    %u:  %f\n",i,p->data[i]);
2393 	}
2394 }
2395 
2396 /* Allocate variable sized data elements */
2397 static int icmU16Fixed16Array_allocate(
2398 	icmBase *pp
2399 ) {
2400 	icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
2401 	icc *icp = p->icp;
2402 
2403 	if (p->size != p->_size) {
2404 		if (p->data != NULL)
2405 			icp->al->free(icp->al, p->data);
2406 		if ((p->data = (double *) icp->al->malloc(icp->al, p->size * sizeof(double))) == NULL) {
2407 			sprintf(icp->err,"icmU16Fixed16Array_alloc: malloc() of icmU16Fixed16Array data failed");
2408 			return icp->errc = 2;
2409 		}
2410 		p->_size = p->size;
2411 	}
2412 	return 0;
2413 }
2414 
2415 /* Free all storage in the object */
2416 static void icmU16Fixed16Array_delete(
2417 	icmBase *pp
2418 ) {
2419 	icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
2420 	icc *icp = p->icp;
2421 
2422 	if (p->data != NULL)
2423 		icp->al->free(icp->al, p->data);
2424 	icp->al->free(icp->al, p);
2425 }
2426 
2427 /* Create an empty object. Return null on error */
2428 static icmBase *new_icmU16Fixed16Array(
2429 	icc *icp
2430 ) {
2431 	icmU16Fixed16Array *p;
2432 	if ((p = (icmU16Fixed16Array *) icp->al->calloc(icp->al,1,sizeof(icmU16Fixed16Array))) == NULL)
2433 		return NULL;
2434 	p->ttype    = icSigU16Fixed16ArrayType;
2435 	p->refcount = 1;
2436 	p->get_size = icmU16Fixed16Array_get_size;
2437 	p->read     = icmU16Fixed16Array_read;
2438 	p->write    = icmU16Fixed16Array_write;
2439 	p->dump     = icmU16Fixed16Array_dump;
2440 	p->allocate = icmU16Fixed16Array_allocate;
2441 	p->del      = icmU16Fixed16Array_delete;
2442 	p->icp      = icp;
2443 
2444 	return (icmBase *)p;
2445 }
2446 
2447 /* ---------------------------------------------------------- */
2448 /* icmS15Fixed16Array object */
2449 
2450 /* Return the number of bytes needed to write this tag */
2451 static unsigned int icmS15Fixed16Array_get_size(
2452 	icmBase *pp
2453 ) {
2454 	icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
2455 	unsigned int len = 0;
2456 	len += 8;			/* 8 bytes for tag and padding */
2457 	len += p->size * 4;	/* 4 byte for each S15Fixed16 */
2458 	return len;
2459 }
2460 
2461 /* read the object, return 0 on success, error code on fail */
2462 static int icmS15Fixed16Array_read(
2463 	icmBase *pp,
2464 	unsigned long len,		/* tag length */
2465 	unsigned long of		/* start offset within file */
2466 ) {
2467 	icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
2468 	icc *icp = p->icp;
2469 	int rv = 0;
2470 	unsigned long i, size;
2471 	char *bp, *buf;
2472 
2473 	if (len < 8) {
2474 		sprintf(icp->err,"icmS15Fixed16Array_read: Tag too small to be legal");
2475 		return icp->errc = 1;
2476 	}
2477 
2478 	/* Allocate a file read buffer */
2479 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2480 		sprintf(icp->err,"icmS15Fixed16Array_read: malloc() failed");
2481 		return icp->errc = 2;
2482 	}
2483 	bp = buf;
2484 
2485 	/* Read portion of file into buffer */
2486 	if (   icp->fp->seek(icp->fp, of) != 0
2487 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
2488 		sprintf(icp->err,"icmS15Fixed16Array_read: fseek() or fread() failed");
2489 		icp->al->free(icp->al, buf);
2490 		return icp->errc = 1;
2491 	}
2492 	p->size = size = (len - 8)/4;		/* Number of elements in the array */
2493 
2494 	if ((rv = p->allocate((icmBase *)p)) != 0) {
2495 		icp->al->free(icp->al, buf);
2496 		return rv;
2497 	}
2498 
2499 	/* Read type descriptor from the buffer */
2500 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
2501 		sprintf(icp->err,"icmS15Fixed16Array_read: Wrong tag type for icmS15Fixed16Array");
2502 		icp->al->free(icp->al, buf);
2503 		return icp->errc = 1;
2504 	}
2505 	bp += 8;	/* Skip padding */
2506 
2507 	/* Read all the data from the buffer */
2508 	for (i = 0; i < size; i++, bp += 4) {
2509 		p->data[i] = read_S15Fixed16Number(bp);
2510 	}
2511 	icp->al->free(icp->al, buf);
2512 	return 0;
2513 }
2514 
2515 /* Write the contents of the object. Return 0 on sucess, error code on failure */
2516 static int icmS15Fixed16Array_write(
2517 	icmBase *pp,
2518 	unsigned long of			/* File offset to write from */
2519 ) {
2520 	icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
2521 	icc *icp = p->icp;
2522 	unsigned long i;
2523 	unsigned int len;
2524 	char *bp, *buf;		/* Buffer to write from */
2525 	int rv = 0;
2526 
2527 	/* Allocate a file write buffer */
2528 	len = p->get_size((icmBase *)p);
2529 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2530 		sprintf(icp->err,"icmS15Fixed16Array_write malloc() failed");
2531 		return icp->errc = 2;
2532 	}
2533 	bp = buf;
2534 
2535 	/* Write type descriptor to the buffer */
2536 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
2537 		sprintf(icp->err,"icmS15Fixed16Array_write: write_SInt32Number() failed");
2538 		icp->al->free(icp->al, buf);
2539 		return icp->errc = rv;
2540 	}
2541 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
2542 
2543 	/* Write all the data to the buffer */
2544 	bp += 8;	/* Skip padding */
2545 	for (i = 0; i < p->size; i++, bp += 4) {
2546 		if ((rv = write_S15Fixed16Number(p->data[i],bp)) != 0) {
2547 			sprintf(icp->err,"icmS15Fixed16Array_write: write_S15Fixed16umber() failed");
2548 			icp->al->free(icp->al, buf);
2549 			return icp->errc = rv;
2550 		}
2551 	}
2552 
2553 	/* Write to the file */
2554 	if (   icp->fp->seek(icp->fp, of) != 0
2555 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
2556 		sprintf(icp->err,"icmS15Fixed16Array_write fseek() or fwrite() failed");
2557 		icp->al->free(icp->al, buf);
2558 		return icp->errc = 2;
2559 	}
2560 	icp->al->free(icp->al, buf);
2561 	return 0;
2562 }
2563 
2564 /* Dump a text description of the object */
2565 static void icmS15Fixed16Array_dump(
2566 	icmBase *pp,
2567 	FILE *op,		/* Output to dump to */
2568 	int   verb		/* Verbosity level */
2569 ) {
2570 	icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
2571 	if (verb <= 0)
2572 		return;
2573 
2574 	fprintf(op,"S15Fixed16Array:\n");
2575 	fprintf(op,"  No. elements = %u\n",p->size);
2576 	if (verb >= 2) {
2577 		unsigned long i;
2578 		for (i = 0; i < p->size; i++)
2579 			fprintf(op,"    %u:  %f\n",i,p->data[i]);
2580 	}
2581 }
2582 
2583 /* Allocate variable sized data elements */
2584 static int icmS15Fixed16Array_allocate(
2585 	icmBase *pp
2586 ) {
2587 	icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
2588 	icc *icp = p->icp;
2589 
2590 	if (p->size != p->_size) {
2591 		if (p->data != NULL)
2592 			icp->al->free(icp->al, p->data);
2593 		if ((p->data = (double *) icp->al->malloc(icp->al, p->size * sizeof(double))) == NULL) {
2594 			sprintf(icp->err,"icmS15Fixed16Array_alloc: malloc() of icmS15Fixed16Array data failed");
2595 			return icp->errc = 2;
2596 		}
2597 		p->_size = p->size;
2598 	}
2599 	return 0;
2600 }
2601 
2602 /* Free all storage in the object */
2603 static void icmS15Fixed16Array_delete(
2604 	icmBase *pp
2605 ) {
2606 	icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
2607 	icc *icp = p->icp;
2608 
2609 	if (p->data != NULL)
2610 		icp->al->free(icp->al, p->data);
2611 	icp->al->free(icp->al, p);
2612 }
2613 
2614 /* Create an empty object. Return null on error */
2615 static icmBase *new_icmS15Fixed16Array(
2616 	icc *icp
2617 ) {
2618 	icmS15Fixed16Array *p;
2619 	if ((p = (icmS15Fixed16Array *) icp->al->calloc(icp->al,1,sizeof(icmS15Fixed16Array))) == NULL)
2620 		return NULL;
2621 	p->ttype    = icSigS15Fixed16ArrayType;
2622 	p->refcount = 1;
2623 	p->get_size = icmS15Fixed16Array_get_size;
2624 	p->read     = icmS15Fixed16Array_read;
2625 	p->write    = icmS15Fixed16Array_write;
2626 	p->dump     = icmS15Fixed16Array_dump;
2627 	p->allocate = icmS15Fixed16Array_allocate;
2628 	p->del      = icmS15Fixed16Array_delete;
2629 	p->icp      = icp;
2630 
2631 	return (icmBase *)p;
2632 }
2633 
2634 /* ---------------------------------------------------------- */
2635 
2636 /* Data conversion support functions */
2637 static int write_XYZNumber(icmXYZNumber *p, char *d) {
2638 	int rv;
2639 	if ((rv = write_S15Fixed16Number(p->X, d + 0)) != 0)
2640 		return rv;
2641 	if ((rv = write_S15Fixed16Number(p->Y, d + 4)) != 0)
2642 		return rv;
2643 	if ((rv = write_S15Fixed16Number(p->Z, d + 8)) != 0)
2644 		return rv;
2645 	return 0;
2646 }
2647 
2648 static int read_XYZNumber(icmXYZNumber *p, char *d) {
2649 	p->X = read_S15Fixed16Number(d + 0);
2650 	p->Y = read_S15Fixed16Number(d + 4);
2651 	p->Z = read_S15Fixed16Number(d + 8);
2652 	return 0;
2653 }
2654 
2655 
2656 /* Helper: Return a string that shows the XYZ number value */
2657 static char *string_XYZNumber(icmXYZNumber *p) {
2658 	static char buf[40];
2659 
2660 	sprintf(buf,"%f, %f, %f", p->X, p->Y, p->Z);
2661 	return buf;
2662 }
2663 
2664 /* Helper: Return a string that shows the XYZ number value, */
2665 /* and the Lab D50 number in paren. */
2666 static char *string_XYZNumber_and_Lab(icmXYZNumber *p) {
2667 	static char buf[50];
2668 	double lab[3];
2669 	lab[0] = p->X;
2670 	lab[1] = p->Y;
2671 	lab[2] = p->Z;
2672 	icmXYZ2Lab(&icmD50, lab, lab);
2673 	sprintf(buf,"%f, %f, %f    [Lab %f, %f, %f]", p->X, p->Y, p->Z, lab[0], lab[1], lab[2]);
2674 	return buf;
2675 }
2676 
2677 /* icmXYZArray object */
2678 
2679 /* Return the number of bytes needed to write this tag */
2680 static unsigned int icmXYZArray_get_size(
2681 	icmBase *pp
2682 ) {
2683 	icmXYZArray *p = (icmXYZArray *)pp;
2684 	unsigned int len = 0;
2685 	len += 8;				/* 8 bytes for tag and padding */
2686 	len += p->size * 12;	/* 12 bytes for each XYZ */
2687 	return len;
2688 }
2689 
2690 /* read the object, return 0 on success, error code on fail */
2691 static int icmXYZArray_read(
2692 	icmBase *pp,
2693 	unsigned long len,		/* tag length */
2694 	unsigned long of		/* start offset within file */
2695 ) {
2696 	icmXYZArray *p = (icmXYZArray *)pp;
2697 	icc *icp = p->icp;
2698 	int rv = 0;
2699 	unsigned long i, size;
2700 	char *bp, *buf;
2701 
2702 	if (len < 8) {
2703 		sprintf(icp->err,"icmXYZArray_read: Tag too small to be legal");
2704 		return icp->errc = 1;
2705 	}
2706 
2707 	/* Allocate a file read buffer */
2708 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2709 		sprintf(icp->err,"icmXYZArray_read: malloc() failed");
2710 		return icp->errc = 2;
2711 	}
2712 	bp = buf;
2713 
2714 	/* Read portion of file into buffer */
2715 	if (   icp->fp->seek(icp->fp, of) != 0
2716 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
2717 		sprintf(icp->err,"icmXYZArray_read: fseek() or fread() failed");
2718 		icp->al->free(icp->al, buf);
2719 		return icp->errc = 1;
2720 	}
2721 	p->size = size = (len - 8)/12;		/* Number of elements in the array */
2722 
2723 	if ((rv = p->allocate((icmBase *)p)) != 0) {
2724 		icp->al->free(icp->al, buf);
2725 		return rv;
2726 	}
2727 
2728 	/* Read type descriptor from the buffer */
2729 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
2730 		sprintf(icp->err,"icmXYZArray_read: Wrong tag type for icmXYZArray");
2731 		icp->al->free(icp->al, buf);
2732 		return icp->errc = 1;
2733 	}
2734 	bp += 8;	/* Skip padding */
2735 
2736 	/* Read all the data from the buffer */
2737 	for (i = 0; i < size; i++, bp += 12) {
2738 		read_XYZNumber(&p->data[i], bp);
2739 	}
2740 	icp->al->free(icp->al, buf);
2741 	return 0;
2742 }
2743 
2744 /* Write the contents of the object. Return 0 on sucess, error code on failure */
2745 static int icmXYZArray_write(
2746 	icmBase *pp,
2747 	unsigned long of			/* File offset to write from */
2748 ) {
2749 	icmXYZArray *p = (icmXYZArray *)pp;
2750 	icc *icp = p->icp;
2751 	unsigned long i;
2752 	unsigned int len;
2753 	char *bp, *buf;		/* Buffer to write from */
2754 	int rv = 0;
2755 
2756 	/* Allocate a file write buffer */
2757 	len = p->get_size((icmBase *)p);
2758 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
2759 		sprintf(icp->err,"icmXYZArray_write malloc() failed");
2760 		return icp->errc = 2;
2761 	}
2762 	bp = buf;
2763 
2764 	/* Write type descriptor to the buffer */
2765 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
2766 		sprintf(icp->err,"icmXYZArray_write: write_SInt32Number() failed");
2767 		icp->al->free(icp->al, buf);
2768 		return icp->errc = rv;
2769 	}
2770 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
2771 
2772 	/* Write all the data to the buffer */
2773 	bp += 8;	/* Skip padding */
2774 	for (i = 0; i < p->size; i++, bp += 12) {
2775 		if ((rv = write_XYZNumber(&p->data[i],bp)) != 0) {
2776 			sprintf(icp->err,"icmXYZArray_write: write_XYZumber() failed");
2777 			icp->al->free(icp->al, buf);
2778 			return icp->errc = rv;
2779 		}
2780 	}
2781 
2782 	/* Write to the file */
2783 	if (   icp->fp->seek(icp->fp, of) != 0
2784 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
2785 		sprintf(icp->err,"icmXYZArray_write fseek() or fwrite() failed");
2786 		icp->al->free(icp->al, buf);
2787 		return icp->errc = 2;
2788 	}
2789 	icp->al->free(icp->al, buf);
2790 	return 0;
2791 }
2792 
2793 /* Dump a text description of the object */
2794 static void icmXYZArray_dump(
2795 	icmBase *pp,
2796 	FILE *op,		/* Output to dump to */
2797 	int   verb		/* Verbosity level */
2798 ) {
2799 	icmXYZArray *p = (icmXYZArray *)pp;
2800 	if (verb <= 0)
2801 		return;
2802 
2803 	fprintf(op,"XYZArray:\n");
2804 	fprintf(op,"  No. elements = %u\n",p->size);
2805 	if (verb >= 2) {
2806 		unsigned long i;
2807 		for (i = 0; i < p->size; i++) {
2808 			fprintf(op,"    %u:  %s\n",i,string_XYZNumber_and_Lab(&p->data[i]));
2809 
2810 		}
2811 	}
2812 }
2813 
2814 
2815 /* Allocate variable sized data elements */
2816 static int icmXYZArray_allocate(
2817 	icmBase *pp
2818 ) {
2819 	icmXYZArray *p = (icmXYZArray *)pp;
2820 	icc *icp = p->icp;
2821 
2822 	if (p->size != p->_size) {
2823 		if (p->data != NULL)
2824 			icp->al->free(icp->al, p->data);
2825 		if ((p->data = (icmXYZNumber *) icp->al->malloc(icp->al, p->size * sizeof(icmXYZNumber))) == NULL) {
2826 			sprintf(icp->err,"icmXYZArray_alloc: malloc() of icmXYZArray data failed");
2827 			return icp->errc = 2;
2828 		}
2829 		p->_size = p->size;
2830 	}
2831 	return 0;
2832 }
2833 
2834 /* Free all storage in the object */
2835 static void icmXYZArray_delete(
2836 	icmBase *pp
2837 ) {
2838 	icmXYZArray *p = (icmXYZArray *)pp;
2839 	icc *icp = p->icp;
2840 
2841 	if (p->data != NULL)
2842 		icp->al->free(icp->al, p->data);
2843 	icp->al->free(icp->al, p);
2844 }
2845 
2846 /* Create an empty object. Return null on error */
2847 static icmBase *new_icmXYZArray(
2848 	icc *icp
2849 ) {
2850 	icmXYZArray *p;
2851 	if ((p = (icmXYZArray *) icp->al->calloc(icp->al,1,sizeof(icmXYZArray))) == NULL)
2852 		return NULL;
2853 	p->ttype    = icSigXYZArrayType;
2854 	p->refcount = 1;
2855 	p->get_size = icmXYZArray_get_size;
2856 	p->read     = icmXYZArray_read;
2857 	p->write    = icmXYZArray_write;
2858 	p->dump     = icmXYZArray_dump;
2859 	p->allocate = icmXYZArray_allocate;
2860 	p->del      = icmXYZArray_delete;
2861 	p->icp      = icp;
2862 
2863 	return (icmBase *)p;
2864 }
2865 
2866 /* ---------------------------------------------------------- */
2867 /* icmCurve object */
2868 
2869 /* Do a forward lookup through the curve */
2870 /* Return 0 on success, 1 if clipping occured, 2 on other error */
2871 static int icmCurve_lookup_fwd(
2872 	icmCurve *p,
2873 	double *out,
2874 	double *in
2875 ) {
2876 	int rv = 0;
2877 	if (p->flag == icmCurveLin) {
2878 		*out = *in;
2879 	} else if (p->flag == icmCurveGamma) {
2880 		double val = *in;
2881 		if (val <= 0.0)
2882 			*out = 0.0;
2883 		else
2884 			*out = pow(val, p->data[0]);
2885 	} else { /* Use linear interpolation */
2886 		int ix;
2887 		double val, w;
2888 		double inputEnt_1 = (double)(p->size-1);
2889 
2890 		val = *in * inputEnt_1;
2891 		if (val < 0.0) {
2892 			val = 0.0;
2893 			rv |= 1;
2894 		} else if (val > inputEnt_1) {
2895 			val = inputEnt_1;
2896 			rv |= 1;
2897 		}
2898 		ix = (int)floor(val);		/* Coordinate */
2899 		if (ix > (p->size-2))
2900 			ix = (p->size-2);
2901 		w = val - (double)ix;		/* weight */
2902 		val = p->data[ix];
2903 		*out = val + w * (p->data[ix+1] - val);
2904 	}
2905 	return rv;
2906 }
2907 
2908 /* - - - - - - - - - - - - */
2909 /* Support for reverse interpolation of 1D lookup tables */
2910 
2911 /* Create a reverse curve lookup acceleration table */
2912 /* return non-zero on error, 2 = malloc error. */
2913 static int icmTable_setup_bwd(
2914 	icc          *icp,			/* Base icc object */
2915 	icmRevTable  *rt,			/* Reverse table data to setup */
2916 	unsigned long size,			/* Size of fwd table */
2917 	double       *data			/* Table */
2918 ) {
2919 	int i;
2920 
2921 	rt->size = size;		/* Stash pointers to these away */
2922 	rt->data = data;
2923 
2924 	/* Find range of output values */
2925 	rt->rmin = 1e300;
2926 	rt->rmax = -1e300;
2927 	for (i = 0; i < rt->size; i++) {
2928 		if (rt->data[i] > rt->rmax)
2929 			rt->rmax = rt->data[i];
2930 		if (rt->data[i] < rt->rmin)
2931 			rt->rmin = rt->data[i];
2932 	}
2933 
2934 	/* Decide on reverse granularity */
2935 	rt->rsize = (rt->size+2)/2;
2936 	rt->qscale = (double)rt->rsize/(rt->rmax - rt->rmin);	/* Scale factor to quantize to */
2937 
2938 	/* Initialize the reverse lookup structures, and get overall min/max */
2939 	if ((rt->rlists = (int **) icp->al->calloc(icp->al, 1, rt->rsize * sizeof(int *))) == NULL) {
2940 		return 2;
2941 	}
2942 
2943 	/* Assign each output value range bucket lists it intersects */
2944 	for (i = 0; i < (rt->size-1); i++) {
2945 		int s, e, j;	/* Start and end indexes (inclusive) */
2946 		s = ((rt->data[i] - rt->rmin) * rt->qscale);
2947 		e = ((rt->data[i+1] - rt->rmin) * rt->qscale);
2948 		if (s > e) {	/* swap */
2949 			int t;
2950 			t = s; s = e; e = t;
2951 		}
2952 		if (e >= rt->rsize)
2953 			e = rt->rsize-1;
2954 
2955 		/* For all buckets that may contain this output range, add index of this output */
2956 		for (j = s; j <= e; j++) {
2957 			int as;			/* Allocation size */
2958 			int nf;			/* Next free slot */
2959 			if (rt->rlists[j] == NULL) {	/* No allocation */
2960 				as = 5;						/* Start with space for 5 */
2961 				if ((rt->rlists[j] = (int *) icp->al->malloc(icp->al, sizeof(int) * as)) == NULL) {
2962 					return 2;
2963 				}
2964 				rt->rlists[j][0] = as;
2965 				nf = rt->rlists[j][1] = 2;
2966 			} else {
2967 				as = rt->rlists[j][0];	/* Allocate space for this list */
2968 				nf = rt->rlists[j][1];	/* Next free location in list */
2969 				if (nf >= as) {			/* need to expand space */
2970 					as *= 2;
2971 					rt->rlists[j] = (int *) icp->al->realloc(icp->al,rt->rlists[j], sizeof(int) * as);
2972 					if (rt->rlists[j] == NULL) {
2973 						return 2;
2974 					}
2975 					rt->rlists[j][0] = as;
2976 				}
2977 			}
2978 			rt->rlists[j][nf++] = i;
2979 			rt->rlists[j][1] = nf;
2980 		}
2981 	}
2982 	rt->inited = 1;
2983 	return 0;
2984 }
2985 
2986 /* Free up any data */
2987 static void icmTable_delete_bwd(
2988 	icc          *icp,			/* Base icc */
2989 	icmRevTable  *rt			/* Reverse table data to setup */
2990 ) {
2991 	if (rt->inited != 0) {
2992 		while (rt->rsize > 0)
2993 			icp->al->free(icp->al, rt->rlists[--rt->rsize]);
2994 		icp->al->free(icp->al, rt->rlists);
2995 		rt->size = 0;			/* Don't keep these */
2996 		rt->data = NULL;
2997 	}
2998 }
2999 
3000 /* Do a reverse lookup through the curve */
3001 /* Return 0 on success, 1 if clipping occured, 2 on other error */
3002 static int icmTable_lookup_bwd(
3003 	icmRevTable *rt,
3004 	double *out,
3005 	double *in
3006 ) {
3007 	int rv = 0;
3008 	int ix, i, k;
3009 	double oval, ival = *in, val;
3010 	double rsize_1;
3011 
3012 	/* Find appropriate reverse list */
3013 	rsize_1 = (double)(rt->rsize-1);
3014 	val = ((ival - rt->rmin) * rt->qscale);
3015 	if (val < 0.0)
3016 		val = 0.0;
3017 	else if (val > rsize_1)
3018 		val = rsize_1;
3019 	ix = (int)floor(val);		/* Coordinate */
3020 
3021 	if (ix > (rt->size-2))
3022 		ix = (rt->size-2);
3023 	if (rt->rlists[ix] != NULL)  {		/* There is a list of fwd candidates */
3024 		/* For each candidate forward range */
3025 		for (i = 2; i < rt->rlists[ix][1]; i++)  {	/* For all fwd indexes */
3026 			double lv,hv;
3027 			k = rt->rlists[ix][i];					/* Base index */
3028 			lv = rt->data[k];
3029 			hv = rt->data[k+1];
3030 			if ((ival >= lv && ival <= hv)	/* If this slot contains output value */
3031 			 || (ival >= hv && ival <= lv)) {
3032 				/* Reverse linear interpolation */
3033 				if (hv == lv) {	/* Technically non-monotonic - due to quantization ? */
3034 					oval = (k + 0.5)/(rt->size-1.0);
3035 				} else
3036 					oval = (k + ((ival - lv)/(hv - lv)))/(rt->size-1.0);
3037 				/* If we kept looking, we would find multiple */
3038 				/* solution for non-monotonic curve */
3039 				*out = oval;
3040 				return rv;
3041 			}
3042 		}
3043 	}
3044 
3045 	/* We have failed to find an exact value, so return the nearest value */
3046 	/* (This is slow !) */
3047 	val = fabs(ival - rt->data[0]);
3048 	for (k = 0, i = 1; i < rt->size; i++) {
3049 		double er;
3050 		er = fabs(ival - rt->data[i]);
3051 		if (er < val) {	/* new best */
3052 			val = er;
3053 			k = i;
3054 		}
3055 	}
3056 	*out = k/(rt->size-1.0);
3057 	rv |= 1;
3058 	return rv;
3059 }
3060 
3061 
3062 /* - - - - - - - - - - - - */
3063 
3064 /* Do a reverse lookup through the curve */
3065 /* Return 0 on success, 1 if clipping occured, 2 on other error */
3066 static int icmCurve_lookup_bwd(
3067 	icmCurve *p,
3068 	double *out,
3069 	double *in
3070 ) {
3071 	icc *icp = p->icp;
3072 	int rv = 0;
3073 	if (p->flag == icmCurveLin) {
3074 		*out = *in;
3075 	} else if (p->flag == icmCurveGamma) {
3076 		double val = *in;
3077 		if (val <= 0.0)
3078 			*out = 0.0;
3079 		else
3080 			*out = pow(val, 1.0/p->data[0]);
3081 	} else { /* Use linear interpolation */
3082 		if (p->rt.inited == 0) {
3083 			rv = icmTable_setup_bwd(icp, &p->rt, p->size, p->data);
3084 			if (rv != 0) {
3085 				sprintf(icp->err,"icmCurve_lookup: Malloc failure in reverse lookup init.");
3086 				return icp->errc = rv;
3087 			}
3088 		}
3089 		rv = icmTable_lookup_bwd(&p->rt, out, in);
3090 	}
3091 	return rv;
3092 }
3093 
3094 /* Return the number of bytes needed to write this tag */
3095 static unsigned int icmCurve_get_size(
3096 	icmBase *pp
3097 ) {
3098 	icmCurve *p = (icmCurve *)pp;
3099 	unsigned int len = 0;
3100 	len += 12;			/* 12 bytes for tag, padding and count */
3101 	len += p->size * 2;	/* 2 bytes for each UInt16 */
3102 	return len;
3103 }
3104 
3105 /* read the object, return 0 on success, error code on fail */
3106 static int icmCurve_read(
3107 	icmBase *pp,
3108 	unsigned long len,		/* tag length */
3109 	unsigned long of		/* start offset within file */
3110 ) {
3111 	icmCurve *p = (icmCurve *)pp;
3112 	icc *icp = p->icp;
3113 	int rv = 0;
3114 	unsigned long i;
3115 	char *bp, *buf, *end;
3116 
3117 	if (len < 12) {
3118 		sprintf(icp->err,"icmCurve_read: Tag too small to be legal");
3119 		return icp->errc = 1;
3120 	}
3121 
3122 	/* Allocate a file read buffer */
3123 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3124 		sprintf(icp->err,"icmCurve_read: malloc() failed");
3125 		return icp->errc = 2;
3126 	}
3127 	bp = buf;
3128 	end = buf + len;
3129 
3130 	/* Read portion of file into buffer */
3131 	if (   icp->fp->seek(icp->fp, of) != 0
3132 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
3133 		sprintf(icp->err,"icmCurve_read: fseek() or fread() failed");
3134 		icp->al->free(icp->al, buf);
3135 		return icp->errc = 1;
3136 	}
3137 
3138 	/* Read type descriptor from the buffer */
3139 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
3140 		sprintf(icp->err,"icmCurve_read: Wrong tag type for icmCurve");
3141 		icp->al->free(icp->al, buf);
3142 		return icp->errc = 1;
3143 	}
3144 
3145 	p->size = read_UInt32Number(bp+8);
3146 	bp = bp + 12;
3147 
3148 	/* Set flag up before allocating */
3149 	if (p->size == 0) {		/* Linear curve */
3150 		p->flag = icmCurveLin;
3151 	} else if (p->size == 1) {	/* Gamma curve */
3152 		p->flag = icmCurveGamma;
3153 	} else {
3154 		p->flag = icmCurveSpec;
3155 	}
3156 
3157 	if ((rv = p->allocate((icmBase *)p)) != 0) {
3158 		icp->al->free(icp->al, buf);
3159 		return rv;
3160 	}
3161 
3162 	if (p->flag == icmCurveGamma) {	/* Gamma curve */
3163 		if ((bp + 1) > end) {
3164 			sprintf(icp->err,"icmCurve_read: Data too short to curve gamma");
3165 			icp->al->free(icp->al, buf);
3166 			return icp->errc = 1;
3167 		}
3168 		p->data[0] = read_U8Fixed8Number(bp);
3169 	} else if (p->flag == icmCurveSpec) {
3170 		/* Read all the data from the buffer */
3171 		for (i = 0; i < p->size; i++, bp += 2) {
3172 			if ((bp + 2) > end) {
3173 				sprintf(icp->err,"icmData_read: Data too short to curve value");
3174 				icp->al->free(icp->al, buf);
3175 				return icp->errc = 1;
3176 			}
3177 			p->data[i] = read_DCS16Number(bp);
3178 		}
3179 	}
3180 	icp->al->free(icp->al, buf);
3181 	return 0;
3182 }
3183 
3184 /* Write the contents of the object. Return 0 on sucess, error code on failure */
3185 static int icmCurve_write(
3186 	icmBase *pp,
3187 	unsigned long of			/* File offset to write from */
3188 ) {
3189 	icmCurve *p = (icmCurve *)pp;
3190 	icc *icp = p->icp;
3191 	unsigned long i;
3192 	unsigned int len;
3193 	char *bp, *buf;		/* Buffer to write from */
3194 	int rv = 0;
3195 
3196 	/* Allocate a file write buffer */
3197 	len = p->get_size((icmBase *)p);
3198 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3199 		sprintf(icp->err,"icmCurve_write malloc() failed");
3200 		return icp->errc = 2;
3201 	}
3202 	bp = buf;
3203 
3204 	/* Write type descriptor to the buffer */
3205 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
3206 		sprintf(icp->err,"icmCurve_write: write_SInt32Number() failed");
3207 		icp->al->free(icp->al, buf);
3208 		return icp->errc = rv;
3209 	}
3210 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
3211 	/* Write count */
3212 	if ((rv = write_UInt32Number(p->size,bp+8)) != 0) {
3213 		sprintf(icp->err,"icmCurve_write: write_UInt32Number() failed");
3214 		icp->al->free(icp->al, buf);
3215 		return icp->errc = rv;
3216 	}
3217 
3218 	/* Write all the data to the buffer */
3219 	bp += 12;	/* Skip padding */
3220 	if (p->flag == icmCurveLin) {
3221 		if (p->size != 0) {
3222 			sprintf(icp->err,"icmCurve_write: Must be exactly 0 entry for Linear");
3223 			icp->al->free(icp->al, buf);
3224 			return icp->errc = 1;
3225 		}
3226 	} else if (p->flag == icmCurveGamma) {
3227 		if (p->size != 1) {
3228 			sprintf(icp->err,"icmCurve_write: Must be exactly 1 entry for Gamma");
3229 			icp->al->free(icp->al, buf);
3230 			return icp->errc = 1;
3231 		}
3232 		if ((rv = write_U8Fixed8Number(p->data[0],bp)) != 0) {
3233 			sprintf(icp->err,"icmCurve_write: write_U8Fixed8umber(%f) failed",p->data[0]);
3234 			icp->al->free(icp->al, buf);
3235 			return icp->errc = rv;
3236 		}
3237 	} else if (p->flag == icmCurveSpec) {
3238 		if (p->size < 2) {
3239 			sprintf(icp->err,"icmCurve_write: Must be 2 or more entries for Specified curve");
3240 			icp->al->free(icp->al, buf);
3241 			return icp->errc = 1;
3242 		}
3243 		for (i = 0; i < p->size; i++, bp += 2) {
3244 			if ((rv = write_DCS16Number(p->data[i],bp)) != 0) {
3245 				sprintf(icp->err,"icmCurve_write: write_UInt16umber(%f) failed",p->data[i]);
3246 				icp->al->free(icp->al, buf);
3247 				return icp->errc = rv;
3248 			}
3249 		}
3250 	}
3251 
3252 	/* Write to the file */
3253 	if (   icp->fp->seek(icp->fp, of) != 0
3254 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
3255 		sprintf(icp->err,"icmCurve_write fseek() or fwrite() failed");
3256 		icp->al->free(icp->al, buf);
3257 		return icp->errc = 2;
3258 	}
3259 	icp->al->free(icp->al, buf);
3260 	return 0;
3261 }
3262 
3263 /* Dump a text description of the object */
3264 static void icmCurve_dump(
3265 	icmBase *pp,
3266 	FILE *op,		/* Output to dump to */
3267 	int   verb		/* Verbosity level */
3268 ) {
3269 	icmCurve *p = (icmCurve *)pp;
3270 	if (verb <= 0)
3271 		return;
3272 
3273 	fprintf(op,"Curve:\n");
3274 
3275 	if (p->flag == icmCurveLin) {
3276 		fprintf(op,"  Curve is linear\n");
3277 	} else if (p->flag == icmCurveGamma) {
3278 		fprintf(op,"  Curve is gamma of %f\n",p->data[0]);
3279 	} else {
3280 		fprintf(op,"  No. elements = %u\n",p->size);
3281 		if (verb >= 2) {
3282 			unsigned long i;
3283 			for (i = 0; i < p->size; i++)
3284 				fprintf(op,"    % 3u:  %f\n",i,p->data[i]);
3285 		}
3286 	}
3287 }
3288 
3289 /* Allocate variable sized data elements */
3290 static int icmCurve_allocate(
3291 	icmBase *pp
3292 ) {
3293 	icmCurve *p = (icmCurve *)pp;
3294 	icc *icp = p->icp;
3295 
3296 	if (p->flag == icmCurveUndef) {
3297 		sprintf(icp->err,"icmCurve_alloc: flag not set");
3298 		return icp->errc = 1;
3299 	} else if (p->flag == icmCurveLin) {
3300 		p->size = 0;
3301 	} else if (p->flag == icmCurveGamma) {
3302 		p->size = 1;
3303 	}
3304 	if (p->size != p->_size) {
3305 		if (p->data != NULL)
3306 			icp->al->free(icp->al, p->data);
3307 		if ((p->data = (double *) icp->al->malloc(icp->al, p->size * sizeof(double))) == NULL) {
3308 			sprintf(icp->err,"icmCurve_alloc: malloc() of icmCurve data failed");
3309 			return icp->errc = 2;
3310 		}
3311 		p->_size = p->size;
3312 	}
3313 	return 0;
3314 }
3315 
3316 /* Free all storage in the object */
3317 static void icmCurve_delete(
3318 	icmBase *pp
3319 ) {
3320 	icmCurve *p = (icmCurve *)pp;
3321 	icc *icp = p->icp;
3322 
3323 	if (p->data != NULL)
3324 		icp->al->free(icp->al, p->data);
3325 	icmTable_delete_bwd(icp, &p->rt);	/* Free reverse table info */
3326 	icp->al->free(icp->al, p);
3327 }
3328 
3329 /* Create an empty object. Return null on error */
3330 static icmBase *new_icmCurve(
3331 	icc *icp
3332 ) {
3333 	icmCurve *p;
3334 	if ((p = (icmCurve *) icp->al->calloc(icp->al,1,sizeof(icmCurve))) == NULL)
3335 		return NULL;
3336 	p->ttype    = icSigCurveType;
3337 	p->refcount = 1;
3338 	p->get_size = icmCurve_get_size;
3339 	p->read     = icmCurve_read;
3340 	p->write    = icmCurve_write;
3341 	p->dump     = icmCurve_dump;
3342 	p->allocate = icmCurve_allocate;
3343 	p->del      = icmCurve_delete;
3344 	p->icp      = icp;
3345 
3346 	p->lookup_fwd = icmCurve_lookup_fwd;
3347 	p->lookup_bwd = icmCurve_lookup_bwd;
3348 
3349 	p->rt.inited = 0;
3350 
3351 	p->flag = icmCurveUndef;
3352 	return (icmBase *)p;
3353 }
3354 
3355 /* ---------------------------------------------------------- */
3356 /* icmData object */
3357 
3358 /* Return the number of bytes needed to write this tag */
3359 static unsigned int icmData_get_size(
3360 	icmBase *pp
3361 ) {
3362 	icmData *p = (icmData *)pp;
3363 	unsigned int len = 0;
3364 	len += 12;			/* 12 bytes for tag and padding */
3365 	len += p->size * 1;	/* 1 byte for each data element */
3366 	return len;
3367 }
3368 
3369 /* read the object, return 0 on success, error code on fail */
3370 static int icmData_read(
3371 	icmBase *pp,
3372 	unsigned long len,		/* tag length */
3373 	unsigned long of		/* start offset within file */
3374 ) {
3375 	icmData *p = (icmData *)pp;
3376 	icc *icp = p->icp;
3377 	int rv = 0;
3378 	unsigned size, f;
3379 	char *bp, *buf;
3380 
3381 	if (len < 12) {
3382 		sprintf(icp->err,"icmData_read: Tag too small to be legal");
3383 		return icp->errc = 1;
3384 	}
3385 
3386 	/* Allocate a file read buffer */
3387 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3388 		sprintf(icp->err,"icmData_read: malloc() failed");
3389 		return icp->errc = 2;
3390 	}
3391 	bp = buf;
3392 
3393 	/* Read portion of file into buffer */
3394 	if (   icp->fp->seek(icp->fp, of) != 0
3395 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
3396 		sprintf(icp->err,"icmData_read: fseek() or fread() failed");
3397 		icp->al->free(icp->al, buf);
3398 		return icp->errc = 1;
3399 	}
3400 	p->size = size = (len - 12)/1;		/* Number of elements in the array */
3401 
3402 	/* Read type descriptor from the buffer */
3403 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
3404 		sprintf(icp->err,"icmData_read: Wrong tag type for icmData");
3405 		icp->al->free(icp->al, buf);
3406 		return icp->errc = 1;
3407 	}
3408 	/* Read the data type flag */
3409 	f = read_UInt32Number(bp+8);
3410 	if (f == 0) {
3411 		p->flag = icmDataASCII;
3412 	} else if (f == 1) {
3413 		p->flag = icmDataBin;
3414 	} else {
3415 		sprintf(icp->err,"icmData_read: Unknown flag value 0x%x",f);
3416 		icp->al->free(icp->al, buf);
3417 		return icp->errc = 1;
3418 	}
3419 	bp += 12;	/* Skip padding and flag */
3420 
3421 	if (p->size > 0) {
3422 		if (p->flag == icmDataASCII) {
3423 			if (check_null_string(bp,p->size) != 0) {
3424 				sprintf(icp->err,"icmData_read: ACSII is not null terminated");
3425 				icp->al->free(icp->al, buf);
3426 				return icp->errc = 1;
3427 			}
3428 		}
3429 		if ((rv = p->allocate((icmBase *)p)) != 0) {
3430 			icp->al->free(icp->al, buf);
3431 			return rv;
3432 		}
3433 
3434 		memcpy((void *)p->data, (void *)bp, p->size);
3435 	}
3436 	icp->al->free(icp->al, buf);
3437 	return 0;
3438 }
3439 
3440 /* Write the contents of the object. Return 0 on sucess, error code on failure */
3441 static int icmData_write(
3442 	icmBase *pp,
3443 	unsigned long of			/* File offset to write from */
3444 ) {
3445 	icmData *p = (icmData *)pp;
3446 	icc *icp = p->icp;
3447 	unsigned int len, f;
3448 	char *bp, *buf;		/* Buffer to write from */
3449 	int rv = 0;
3450 
3451 	/* Allocate a file write buffer */
3452 	len = p->get_size((icmBase *)p);
3453 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3454 		sprintf(icp->err,"icmData_write malloc() failed");
3455 		return icp->errc = 2;
3456 	}
3457 	bp = buf;
3458 
3459 	/* Write type descriptor to the buffer */
3460 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
3461 		sprintf(icp->err,"icmData_write: write_SInt32Number() failed");
3462 		icp->al->free(icp->al, buf);
3463 		return icp->errc = rv;
3464 	}
3465 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
3466 	switch(p->flag) {
3467 		case icmDataASCII:
3468 			f = 0;
3469 			break;
3470 		case icmDataBin:
3471 			f = 1;
3472 			break;
3473 		default:
3474 			sprintf(icp->err,"icmData_write: Unknown Data Flag value");
3475 			icp->al->free(icp->al, buf);
3476 			return icp->errc = 1;
3477 	}
3478 	/* Write data flag descriptor to the buffer */
3479 	if ((rv = write_UInt32Number(f,bp+8)) != 0) {
3480 		sprintf(icp->err,"icmData_write: write_SInt32Number() failed");
3481 		icp->al->free(icp->al, buf);
3482 		return icp->errc = rv;
3483 	}
3484 	bp += 12;	/* Skip padding */
3485 
3486 	if (p->data != NULL) {
3487 		if (p->flag == icmDataASCII) {
3488 			if ((rv = check_null_string((char*)p->data, p->size)) != 0) {	/* RSC added cast */
3489 				sprintf(icp->err,"icmData_write: ASCII is not null terminated");
3490 				icp->al->free(icp->al, buf);
3491 				return icp->errc = 1;
3492 			}
3493 		}
3494 		memcpy((void *)bp, (void *)p->data, p->size);
3495 	}
3496 
3497 	/* Write to the file */
3498 	if (   icp->fp->seek(icp->fp, of) != 0
3499 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
3500 		sprintf(icp->err,"icmData_write fseek() or fwrite() failed");
3501 		icp->al->free(icp->al, buf);
3502 		return icp->errc = 2;
3503 	}
3504 	icp->al->free(icp->al, buf);
3505 	return 0;
3506 }
3507 
3508 /* Dump a text description of the object */
3509 static void icmData_dump(
3510 	icmBase *pp,
3511 	FILE *op,		/* Output to dump to */
3512 	int   verb		/* Verbosity level */
3513 ) {
3514 	icmData *p = (icmData *)pp;
3515 	unsigned long i, r, c, size;
3516 
3517 	if (verb <= 0)
3518 		return;
3519 
3520 	fprintf(op,"Data:\n");
3521 	switch(p->flag) {
3522 		case icmDataASCII:
3523 			fprintf(op,"  ASCII data\n");
3524 			size = p->size > 0 ? p->size-1 : 0;
3525 			break;
3526 		case icmDataBin:
3527 			fprintf(op,"  Binary data\n");
3528 			size = p->size;
3529 			break;
3530 	}
3531 	fprintf(op,"  No. elements = %u\n",p->size);
3532 
3533 	i = 0;
3534 	for (r = 1;; r++) {		/* count rows */
3535 		if (i >= size) {
3536 			fprintf(op,"\n");
3537 			break;
3538 		}
3539 		if (r > 1 && verb < 2) {
3540 			fprintf(op,"...\n");
3541 			break;			/* Print 1 row if not verbose */
3542 		}
3543 		c = 1;
3544 		fprintf(op,"    0x%04x: ",i);
3545 		c += 10;
3546 		while (i < size && c < 75) {
3547 			if (p->flag == icmDataASCII) {
3548 				if (isprint(p->data[i])) {
3549 					fprintf(op,"%c",p->data[i]);
3550 					c++;
3551 				} else {
3552 					fprintf(op,"\\%03o",p->data[i]);
3553 					c += 4;
3554 				}
3555 			} else {
3556 				fprintf(op,"%02x ",p->data[i]);
3557 				c += 3;
3558 			}
3559 			i++;
3560 		}
3561 		if (i < size)
3562 			fprintf(op,"\n");
3563 	}
3564 }
3565 
3566 /* Allocate variable sized data elements */
3567 static int icmData_allocate(
3568 	icmBase *pp
3569 ) {
3570 	icmData *p = (icmData *)pp;
3571 	icc *icp = p->icp;
3572 
3573 	if (p->size != p->_size) {
3574 		if (p->data != NULL)
3575 			icp->al->free(icp->al, p->data);
3576 		if ((p->data = (unsigned char *) icp->al->malloc(icp->al, p->size * sizeof(unsigned char))) == NULL) {
3577 			sprintf(icp->err,"icmData_alloc: malloc() of icmData data failed");
3578 			return icp->errc = 2;
3579 		}
3580 		p->_size = p->size;
3581 	}
3582 	return 0;
3583 }
3584 
3585 /* Free all storage in the object */
3586 static void icmData_delete(
3587 	icmBase *pp
3588 ) {
3589 	icmData *p = (icmData *)pp;
3590 	icc *icp = p->icp;
3591 
3592 	if (p->data != NULL)
3593 		icp->al->free(icp->al, p->data);
3594 	icp->al->free(icp->al, p);
3595 }
3596 
3597 /* Create an empty object. Return null on error */
3598 static icmBase *new_icmData(
3599 	icc *icp
3600 ) {
3601 	icmData *p;
3602 	if ((p = (icmData *) icp->al->calloc(icp->al,1,sizeof(icmData))) == NULL)
3603 		return NULL;
3604 	p->ttype    = icSigDataType;
3605 	p->refcount = 1;
3606 	p->get_size = icmData_get_size;
3607 	p->read     = icmData_read;
3608 	p->write    = icmData_write;
3609 	p->dump     = icmData_dump;
3610 	p->allocate = icmData_allocate;
3611 	p->del      = icmData_delete;
3612 	p->icp      = icp;
3613 
3614 	p->flag = icmDataUndef;
3615 	return (icmBase *)p;
3616 }
3617 
3618 /* ---------------------------------------------------------- */
3619 /* icmText object */
3620 
3621 /* Return the number of bytes needed to write this tag */
3622 static unsigned int icmText_get_size(
3623 	icmBase *pp
3624 ) {
3625 	icmText *p = (icmText *)pp;
3626 	unsigned int len = 0;
3627 	len += 8;			/* 8 bytes for tag and padding */
3628 	len += p->size;		/* 1 byte for each character element (inc. null) */
3629 	return len;
3630 }
3631 
3632 /* read the object, return 0 on success, error code on fail */
3633 static int icmText_read(
3634 	icmBase *pp,
3635 	unsigned long len,		/* tag length */
3636 	unsigned long of		/* start offset within file */
3637 ) {
3638 	icmText *p = (icmText *)pp;
3639 	icc *icp = p->icp;
3640 	int rv = 0;
3641 	char *bp, *buf;
3642 
3643 	if (len < 8) {
3644 		sprintf(icp->err,"icmText_read: Tag too short to be legal");
3645 		return icp->errc = 1;
3646 	}
3647 
3648 	/* Allocate a file read buffer */
3649 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3650 		sprintf(icp->err,"icmText_read: malloc() failed");
3651 		return icp->errc = 2;
3652 	}
3653 	bp = buf;
3654 
3655 	/* Read portion of file into buffer */
3656 	if (   icp->fp->seek(icp->fp, of) != 0
3657 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
3658 		sprintf(icp->err,"icmText_read: fseek() or fread() failed");
3659 		icp->al->free(icp->al, buf);
3660 		return icp->errc = 1;
3661 	}
3662 	p->size = (len - 8)/1;		/* Number of elements in the array */
3663 
3664 	/* Read type descriptor from the buffer */
3665 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
3666 		sprintf(icp->err,"icmText_read: Wrong tag type for icmText");
3667 		icp->al->free(icp->al, buf);
3668 		return icp->errc = 1;
3669 	}
3670 	bp = bp + 8;
3671 
3672 	if (p->size > 0) {
3673 		if (check_null_string(bp,p->size) != 0) {
3674 			sprintf(icp->err,"icmText_read: text is not null terminated");
3675 			icp->al->free(icp->al, buf);
3676 			return icp->errc = 1;
3677 		}
3678 		if ((rv = p->allocate((icmBase *)p)) != 0) {
3679 			icp->al->free(icp->al, buf);
3680 			return rv;
3681 		}
3682 		memcpy((void *)p->data, (void *)bp, p->size);
3683 	}
3684 	icp->al->free(icp->al, buf);
3685 	return 0;
3686 }
3687 
3688 /* Write the contents of the object. Return 0 on sucess, error code on failure */
3689 static int icmText_write(
3690 	icmBase *pp,
3691 	unsigned long of			/* File offset to write from */
3692 ) {
3693 	icmText *p = (icmText *)pp;
3694 	icc *icp = p->icp;
3695 	unsigned int len;
3696 	char *bp, *buf;		/* Buffer to write from */
3697 	int rv = 0;
3698 
3699 	/* Allocate a file write buffer */
3700 	len = p->get_size((icmBase *)p);
3701 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3702 		sprintf(icp->err,"icmText_write malloc() failed");
3703 		return icp->errc = 2;
3704 	}
3705 	bp = buf;
3706 
3707 	/* Write type descriptor to the buffer */
3708 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
3709 		sprintf(icp->err,"icmText_write: write_SInt32Number() failed");
3710 		icp->al->free(icp->al, buf);
3711 		return icp->errc = rv;
3712 	}
3713 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
3714 	bp = bp + 8;
3715 
3716 	if (p->data != NULL) {
3717 		if ((rv = check_null_string(p->data, p->size)) != 0) {
3718 			sprintf(icp->err,"icmText_write: text is not null terminated");
3719 			icp->al->free(icp->al, buf);
3720 			return icp->errc = 1;
3721 		}
3722 		memcpy((void *)bp, (void *)p->data, p->size);
3723 	}
3724 
3725 	/* Write to the file */
3726 	if (   icp->fp->seek(icp->fp, of) != 0
3727 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
3728 		sprintf(icp->err,"icmText_write fseek() or fwrite() failed");
3729 		icp->al->free(icp->al, buf);
3730 		return icp->errc = 2;
3731 	}
3732 	icp->al->free(icp->al, buf);
3733 	return 0;
3734 }
3735 
3736 /* Dump a text description of the object */
3737 static void icmText_dump(
3738 	icmBase *pp,
3739 	FILE *op,		/* Output to dump to */
3740 	int   verb		/* Verbosity level */
3741 ) {
3742 	icmText *p = (icmText *)pp;
3743 	unsigned long i, r, c, size;
3744 
3745 	if (verb <= 0)
3746 		return;
3747 
3748 	fprintf(op,"Text:\n");
3749 	fprintf(op,"  No. chars = %u\n",p->size);
3750 
3751 	size = p->size > 0 ? p->size-1 : 0;
3752 	i = 0;
3753 	for (r = 1;; r++) {		/* count rows */
3754 		if (i >= size) {
3755 			fprintf(op,"\n");
3756 			break;
3757 		}
3758 		if (r > 1 && verb < 2) {
3759 			fprintf(op,"...\n");
3760 			break;			/* Print 1 row if not verbose */
3761 		}
3762 		c = 1;
3763 		fprintf(op,"    0x%04x: ",i);
3764 		c += 10;
3765 		while (i < size && c < 75) {
3766 			if (isprint(p->data[i])) {
3767 				fprintf(op,"%c",p->data[i]);
3768 				c++;
3769 			} else {
3770 				fprintf(op,"\\%03o",p->data[i]);
3771 				c += 4;
3772 			}
3773 			i++;
3774 		}
3775 		if (i < size)
3776 			fprintf(op,"\n");
3777 	}
3778 }
3779 
3780 /* Allocate variable sized data elements */
3781 static int icmText_allocate(
3782 	icmBase *pp
3783 ) {
3784 	icmText *p = (icmText *)pp;
3785 	icc *icp = p->icp;
3786 
3787 	if (p->size != p->_size) {
3788 		if (p->data != NULL)
3789 			icp->al->free(icp->al, p->data);
3790 		if ((p->data = (char *) icp->al->malloc(icp->al, p->size * sizeof(char))) == NULL) {
3791 			sprintf(icp->err,"icmText_alloc: malloc() of icmText data failed");
3792 			return icp->errc = 2;
3793 		}
3794 		p->_size = p->size;
3795 	}
3796 	return 0;
3797 }
3798 
3799 /* Free all storage in the object */
3800 static void icmText_delete(
3801 	icmBase *pp
3802 ) {
3803 	icmText *p = (icmText *)pp;
3804 	icc *icp = p->icp;
3805 
3806 	if (p->data != NULL)
3807 		icp->al->free(icp->al, p->data);
3808 	icp->al->free(icp->al, p);
3809 }
3810 
3811 /* Create an empty object. Return null on error */
3812 static icmBase *new_icmText(
3813 	icc *icp
3814 ) {
3815 	icmText *p;
3816 	if ((p = (icmText *) icp->al->calloc(icp->al,1,sizeof(icmText))) == NULL)
3817 		return NULL;
3818 	p->ttype    = icSigTextType;
3819 	p->refcount = 1;
3820 	p->get_size = icmText_get_size;
3821 	p->read     = icmText_read;
3822 	p->write    = icmText_write;
3823 	p->dump     = icmText_dump;
3824 	p->allocate = icmText_allocate;
3825 	p->del      = icmText_delete;
3826 	p->icp      = icp;
3827 
3828 	return (icmBase *)p;
3829 }
3830 
3831 /* ---------------------------------------------------------- */
3832 
3833 /* Data conversion support functions */
3834 static int write_DateTimeNumber(icmDateTimeNumber *p, char *d) {
3835 	int rv;
3836 	if (p->year < 1900 || p->year > 3000
3837 	 || p->month == 0 || p->month > 12
3838 	 || p->day == 0 || p->day > 31
3839 	 || p->hours > 23
3840 	 || p->minutes > 59
3841 	 || p->seconds > 59)
3842 		return 1;
3843 
3844 	if ((rv = write_UInt16Number(p->year,    d + 0)) != 0)
3845 		return rv;
3846 	if ((rv = write_UInt16Number(p->month,   d + 2)) != 0)
3847 		return rv;
3848 	if ((rv = write_UInt16Number(p->day,     d + 4)) != 0)
3849 		return rv;
3850 	if ((rv = write_UInt16Number(p->hours,   d + 6)) != 0)
3851 		return rv;
3852 	if ((rv = write_UInt16Number(p->minutes, d + 8)) != 0)
3853 		return rv;
3854 	if ((rv = write_UInt16Number(p->seconds, d + 10)) != 0)
3855 		return rv;
3856 	return 0;
3857 }
3858 
3859 static int read_DateTimeNumber(icmDateTimeNumber *p, char *d) {
3860 	p->year    = read_UInt16Number(d + 0);
3861 	p->month   = read_UInt16Number(d + 2);
3862 	p->day     = read_UInt16Number(d + 4);
3863 	p->hours   = read_UInt16Number(d + 6);
3864 	p->minutes = read_UInt16Number(d + 8);
3865 	p->seconds = read_UInt16Number(d + 10);
3866 
3867 	if (p->year < 1900 || p->year > 3000
3868 	 || p->month == 0 || p->month > 12
3869 	 || p->day == 0 || p->day > 31
3870 	 || p->hours > 23
3871 	 || p->minutes > 59
3872 	 || p->seconds > 59) {
3873 		unsigned int tt;
3874 
3875 		/* Check for Adobe problem */
3876 		if (p->month < 1900 || p->month > 3000
3877 		 || p->year == 0 || p->year > 12
3878 		 || p->hours == 0 || p->hours > 31
3879 		 || p->day > 23
3880 		 || p->seconds > 59
3881 		 || p->minutes > 59)
3882 			return 1;			/* Nope */
3883 
3884 		/* Correct Adobe's faulty profile */
3885 		tt = p->month; p->month = p->year; p->year = tt;
3886 		tt = p->hours; p->hours = p->day; p->day = tt;
3887 		tt = p->seconds; p->seconds = p->minutes; p->minutes = tt;
3888 		return 0;
3889 	}
3890 	return 0;
3891 }
3892 
3893 /* Return a string that shows the given date and time */
3894 static char *string_DateTimeNumber(icmDateTimeNumber *p) {
3895 	static char *mstring[13] = {"Bad", "Jan","Feb","Mar","Apr","May","Jun",
3896 	                                "Jul","Aug","Sep","Oct","Nov","Dec"};
3897 	static char buf[80];
3898 
3899 	sprintf(buf,"%d %s %4d, %d:%02d:%02d",
3900 	                p->day, mstring[p->month > 12 ? 0 : p->month], p->year,
3901 	                p->hours, p->minutes, p->seconds);
3902 	return buf;
3903 }
3904 
3905 /* Set the DateTime structure to the current date and time */
3906 static void setcur_DateTimeNumber(icmDateTimeNumber *p) {
3907 	time_t cclk;
3908 	struct tm *ctm;
3909 
3910 	cclk = time(NULL);
3911 	ctm = localtime(&cclk);
3912 
3913 	p->year    = ctm->tm_year + 1900;
3914 	p->month   = ctm->tm_mon + 1;
3915 	p->day     = ctm->tm_mday;
3916 	p->hours   = ctm->tm_hour;
3917 	p->minutes = ctm->tm_min;
3918 	p->seconds = ctm->tm_sec;
3919 }
3920 
3921 /* Return the number of bytes needed to write this tag */
3922 static unsigned int icmDateTimeNumber_get_size(
3923 	icmBase *pp
3924 ) {
3925 	icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
3926 	unsigned int len = 0;
3927 	len += 8;			/* 8 bytes for tag and padding */
3928 	len += 12;			/* 12 bytes for Date & Time */
3929 	return len;
3930 }
3931 
3932 /* read the object, return 0 on success, error code on fail */
3933 static int icmDateTimeNumber_read(
3934 	icmBase *pp,
3935 	unsigned long len,		/* tag length */
3936 	unsigned long of		/* start offset within file */
3937 ) {
3938 	icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
3939 	icc *icp = p->icp;
3940 	int rv;
3941 	char *bp, *buf;
3942 
3943 	if (len < 20) {
3944 		sprintf(icp->err,"icmDateTimeNumber_read: Tag too small to be legal");
3945 		return icp->errc = 1;
3946 	}
3947 
3948 	/* Allocate a file read buffer */
3949 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3950 		sprintf(icp->err,"icmDateTimeNumber_read: malloc() failed");
3951 		return icp->errc = 2;
3952 	}
3953 	bp = buf;
3954 
3955 	/* Read portion of file into buffer */
3956 	if (   icp->fp->seek(icp->fp, of) != 0
3957 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
3958 		sprintf(icp->err,"icmDateTimeNumber_read: fseek() or fread() failed");
3959 		icp->al->free(icp->al, buf);
3960 		return icp->errc = 1;
3961 	}
3962 
3963 	/* Read type descriptor from the buffer */
3964 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
3965 		sprintf(icp->err,"icmDateTimeNumber_read: Wrong tag type for icmDateTimeNumber");
3966 		icp->al->free(icp->al, buf);
3967 		return icp->errc = 1;
3968 	}
3969 	bp += 8;	/* Skip padding */
3970 
3971 	/* Read the time and date from buffer */
3972 	if((rv = read_DateTimeNumber(p, bp)) != 0) {
3973 		sprintf(icp->err,"icmDateTimeNumber_read: Corrupted DateTime");
3974 		icp->al->free(icp->al, buf);
3975 		return icp->errc = rv;
3976 	}
3977 
3978 	icp->al->free(icp->al, buf);
3979 	return 0;
3980 }
3981 
3982 /* Write the contents of the object. Return 0 on sucess, error code on failure */
3983 static int icmDateTimeNumber_write(
3984 	icmBase *pp,
3985 	unsigned long of			/* File offset to write from */
3986 ) {
3987 	icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
3988 	icc *icp = p->icp;
3989 	unsigned int len;
3990 	char *bp, *buf;		/* Buffer to write from */
3991 	int rv = 0;
3992 
3993 	/* Allocate a file write buffer */
3994 	len = p->get_size((icmBase *)p);
3995 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
3996 		sprintf(icp->err,"icmDateTimeNumber_write malloc() failed");
3997 		return icp->errc = 2;
3998 	}
3999 	bp = buf;
4000 
4001 	/* Write type descriptor to the buffer */
4002 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
4003 		sprintf(icp->err,"icmDateTimeNumber_write: write_SInt32Number() failed");
4004 		icp->al->free(icp->al, buf);
4005 		return icp->errc = rv;
4006 	}
4007 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
4008 
4009 	/* Write all the data to the buffer */
4010 	bp += 8;	/* Skip padding */
4011 	if ((rv = write_DateTimeNumber(p, bp)) != 0) {
4012 		sprintf(icp->err,"icmDateTimeNumber_write: write_DateTimeNumber() failed");
4013 		icp->al->free(icp->al, buf);
4014 		return icp->errc = rv;
4015 	}
4016 
4017 	/* Write to the file */
4018 	if (   icp->fp->seek(icp->fp, of) != 0
4019 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
4020 		sprintf(icp->err,"icmDateTimeNumber_write fseek() or fwrite() failed");
4021 		icp->al->free(icp->al, buf);
4022 		return icp->errc = 2;
4023 	}
4024 	icp->al->free(icp->al, buf);
4025 	return 0;
4026 }
4027 
4028 /* Dump a text description of the object */
4029 static void icmDateTimeNumber_dump(
4030 	icmBase *pp,
4031 	FILE *op,		/* Output to dump to */
4032 	int   verb		/* Verbosity level */
4033 ) {
4034 	icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
4035 	if (verb <= 0)
4036 		return;
4037 
4038 	fprintf(op,"DateTimeNumber:\n");
4039 	fprintf(op,"  Date = %s\n", string_DateTimeNumber(p));
4040 }
4041 
4042 /* Allocate variable sized data elements */
4043 static int icmDateTimeNumber_allocate(
4044 	icmBase *pp
4045 ) {
4046 	icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
4047 
4048 	/* Nothing to do */
4049 	return 0;
4050 }
4051 
4052 /* Free all storage in the object */
4053 static void icmDateTimeNumber_delete(
4054 	icmBase *pp
4055 ) {
4056 	icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
4057 	icc *icp = p->icp;
4058 
4059 	icp->al->free(icp->al, p);
4060 }
4061 
4062 /* Create an empty object. Return null on error */
4063 static icmBase *new_icmDateTimeNumber(
4064 	icc *icp
4065 ) {
4066 	icmDateTimeNumber *p;
4067 	if ((p = (icmDateTimeNumber *) icp->al->calloc(icp->al,1,sizeof(icmDateTimeNumber))) == NULL)
4068 		return NULL;
4069 	p->ttype    = icSigDateTimeType;
4070 	p->refcount = 1;
4071 	p->get_size = icmDateTimeNumber_get_size;
4072 	p->read     = icmDateTimeNumber_read;
4073 	p->write    = icmDateTimeNumber_write;
4074 	p->dump     = icmDateTimeNumber_dump;
4075 	p->allocate = icmDateTimeNumber_allocate;
4076 	p->del      = icmDateTimeNumber_delete;
4077 	p->icp      = icp;
4078 
4079 	setcur_DateTimeNumber(p);	/* Default to current date and time */
4080 	return (icmBase *)p;
4081 }
4082 
4083 /* ---------------------------------------------------------- */
4084 /* icmLut object */
4085 
4086 /* Utility function - raise one integer to an integer power */
4087 unsigned int uipow(unsigned int a, unsigned int b) {
4088 	unsigned int rv = 1;
4089 	for (; b > 0; b--)
4090 		rv *= a;
4091 	return rv;
4092 }
4093 
4094 /* - - - - - - - - - - - - - - - - */
4095 /* Check if the matrix is non-zero */
4096 static int icmLut_nu_matrix(
4097 	icmLut *p		/* Pointer to Lut object */
4098 ) {
4099 	int i, j;
4100 
4101 	for (j = 0; j < 3; j++) {		/* Rows */
4102 		for (i = 0; i < 3; i++) {	/* Columns */
4103 			if (   (i == j && p->e[j][i] != 1.0)
4104 			    || (i != j && p->e[j][i] != 0.0))
4105 				return 1;
4106 		}
4107 	}
4108 	return 0;
4109 }
4110 
4111 /* return the locations of the minimum and */
4112 /* maximum values of the given channel, in the clut */
4113 void icmLut_min_max(
4114 	icmLut *p,		/* Pointer to Lut object */
4115 	double *minp,	/* Return position of min/max */
4116 	double *maxp,
4117 	int chan		/* Chanel, -1 for average of all */
4118 ) {
4119 	double *tp;
4120 	double minv, maxv;	/* Values */
4121 	int e, ee, f;
4122 	double gc[MAX_CHAN];	/* Grid coordinate */
4123 
4124 	minv = 1e6;
4125 	maxv = -1e6;
4126 
4127 	for (e = 0; e < p->inputChan; e++)
4128 		gc[e] = 0;	/* init coords */
4129 
4130 	/* Search the whole table */
4131 	for (tp = p->clutTable, e = 0; e < p->inputChan; tp += p->outputChan) {
4132 		double v;
4133 		if (chan == -1) {
4134 			for (v = 0.0, f = 0; f < p->outputChan; f++)
4135 				v += tp[f];
4136 		} else {
4137 			v = tp[chan];
4138 		}
4139 		if (v < minv) {
4140 			minv = v;
4141 			for (ee = 0; ee < p->inputChan; ee++)
4142 				minp[ee] = gc[ee]/(p->clutPoints-1.0);
4143 		}
4144 		if (v > maxv) {
4145 			maxv = v;
4146 			for (ee = 0; ee < p->inputChan; ee++)
4147 				maxp[ee] = gc[ee]/(p->clutPoints-1.0);
4148 		}
4149 
4150 		/* Increment coord */
4151 		for (e = 0; e < p->inputChan; e++) {
4152 			gc[e]++;
4153 			if (gc[e] < p->clutPoints)
4154 				break;	/* No carry */
4155 			gc[e] = 0;
4156 		}
4157 	}
4158 }
4159 
4160 /* Convert XYZ throught Luts matrix */
4161 /* Return 0 on success, 1 if clipping occured, 2 on other error */
4162 static int icmLut_lookup_matrix(
4163 icmLut *p,		/* Pointer to Lut object */
4164 double *out,	/* Output array[outputChan] in ICC order - see Table 39 in 6.5.5 */
4165 double *in		/* Input array[inputChan] */
4166 ) {
4167 	double t0,t1;	/* Take care if out == in */
4168 	t0     = p->e[0][0] * in[0] + p->e[0][1] * in[1] + p->e[0][2] * in[2];
4169 	t1     = p->e[1][0] * in[0] + p->e[1][1] * in[1] + p->e[1][2] * in[2];
4170 	out[2] = p->e[2][0] * in[0] + p->e[2][1] * in[1] + p->e[2][2] * in[2];
4171 	out[0] = t0;
4172 	out[1] = t1;
4173 
4174 	return 0;
4175 }
4176 
4177 /* Convert normalized numbers though this Luts input tables. */
4178 /* Return 0 on success, 1 if clipping occured, 2 on other error */
4179 static int icmLut_lookup_input(
4180 icmLut *p,		/* Pointer to Lut object */
4181 double *out,	/* Output array[inputChan] */
4182 double *in		/* Input array[inputChan] */
4183 ) {
4184 	int rv = 0;
4185 	int ix,n;
4186 	double inputEnt_1 = (double)(p->inputEnt-1);
4187 	double *table = p->inputTable;
4188 
4189 	/* Use linear interpolation */
4190 	for (n = 0; n < p->inputChan; n++, table += p->inputEnt) {
4191 		double val, w;
4192 		val = in[n] * inputEnt_1;
4193 		if (val < 0.0) {
4194 			val = 0.0;
4195 			rv |= 1;
4196 		} else if (val > inputEnt_1) {
4197 			val = inputEnt_1;
4198 			rv |= 1;
4199 		}
4200 		ix = (int)floor(val);		/* Grid coordinate */
4201 		if (ix > (p->inputEnt-2))
4202 			ix = (p->inputEnt-2);
4203 		w = val - (double)ix;		/* weight */
4204 		val = table[ix];
4205 		out[n] = val + w * (table[ix+1] - val);
4206 	}
4207 	return rv;
4208 }
4209 
4210 /* Convert normalized numbers though this Luts multi-dimensional table. */
4211 /* using n-linear interpolation. */
4212 static int icmLut_lookup_clut_nl(
4213 /* Return 0 on success, 1 if clipping occured, 2 on other error */
4214 icmLut *p,		/* Pointer to Lut object */
4215 double *out,	/* Output array[inputChan] */
4216 double *in		/* Input array[outputChan] */
4217 ) {
4218 	icc *icp = p->icp;
4219 	int rv = 0;
4220 	double *gp;					/* Pointer to grid cube base */
4221 	double co[MAX_CHAN];		/* Coordinate offset with the grid cell */
4222 	double *gw, GW[1 << 8];		/* weight for each grid cube corner */
4223 
4224 	if (p->inputChan <= 8) {
4225 		gw = GW;				/* Use stack allocation */
4226 	} else {
4227 		if ((gw = (double *) icp->al->malloc(icp->al, (1 << p->inputChan) * sizeof(double))) == NULL) {
4228 			sprintf(icp->err,"icmLut_lookup_clut: malloc() failed");
4229 			return icp->errc = 2;
4230 		}
4231 	}
4232 
4233 	/* We are using an n-linear (ie. Trilinear for 3D input) interpolation. */
4234 	/* The implimentation here uses more multiplies that some other schemes, */
4235 	/* (for instance, see "Tri-Linear Interpolation" by Steve Hill, */
4236 	/* Graphics Gems IV, page 521), but has less involved bookeeping, */
4237 	/* needs less local storage for intermediate output values, does fewer */
4238 	/* output and intermediate value reads, and fp multiplies are fast on */
4239 	/* todays processors! */
4240 
4241 	/* Compute base index into grid and coordinate offsets */
4242 	{
4243 		int e;
4244 		double clutPoints_1 = (double)(p->clutPoints-1);
4245 		int    clutPoints_2 = p->clutPoints-2;
4246 		gp = p->clutTable;		/* Base of grid array */
4247 
4248 		for (e = 0; e < p->inputChan; e++) {
4249 			int x;
4250 			double val;
4251 			val = in[e] * clutPoints_1;
4252 			if (val < 0.0) {
4253 				val = 0.0;
4254 				rv |= 1;
4255 			} else if (val > clutPoints_1) {
4256 				val = clutPoints_1;
4257 				rv |= 1;
4258 			}
4259 			x = (int)floor(val);		/* Grid coordinate */
4260 			if (x > clutPoints_2)
4261 				x = clutPoints_2;
4262 			co[e] = val - (double)x;	/* 1.0 - weight */
4263 			gp += x * p->dinc[e];		/* Add index offset for base of cube */
4264 		}
4265 	}
4266 	/* Compute corner weights needed for interpolation */
4267 	{
4268 		int e, i, g = 1;
4269 		gw[0] = 1.0;
4270 		for (e = 0; e < p->inputChan; e++) {
4271 			for (i = 0; i < g; i++) {
4272 				gw[g+i] = gw[i] * co[e];
4273 				gw[i] *= (1.0 - co[e]);
4274 			}
4275 			g *= 2;
4276 		}
4277 	}
4278 	/* Now compute the output values */
4279 	{
4280 		int i, f;
4281 		double w = gw[0];
4282 		double *d = gp + p->dcube[0];
4283 		for (f = 0; f < p->outputChan; f++)			/* Base of cube */
4284 			out[f] = w * d[f];
4285 		for (i = 1; i < (1 << p->inputChan); i++) {	/* For all other corners of cube */
4286 			w = gw[i];				/* Strength reduce */
4287 			d = gp + p->dcube[i];
4288 			for (f = 0; f < p->outputChan; f++) {
4289 				out[f] += w * d[f];
4290 			}
4291 		}
4292 	}
4293 	if (gw != GW)
4294 		icp->al->free(icp->al, (void *)gw);
4295 	return rv;
4296 }
4297 
4298 /* Convert normalized numbers though this Luts multi-dimensional table */
4299 /* using simplex interpolation. */
4300 static int icmLut_lookup_clut_sx(
4301 /* Return 0 on success, 1 if clipping occured, 2 on other error */
4302 icmLut *p,		/* Pointer to Lut object */
4303 double *out,	/* Output array[inputChan] */
4304 double *in		/* Input array[outputChan] */
4305 ) {
4306 	int rv = 0;
4307 	double *gp;					/* Pointer to grid cube base */
4308 	double co[MAX_CHAN];		/* Coordinate offset with the grid cell */
4309 	int    si[MAX_CHAN];		/* co[] Sort index, [0] = smalest */
4310 
4311 	/* We are using a simplex (ie. tetrahedral for 3D input) interpolation. */
4312 	/* This method is more appropriate for XYZ/RGB/CMYK input spaces, */
4313 
4314 	/* Compute base index into grid and coordinate offsets */
4315 	{
4316 		int e;
4317 		double clutPoints_1 = (double)(p->clutPoints-1);
4318 		int    clutPoints_2 = p->clutPoints-2;
4319 		gp = p->clutTable;		/* Base of grid array */
4320 
4321 		for (e = 0; e < p->inputChan; e++) {
4322 			int x;
4323 			double val;
4324 			val = in[e] * clutPoints_1;
4325 			if (val < 0.0) {
4326 				val = 0.0;
4327 				rv |= 1;
4328 			} else if (val > clutPoints_1) {
4329 				val = clutPoints_1;
4330 				rv |= 1;
4331 			}
4332 			x = (int)floor(val);		/* Grid coordinate */
4333 			if (x > clutPoints_2)
4334 				x = clutPoints_2;
4335 			co[e] = val - (double)x;	/* 1.0 - weight */
4336 			gp += x * p->dinc[e];		/* Add index offset for base of cube */
4337 		}
4338 	}
4339 	/* Do selection sort on coordinates */
4340 	{
4341 		int e, f;
4342 		for (e = 0; e < p->inputChan; e++)
4343 			si[e] = e;						/* Initial unsorted indexes */
4344 		for (e = 0; e < (p->inputChan-1); e++) {
4345 			double cosn;
4346 			cosn = co[si[e]];				/* Current smallest value */
4347 			for (f = e+1; f < p->inputChan; f++) {	/* Check against rest */
4348 				int tt;
4349 				tt = si[f];
4350 				if (cosn > co[tt]) {
4351 					si[f] = si[e]; 			/* Exchange */
4352 					si[e] = tt;
4353 					cosn = co[tt];
4354 				}
4355 			}
4356 		}
4357 	}
4358 	/* Now compute the weightings, simplex vertices and output values */
4359 	{
4360 		int e, f;
4361 		double w;		/* Current vertex weight */
4362 
4363 		w = 1.0 - co[si[p->inputChan-1]];		/* Vertex at base of cell */
4364 		for (f = 0; f < p->outputChan; f++)
4365 			out[f] = w * gp[f];
4366 
4367 		for (e = p->inputChan-1; e > 0; e--) {	/* Middle verticies */
4368 			w = co[si[e]] - co[si[e-1]];
4369 			gp += p->dinc[si[e]];				/* Move to top of cell in next largest dimension */
4370 			for (f = 0; f < p->outputChan; f++)
4371 				out[f] += w * gp[f];
4372 		}
4373 
4374 		w = co[si[0]];
4375 		gp += p->dinc[si[0]];		/* Far corner from base of cell */
4376 		for (f = 0; f < p->outputChan; f++)
4377 			out[f] += w * gp[f];
4378 	}
4379 	return rv;
4380 }
4381 
4382 /* Convert normalized numbers though this Luts output tables. */
4383 /* Return 0 on success, 1 if clipping occured, 2 on other error */
4384 static int icmLut_lookup_output(
4385 icmLut *p,		/* Pointer to Lut object */
4386 double *out,	/* Output array[outputChan] */
4387 double *in		/* Input array[outputChan] */
4388 ) {
4389 	int rv = 0;
4390 	int ix,n;
4391 	double outputEnt_1 = (double)(p->outputEnt-1);
4392 	double *table = p->outputTable;
4393 
4394 	/* Use linear interpolation */
4395 	for (n = 0; n < p->outputChan; n++, table += p->outputEnt) {
4396 		double val, w;
4397 		val = in[n] * outputEnt_1;
4398 		if (val < 0.0) {
4399 			val = 0.0;
4400 			rv |= 1;
4401 		} else if (val > outputEnt_1) {
4402 			val = outputEnt_1;
4403 			rv |= 1;
4404 		}
4405 		ix = (int)floor(val);		/* Grid coordinate */
4406 		if (ix > (p->outputEnt-2))
4407 			ix = (p->outputEnt-2);
4408 		w = val - (double)ix;		/* weight */
4409 		val = table[ix];
4410 		out[n] = val + w * (table[ix+1] - val);
4411 	}
4412 	return rv;
4413 }
4414 
4415 /* ----------------------------------------------- */
4416 /* Pseudo - Hilbert count sequencer */
4417 
4418 /* This multi-dimensional count sequence is a distributed */
4419 /* Gray code sequence, with direction reversal on every */
4420 /* alternate power of 2 scale. */
4421 /* It is intended to aid cache coherence in multi-dimensional */
4422 /* regular sampling. It approximates the Hilbert curve sequence. */
4423 
4424 /* Initialise, returns total usable count */
4425 unsigned
4426 psh_init(
4427 psh *p,	/* Pointer to structure to initialise */
4428 int      di,	/* Dimensionality */
4429 unsigned res,	/* Size per coordinate */
4430 int co[]		/* Coordinates to initialise (May be NULL) */
4431 ) {
4432 	int e;
4433 
4434 	p->di = di;
4435 	p->res = res;
4436 
4437 	/* Compute bits */
4438 	for (p->bits = 0; (1 << p->bits) < res; p->bits++)
4439 		;
4440 
4441 	/* Compute the total count mask */
4442 	p->tmask = ((((unsigned)1) << (p->bits * di))-1);
4443 
4444 	/* Compute usable count */
4445 	p->count = 1;
4446 	for (e = 0; e < di; e++)
4447 		p->count *= res;
4448 
4449 	p->ix = 0;
4450 
4451 	if (co != NULL) {
4452 		for (e = 0; e < di; e++)
4453 			co[e] = 0;
4454 	}
4455 
4456 	return p->count;
4457 }
4458 
4459 /* Reset the counter */
4460 void
4461 psh_reset(
4462 psh *p	/* Pointer to structure */
4463 ) {
4464 	p->ix = 0;
4465 }
4466 
4467 /* Increment pseudo-hilbert coordinates */
4468 /* Return non-zero if count rolls over to 0 */
4469 int
4470 psh_inc(
4471 psh *p,	/* Pointer to structure */
4472 int co[]		/* Coordinates to return */
4473 ) {
4474 	int di = p->di;
4475 	int res = p->res;
4476 	int bits = p->bits;
4477 	int e;
4478 
4479 	do {
4480 		int b;
4481 		int gix;	/* Gray code index */
4482 
4483 		p->ix = (p->ix + 1) & p->tmask;
4484 
4485 		gix = p->ix ^ (p->ix >> 1);		/* Convert to gray code index */
4486 
4487 		for (e = 0; e < di; e++)
4488 			co[e] = 0;
4489 
4490 		for (b = 0; b < bits; b++) {	/* Distribute bits */
4491 			if (b & 1) {
4492 				for (e = di-1; e >= 0; e--)  {		/* In reverse order */
4493 					co[e] |= (gix & 1) << b;
4494 					gix >>= 1;
4495 				}
4496 			} else {
4497 				for (e = 0; e < di; e++)  {			/* In normal order */
4498 					co[e] |= (gix & 1) << b;
4499 					gix >>= 1;
4500 				}
4501 			}
4502 		}
4503 
4504 		/* Convert from Gray to binary coordinates */
4505 		for (e = 0; e < di; e++)  {
4506 			unsigned sh, tv;
4507 
4508 			for(sh = 1, tv = co[e];; sh <<= 1) {
4509 				unsigned ptv = tv;
4510 				tv ^= (tv >> sh);
4511 				if (ptv <= 1 || sh == 16)
4512 					break;
4513 			}
4514 			if (tv >= res)	/* Dumbo filter - increment again if outside cube range */
4515 				break;
4516 			co[e] = tv;
4517 		}
4518 
4519 	} while (e < di);
4520 
4521 	return (p->ix == 0);
4522 }
4523 
4524 /* ------------------------------------------------------- */
4525 /* Parameter to getNormFunc function */
4526 typedef enum {
4527     icmFromLuti   = 0,  /* return "fromo Lut normalized index" conversion function */
4528     icmToLuti     = 1,  /* return "to Lut normalized index" conversion function */
4529     icmFromLutv   = 2,  /* return "from Lut normalized value" conversion function */
4530     icmToLutv     = 3   /* return "to Lut normalized value" conversion function */
4531 } icmNormFlag;
4532 
4533 /* Return an appropriate color space normalization function, */
4534 /* given the color space and Lut type */
4535 /* Return 0 on success, 1 on match failure */
4536 static int getNormFunc(
4537 	icColorSpaceSignature csig,
4538 	icTagTypeSignature    tagSig,
4539 	icmNormFlag           flag,
4540 	void               (**nfunc)(double *out, double *in)
4541 );
4542 
4543 /* Helper function to initialize the three tables contents */
4544 /* from supplied transfer functions. */
4545 /* Set errc and return error number */
4546 static int icmLut_set_tables (
4547 icmLut *p,									/* Pointer to Lut object */
4548 void   *cbctx,								/* Opaque callback context pointer value */
4549 icColorSpaceSignature insig, 				/* Input color space */
4550 icColorSpaceSignature outsig, 				/* Output color space */
4551 void (*infunc)(void *cbcntx, double *out, double *in),
4552 								/* Input transfer function, inspace->inspace' (NULL = default) */
4553 double *inmin, double *inmax,				/* Maximum range of inspace' values (NULL = default) */
4554 void (*clutfunc)(void *cbctx, double *out, double *in),
4555 								/* inspace' -> outspace' transfer function */
4556 double *clutmin, double *clutmax,			/* Maximum range of outspace' values (NULL = default) */
4557 void (*outfunc)(void *cbctx, double *out, double *in)
4558 								/* Output transfer function, outspace'->outspace (NULL = deflt) */
4559 ) {
4560 	icc *icp = p->icp;
4561 	int j, n, e;
4562 	int ii[MAX_CHAN];		/* Index value */
4563 	psh counter;			/* Pseudo-Hilbert counter */
4564 	double _iv[2 * MAX_CHAN], *iv = &_iv[MAX_CHAN];	/* Real index value/table value */
4565 	double imin[MAX_CHAN], imax[MAX_CHAN];
4566 	double omin[MAX_CHAN], omax[MAX_CHAN];
4567 	void (*ifromindex)(double *out, double *in);	/* Index to input color space function */
4568 	void (*itoentry)(double *out, double *in);		/* Input color space to entry function */
4569 	void (*ifromentry)(double *out, double *in);	/* Entry to input color space function */
4570 	void (*otoentry)(double *out, double *in);		/* Output colorspace to table value function */
4571 	void (*ofromentry)(double *out, double *in);	/* Table value to output color space function */
4572 
4573 	if (getNormFunc(insig, p->ttype, icmFromLuti, &ifromindex) != 0) {
4574 		sprintf(icp->err,"icmLut_set_tables index to input colorspace function lookup failed");
4575 		return icp->errc = 1;
4576 	}
4577 	if (getNormFunc(insig, p->ttype, icmToLutv, &itoentry) != 0) {
4578 		sprintf(icp->err,"icmLut_set_tables input colorspace to table entry function lookup failed");
4579 		return icp->errc = 1;
4580 	}
4581 	if (getNormFunc(insig, p->ttype, icmFromLutv, &ifromentry) != 0) {
4582 		sprintf(icp->err,"icmLut_set_tables table entry to input colorspace function lookup failed");
4583 		return icp->errc = 1;
4584 	}
4585 
4586 	if (getNormFunc(outsig, p->ttype, icmToLutv, &otoentry) != 0) {
4587 		sprintf(icp->err,"icmLut_set_tables output colorspace to table entry function lookup failed");
4588 		return icp->errc = 1;
4589 	}
4590 	if (getNormFunc(outsig, p->ttype, icmFromLutv, &ofromentry) != 0) {
4591 		sprintf(icp->err,"icmLut_set_tables table entry to output colorspace function lookup failed");
4592 		return icp->errc = 1;
4593 	}
4594 
4595 	/* Setup input table value min-max */
4596 	if (inmin == NULL) {
4597 #ifdef SYMETRICAL_DEFAULT_LAB_RANGE	/* Try symetrical range */
4598 		if (insig == icSigLabData) { /* Special case Lab */
4599 			double mn[3], mx[3];
4600 			/* Because the symetric range will cause slight clipping, */
4601 			/* only do it if the input table has sufficient resolution */
4602 			/* to represent the clipping faithfuly. */
4603 			if (p->inputEnt >= 64) {
4604 				mn[0] =   0.0, mn[1] = -127.0, mn[2] = -127.0;
4605 				mx[0] = 100.0, mx[1] =  127.0, mx[2] =  127.0;
4606 				itoentry(imin, mn);	/* Convert from input color space to table representation */
4607 				itoentry(imax, mx);
4608 			} else {
4609 				for (e = 0; e < p->inputChan; e++) {
4610 					imin[e] = 0.0;
4611 					imax[e] = 1.0;
4612 				}
4613 			}
4614 		} else
4615 #endif
4616 		{
4617 			for (e = 0; e < p->inputChan; e++) {
4618 				imin[e] = 0.0;		/* We are assuming this is true for all other color spaces. */
4619 				imax[e] = 1.0;
4620 			}
4621 		}
4622 	} else {
4623 		itoentry(imin, inmin);	/* Convert from input color space to table representation */
4624 		itoentry(imax, inmax);
4625 	}
4626 
4627 	/* Setup output table value min-max */
4628 	if (clutmin == NULL) {
4629 #ifdef SYMETRICAL_DEFAULT_LAB_RANGE	/* Try symetrical range */
4630 		if (outsig == icSigLabData) { /* Special case Lab */
4631 			double mn[3], mx[3];
4632 			/* Because the symetric range will cause slight clipping, */
4633 			/* only do it if the output table has sufficient resolution */
4634 			/* to represent the clipping faithfuly. */
4635 			if (p->outputEnt >= 64) {
4636 				mn[0] =   0.0, mn[1] = -127.0, mn[2] = -127.0;
4637 				mx[0] = 100.0, mx[1] =  127.0, mx[2] =  127.0;
4638 				otoentry(omin, mn);/* Convert from output color space to table representation */
4639 				otoentry(omax, mx);
4640 			} else {
4641 				for (e = 0; e < p->inputChan; e++) {
4642 					omin[e] = 0.0;
4643 					omax[e] = 1.0;
4644 				}
4645 			}
4646 		} else
4647 #endif
4648 		{
4649 			for (e = 0; e < p->outputChan; e++) {
4650 				omin[e] = 0.0;		/* We are assuming this is true for all other color spaces. */
4651 				omax[e] = 1.0;
4652 			}
4653 		}
4654 	} else {
4655 		otoentry(omin, clutmin);/* Convert from output color space to table representation */
4656 		otoentry(omax, clutmax);
4657 	}
4658 
4659 	/* Create the input table entry values */
4660 	for (n = 0; n < p->inputEnt; n++) {
4661 		double fv;
4662 		fv = n/(p->inputEnt-1.0);
4663 		for (e = 0; e < p->inputChan; e++)
4664 			iv[e] = fv;
4665 
4666 		ifromindex(iv,iv);			/* Convert from index value to input color space value */
4667 
4668 		if (infunc != NULL)
4669 			infunc(cbctx, iv, iv);	/* In colorspace -> input table -> In colorspace. */
4670 
4671 		itoentry(iv,iv);			/* Convert from input color space value to table value */
4672 
4673 		/* Expand used range to 0.0 - 1.0, and clip to legal values */
4674 		/* Note that if the range is reduced, and clipping occurs, */
4675 		/* then there should be enough resolution within the input */
4676 		/* table, to represent the sharp edges of the clipping. */
4677 		for (e = 0; e < p->inputChan; e++) {
4678 			double tt;
4679 			tt = (iv[e] - imin[e])/(imax[e] - imin[e]);
4680 			if (tt < 0.0)
4681 				tt = 0.0;
4682 			else if (tt > 1.0)
4683 				tt = 1.0;
4684 			iv[e] = tt;
4685 		}
4686 
4687 		for (e = 0; e < p->inputChan; e++) 		/* Input tables */
4688 			p->inputTable[e * p->inputEnt + n] = iv[e];
4689 	}
4690 
4691 	/* Create the multi-dimensional lookup table values */
4692 
4693 	/* To make this clut function cache friendly, we use the pseudo-hilbert */
4694 	/* count sequence. This keeps each point close to the last in the */
4695 	/* multi-dimensional space. */
4696 
4697 	psh_init(&counter, p->inputChan, p->clutPoints, ii);	/* Initialise counter */
4698 
4699 	/* Itterate through all verticies in the grid */
4700 	for (;;) {
4701 		int ti;			/* Table index */
4702 
4703 		for (ti = e = 0; e < p->inputChan; e++) { 	/* Input tables */
4704 			ti += ii[e] * p->dinc[e];				/* Clut index */
4705 			iv[e] = ii[e]/(p->clutPoints-1.0);		/* Vertex coordinates */
4706 			iv[e] = iv[e] * (imax[e] - imin[e]) + imin[e]; /* Undo expansion to 0.0 - 1.0 */
4707 			*((int *)&iv[-e-1]) = ii[e];			/* Trick to supply grid index in iv[] */
4708 		}
4709 
4710 		ifromentry(iv,iv);			/* Convert from table value to input color space */
4711 
4712 		/* Apply incolor -> outcolor function we want to represent */
4713 		clutfunc(cbctx, iv, iv);
4714 
4715 		otoentry(iv,iv);			/* Convert from output color space value to table value */
4716 
4717 		/* Expand used range to 0.0 - 1.0, and clip to legal values */
4718 		for (e = 0; e < p->outputChan; e++) {
4719 			double tt;
4720 			tt = (iv[e] - omin[e])/(omax[e] - omin[e]);
4721 			if (tt < 0.0)
4722 				tt = 0.0;
4723 			else if (tt > 1.0)
4724 				tt = 1.0;
4725 			iv[e] = tt;
4726 		}
4727 
4728 		for (e = 0; e < p->outputChan; e++) 	/* Output chans */
4729 			p->clutTable[ti++] = iv[e];
4730 
4731 		/* Increment index within block (Reverse index significancd) */
4732 		if (psh_inc(&counter, ii))
4733 			break;
4734 	}
4735 
4736 	/* Create the output table entry values */
4737 	for (n = 0; n < p->outputEnt; n++) {
4738 		double fv;
4739 		fv = n/(p->outputEnt-1.0);
4740 		for (e = 0; e < p->outputChan; e++)
4741 			iv[e] = fv;
4742 
4743 		/* Undo expansion to 0.0 - 1.0 */
4744 		for (e = 0; e < p->outputChan; e++) 		/* Output tables */
4745 			iv[e] = iv[e] * (omax[e] - omin[e]) + omin[e];
4746 
4747 		ofromentry(iv,iv);			/* Convert from table value to output color space value */
4748 
4749 		if (outfunc != NULL)
4750 			outfunc(cbctx, iv, iv);	/* Out colorspace -> output table -> out colorspace. */
4751 
4752 		otoentry(iv,iv);			/* Convert from output color space value to table value */
4753 
4754 		/* Clip to legal values */
4755 		for (e = 0; e < p->outputChan; e++) {
4756 			double tt;
4757 			tt = iv[e];
4758 			if (tt < 0.0)
4759 				tt = 0.0;
4760 			else if (tt > 1.0)
4761 				tt = 1.0;
4762 			iv[e] = tt;
4763 		}
4764 
4765 		for (e = 0; e < p->outputChan; e++) 		/* Input tables */
4766 			p->outputTable[e * p->outputEnt + n] = iv[e];
4767 	}
4768 	return 0;
4769 }
4770 
4771 /* - - - - - - - - - - - - - - - - */
4772 /* Return the number of bytes needed to write this tag */
4773 static unsigned int icmLut_get_size(
4774 	icmBase *pp
4775 ) {
4776 	icmLut *p = (icmLut *)pp;
4777 	unsigned int len = 0;
4778 
4779 	if (p->ttype == icSigLut8Type) {
4780 		len += 48;			/* tag and header */
4781 		len += 1 * (p->inputChan * p->inputEnt);
4782 		len += 1 * (p->outputChan * uipow(p->clutPoints,p->inputChan));
4783 		len += 1 * (p->outputChan * p->outputEnt);
4784 	} else {
4785 		len += 52;			/* tag and header */
4786 		len += 2 * (p->inputChan * p->inputEnt);
4787 		len += 2 * (p->outputChan * uipow(p->clutPoints,p->inputChan));
4788 		len += 2 * (p->outputChan * p->outputEnt);
4789 	}
4790 	return len;
4791 }
4792 
4793 /* read the object, return 0 on success, error code on fail */
4794 static int icmLut_read(
4795 	icmBase *pp,
4796 	unsigned long len,		/* tag length */
4797 	unsigned long of		/* start offset within file */
4798 ) {
4799 	icmLut *p = (icmLut *)pp;
4800 	icc *icp = p->icp;
4801 	int rv = 0;
4802 	unsigned long i, j, g, size;
4803 	char *bp, *buf;
4804 
4805 	if (len < 4) {
4806 		sprintf(icp->err,"icmLut_read: Tag too small to be legal");
4807 		return icp->errc = 1;
4808 	}
4809 
4810 	/* Allocate a file read buffer */
4811 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
4812 		sprintf(icp->err,"icmLut_read: malloc() failed");
4813 		return icp->errc = 2;
4814 	}
4815 	bp = buf;
4816 
4817 	/* Read portion of file into buffer */
4818 	if (   icp->fp->seek(icp->fp, of) != 0
4819 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
4820 		sprintf(icp->err,"icmLut_read: fseek() or fread() failed");
4821 		icp->al->free(icp->al, buf);
4822 		return icp->errc = 1;
4823 	}
4824 
4825 	/* Read type descriptor from the buffer */
4826 	p->ttype = (icTagTypeSignature)read_SInt32Number(bp);
4827 	if (p->ttype != icSigLut8Type && p->ttype != icSigLut16Type) {
4828 		sprintf(icp->err,"icmLut_read: Wrong tag type for icmLut");
4829 		icp->al->free(icp->al, buf);
4830 		return icp->errc = 1;
4831 	}
4832 
4833 	if (p->ttype == icSigLut8Type) {
4834 		if (len < 48) {
4835 			sprintf(icp->err,"icmLut_read: Tag too small to be legal");
4836 			icp->al->free(icp->al, buf);
4837 			return icp->errc = 1;
4838 		}
4839 	} else {
4840 		if (len < 52) {
4841 			sprintf(icp->err,"icmLut_read: Tag too small to be legal");
4842 			icp->al->free(icp->al, buf);
4843 			return icp->errc = 1;
4844 		}
4845 	}
4846 
4847 	/* Read in the info common to 8 and 16 bit Lut */
4848 	p->inputChan = read_UInt8Number(bp+8);
4849 	p->outputChan = read_UInt8Number(bp+9);
4850 	p->clutPoints = read_UInt8Number(bp+10);
4851 
4852 	/* Sanity check */
4853 	if (p->inputChan > MAX_CHAN) {
4854 		sprintf(icp->err,"icmLut_read: Can't handle > %d input channels\n",MAX_CHAN);
4855 		return icp->errc = 1;
4856 	}
4857 
4858 	if (p->outputChan > MAX_CHAN) {
4859 		sprintf(icp->err,"icmLut_read: Can't handle > %d output channels\n",MAX_CHAN);
4860 		return icp->errc = 1;
4861 	}
4862 
4863 	/* Read 3x3 transform matrix */
4864 	for (j = 0; j < 3; j++) {		/* Rows */
4865 		for (i = 0; i < 3; i++) {	/* Columns */
4866 			p->e[j][i] = read_S15Fixed16Number(bp + 12 + ((j * 3 + i) * 4));
4867 		}
4868 	}
4869 	/* Read 16 bit specific stuff */
4870 	if (p->ttype == icSigLut8Type) {
4871 		p->inputEnt  = 256;	/* By definition */
4872 		p->outputEnt = 256;	/* By definition */
4873 		bp = buf+48;
4874 	} else {
4875 		p->inputEnt  = read_UInt16Number(bp+48);
4876 		p->outputEnt = read_UInt16Number(bp+50);
4877 		bp = buf+52;
4878 	}
4879 
4880 	if (len < icmLut_get_size((icmBase *)p)) {
4881 		sprintf(icp->err,"icmLut_read: Tag too small for contents");
4882 		icp->al->free(icp->al, buf);
4883 		return icp->errc = 1;
4884 	}
4885 
4886 	/* Read the input tables */
4887 	size = (p->inputChan * p->inputEnt);
4888 	if ((rv = p->allocate((icmBase *)p)) != 0) {
4889 		icp->al->free(icp->al, buf);
4890 		return rv;
4891 	}
4892 	if (p->ttype == icSigLut8Type) {
4893 		for (i = 0; i < size; i++, bp += 1)
4894 			p->inputTable[i] = read_DCS8Number(bp);
4895 	} else {
4896 		for (i = 0; i < size; i++, bp += 2)
4897 			p->inputTable[i] = read_DCS16Number(bp);
4898 	}
4899 
4900 	/* Read the clut table */
4901 	size = (p->outputChan * uipow(p->clutPoints,p->inputChan));
4902 	if ((rv = p->allocate((icmBase *)p)) != 0) {
4903 		icp->al->free(icp->al, buf);
4904 		return rv;
4905 	}
4906 	if (p->ttype == icSigLut8Type) {
4907 		for (i = 0; i < size; i++, bp += 1)
4908 			p->clutTable[i] = read_DCS8Number(bp);
4909 	} else {
4910 		for (i = 0; i < size; i++, bp += 2)
4911 			p->clutTable[i] = read_DCS16Number(bp);
4912 	}
4913 
4914 	/* Read the output tables */
4915 	size = (p->outputChan * p->outputEnt);
4916 	if ((rv = p->allocate((icmBase *)p)) != 0) {
4917 		icp->al->free(icp->al, buf);
4918 		return rv;
4919 	}
4920 	if (p->ttype == icSigLut8Type) {
4921 		for (i = 0; i < size; i++, bp += 1)
4922 			p->outputTable[i] = read_DCS8Number(bp);
4923 	} else {
4924 		for (i = 0; i < size; i++, bp += 2)
4925 			p->outputTable[i] = read_DCS16Number(bp);
4926 	}
4927 
4928 	/* Private: compute dimensional increment though clut */
4929 	/* Note that first channel varies least rapidly. */
4930 	i = p->inputChan-1;
4931 	p->dinc[i--] = p->outputChan;
4932 	for (; i < p->inputChan; i--)
4933 		p->dinc[i] = p->dinc[i+1] * p->clutPoints;
4934 
4935 	/* Private: compute offsets from base of cube to other corners */
4936 	for (p->dcube[0] = 0, g = 1, j = 0; j < p->inputChan; j++) {
4937 		for (i = 0; i < g; i++)
4938 			p->dcube[g+i] = p->dcube[i] + p->dinc[j];
4939 		g *= 2;
4940 	}
4941 
4942 	icp->al->free(icp->al, buf);
4943 	return 0;
4944 }
4945 
4946 /* Write the contents of the object. Return 0 on sucess, error code on failure */
4947 static int icmLut_write(
4948 	icmBase *pp,
4949 	unsigned long of			/* File offset to write from */
4950 ) {
4951 	icmLut *p = (icmLut *)pp;
4952 	icc *icp = p->icp;
4953 	unsigned long i,j;
4954 	unsigned int len, size;
4955 	char *bp, *buf;		/* Buffer to write from */
4956 	int rv = 0;
4957 
4958 	/* Allocate a file write buffer */
4959 	len = p->get_size((icmBase *)p);
4960 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
4961 		sprintf(icp->err,"icmLut_write malloc() failed");
4962 		return icp->errc = 2;
4963 	}
4964 	bp = buf;
4965 
4966 	/* Write type descriptor to the buffer */
4967 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
4968 		sprintf(icp->err,"icmLut_write: write_SInt32Number() failed");
4969 		icp->al->free(icp->al, buf);
4970 		return icp->errc = rv;
4971 	}
4972 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
4973 
4974 	/* Write the info common to 8 and 16 bit Lut */
4975 	if ((rv = write_UInt8Number(p->inputChan, bp+8)) != 0) {
4976 		sprintf(icp->err,"icmLut_write: write_UInt8Number() failed");
4977 		icp->al->free(icp->al, buf);
4978 		return icp->errc = rv;
4979 	}
4980 	if ((rv = write_UInt8Number(p->outputChan, bp+9)) != 0) {
4981 		sprintf(icp->err,"icmLut_write: write_UInt8Number() failed");
4982 		icp->al->free(icp->al, buf);
4983 		return icp->errc = rv;
4984 	}
4985 	if ((rv = write_UInt8Number(p->clutPoints, bp+10)) != 0) {
4986 		sprintf(icp->err,"icmLut_write: write_UInt8Number() failed");
4987 		icp->al->free(icp->al, buf);
4988 		return icp->errc = rv;
4989 	}
4990 
4991 	/* Write 3x3 transform matrix */
4992 	for (j = 0; j < 3; j++) {		/* Rows */
4993 		for (i = 0; i < 3; i++) {	/* Columns */
4994 			if ((rv = write_S15Fixed16Number(p->e[j][i],bp + 12 + ((j * 3 + i) * 4))) != 0) {
4995 				sprintf(icp->err,"icmLut_write: write_S15Fixed16Number() failed");
4996 				icp->al->free(icp->al, buf);
4997 				return icp->errc = rv;
4998 			}
4999 		}
5000 	}
5001 
5002 	/* Write 16 bit specific stuff */
5003 	if (p->ttype == icSigLut8Type) {
5004 		if (p->inputEnt != 256 || p->outputEnt != 256) {
5005 			sprintf(icp->err,"icmLut_write: 8 bit Input and Output tables must be 256 entries");
5006 			icp->al->free(icp->al, buf);
5007 			return icp->errc = 1;
5008 		}
5009 		bp = buf+48;
5010 	} else {
5011 		if ((rv = write_UInt16Number(p->inputEnt, bp+48)) != 0) {
5012 			sprintf(icp->err,"icmLut_write: write_UInt16Number() failed");
5013 			icp->al->free(icp->al, buf);
5014 			return icp->errc = rv;
5015 		}
5016 		if ((rv = write_UInt16Number(p->outputEnt, bp+50)) != 0) {
5017 			sprintf(icp->err,"icmLut_write: write_UInt16Number() failed");
5018 			icp->al->free(icp->al, buf);
5019 			return icp->errc = rv;
5020 		}
5021 		bp = buf+52;
5022 	}
5023 
5024 	/* Write the input tables */
5025 	size = (p->inputChan * p->inputEnt);
5026 	if (p->ttype == icSigLut8Type) {
5027 		for (i = 0; i < size; i++, bp += 1) {
5028 			if ((rv = write_DCS8Number(p->inputTable[i], bp)) != 0) {
5029 				sprintf(icp->err,"icmLut_write: inputTable write_DCS8Number() failed");
5030 				icp->al->free(icp->al, buf);
5031 				return icp->errc = rv;
5032 			}
5033 		}
5034 	} else {
5035 		for (i = 0; i < size; i++, bp += 2) {
5036 			if ((rv = write_DCS16Number(p->inputTable[i], bp)) != 0) {
5037 				sprintf(icp->err,"icmLut_write: inputTable write_DCS16Number(%f) failed",p->inputTable[i]);
5038 				icp->al->free(icp->al, buf);
5039 				return icp->errc = rv;
5040 			}
5041 		}
5042 	}
5043 
5044 	/* Write the clut table */
5045 	size = (p->outputChan * uipow(p->clutPoints,p->inputChan));
5046 	if (p->ttype == icSigLut8Type) {
5047 		for (i = 0; i < size; i++, bp += 1) {
5048 			if ((rv = write_DCS8Number(p->clutTable[i], bp)) != 0) {
5049 				sprintf(icp->err,"icmLut_write: clutTable write_DCS8Number() failed");
5050 				icp->al->free(icp->al, buf);
5051 				return icp->errc = rv;
5052 			}
5053 		}
5054 	} else {
5055 		for (i = 0; i < size; i++, bp += 2) {
5056 			if ((rv = write_DCS16Number(p->clutTable[i], bp)) != 0) {
5057 				sprintf(icp->err,"icmLut_write: clutTable write_DCS16Number(%f) failed",p->clutTable[i]);
5058 				icp->al->free(icp->al, buf);
5059 				return icp->errc = rv;
5060 			}
5061 		}
5062 	}
5063 
5064 	/* Write the output tables */
5065 	size = (p->outputChan * p->outputEnt);
5066 	if (p->ttype == icSigLut8Type) {
5067 		for (i = 0; i < size; i++, bp += 1) {
5068 			if ((rv = write_DCS8Number(p->outputTable[i], bp)) != 0) {
5069 				sprintf(icp->err,"icmLut_write: outputTable write_DCS8Number() failed");
5070 				icp->al->free(icp->al, buf);
5071 				return icp->errc = rv;
5072 			}
5073 		}
5074 	} else {
5075 		for (i = 0; i < size; i++, bp += 2) {
5076 			if ((rv = write_DCS16Number(p->outputTable[i], bp)) != 0) {
5077 				sprintf(icp->err,"icmLut_write: outputTable write_DCS16Number(%f) failed",p->outputTable[i]);
5078 				icp->al->free(icp->al, buf);
5079 				return icp->errc = rv;
5080 			}
5081 		}
5082 	}
5083 
5084 	/* Write buffer to the file */
5085 	if (   icp->fp->seek(icp->fp, of) != 0
5086 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
5087 		sprintf(icp->err,"icmLut_write fseek() or fwrite() failed");
5088 		icp->al->free(icp->al, buf);
5089 		return icp->errc = 2;
5090 	}
5091 	icp->al->free(icp->al, buf);
5092 	return 0;
5093 }
5094 
5095 /* Dump a text description of the object */
5096 static void icmLut_dump(
5097 	icmBase *pp,
5098 	FILE *op,		/* Output to dump to */
5099 	int   verb		/* Verbosity level */
5100 ) {
5101 	icmLut *p = (icmLut *)pp;
5102 	if (verb <= 0)
5103 		return;
5104 
5105 	if (p->ttype == icSigLut8Type) {
5106 		fprintf(op,"Lut8:\n");
5107 	} else {
5108 		fprintf(op,"Lut16:\n");
5109 	}
5110 	fprintf(op,"  Input Channels = %u\n",p->inputChan);
5111 	fprintf(op,"  Output Channels = %u\n",p->outputChan);
5112 	fprintf(op,"  CLUT resolution = %u\n",p->clutPoints);
5113 	fprintf(op,"  Input Table entries = %u\n",p->inputEnt);
5114 	fprintf(op,"  Output Table entries = %u\n",p->outputEnt);
5115 	fprintf(op,"  XYZ matrix =  %f, %f, %f\n",p->e[0][0],p->e[0][1],p->e[0][2]);
5116 	fprintf(op,"                %f, %f, %f\n",p->e[1][0],p->e[1][1],p->e[1][2]);
5117 	fprintf(op,"                %f, %f, %f\n",p->e[2][0],p->e[2][1],p->e[2][2]);
5118 
5119 	if (verb >= 2) {
5120 		unsigned int i,size;
5121 		int j;
5122 		unsigned int ii[MAX_CHAN];	/* maximum no of input channels */
5123 
5124 		fprintf(op,"  Input table:\n");
5125 		for (i = 0; i < p->inputEnt; i++) {
5126 			fprintf(op,"    %3u: ",i);
5127 			for (j = 0; j < p->inputChan; j++)
5128 				fprintf(op," %1.10f",p->inputTable[j * p->inputEnt + i]);
5129 			fprintf(op,"\n");
5130 		}
5131 
5132 		fprintf(op,"\n  CLUT table:\n");
5133 		if (p->inputChan > MAX_CHAN) {
5134 			fprintf(op,"  !!Can't dump > %d input channel CLUT table!!\n",MAX_CHAN);
5135 		} else {
5136 			size = (p->outputChan * uipow(p->clutPoints,p->inputChan));
5137 			for (j = 0; j < p->inputChan; j++)
5138 				ii[j] = 0;
5139 			for (i = 0; i < size;) {
5140 				int k;
5141 				/* Print table entry index */
5142 				fprintf(op,"   ");
5143 				for (j = p->inputChan-1; j >= 0; j--)
5144 					fprintf(op," %2u",ii[j]);
5145 				fprintf(op,":");
5146 				/* Print table entry contents */
5147 				for (k = 0; k < p->outputChan; k++, i++)
5148 					fprintf(op," %1.10f",p->clutTable[i]);
5149 				fprintf(op,"\n");
5150 
5151 				for (j = 0; j < p->inputChan; j++) { /* Increment index */
5152 					ii[j]++;
5153 					if (ii[j] < p->clutPoints)
5154 						break;	/* No carry */
5155 					ii[j] = 0;
5156 				}
5157 			}
5158 		}
5159 
5160 		fprintf(op,"\n  Output table:\n");
5161 		for (i = 0; i < p->outputEnt; i++) {
5162 			fprintf(op,"    %3u: ",i);
5163 			for (j = 0; j < p->outputChan; j++)
5164 				fprintf(op," %1.10f",p->outputTable[j * p->outputEnt + i]);
5165 			fprintf(op,"\n");
5166 		}
5167 
5168 	}
5169 }
5170 
5171 /* Allocate variable sized data elements */
5172 static int icmLut_allocate(
5173 	icmBase *pp
5174 ) {
5175 	unsigned int i, j, g, size;
5176 	icmLut *p = (icmLut *)pp;
5177 	icc *icp = p->icp;
5178 
5179 	/* Sanity check */
5180 	if (p->inputChan > MAX_CHAN) {
5181 		sprintf(icp->err,"icmLut_alloc: Can't handle > %d input channels\n",MAX_CHAN);
5182 		return icp->errc = 1;
5183 	}
5184 
5185 	if (p->outputChan > MAX_CHAN) {
5186 		sprintf(icp->err,"icmLut_alloc: Can't handle > %d output channels\n",MAX_CHAN);
5187 		return icp->errc = 1;
5188 	}
5189 
5190 	size = (p->inputChan * p->inputEnt);
5191 	if (size != p->inputTable_size) {
5192 		if (p->inputTable != NULL)
5193 			icp->al->free(icp->al, p->inputTable);
5194 		if ((p->inputTable = (double *) icp->al->calloc(icp->al,sizeof(double), size)) == NULL) {
5195 			sprintf(icp->err,"icmLut_alloc: calloc() of Lut inputTable data failed");
5196 			return icp->errc = 2;
5197 		}
5198 		p->inputTable_size = size;
5199 	}
5200 	size = (p->outputChan * uipow(p->clutPoints,p->inputChan));
5201 	if (size != p->clutTable_size) {
5202 		if (p->clutTable != NULL)
5203 			icp->al->free(icp->al, p->clutTable);
5204 		if ((p->clutTable = (double *) icp->al->calloc(icp->al,sizeof(double), size)) == NULL) {
5205 			sprintf(icp->err,"icmLut_alloc: calloc() of Lut clutTable data failed");
5206 			return icp->errc = 2;
5207 		}
5208 		p->clutTable_size = size;
5209 	}
5210 	size = (p->outputChan * p->outputEnt);
5211 	if (size != p->outputTable_size) {
5212 		if (p->outputTable != NULL)
5213 			icp->al->free(icp->al, p->outputTable);
5214 		if ((p->outputTable = (double *) icp->al->calloc(icp->al,sizeof(double), size)) == NULL) {
5215 			sprintf(icp->err,"icmLut_alloc: calloc() of Lut outputTable data failed");
5216 			return icp->errc = 2;
5217 		}
5218 		p->outputTable_size = size;
5219 	}
5220 
5221 	/* Private: compute dimensional increment though clut */
5222 	/* Note that first channel varies least rapidly. */
5223 	i = p->inputChan-1;
5224 	p->dinc[i--] = p->outputChan;
5225 	for (; i < p->inputChan; i--)
5226 		p->dinc[i] = p->dinc[i+1] * p->clutPoints;
5227 
5228 	/* Private: compute offsets from base of cube to other corners */
5229 	for (p->dcube[0] = 0, g = 1, j = 0; j < p->inputChan; j++) {
5230 		for (i = 0; i < g; i++)
5231 			p->dcube[g+i] = p->dcube[i] + p->dinc[j];
5232 		g *= 2;
5233 	}
5234 
5235 	return 0;
5236 }
5237 
5238 /* Free all storage in the object */
5239 static void icmLut_delete(
5240 	icmBase *pp
5241 ) {
5242 	icmLut *p = (icmLut *)pp;
5243 	icc *icp = p->icp;
5244 
5245 	if (p->inputTable != NULL)
5246 		icp->al->free(icp->al, p->inputTable);
5247 	if (p->clutTable != NULL)
5248 		icp->al->free(icp->al, p->clutTable);
5249 	if (p->outputTable != NULL)
5250 		icp->al->free(icp->al, p->outputTable);
5251 	icmTable_delete_bwd(icp, &p->rit);
5252 	icmTable_delete_bwd(icp, &p->rot);
5253 	icp->al->free(icp->al, p);
5254 }
5255 
5256 /* Create an empty object. Return null on error */
5257 static icmBase *new_icmLut(
5258 	icc *icp
5259 ) {
5260 	int i,j;
5261 	icmLut *p;
5262 	if ((p = (icmLut *) icp->al->calloc(icp->al,1,sizeof(icmLut))) == NULL)
5263 		return NULL;
5264 	p->ttype    = icSigLut16Type;
5265 	p->refcount = 1;
5266 	p->get_size = icmLut_get_size;
5267 	p->read     = icmLut_read;
5268 	p->write    = icmLut_write;
5269 	p->dump     = icmLut_dump;
5270 	p->allocate = icmLut_allocate;
5271 	p->del      = icmLut_delete;
5272 
5273 	/* Lookup methods */
5274 	p->nu_matrix      = icmLut_nu_matrix;
5275 	p->min_max        = icmLut_min_max;
5276 	p->lookup_matrix  = icmLut_lookup_matrix;
5277 	p->lookup_input   = icmLut_lookup_input;
5278 	p->lookup_clut_nl = icmLut_lookup_clut_nl;
5279 	p->lookup_clut_sx = icmLut_lookup_clut_sx;
5280 	p->lookup_output  = icmLut_lookup_output;
5281 
5282 	/* Set method */
5283 	p->set_tables = icmLut_set_tables;
5284 
5285 	p->icp      = icp;
5286 
5287 	/* Set matrix to reasonable default */
5288 	for (i = 0; i < 3; i++)
5289 		for (j = 0; j < 3; j++) {
5290 			if (i == j)
5291 				p->e[i][j] = 1.0;
5292 			else
5293 				p->e[i][j] = 0.0;
5294 		}
5295 
5296 	/* Init lookups to non-dangerous values */
5297 	for (i = 0; i < MAX_CHAN; i++)
5298 		p->dinc[i] = 0;
5299 
5300 	for (i = 0; i < (1 << MAX_CHAN); i++)
5301 		p->dcube[i] = 0;
5302 
5303 	p->rit.inited = 0;
5304 	p->rot.inited = 0;
5305 
5306 	return (icmBase *)p;
5307 }
5308 
5309 /* ---------------------------------------------------------- */
5310 /* Measurement */
5311 
5312 /* Return the number of bytes needed to write this tag */
5313 static unsigned int icmMeasurement_get_size(
5314 	icmBase *pp
5315 ) {
5316 	icmMeasurement *p = (icmMeasurement *)pp;
5317 	unsigned int len = 0;
5318 	len += 8;			/* 8 bytes for tag and padding */
5319 	len += 4;			/* 4 for standard observer */
5320 	len += 12;			/* 12 for XYZ of measurement backing */
5321 	len += 4;			/* 4 for measurement geometry */
5322 	len += 4;			/* 4 for measurement flare */
5323 	len += 4;			/* 4 for standard illuminant */
5324 	return len;
5325 }
5326 
5327 /* read the object, return 0 on success, error code on fail */
5328 static int icmMeasurement_read(
5329 	icmBase *pp,
5330 	unsigned long len,		/* tag length */
5331 	unsigned long of		/* start offset within file */
5332 ) {
5333 	icmMeasurement *p = (icmMeasurement *)pp;
5334 	icc *icp = p->icp;
5335 	int rv;
5336 	char *bp, *buf;
5337 
5338 	if (len < 36) {
5339 		sprintf(icp->err,"icmMeasurement_read: Tag too small to be legal");
5340 		return icp->errc = 1;
5341 	}
5342 
5343 	/* Allocate a file read buffer */
5344 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
5345 		sprintf(icp->err,"icmMeasurement_read: malloc() failed");
5346 		return icp->errc = 2;
5347 	}
5348 	bp = buf;
5349 
5350 	/* Read portion of file into buffer */
5351 	if (   icp->fp->seek(icp->fp, of) != 0
5352 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
5353 		sprintf(icp->err,"icmMeasurement_read: fseek() or fread() failed");
5354 		icp->al->free(icp->al, buf);
5355 		return icp->errc = 1;
5356 	}
5357 
5358 	/* Read type descriptor from the buffer */
5359 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
5360 		sprintf(icp->err,"icmMeasurement_read: Wrong tag type for icmMeasurement");
5361 		icp->al->free(icp->al, buf);
5362 		return icp->errc = 1;
5363 	}
5364 
5365 	/* Read the encoded standard observer */
5366 	p->observer = (icStandardObserver)read_SInt32Number(bp + 8);
5367 
5368 	/* Read the XYZ values for measurement backing */
5369 	if ((rv = read_XYZNumber(&p->backing, bp+12)) != 0) {
5370 		sprintf(icp->err,"icmMeasurement: read_XYZNumber error");
5371 		icp->al->free(icp->al, buf);
5372 		return icp->errc = rv;
5373 	}
5374 
5375 	/* Read the encoded measurement geometry */
5376 	p->geometry = (icMeasurementGeometry)read_SInt32Number(bp + 24);
5377 
5378 	/* Read the proportion of flare  */
5379 	p->flare = read_U16Fixed16Number(bp + 28);
5380 
5381 	/* Read the encoded standard illuminant */
5382 	p->illuminant = (icMeasurementFlare)read_SInt32Number(bp + 32);
5383 
5384 	icp->al->free(icp->al, buf);
5385 	return 0;
5386 }
5387 
5388 /* Write the contents of the object. Return 0 on sucess, error code on failure */
5389 static int icmMeasurement_write(
5390 	icmBase *pp,
5391 	unsigned long of			/* File offset to write from */
5392 ) {
5393 	icmMeasurement *p = (icmMeasurement *)pp;
5394 	icc *icp = p->icp;
5395 	unsigned int len;
5396 	char *bp, *buf;		/* Buffer to write from */
5397 	int rv = 0;
5398 
5399 	/* Allocate a file write buffer */
5400 	len = p->get_size((icmBase *)p);
5401 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
5402 		sprintf(icp->err,"icmMeasurement_write malloc() failed");
5403 		return icp->errc = 2;
5404 	}
5405 	bp = buf;
5406 
5407 	/* Write type descriptor to the buffer */
5408 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
5409 		sprintf(icp->err,"icmMeasurement_write, type: write_SInt32Number() failed");
5410 		icp->al->free(icp->al, buf);
5411 		return icp->errc = rv;
5412 	}
5413 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
5414 
5415 	/* Write the encoded standard observer */
5416 	if ((rv = write_SInt32Number((int)p->observer, bp + 8)) != 0) {
5417 		sprintf(icp->err,"icmMeasurementa_write, observer: write_SInt32Number() failed");
5418 		icp->al->free(icp->al, buf);
5419 		return icp->errc = rv;
5420 	}
5421 
5422 	/* Write the XYZ values for measurement backing */
5423 	if ((rv = write_XYZNumber(&p->backing, bp+12)) != 0) {
5424 		sprintf(icp->err,"icmMeasurement, backing: write_XYZNumber error");
5425 		icp->al->free(icp->al, buf);
5426 		return icp->errc = rv;
5427 	}
5428 
5429 	/* Write the encoded measurement geometry */
5430 	if ((rv = write_SInt32Number((int)p->geometry, bp + 24)) != 0) {
5431 		sprintf(icp->err,"icmMeasurementa_write, geometry: write_SInt32Number() failed");
5432 		icp->al->free(icp->al, buf);
5433 		return icp->errc = rv;
5434 	}
5435 
5436 	/* Write the proportion of flare */
5437 	if ((rv = write_U16Fixed16Number(p->flare, bp + 28)) != 0) {
5438 		sprintf(icp->err,"icmMeasurementa_write, flare: write_U16Fixed16Number() failed");
5439 		icp->al->free(icp->al, buf);
5440 		return icp->errc = rv;
5441 	}
5442 
5443 	/* Write the encoded standard illuminant */
5444 	if ((rv = write_SInt32Number((int)p->illuminant, bp + 32)) != 0) {
5445 		sprintf(icp->err,"icmMeasurementa_write, illuminant: write_SInt32Number() failed");
5446 		icp->al->free(icp->al, buf);
5447 		return icp->errc = rv;
5448 	}
5449 
5450 	/* Write to the file */
5451 	if (   icp->fp->seek(icp->fp, of) != 0
5452 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
5453 		sprintf(icp->err,"icmMeasurement_write fseek() or fwrite() failed");
5454 		icp->al->free(icp->al, buf);
5455 		return icp->errc = 2;
5456 	}
5457 	icp->al->free(icp->al, buf);
5458 	return 0;
5459 }
5460 
5461 /* Dump a text description of the object */
5462 static void icmMeasurement_dump(
5463 	icmBase *pp,
5464 	FILE *op,		/* Output to dump to */
5465 	int   verb		/* Verbosity level */
5466 ) {
5467 	icmMeasurement *p = (icmMeasurement *)pp;
5468 	if (verb <= 0)
5469 		return;
5470 
5471 	fprintf(op,"Measurement:\n");
5472 	fprintf(op,"  Standard Observer = %s\n", string_StandardObserver(p->observer));
5473 	fprintf(op,"  XYZ for Measurement Backing = %s\n", string_XYZNumber_and_Lab(&p->backing));
5474 	fprintf(op,"  Measurement Geometry = %s\n", string_MeasurementGeometry(p->geometry));
5475 	fprintf(op,"  Measurement Flare = %5.1f%%\n", p->flare * 100.0);
5476 	fprintf(op,"  Standard Illuminant = %s\n", string_Illuminant(p->illuminant));
5477 }
5478 
5479 /* Allocate variable sized data elements */
5480 static int icmMeasurement_allocate(
5481 	icmBase *pp
5482 ) {
5483 	icmMeasurement *p = (icmMeasurement *)pp;
5484 
5485 	/* Nothing to do */
5486 	return 0;
5487 }
5488 
5489 /* Free all storage in the object */
5490 static void icmMeasurement_delete(
5491 	icmBase *pp
5492 ) {
5493 	icmMeasurement *p = (icmMeasurement *)pp;
5494 	icc *icp = p->icp;
5495 
5496 	icp->al->free(icp->al, p);
5497 }
5498 
5499 /* Create an empty object. Return null on error */
5500 static icmBase *new_icmMeasurement(
5501 	icc *icp
5502 ) {
5503 	icmMeasurement *p;
5504 	if ((p = (icmMeasurement *) icp->al->calloc(icp->al,1,sizeof(icmMeasurement))) == NULL)
5505 		return NULL;
5506 	p->ttype    = icSigMeasurementType;
5507 	p->refcount = 1;
5508 	p->get_size = icmMeasurement_get_size;
5509 	p->read     = icmMeasurement_read;
5510 	p->write    = icmMeasurement_write;
5511 	p->dump     = icmMeasurement_dump;
5512 	p->allocate = icmMeasurement_allocate;
5513 	p->del      = icmMeasurement_delete;
5514 	p->icp      = icp;
5515 
5516 	return (icmBase *)p;
5517 }
5518 
5519 /* ---------------------------------------------------------- */
5520 
5521 /* Named color structure read/write support */
5522 static int read_NamedColorVal(
5523 	icmNamedColorVal *p,
5524 	char *bp,
5525 	char *end,
5526 	icColorSpaceSignature pcs,		/* Header Profile Connection Space */
5527 	unsigned int ndc				/* Number of device corrds */
5528 ) {
5529 	icc *icp = p->icp;
5530 	int i;
5531 	unsigned int mxl;	/* Max possible string length */
5532 
5533 	mxl = (end - bp) < 32 ? (end - bp) : 32;
5534 	if (check_null_string(bp,mxl)) {
5535 		sprintf(icp->err,"icmNamedColorVal_read: Root name string not terminated");
5536 		return icp->errc = 1;
5537 	}
5538 	strcpy((void *)p->root, (void *)bp);
5539 	bp += strlen(p->root) + 1;
5540 	if ((bp + ndc) > end) {
5541 		sprintf(icp->err,"icmNamedColorVal_read: Data too short to read device coords");
5542 		return icp->errc = 1;
5543 	}
5544 	for (i = 0; i < ndc; i++) {
5545 		p->deviceCoords[i] = read_DCS8Number(bp);
5546 		bp += 1;
5547 	}
5548 	return 0;
5549 }
5550 
5551 static int read_NamedColorVal2(
5552 	icmNamedColorVal *p,
5553 	char *bp,
5554 	char *end,
5555 	icColorSpaceSignature pcs,		/* Header Profile Connection Space */
5556 	unsigned int ndc				/* Number of device corrds */
5557 ) {
5558 	icc *icp = p->icp;
5559 	int i;
5560 	if ((bp + 32 + 6 + ndc * 2) > end) {
5561 		sprintf(icp->err,"icmNamedColorVal2_read: Data too short to read");
5562 		return icp->errc = 1;
5563 	}
5564 	if (check_null_string(bp,32)) {
5565 		sprintf(icp->err,"icmNamedColorVal2_read: Root name string not terminated");
5566 		return icp->errc = 1;
5567 	}
5568 	memcpy((void *)p->root,(void *)(bp + 0),32);
5569 	switch(pcs) {
5570 		case icSigXYZData:
5571 				p->pcsCoords[0] = read_PCSXYZ16Number(bp+32);
5572 				p->pcsCoords[1] = read_PCSXYZ16Number(bp+34);
5573 				p->pcsCoords[2] = read_PCSXYZ16Number(bp+36);
5574 			break;
5575 	   	case icSigLabData:
5576 				p->pcsCoords[0] =  read_PCSL16Number(bp+32);
5577 				p->pcsCoords[1] = read_PCSab16Number(bp+34);
5578 				p->pcsCoords[2] = read_PCSab16Number(bp+36);
5579 			break;
5580 		default:
5581 			return 1;		/* Unknown PCS */
5582 	}
5583 	for (i = 0; i < ndc; i++)
5584 		p->deviceCoords[i] = read_DCS16Number(bp + 32 + 6 + 2 * i);
5585 	return 0;
5586 }
5587 
5588 static int write_NamedColorVal(
5589 	icmNamedColorVal *p,
5590 	char *d,
5591 	icColorSpaceSignature pcs,		/* Header Profile Connection Space */
5592 	unsigned int ndc				/* Number of device corrds */
5593 ) {
5594 	icc *icp = p->icp;
5595 	int i, rv = 0;
5596 	if (check_null_string(p->root,32) != 0) {
5597 		sprintf(icp->err,"icmNamedColorVal_write: Root string names is unterminated");
5598 		return icp->errc = 1;
5599 	}
5600 	strcpy((void *)d,(void *)p->root);
5601 	d += strlen(p->root) + 1;
5602 	for (i = 0; i < ndc; i++) {
5603 		if ((rv = write_DCS8Number(p->deviceCoords[i], d)) != 0) {
5604 			sprintf(icp->err,"icmNamedColorVal_write: write of device coord failed");
5605 			return icp->errc = 1;
5606 		}
5607 		d += 1;
5608 	}
5609 	return 0;
5610 }
5611 
5612 static int write_NamedColorVal2(
5613 	icmNamedColorVal *p,
5614 	char *bp,
5615 	icColorSpaceSignature pcs,		/* Header Profile Connection Space */
5616 	unsigned int ndc				/* Number of device corrds */
5617 ) {
5618 	icc *icp = p->icp;
5619 	int i, rv = 0;
5620 	if (check_null_string(p->root,32)) {
5621 		sprintf(icp->err,"icmNamedColorVal2_write: Root string names is unterminated");
5622 		return icp->errc = 1;
5623 	}
5624 	memcpy((void *)(bp + 0),(void *)p->root,32);
5625 	switch(pcs) {
5626 		case icSigXYZData:
5627 				rv |= write_PCSXYZ16Number(p->pcsCoords[0], bp+32);
5628 				rv |= write_PCSXYZ16Number(p->pcsCoords[1], bp+34);
5629 				rv |= write_PCSXYZ16Number(p->pcsCoords[2], bp+36);
5630 			break;
5631     	case icSigLabData:
5632 				rv |=  write_PCSL16Number(p->pcsCoords[0], bp+32);
5633 				rv |= write_PCSab16Number(p->pcsCoords[1], bp+34);
5634 				rv |= write_PCSab16Number(p->pcsCoords[2], bp+36);
5635 			break;
5636 		default:
5637 			sprintf(icp->err,"icmNamedColorVal2_write: Unknown PCS");
5638 			return icp->errc = 1;
5639 	}
5640 	if (rv) {
5641 		sprintf(icp->err,"icmNamedColorVal2_write: write of PCS coord failed");
5642 		return icp->errc = 1;
5643 	}
5644 	for (i = 0; i < ndc; i++) {
5645 		if ((rv = write_DCS16Number(p->deviceCoords[i], bp + 32 + 6 + 2 * i)) != 0) {
5646 			sprintf(icp->err,"icmNamedColorVal2_write: write of device coord failed");
5647 			return icp->errc = 1;
5648 		}
5649 	}
5650 	return 0;
5651 }
5652 
5653 /* - - - - - - - - - - - */
5654 /* icmNamedColor object */
5655 
5656 /* Return the number of bytes needed to write this tag */
5657 static unsigned int icmNamedColor_get_size(
5658 	icmBase *pp
5659 ) {
5660 	icmNamedColor *p = (icmNamedColor *)pp;
5661 	unsigned int len = 0;
5662 	if (p->ttype == icSigNamedColorType) {
5663 		unsigned int i;
5664 		len += 8;			/* 8 bytes for tag and padding */
5665 		len += 4;			/* 4 for vendor specific flags */
5666 		len += 4;			/* 4 for count of named colors */
5667 		len += strlen(p->prefix) + 1; /* prefix of color names */
5668 		len += strlen(p->suffix) + 1; /* suffix of color names */
5669 		for (i = 0; i < p->count; i++) {
5670 			len += strlen(p->data[i].root) + 1; /* color names */
5671 			len += p->nDeviceCoords * 1;	/* bytes for each named color */
5672 		}
5673 	} else {	/* Named Color 2 */
5674 		len += 8;			/* 8 bytes for tag and padding */
5675 		len += 4;			/* 4 for vendor specific flags */
5676 		len += 4;			/* 4 for count of named colors */
5677 		len += 4;			/* 4 for number of device coords */
5678 		len += 32;			/* 32 for prefix of color names */
5679 		len += 32;			/* 32 for suffix of color names */
5680 		len += p->count * (32 + 6 + p->nDeviceCoords * 2);	/* bytes for each named color */
5681 	}
5682 	return len;
5683 }
5684 
5685 /* read the object, return 0 on success, error code on fail */
5686 static int icmNamedColor_read(
5687 	icmBase *pp,
5688 	unsigned long len,		/* tag length */
5689 	unsigned long of		/* start offset within file */
5690 ) {
5691 	icmNamedColor *p = (icmNamedColor *)pp;
5692 	icc *icp = p->icp;
5693 	unsigned long i;
5694 	char *bp, *buf, *end;
5695 	int rv = 0;
5696 
5697 	if (len < 4) {
5698 		sprintf(icp->err,"icmNamedColor_read: Tag too small to be legal");
5699 		return icp->errc = 1;
5700 	}
5701 
5702 	/* Allocate a file read buffer */
5703 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
5704 		sprintf(icp->err,"icmNamedColor_read: malloc() failed");
5705 		return icp->errc = 2;
5706 	}
5707 	bp = buf;
5708 	end = buf + len;
5709 
5710 	/* Read portion of file into buffer */
5711 	if (   icp->fp->seek(icp->fp, of) != 0
5712 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
5713 		sprintf(icp->err,"icmNamedColor_read: fseek() or fread() failed");
5714 		icp->al->free(icp->al, buf);
5715 		return icp->errc = 1;
5716 	}
5717 
5718 	/* Read type descriptor from the buffer */
5719 	p->ttype = (icTagTypeSignature)read_SInt32Number(bp);
5720 	if (p->ttype != icSigNamedColorType && p->ttype != icSigNamedColor2Type) {
5721 		sprintf(icp->err,"icmNamedColor_read: Wrong tag type for icmNamedColor");
5722 		icp->al->free(icp->al, buf);
5723 		return icp->errc = 1;
5724 	}
5725 
5726 	if (p->ttype == icSigNamedColorType) {
5727 		if (len < 16) {
5728 			sprintf(icp->err,"icmNamedColor_read: Tag too small to be legal");
5729 			icp->al->free(icp->al, buf);
5730 			return icp->errc = 1;
5731 		}
5732 		/* Make sure that the number of device coords in known */
5733 		p->nDeviceCoords = number_ColorSpaceSignature(icp->header->colorSpace);
5734 		if (p->nDeviceCoords > MAX_CHAN) {
5735 			sprintf(icp->err,"icmNamedColor_read: Can't handle more than %d device channels",MAX_CHAN);
5736 			icp->al->free(icp->al, buf);
5737 			return icp->errc = 1;
5738 		}
5739 
5740 	} else {	/* icmNC2 */
5741 		if (len < 84) {
5742 			sprintf(icp->err,"icmNamedColor_read: Tag too small to be legal");
5743 			icp->al->free(icp->al, buf);
5744 			return icp->errc = 1;
5745 		}
5746 	}
5747 
5748 	/* Read vendor specific flag */
5749 	p->vendorFlag = read_UInt32Number(bp+8);
5750 
5751 	/* Read count of named colors */
5752 	p->count = read_UInt32Number(bp+12);
5753 
5754 	if (p->ttype == icSigNamedColorType) {
5755 		unsigned int mxl;	/* Max possible string length */
5756 		bp = bp + 16;
5757 
5758 		/* Prefix for each color name */
5759 		mxl = (end - bp) < 32 ? (end - bp) : 32;
5760 		if (check_null_string(bp,mxl) != 0) {
5761 			sprintf(icp->err,"icmNamedColor_read: Color prefix is not null terminated");
5762 			icp->al->free(icp->al, buf);
5763 			return icp->errc = 1;
5764 		}
5765 		strcpy((void *)p->prefix, (void *)bp);
5766 		bp += strlen(p->prefix) + 1;
5767 
5768 		/* Suffix for each color name */
5769 		mxl = (end - bp) < 32 ? (end - bp) : 32;
5770 		if (check_null_string(bp,mxl) != 0) {
5771 			sprintf(icp->err,"icmNamedColor_read: Color suffix is not null terminated");
5772 			icp->al->free(icp->al, buf);
5773 			return icp->errc = 1;
5774 		}
5775 		strcpy((void *)p->suffix, (void *)bp);
5776 		bp += strlen(p->suffix) + 1;
5777 
5778 		if ((rv = p->allocate((void *)p)) != 0) {
5779 			icp->al->free(icp->al, buf);
5780 			return rv;
5781 		}
5782 
5783 		/* Read all the data from the buffer */
5784 		for (i = 0; i < p->count; i++) {
5785 			if ((rv = read_NamedColorVal(p->data+i, bp, end, icp->header->pcs, p->nDeviceCoords)) != 0) {
5786 				icp->al->free(icp->al, buf);
5787 				return rv;
5788 			}
5789 			bp += strlen(p->data[i].root) + 1;
5790 			bp += p->nDeviceCoords * 1;
5791 		}
5792 	} else {  /* icmNC2 */
5793 		/* Number of device coords per color */
5794 		p->nDeviceCoords = read_UInt32Number(bp+16);
5795 
5796 		/* Prefix for each color name */
5797 		memcpy((void *)p->prefix, (void *)(bp + 20), 32);
5798 		if (check_null_string(p->prefix,32) != 0) {
5799 			sprintf(icp->err,"icmNamedColor_read: Color prefix is not null terminated");
5800 			icp->al->free(icp->al, buf);
5801 			return icp->errc = 1;
5802 		}
5803 
5804 		/* Suffix for each color name */
5805 		memcpy((void *)p->suffix, (void *)(bp + 52), 32);
5806 		if (check_null_string(p->suffix,32) != 0) {
5807 			sprintf(icp->err,"icmNamedColor_read: Color suffix is not null terminated");
5808 			icp->al->free(icp->al, buf);
5809 			return icp->errc = 1;
5810 		}
5811 
5812 		if ((rv = p->allocate((icmBase *)p)) != 0) {
5813 			icp->al->free(icp->al, buf);
5814 			return rv;
5815 		}
5816 
5817 		/* Read all the data from the buffer */
5818 		bp = bp + 84;
5819 		for (i = 0; i < p->count; i++, bp += (32 + 6 + p->nDeviceCoords * 2)) {
5820 			if ((rv = read_NamedColorVal2(p->data+i, bp, end, icp->header->pcs, p->nDeviceCoords)) != 0) {
5821 				icp->al->free(icp->al, buf);
5822 				return rv;
5823 			}
5824 		}
5825 	}
5826 	icp->al->free(icp->al, buf);
5827 	return rv;
5828 }
5829 
5830 /* Write the contents of the object. Return 0 on sucess, error code on failure */
5831 static int icmNamedColor_write(
5832 	icmBase *pp,
5833 	unsigned long of			/* File offset to write from */
5834 ) {
5835 	icmNamedColor *p = (icmNamedColor *)pp;
5836 	icc *icp = p->icp;
5837 	unsigned long i;
5838 	unsigned int len;
5839 	char *bp, *buf;		/* Buffer to write from */
5840 	int rv = 0;
5841 
5842 	/* Allocate a file write buffer */
5843 	len = p->get_size((icmBase *)p);
5844 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
5845 		sprintf(icp->err,"icmNamedColor_write malloc() failed");
5846 		return icp->errc = 2;
5847 	}
5848 	bp = buf;
5849 
5850 	/* Write type descriptor to the buffer */
5851 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
5852 		sprintf(icp->err,"icmNamedColor_write: write_SInt32Number() failed");
5853 		icp->al->free(icp->al, buf);
5854 		return icp->errc = rv;
5855 	}
5856 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
5857 
5858 	/* Write vendor specific flag */
5859 	if ((rv = write_UInt32Number(p->vendorFlag, bp+8)) != 0) {
5860 		sprintf(icp->err,"icmNamedColor_write: write_UInt32Number() failed");
5861 		icp->al->free(icp->al, buf);
5862 		return icp->errc = rv;
5863 	}
5864 
5865 	/* Write count of named colors */
5866 	if ((rv = write_UInt32Number(p->count, bp+12)) != 0) {
5867 		sprintf(icp->err,"icmNamedColor_write: write_UInt32Number() failed");
5868 		icp->al->free(icp->al, buf);
5869 		return icp->errc = rv;
5870 	}
5871 
5872 	if (p->ttype == icSigNamedColorType) {
5873 		bp = bp + 16;
5874 
5875 		/* Prefix for each color name */
5876 		if ((rv = check_null_string(p->prefix,32)) != 0) {
5877 			sprintf(icp->err,"icmNamedColor_write: Color prefix is not null terminated");
5878 			icp->al->free(icp->al, buf);
5879 			return icp->errc = 1;
5880 		}
5881 		strcpy((void *)bp, (void *)p->prefix);
5882 		bp += strlen(p->prefix) + 1;
5883 
5884 		/* Suffix for each color name */
5885 		if (check_null_string(p->suffix,32)) {
5886 			sprintf(icp->err,"icmNamedColor_write: Color sufix is not null terminated");
5887 			icp->al->free(icp->al, buf);
5888 			return icp->errc = 1;
5889 		}
5890 		strcpy((void *)bp, (void *)p->suffix);
5891 		bp += strlen(p->suffix) + 1;
5892 
5893 		/* Write all the data to the buffer */
5894 
5895 		for (i = 0; i < p->count; i++) {
5896 			if ((rv = write_NamedColorVal(p->data+i, bp, icp->header->pcs, p->nDeviceCoords)) != 0) {
5897 				icp->al->free(icp->al, buf);
5898 				return rv;
5899 			}
5900 			bp += strlen(p->data[i].root) + 1;
5901 			bp += p->nDeviceCoords * 1;
5902 		}
5903 	} else {	/* icmNC2 */
5904 		/* Number of device coords per color */
5905 		if ((rv = write_UInt32Number(p->nDeviceCoords, bp+16)) != 0) {
5906 			sprintf(icp->err,"icmNamedColor_write: write_UInt32Number() failed");
5907 			icp->al->free(icp->al, buf);
5908 			return icp->errc = rv;
5909 		}
5910 
5911 		/* Prefix for each color name */
5912 		if ((rv = check_null_string(p->prefix,32)) != 0) {
5913 			sprintf(icp->err,"icmNamedColor_write: Color prefix is not null terminated");
5914 			icp->al->free(icp->al, buf);
5915 			return icp->errc = 1;
5916 		}
5917 		memcpy((void *)(bp + 20), (void *)p->prefix, 32);
5918 
5919 		/* Suffix for each color name */
5920 		if (check_null_string(p->suffix,32)) {
5921 			sprintf(icp->err,"icmNamedColor_write: Color sufix is not null terminated");
5922 			icp->al->free(icp->al, buf);
5923 			return icp->errc = 1;
5924 		}
5925 		memcpy((void *)(bp + 52), (void *)p->suffix, 32);
5926 
5927 		/* Write all the data to the buffer */
5928 		bp = bp + 84;
5929 		for (i = 0; i < p->count; i++, bp += (32 + 6 + p->nDeviceCoords * 2)) {
5930 			if ((rv = write_NamedColorVal2(p->data+i, bp, icp->header->pcs, p->nDeviceCoords)) != 0) {
5931 				icp->al->free(icp->al, buf);
5932 				return rv;
5933 			}
5934 		}
5935 	}
5936 
5937 	/* Write to the file */
5938 	if (   icp->fp->seek(icp->fp, of) != 0
5939 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
5940 		sprintf(icp->err,"icmNamedColor_write fseek() or fwrite() failed");
5941 		icp->al->free(icp->al, buf);
5942 		return icp->errc = 2;
5943 	}
5944 	icp->al->free(icp->al, buf);
5945 	return 0;
5946 }
5947 
5948 /* Dump a text description of the object */
5949 static void icmNamedColor_dump(
5950 	icmBase *pp,
5951 	FILE *op,		/* Output to dump to */
5952 	int   verb		/* Verbosity level */
5953 ) {
5954 	icmNamedColor *p = (icmNamedColor *)pp;
5955 	icc *icp = p->icp;
5956 	if (verb <= 0)
5957 		return;
5958 
5959 	if (p->ttype == icSigNamedColorType)
5960 		fprintf(op,"NamedColor:\n");
5961 	else
5962 		fprintf(op,"NamedColor2:\n");
5963 	fprintf(op,"  Vendor Flag = 0x%x\n",p->vendorFlag);
5964 	fprintf(op,"  No. colors  = %u\n",p->count);
5965 	fprintf(op,"  No. dev. coords = %u\n",p->nDeviceCoords);
5966 	fprintf(op,"  Name prefix = '%s'\n",p->prefix);
5967 	fprintf(op,"  Name suffix = '%s'\n",p->suffix);
5968 	if (verb >= 2) {
5969 		unsigned long i, n;
5970 		icmNamedColorVal *vp;
5971 		for (i = 0; i < p->count; i++) {
5972 			vp = p->data + i;
5973 			fprintf(op,"    Color %u:\n",i);
5974 			fprintf(op,"      Name root = '%s'\n",vp->root);
5975 
5976 			if (p->ttype == icSigNamedColor2Type) {
5977 				switch(icp->header->pcs) {
5978 					case icSigXYZData:
5979 							fprintf(op,"      XYZ = %f, %f, %f'\n",
5980 							        vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]);
5981 						break;
5982 			    	case icSigLabData:
5983 							fprintf(op,"      Lab = %f, %f, %f'\n",
5984 							        vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]);
5985 						break;
5986 					default:
5987 							fprintf(op,"      Unexpected PCS\n");
5988 						break;
5989 				}
5990 			}
5991 			if (p->nDeviceCoords > 0) {
5992 				fprintf(op,"      Device Coords = ");
5993 				for (n = 0; n < p->nDeviceCoords; n++) {
5994 					if (n > 0)
5995 						printf(", ");
5996 					printf("%f",vp->deviceCoords[n]);
5997 				}
5998 				printf("\n");
5999 			}
6000 		}
6001 	}
6002 }
6003 
6004 /* Allocate variable sized data elements */
6005 static int icmNamedColor_allocate(
6006 	icmBase *pp
6007 ) {
6008 	icmNamedColor *p = (icmNamedColor *)pp;
6009 	icc *icp = p->icp;
6010 
6011 	if (p->count != p->_count) {
6012 		unsigned int i;
6013 		if (p->data != NULL)
6014 			icp->al->free(icp->al, p->data);
6015 		if ((p->data = (icmNamedColorVal *) icp->al->calloc(icp->al,p->count, sizeof(icmNamedColorVal))) == NULL) {
6016 			sprintf(icp->err,"icmNamedColor_alloc: malloc() of icmNamedColor data failed");
6017 			return icp->errc = 2;
6018 		}
6019 		for (i = 0; i < p->count; i++) {
6020 			p->data[i].icp = icp;	/* Do init */
6021 		}
6022 		p->_count = p->count;
6023 	}
6024 	return 0;
6025 }
6026 
6027 /* Free all storage in the object */
6028 static void icmNamedColor_delete(
6029 	icmBase *pp
6030 ) {
6031 	icmNamedColor *p = (icmNamedColor *)pp;
6032 	icc *icp = p->icp;
6033 
6034 	if (p->data != NULL)
6035 		icp->al->free(icp->al, p->data);
6036 	icp->al->free(icp->al, p);
6037 }
6038 
6039 /* Create an empty object. Return null on error */
6040 static icmBase *new_icmNamedColor(
6041 	icc *icp
6042 ) {
6043 	icmNamedColor *p;
6044 	if ((p = (icmNamedColor *) icp->al->calloc(icp->al,1,sizeof(icmNamedColor))) == NULL)
6045 		return NULL;
6046 	p->ttype    = icSigNamedColor2Type;
6047 	p->refcount = 1;
6048 	p->get_size = icmNamedColor_get_size;
6049 	p->read     = icmNamedColor_read;
6050 	p->write    = icmNamedColor_write;
6051 	p->dump     = icmNamedColor_dump;
6052 	p->allocate = icmNamedColor_allocate;
6053 	p->del      = icmNamedColor_delete;
6054 	p->icp      = icp;
6055 
6056 	/* Default the the number of device coords appropriately for NamedColorType */
6057 	p->nDeviceCoords = number_ColorSpaceSignature(icp->header->colorSpace);
6058 
6059 	return (icmBase *)p;
6060 }
6061 
6062 /* ---------------------------------------------------------- */
6063 /* textDescription */
6064 
6065 /* Return the number of bytes needed to write this tag */
6066 static unsigned int icmTextDescription_get_size(
6067 	icmBase *pp
6068 ) {
6069 	icmTextDescription *p = (icmTextDescription *)pp;
6070 	unsigned int len = 0;
6071 	len += 8;			/* 8 bytes for tag and padding */
6072 	len += 4 + p->size;	/* Ascii string length + ascii string */
6073 	len += 8 + 2 * p->ucSize;	/* Unicode language code + length + string */
6074 	len += 3 + 67;		/* ScriptCode code, length string */
6075 	return len;
6076 }
6077 
6078 /* read the object, return 0 on success, error code on fail */
6079 static int icmTextDescription_read(
6080 	icmBase *pp,
6081 	unsigned long len,		/* tag length */
6082 	unsigned long of		/* start offset within file */
6083 ) {
6084 	icmTextDescription *p = (icmTextDescription *)pp;
6085 	icc *icp = p->icp;
6086 	int rv;
6087 	char *bp, *buf, *end;
6088 
6089 	if (len < 90) {
6090 		sprintf(icp->err,"icmTextDescription_read: Tag too small to be legal");
6091 		return icp->errc = 1;
6092 	}
6093 
6094 	/* Allocate a file read buffer */
6095 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
6096 		sprintf(icp->err,"icmTextDescription_read: malloc() failed");
6097 		return icp->errc = 2;
6098 	}
6099 	bp = buf;
6100 	end = buf + len;
6101 
6102 	/* Read portion of file into buffer */
6103 	if (   icp->fp->seek(icp->fp, of) != 0
6104 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
6105 		sprintf(icp->err,"icmTextDescription_read: fseek() or fread() failed");
6106 		icp->al->free(icp->al, buf);
6107 		return icp->errc = 1;
6108 	}
6109 
6110 	/* Read from the buffer into the structure */
6111 	if ((rv = p->core_read(p, &bp, end)) != 0) {
6112 		icp->al->free(icp->al, buf);
6113 		return rv;
6114 	}
6115 
6116 	icp->al->free(icp->al, buf);
6117 	return 0;
6118 }
6119 
6120 /* core read the object, return 0 on success, error code on fail */
6121 static int icmTextDescription_core_read(
6122 	icmTextDescription *p,
6123 	char **bpp,				/* Pointer to buffer pointer, returns next after read */
6124 	char *end				/* Pointer to past end of read buffer */
6125 ) {
6126 	icc *icp = p->icp;
6127 	int rv = 0;
6128 	char *bp = *bpp;
6129 
6130 	if ((bp + 8) > end) {
6131 		sprintf(icp->err,"icmTextDescription_read: Data too short to type descriptor");
6132 		*bpp = bp;
6133 		return icp->errc = 1;
6134 	}
6135 
6136 	p->size = read_UInt32Number(bp);
6137 	/* Read type descriptor from the buffer */
6138 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
6139 		*bpp = bp;
6140 		sprintf(icp->err,"icmTextDescription_read: Wrong tag type for icmTextDescription");
6141 		return icp->errc = 1;
6142 	}
6143 	bp = bp + 8;
6144 
6145 	/* Read the Ascii string */
6146 	if ((bp + 4) > end) {
6147 		*bpp = bp;
6148 		sprintf(icp->err,"icmTextDescription_read: Data too short to read Ascii header");
6149 		return icp->errc = 1;
6150 	}
6151 	p->size = read_UInt32Number(bp);
6152 	bp += 4;
6153 	if (p->size > 0) {
6154 		if ((bp + p->size) > end) {
6155 			*bpp = bp;
6156 			sprintf(icp->err,"icmTextDescription_read: Data to short to read Ascii string");
6157 			return icp->errc = 1;
6158 		}
6159 		if (check_null_string(bp,p->size)) {
6160 			*bpp = bp;
6161 			sprintf(icp->err,"icmTextDescription_read: ascii string is not terminated");
6162 			return icp->errc = 1;
6163 		}
6164 		if ((rv = p->allocate((icmBase *)p)) != 0) {
6165 			return rv;
6166 		}
6167 		strcpy((void *)p->desc, (void *)bp);
6168 		bp += p->size;
6169 	}
6170 
6171 	/* Read the Unicode string */
6172 	if ((bp + 8) > end) {
6173 		*bpp = bp;
6174 		sprintf(icp->err,"icmTextDescription_read: Data too short to read Unicode string");
6175 		return icp->errc = 1;
6176 	}
6177 	p->ucLangCode = read_UInt32Number(bp);
6178 	bp += 4;
6179 	p->ucSize = read_UInt32Number(bp);
6180 	bp += 4;
6181 	if (p->ucSize > 0) {
6182 		ORD16 *up;
6183 		if ((bp + 2 * p->ucSize) > end) {
6184 			*bpp = bp;
6185 			sprintf(icp->err,"icmTextDescription_read: Data too short to read Unicode string");
6186 			return icp->errc = 1;
6187 		}
6188 		if (check_null_string16(bp,p->ucSize)) {
6189 			*bpp = bp;
6190 			sprintf(icp->err,"icmTextDescription_read: Unicode string is not terminated");
6191 			return icp->errc = 1;
6192 		}
6193 		if ((rv = p->allocate((icmBase *)p)) != 0) {
6194 			return rv;
6195 		}
6196 		for(up = p->ucDesc; bp[0] != 0 || bp[1] != 0; up++, bp += 2)
6197 			*up = read_UInt16Number(bp);
6198 		*up = 0;	/* Unicode null */
6199 		bp += 2;
6200 	}
6201 
6202 	/* Read the ScriptCode string */
6203 	if ((bp + 3) > end) {
6204 		*bpp = bp;
6205 		sprintf(icp->err,"icmTextDescription_read: Data too short to read ScriptCode header");
6206 		return icp->errc = 1;
6207 	}
6208 	p->scCode = read_UInt16Number(bp);
6209 	bp += 2;
6210 	p->scSize = read_UInt8Number(bp);
6211 	bp += 1;
6212 	if (p->scSize > 0) {
6213 		if (p->scSize > 67) {
6214 			*bpp = bp;
6215 			sprintf(icp->err,"icmTextDescription_read: ScriptCode string too long");
6216 			return icp->errc = 1;
6217 		}
6218 		if ((bp + p->scSize) > end) {
6219 			*bpp = bp;
6220 			sprintf(icp->err,"icmTextDescription_read: Data too short to read ScriptCode string");
6221 			return icp->errc = 1;
6222 		}
6223 		if (check_null_string(bp,p->scSize)) {
6224 			*bpp = bp;
6225 			sprintf(icp->err,"icmTextDescription_read: ScriptCode string is not terminated");
6226 			return icp->errc = 1;
6227 		}
6228 		memcpy((void *)p->scDesc, (void *)bp, p->scSize);
6229 	} else {
6230 		memset((void *)p->scDesc, 0, 67);
6231 	}
6232 	bp += 67;
6233 
6234 	*bpp = bp;
6235 	return 0;
6236 }
6237 
6238 /* Write the contents of the object. Return 0 on sucess, error code on failure */
6239 static int icmTextDescription_write(
6240 	icmBase *pp,
6241 	unsigned long of			/* File offset to write from */
6242 ) {
6243 	icmTextDescription *p = (icmTextDescription *)pp;
6244 	icc *icp = p->icp;
6245 	unsigned int len;
6246 	char *bp, *buf;		/* Buffer to write from */
6247 	int rv = 0;
6248 
6249 	/* Allocate a file write buffer */
6250 	len = p->get_size((icmBase *)p);
6251 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
6252 		sprintf(icp->err,"icmTextDescription_write malloc() failed");
6253 		return icp->errc = 2;
6254 	}
6255 	bp = buf;
6256 
6257 	/* Write to the buffer from the structure */
6258 	if ((rv = p->core_write(p, &bp)) != 0) {
6259 		icp->al->free(icp->al, buf);
6260 		return rv;
6261 	}
6262 
6263 	/* Write to the file */
6264 	if (   icp->fp->seek(icp->fp, of) != 0
6265 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
6266 		sprintf(icp->err,"icmTextDescription_write fseek() or fwrite() failed");
6267 		icp->al->free(icp->al, buf);
6268 		return icp->errc = 2;
6269 	}
6270 	icp->al->free(icp->al, buf);
6271 	return 0;
6272 }
6273 
6274 /* Core write the contents of the object. Return 0 on sucess, error code on failure */
6275 static int icmTextDescription_core_write(
6276 	icmTextDescription *p,
6277 	char **bpp				/* Pointer to buffer pointer, returns next after read */
6278 ) {
6279 	icc *icp = p->icp;
6280 	char *bp = *bpp;
6281 	int rv = 0;
6282 
6283 	/* Write type descriptor to the buffer */
6284 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
6285 		sprintf(icp->err,"icmTextDescription_write: write_SInt32Number() failed");
6286 		*bpp = bp;
6287 		return icp->errc = rv;
6288 	}
6289 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
6290 	bp = bp + 8;
6291 
6292 	/* Write the Ascii string */
6293 	if ((rv = write_UInt32Number(p->size,bp)) != 0) {
6294 		sprintf(icp->err,"icmTextDescription_write: write_UInt32Number() failed");
6295 		*bpp = bp;
6296 		return icp->errc = rv;
6297 	}
6298 	bp += 4;
6299 	if (p->size > 0) {
6300 		if (check_null_string(p->desc,p->size)) {
6301 			*bpp = bp;
6302 			sprintf(icp->err,"icmTextDescription_write: ascii string is not terminated");
6303 			return icp->errc = 1;
6304 		}
6305 		strcpy((void *)bp, (void *)p->desc);
6306 		bp += p->size;
6307 	}
6308 
6309 	/* Write the Unicode string */
6310 	if ((rv = write_UInt32Number(p->ucLangCode, bp)) != 0) {
6311 		sprintf(icp->err,"icmTextDescription_write: write_UInt32Number() failed");
6312 		*bpp = bp;
6313 		return icp->errc = rv;
6314 	}
6315 	bp += 4;
6316 	if ((rv = write_UInt32Number(p->ucSize, bp)) != 0) {
6317 		sprintf(icp->err,"icmTextDescription_write: write_UInt32Number() failed");
6318 		*bpp = bp;
6319 		return icp->errc = rv;
6320 	}
6321 	bp += 4;
6322 	if (p->ucSize > 0) {
6323 		ORD16 *up;
6324 		if (check_null_string16((char *)p->ucDesc,p->ucSize)) {
6325 			*bpp = bp;
6326 			sprintf(icp->err,"icmTextDescription_write: Unicode string is not terminated");
6327 			return icp->errc = 1;
6328 		}
6329 		for(up = p->ucDesc; *up != 0; up++, bp += 2) {
6330 			if ((rv = write_UInt16Number(((unsigned int)*up), bp)) != 0) {
6331 				sprintf(icp->err,"icmTextDescription_write: write_UInt16Number() failed");
6332 				*bpp = bp;
6333 				return icp->errc = rv;
6334 			}
6335 		}
6336 		bp[0] = 0;	/* null */
6337 		bp[1] = 0;
6338 		bp += 2;
6339 	}
6340 
6341 	/* Write the ScriptCode string */
6342 	if ((rv = write_UInt16Number(p->scCode, bp)) != 0) {
6343 		sprintf(icp->err,"icmTextDescription_write: write_UInt16Number() failed");
6344 		*bpp = bp;
6345 		return icp->errc = rv;
6346 	}
6347 	bp += 2;
6348 	if ((rv = write_UInt8Number(p->scSize, bp)) != 0) {
6349 		sprintf(icp->err,"icmTextDescription_write: write_UInt8Number() failed");
6350 		*bpp = bp;
6351 		return icp->errc = rv;
6352 	}
6353 	bp += 1;
6354 	if (p->scSize > 0) {
6355 		if (p->scSize > 67) {
6356 			*bpp = bp;
6357 			sprintf(icp->err,"icmTextDescription_write: ScriptCode string too long");
6358 			return icp->errc = 1;
6359 		}
6360 		if (check_null_string((char*)p->scDesc,p->scSize)) {	/* RSC added cast */
6361 			*bpp = bp;
6362 			sprintf(icp->err,"icmTextDescription_write: ScriptCode string is not terminated");
6363 			return icp->errc = 1;
6364 		}
6365 		memcpy((void *)bp, (void *)p->scDesc, 67);
6366 	} else {
6367 		memset((void *)bp, 0, 67);
6368 	}
6369 	bp += 67;
6370 
6371 	*bpp = bp;
6372 	return 0;
6373 }
6374 
6375 /* Dump a text description of the object */
6376 static void icmTextDescription_dump(
6377 	icmBase *pp,
6378 	FILE *op,		/* Output to dump to */
6379 	int   verb		/* Verbosity level */
6380 ) {
6381 	icmTextDescription *p = (icmTextDescription *)pp;
6382 	unsigned long i, r, c;
6383 
6384 	if (verb <= 0)
6385 		return;
6386 
6387 	fprintf(op,"TextDescription:\n");
6388 
6389 	if (p->size > 0) {
6390 		unsigned long size = p->size > 0 ? p->size-1 : 0;
6391 		fprintf(op,"  ASCII data, length %u chars:\n",p->size);
6392 
6393 		i = 0;
6394 		for (r = 1;; r++) {		/* count rows */
6395 			if (i >= size) {
6396 				fprintf(op,"\n");
6397 				break;
6398 			}
6399 			if (r > 1 && verb < 2) {
6400 				fprintf(op,"...\n");
6401 				break;			/* Print 1 row if not verbose */
6402 			}
6403 			c = 1;
6404 			fprintf(op,"    0x%04x: ",i);
6405 			c += 10;
6406 			while (i < size && c < 75) {
6407 				if (isprint(p->desc[i])) {
6408 					fprintf(op,"%c",p->desc[i]);
6409 					c++;
6410 				} else {
6411 					fprintf(op,"\\%03o",p->desc[i]);
6412 					c += 4;
6413 				}
6414 				i++;
6415 			}
6416 			if (i < size)
6417 				fprintf(op,"\n");
6418 		}
6419 	} else {
6420 		fprintf(op,"  No ASCII data\n");
6421 	}
6422 
6423 	/* Can't dump Unicode or ScriptCode as text with portable code */
6424 	if (p->ucSize > 0) {
6425 		unsigned long size = p->ucSize;
6426 		fprintf(op,"  Unicode Data, Language code 0x%x, length %u chars\n",
6427 		        p->ucLangCode, p->ucSize);
6428 		i = 0;
6429 		for (r = 1;; r++) {		/* count rows */
6430 			if (i >= size) {
6431 				fprintf(op,"\n");
6432 				break;
6433 			}
6434 			if (r > 1 && verb < 2) {
6435 				fprintf(op,"...\n");
6436 				break;			/* Print 1 row if not verbose */
6437 			}
6438 			c = 1;
6439 			fprintf(op,"    0x%04x: ",i);
6440 			c += 10;
6441 			while (i < size && c < 75) {
6442 				fprintf(op,"%04x ",p->ucDesc[i]);
6443 				c += 5;
6444 				i++;
6445 			}
6446 			if (i < size)
6447 				fprintf(op,"\n");
6448 		}
6449 	} else {
6450 		fprintf(op,"  No Unicode data\n");
6451 	}
6452 	if (p->scSize > 0) {
6453 		unsigned long size = p->scSize;
6454 		fprintf(op,"  ScriptCode Data, Code 0x%x, length %u chars\n",
6455 		        p->scCode, p->scSize);
6456 		i = 0;
6457 		for (r = 1;; r++) {		/* count rows */
6458 			if (i >= size) {
6459 				fprintf(op,"\n");
6460 				break;
6461 			}
6462 			if (r > 1 && verb < 2) {
6463 				fprintf(op,"...\n");
6464 				break;			/* Print 1 row if not verbose */
6465 			}
6466 			c = 1;
6467 			fprintf(op,"    0x%04x: ",i);
6468 			c += 10;
6469 			while (i < size && c < 75) {
6470 				fprintf(op,"%02x ",p->scDesc[i]);
6471 				c += 3;
6472 				i++;
6473 			}
6474 			if (i < size)
6475 				fprintf(op,"\n");
6476 		}
6477 	} else {
6478 		fprintf(op,"  No ScriptCode data\n");
6479 	}
6480 }
6481 
6482 /* Allocate variable sized data elements */
6483 static int icmTextDescription_allocate(
6484 	icmBase *pp
6485 ) {
6486 	icmTextDescription *p = (icmTextDescription *)pp;
6487 	icc *icp = p->icp;
6488 
6489 	if (p->size != p->_size) {
6490 		if (p->desc != NULL)
6491 			icp->al->free(icp->al, p->desc);
6492 		if ((p->desc = (char *) icp->al->malloc(icp->al, p->size * sizeof(char))) == NULL) {
6493 			sprintf(icp->err,"icmTextDescription_alloc: malloc() of Ascii description failed");
6494 			return icp->errc = 2;
6495 		}
6496 		p->_size = p->size;
6497 	}
6498 	if (p->ucSize != p->uc_size) {
6499 		if (p->ucDesc != NULL)
6500 			icp->al->free(icp->al, p->ucDesc);
6501 		if ((p->ucDesc = (ORD16 *) icp->al->malloc(icp->al, p->ucSize * sizeof(ORD16))) == NULL) {
6502 			sprintf(icp->err,"icmTextDescription_alloc: malloc() of Unicode description failed");
6503 			return icp->errc = 2;
6504 		}
6505 		p->uc_size = p->ucSize;
6506 	}
6507 	return 0;
6508 }
6509 
6510 /* Free all variable sized elements */
6511 void icmTextDescription_unallocate(
6512 	icmTextDescription *p
6513 ) {
6514 	icc *icp = p->icp;
6515 
6516 	if (p->desc != NULL)
6517 		icp->al->free(icp->al, p->desc);
6518 	if (p->ucDesc != NULL)
6519 		icp->al->free(icp->al, p->ucDesc);
6520 }
6521 
6522 /* Free all storage in the object */
6523 static void icmTextDescription_delete(
6524 	icmBase *pp
6525 ) {
6526 	icmTextDescription *p = (icmTextDescription *)pp;
6527 	icc *icp = p->icp;
6528 
6529 	icmTextDescription_unallocate(p);
6530 	icp->al->free(icp->al, p);
6531 }
6532 
6533 /* Initialze a named object */
6534 static void icmTextDescription_init(
6535 	icmTextDescription *p,
6536 	icc *icp
6537 ) {
6538 	memset((void *)p, 0, sizeof(icmTextDescription));	/* Imitate calloc */
6539 
6540 	p->ttype    = icSigTextDescriptionType;
6541 	p->refcount = 1;
6542 	p->get_size = icmTextDescription_get_size;
6543 	p->read     = icmTextDescription_read;
6544 	p->write    = icmTextDescription_write;
6545 	p->dump     = icmTextDescription_dump;
6546 	p->allocate = icmTextDescription_allocate;
6547 	p->del      = icmTextDescription_delete;
6548 	p->icp      = icp;
6549 
6550 	p->core_read  = icmTextDescription_core_read;
6551 	p->core_write = icmTextDescription_core_write;
6552 }
6553 
6554 /* Create an empty object. Return null on error */
6555 static icmBase *new_icmTextDescription(
6556 	icc *icp
6557 ) {
6558 	icmTextDescription *p;
6559 	if ((p = (icmTextDescription *) icp->al->calloc(icp->al,1,sizeof(icmTextDescription))) == NULL)
6560 		return NULL;
6561 
6562 	icmTextDescription_init(p,icp);
6563 	return (icmBase *)p;
6564 }
6565 
6566 /* ---------------------------------------------------------- */
6567 
6568 /* Support for icmDescStruct */
6569 
6570 /* Return the number of bytes needed to write this tag */
6571 static unsigned int icmDescStruct_get_size(
6572 	icmDescStruct *p
6573 ) {
6574 	unsigned int len = 0;
6575 	len += 20;				/* 20 bytes for header info */
6576 	len += p->device.get_size((icmBase *)&p->device);
6577 	len += p->model.get_size((icmBase *)&p->model);
6578 	return len;
6579 }
6580 
6581 /* read the object, return 0 on success, error code on fail */
6582 static int icmDescStruct_read(
6583 	icmDescStruct *p,
6584 	char **bpp,				/* Pointer to buffer pointer, returns next after read */
6585 	char *end				/* Pointer to past end of read buffer */
6586 ) {
6587 	icc *icp = p->icp;
6588 	char *bp = *bpp;
6589 	int rv = 0;
6590 
6591 	if ((bp + 20) > end) {
6592 		sprintf(icp->err,"icmDescStruct_read: Data too short read header");
6593 		*bpp = bp;
6594 		return icp->errc = 1;
6595 	}
6596 
6597     p->deviceMfg = read_SInt32Number(bp + 0);
6598     p->deviceModel = read_UInt32Number(bp + 4);
6599     read_UInt64Number(&p->attributes, bp + 8);
6600 	p->technology = read_UInt32Number(bp + 16);
6601 	*bpp = bp += 20;
6602 
6603 	/* Read the device text description */
6604 	if ((rv = p->device.core_read(&p->device, bpp, end)) != 0) {
6605 		return rv;
6606 	}
6607 
6608 	/* Read the model text description */
6609 	if ((rv = p->model.core_read(&p->model, bpp, end)) != 0) {
6610 		return rv;
6611 	}
6612 
6613 	return 0;
6614 }
6615 
6616 /* Write the contents of the object. Return 0 on sucess, error code on failure */
6617 static int icmDescStruct_write(
6618 	icmDescStruct *p,
6619 	char **bpp				/* Pointer to buffer pointer, returns next after read */
6620 ) {
6621 	icc *icp = p->icp;
6622 	char *bp = *bpp;
6623 	int rv = 0;
6624 
6625     if ((rv = write_SInt32Number(p->deviceMfg, bp + 0)) != 0) {
6626 		sprintf(icp->err,"icmDescStruct_write: write_SInt32Number() failed");
6627 		*bpp = bp;
6628 		return icp->errc = rv;
6629 	}
6630     if ((rv = write_UInt32Number(p->deviceModel, bp + 4)) != 0) {
6631 		sprintf(icp->err,"icmDescStruct_write: write_UInt32Number() failed");
6632 		*bpp = bp;
6633 		return icp->errc = rv;
6634 	}
6635     if ((rv = write_UInt64Number(&p->attributes, bp + 8)) != 0) {
6636 		sprintf(icp->err,"icmDescStruct_write: write_UInt64Number() failed");
6637 		*bpp = bp;
6638 		return icp->errc = rv;
6639 	}
6640 	if ((rv = write_UInt32Number(p->technology, bp + 16)) != 0) {
6641 		sprintf(icp->err,"icmDescStruct_write: write_UInt32Number() failed");
6642 		*bpp = bp;
6643 		return icp->errc = rv;
6644 	}
6645 	*bpp = bp += 20;
6646 
6647 	/* Write the device text description */
6648 	if ((rv = p->device.core_write(&p->device, bpp)) != 0) {
6649 		return rv;
6650 	}
6651 
6652 	/* Write the model text description */
6653 	if ((rv = p->model.core_write(&p->model, bpp)) != 0) {
6654 		return rv;
6655 	}
6656 
6657 	return 0;
6658 }
6659 
6660 /* Dump a text description of the object */
6661 static void icmDescStruct_dump(
6662 	icmDescStruct *p,
6663 	FILE *op,		/* Output to dump to */
6664 	int   verb,		/* Verbosity level */
6665 	int   index		/* Description index */
6666 ) {
6667 	if (verb <= 0)
6668 		return;
6669 
6670 	fprintf(op,"DescStruct %u:\n",index);
6671 	if (verb >= 1) {
6672 		fprintf(op,"  Dev. Mnfctr.    = %s\n",tag2str(p->deviceMfg));	/* ~~~ */
6673 		fprintf(op,"  Dev. Model      = %s\n",tag2str(p->deviceModel));	/* ~~~ */
6674 		fprintf(op,"  Dev. Attrbts    = %s\n", string_DeviceAttributes(p->attributes.l));
6675 		fprintf(op,"  Dev. Technology = %s\n", string_TechnologySignature(p->technology));
6676 		p->device.dump((icmBase *)&p->device, op,verb);
6677 		p->model.dump((icmBase *)&p->model, op,verb);
6678 		fprintf(op,"\n");
6679 	}
6680 }
6681 
6682 /* Allocate variable sized data elements (ie. descriptions) */
6683 static int icmDescStruct_allocate(
6684 	icmDescStruct *p
6685 ) {
6686 	int rv;
6687 
6688 	if ((rv = p->device.allocate((icmBase *)&p->device)) != 0) {
6689 		return rv;
6690 	}
6691 	if ((rv = p->model.allocate((icmBase *)&p->model)) != 0) {
6692 		return rv;
6693 	}
6694 	return 0;
6695 }
6696 
6697 /* Free all storage in the object */
6698 static void icmDescStruct_delete(
6699 	icmDescStruct *p
6700 ) {
6701 	icmTextDescription_unallocate(&p->device);
6702 	icmTextDescription_unallocate(&p->model);
6703 }
6704 
6705 /* Init a DescStruct object */
6706 static void icmDescStruct_init(
6707 	icmDescStruct *p,
6708 	icc *icp
6709 ) {
6710 
6711 	p->allocate = icmDescStruct_allocate;
6712 	p->icp = icp;
6713 
6714 	icmTextDescription_init(&p->device, icp);
6715 	icmTextDescription_init(&p->model, icp);
6716 }
6717 
6718 /* - - - - - - - - - - - - - - - */
6719 /* icmProfileSequenceDesc object */
6720 
6721 /* Return the number of bytes needed to write this tag */
6722 static unsigned int icmProfileSequenceDesc_get_size(
6723 	icmBase *pp
6724 ) {
6725 	icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
6726 	unsigned int len = 0;
6727 	unsigned int i;
6728 	len += 12;				/* 8 bytes for tag, padding and count */
6729 	for (i = 0; i < p->count; i++) {	/* All the description structures */
6730 		len += icmDescStruct_get_size(&p->data[i]);
6731 	}
6732 	return len;
6733 }
6734 
6735 /* read the object, return 0 on success, error code on fail */
6736 static int icmProfileSequenceDesc_read(
6737 	icmBase *pp,
6738 	unsigned long len,		/* tag length */
6739 	unsigned long of		/* start offset within file */
6740 ) {
6741 	icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
6742 	icc *icp = p->icp;
6743 	unsigned long i;
6744 	char *bp, *buf, *end;
6745 	int rv = 0;
6746 
6747 	if (len < 12) {
6748 		sprintf(icp->err,"icmProfileSequenceDesc_read: Tag too small to be legal");
6749 		return icp->errc = 1;
6750 	}
6751 
6752 	/* Allocate a file read buffer */
6753 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
6754 		sprintf(icp->err,"icmProfileSequenceDesc_read: malloc() failed");
6755 		return icp->errc = 2;
6756 	}
6757 	bp = buf;
6758 	end = buf + len;
6759 
6760 	/* Read portion of file into buffer */
6761 	if (   icp->fp->seek(icp->fp, of) != 0
6762 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
6763 		sprintf(icp->err,"icmProfileSequenceDesc_read: fseek() or fread() failed");
6764 		icp->al->free(icp->al, buf);
6765 		return icp->errc = 1;
6766 	}
6767 
6768 	/* Read type descriptor from the buffer */
6769 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
6770 		sprintf(icp->err,"icmProfileSequenceDesc_read: Wrong tag type for icmProfileSequenceDesc");
6771 		icp->al->free(icp->al, buf);
6772 		return icp->errc = 1;
6773 	}
6774 	bp += 8;	/* Skip padding */
6775 
6776 	p->count = read_UInt32Number(bp);	/* Number of sequence descriptions */
6777 	bp += 4;
6778 
6779 	/* Read all the sequence descriptions */
6780 	if ((rv = p->allocate((icmBase *)p)) != 0) {
6781 		icp->al->free(icp->al, buf);
6782 		return rv;
6783 	}
6784 	for (i = 0; i < p->count; i++) {
6785 		if ((rv = icmDescStruct_read(&p->data[i], &bp, end)) != 0) {
6786 			icp->al->free(icp->al, buf);
6787 			return rv;
6788 		}
6789 	}
6790 
6791 	icp->al->free(icp->al, buf);
6792 	return 0;
6793 }
6794 
6795 /* Write the contents of the object. Return 0 on sucess, error code on failure */
6796 static int icmProfileSequenceDesc_write(
6797 	icmBase *pp,
6798 	unsigned long of			/* File offset to write from */
6799 ) {
6800 	icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
6801 	icc *icp = p->icp;
6802 	unsigned long i;
6803 	unsigned int len;
6804 	char *bp, *buf;		/* Buffer to write from */
6805 	int rv = 0;
6806 
6807 	/* Allocate a file write buffer */
6808 	len = p->get_size((icmBase *)p);
6809 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
6810 		sprintf(icp->err,"icmProfileSequenceDesc_write malloc() failed");
6811 		return icp->errc = 2;
6812 	}
6813 	bp = buf;
6814 
6815 	/* Write type descriptor to the buffer */
6816 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
6817 		sprintf(icp->err,"icmProfileSequenceDesc_write: write_SInt32Number() failed");
6818 		icp->al->free(icp->al, buf);
6819 		return icp->errc = rv;
6820 	}
6821 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
6822 
6823 	if ((rv = write_UInt32Number(p->count,bp+8)) != 0) {
6824 		sprintf(icp->err,"icmProfileSequenceDesc_write: write_UInt32Number() failed");
6825 		icp->al->free(icp->al, buf);
6826 		return icp->errc = rv;
6827 	}
6828 	bp = bp + 12;
6829 
6830 	/* Write all the description structures */
6831 	for (i = 0; i < p->count; i++) {
6832 		if ((rv = icmDescStruct_write(&p->data[i], &bp)) != 0) {
6833 			icp->al->free(icp->al, buf);
6834 			return rv;
6835 		}
6836 	}
6837 
6838 	/* Write to the file */
6839 	if (   icp->fp->seek(icp->fp, of) != 0
6840 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
6841 		sprintf(icp->err,"icmProfileSequenceDesc_write fseek() or fwrite() failed");
6842 		icp->al->free(icp->al, buf);
6843 		return icp->errc = 2;
6844 	}
6845 	icp->al->free(icp->al, buf);
6846 	return 0;
6847 }
6848 
6849 /* Dump a text description of the object */
6850 static void icmProfileSequenceDesc_dump(
6851 	icmBase *pp,
6852 	FILE *op,		/* Output to dump to */
6853 	int   verb		/* Verbosity level */
6854 ) {
6855 	icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
6856 	if (verb <= 0)
6857 		return;
6858 
6859 	fprintf(op,"ProfileSequenceDesc:\n");
6860 	fprintf(op,"  No. elements = %u\n",p->count);
6861 	if (verb >= 2) {
6862 		unsigned long i;
6863 		for (i = 0; i < p->count; i++)
6864 			icmDescStruct_dump(&p->data[i], op, verb-1, i);
6865 	}
6866 }
6867 
6868 /* Allocate variable sized data elements (ie. count of profile descriptions) */
6869 static int icmProfileSequenceDesc_allocate(
6870 	icmBase *pp
6871 ) {
6872 	icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
6873 	icc *icp = p->icp;
6874 	unsigned int i;
6875 
6876 	if (p->count != p->_count) {
6877 		if (p->data != NULL)
6878 			icp->al->free(icp->al, p->data);
6879 		if ((p->data = (icmDescStruct *) icp->al->malloc(icp->al, p->count * sizeof(icmDescStruct))) == NULL) {
6880 			sprintf(icp->err,"icmProfileSequenceDesc_allocate Allocation of DescStruct array failed");
6881 			return icp->errc = 2;
6882 		}
6883 		/* Now init the DescStructs */
6884 		for (i = 0; i < p->count; i++) {
6885 			icmDescStruct_init(&p->data[i], icp);
6886 		}
6887 		p->_count = p->count;
6888 	}
6889 	return 0;
6890 }
6891 
6892 /* Free all storage in the object */
6893 static void icmProfileSequenceDesc_delete(
6894 	icmBase *pp
6895 ) {
6896 	icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
6897 	icc *icp = p->icp;
6898 	unsigned int i;
6899 
6900 	for (i = 0; i < p->count; i++) {
6901 		icmDescStruct_delete(&p->data[i]);	/* Free allocated contents */
6902 	}
6903 	if (p->data != NULL)
6904 		icp->al->free(icp->al, p->data);
6905 	icp->al->free(icp->al, p);
6906 }
6907 
6908 /* Create an empty object. Return null on error */
6909 static icmBase *new_icmProfileSequenceDesc(
6910 	icc *icp
6911 ) {
6912 	icmProfileSequenceDesc *p;
6913 	if ((p = (icmProfileSequenceDesc *) icp->al->calloc(icp->al,1,sizeof(icmProfileSequenceDesc))) == NULL)
6914 		return NULL;
6915 	p->ttype    = icSigProfileSequenceDescType;
6916 	p->refcount = 1;
6917 	p->get_size = icmProfileSequenceDesc_get_size;
6918 	p->read     = icmProfileSequenceDesc_read;
6919 	p->write    = icmProfileSequenceDesc_write;
6920 	p->dump     = icmProfileSequenceDesc_dump;
6921 	p->allocate = icmProfileSequenceDesc_allocate;
6922 	p->del      = icmProfileSequenceDesc_delete;
6923 	p->icp      = icp;
6924 
6925 	return (icmBase *)p;
6926 }
6927 
6928 /* ---------------------------------------------------------- */
6929 /* Signature */
6930 
6931 /* Return the number of bytes needed to write this tag */
6932 static unsigned int icmSignature_get_size(
6933 	icmBase *pp
6934 ) {
6935 	icmSignature *p = (icmSignature *)pp;
6936 	unsigned int len = 0;
6937 	len += 8;			/* 8 bytes for tag and padding */
6938 	len += 4;			/* 4 for signature */
6939 	return len;
6940 }
6941 
6942 /* read the object, return 0 on success, error code on fail */
6943 static int icmSignature_read(
6944 	icmBase *pp,
6945 	unsigned long len,		/* tag length */
6946 	unsigned long of		/* start offset within file */
6947 ) {
6948 	icmSignature *p = (icmSignature *)pp;
6949 	icc *icp = p->icp;
6950 	char *bp, *buf;
6951 
6952 	if (len < 12) {
6953 		sprintf(icp->err,"icmSignature_read: Tag too small to be legal");
6954 		return icp->errc = 1;
6955 	}
6956 
6957 	/* Allocate a file read buffer */
6958 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
6959 		sprintf(icp->err,"icmSignature_read: malloc() failed");
6960 		return icp->errc = 2;
6961 	}
6962 	bp = buf;
6963 
6964 	/* Read portion of file into buffer */
6965 	if (   icp->fp->seek(icp->fp, of) != 0
6966 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
6967 		sprintf(icp->err,"icmSignature_read: fseek() or fread() failed");
6968 		icp->al->free(icp->al, buf);
6969 		return icp->errc = 1;
6970 	}
6971 
6972 	/* Read type descriptor from the buffer */
6973 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
6974 		sprintf(icp->err,"icmSignaturSignatureng tag type for icmSignature");
6975 		icp->al->free(icp->al, buf);
6976 		return icp->errc = 1;
6977 	}
6978 
6979 	/* Read the encoded measurement geometry */
6980 	p->sig = (icTechnologySignature)read_SInt32Number(bp + 8);
6981 
6982 	icp->al->free(icp->al, buf);
6983 	return 0;
6984 }
6985 
6986 /* Write the contents of the object. Return 0 on sucess, error code on failure */
6987 static int icmSignature_write(
6988 	icmBase *pp,
6989 	unsigned long of			/* File offset to write from */
6990 ) {
6991 	icmSignature *p = (icmSignature *)pp;
6992 	icc *icp = p->icp;
6993 	unsigned int len;
6994 	char *bp, *buf;		/* Buffer to write from */
6995 	int rv = 0;
6996 
6997 	/* Allocate a file write buffer */
6998 	len = p->get_size((icmBase *)p);
6999 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7000 		sprintf(icp->err,"icmSignature_write malloc() failed");
7001 		return icp->errc = 2;
7002 	}
7003 	bp = buf;
7004 
7005 	/* Write type descriptor to the buffer */
7006 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
7007 		sprintf(icp->err,"icmSignature_write: write_SInt32Number() failed");
7008 		icp->al->free(icp->al, buf);
7009 		return icp->errc = rv;
7010 	}
7011 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
7012 
7013 	/* Write the signature */
7014 	if ((rv = write_SInt32Number((int)p->sig, bp + 8)) != 0) {
7015 		sprintf(icp->err,"icmSignaturea_write: write_SInt32Number() failed");
7016 		icp->al->free(icp->al, buf);
7017 		return icp->errc = rv;
7018 	}
7019 
7020 	/* Write to the file */
7021 	if (   icp->fp->seek(icp->fp, of) != 0
7022 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
7023 		sprintf(icp->err,"icmSignature_write fseek() or fwrite() failed");
7024 		icp->al->free(icp->al, buf);
7025 		return icp->errc = 2;
7026 	}
7027 	icp->al->free(icp->al, buf);
7028 	return 0;
7029 }
7030 
7031 /* Dump a text description of the object */
7032 static void icmSignature_dump(
7033 	icmBase *pp,
7034 	FILE *op,		/* Output to dump to */
7035 	int   verb		/* Verbosity level */
7036 ) {
7037 	icmSignature *p = (icmSignature *)pp;
7038 	if (verb <= 0)
7039 		return;
7040 
7041 	fprintf(op,"Signature\n");
7042 	fprintf(op,"  Technology = %s\n", string_TechnologySignature(p->sig));
7043 }
7044 
7045 /* Allocate variable sized data elements */
7046 static int icmSignature_allocate(
7047 	icmBase *pp
7048 ) {
7049 	icmSignature *p = (icmSignature *)pp;
7050 
7051 	/* Nothing to do */
7052 	return 0;
7053 }
7054 
7055 /* Free all storage in the object */
7056 static void icmSignature_delete(
7057 	icmBase *pp
7058 ) {
7059 	icmSignature *p = (icmSignature *)pp;
7060 	icc *icp = p->icp;
7061 
7062 	icp->al->free(icp->al, p);
7063 }
7064 
7065 /* Create an empty object. Return null on error */
7066 static icmBase *new_icmSignature(
7067 	icc *icp
7068 ) {
7069 	icmSignature *p;
7070 	if ((p = (icmSignature *) icp->al->calloc(icp->al,1,sizeof(icmSignature))) == NULL)
7071 		return NULL;
7072 	p->ttype    = icSigSignatureType;
7073 	p->refcount = 1;
7074 	p->get_size = icmSignature_get_size;
7075 	p->read     = icmSignature_read;
7076 	p->write    = icmSignature_write;
7077 	p->dump     = icmSignature_dump;
7078 	p->allocate = icmSignature_allocate;
7079 	p->del      = icmSignature_delete;
7080 	p->icp      = icp;
7081 
7082 	return (icmBase *)p;
7083 }
7084 
7085 /* ---------------------------------------------------------- */
7086 
7087 /* Data conversion support functions */
7088 static int read_ScreeningData(icmScreeningData *p, char *d) {
7089 	p->frequency = read_S15Fixed16Number(d + 0);
7090 	p->angle     = read_S15Fixed16Number(d + 4);
7091 	p->spotShape = (icSpotShape)read_SInt32Number(d + 8);
7092 	return 0;
7093 }
7094 
7095 static int write_ScreeningData(icmScreeningData *p, char *d) {
7096 	int rv;
7097 	if ((rv = write_S15Fixed16Number(p->frequency, d + 0)) != 0)
7098 		return rv;
7099 	if ((rv = write_S15Fixed16Number(p->angle, d + 4)) != 0)
7100 		return rv;
7101 	if ((rv = write_SInt32Number((int)p->spotShape, d + 8)) != 0)
7102 		return rv;
7103 	return 0;
7104 }
7105 
7106 
7107 /* icmScreening object */
7108 
7109 /* Return the number of bytes needed to write this tag */
7110 static unsigned int icmScreening_get_size(
7111 	icmBase *pp
7112 ) {
7113 	icmScreening *p = (icmScreening *)pp;
7114 	unsigned int len = 0;
7115 	len += 16;				/* 16 bytes for tag, padding, flag & channeles */
7116 	len += p->channels * 12;	/* 12 bytes for each channel */
7117 	return len;
7118 }
7119 
7120 /* read the object, return 0 on success, error code on fail */
7121 static int icmScreening_read(
7122 	icmBase *pp,
7123 	unsigned long len,		/* tag length */
7124 	unsigned long of		/* start offset within file */
7125 ) {
7126 	icmScreening *p = (icmScreening *)pp;
7127 	icc *icp = p->icp;
7128 	int rv = 0;
7129 	unsigned long i;
7130 	char *bp, *buf, *end;
7131 
7132 	if (len < 12) {
7133 		sprintf(icp->err,"icmScreening_read: Tag too small to be legal");
7134 		return icp->errc = 1;
7135 	}
7136 
7137 	/* Allocate a file read buffer */
7138 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7139 		sprintf(icp->err,"icmScreening_read: malloc() failed");
7140 		return icp->errc = 2;
7141 	}
7142 	bp = buf;
7143 	end = buf + len;
7144 
7145 	/* Read portion of file into buffer */
7146 	if (   icp->fp->seek(icp->fp, of) != 0
7147 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
7148 		sprintf(icp->err,"icmScreening_read: fseek() or fread() failed");
7149 		icp->al->free(icp->al, buf);
7150 		return icp->errc = 1;
7151 	}
7152 
7153 	/* Read type descriptor from the buffer */
7154 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
7155 		sprintf(icp->err,"icmScreening_read: Wrong tag type for icmScreening");
7156 		icp->al->free(icp->al, buf);
7157 		return icp->errc = 1;
7158 	}
7159 	p->screeningFlag = read_UInt32Number(bp+8);		/* Flags */
7160 	p->channels      = read_UInt32Number(bp+12);	/* Number of channels */
7161 	bp = bp + 16;
7162 
7163 	if ((rv = p->allocate((icmBase *)p)) != 0) {
7164 		icp->al->free(icp->al, buf);
7165 		return rv;
7166 	}
7167 
7168 	/* Read all the data from the buffer */
7169 	for (i = 0; i < p->channels; i++, bp += 12) {
7170 		if ((bp + 12) > end) {
7171 			sprintf(icp->err,"icmScreening_read: Data too short to read Screening Data");
7172 			icp->al->free(icp->al, buf);
7173 			return icp->errc = 1;
7174 		}
7175 		read_ScreeningData(&p->data[i], bp);
7176 	}
7177 	icp->al->free(icp->al, buf);
7178 	return 0;
7179 }
7180 
7181 /* Write the contents of the object. Return 0 on sucess, error code on failure */
7182 static int icmScreening_write(
7183 	icmBase *pp,
7184 	unsigned long of			/* File offset to write from */
7185 ) {
7186 	icmScreening *p = (icmScreening *)pp;
7187 	icc *icp = p->icp;
7188 	unsigned long i;
7189 	unsigned int len;
7190 	char *bp, *buf;		/* Buffer to write from */
7191 	int rv = 0;
7192 
7193 	/* Allocate a file write buffer */
7194 	len = p->get_size((icmBase *)p);
7195 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7196 		sprintf(icp->err,"icmScreening_write malloc() failed");
7197 		return icp->errc = 2;
7198 	}
7199 	bp = buf;
7200 
7201 	/* Write type descriptor to the buffer */
7202 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
7203 		sprintf(icp->err,"icmScreening_write: write_SInt32Number() failed");
7204 		icp->al->free(icp->al, buf);
7205 		return icp->errc = rv;
7206 	}
7207 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
7208 
7209 	if ((rv = write_UInt32Number(p->screeningFlag,bp+8)) != 0) {
7210 			sprintf(icp->err,"icmScreening_write: write_UInt32Number() failed");
7211 			icp->al->free(icp->al, buf);
7212 			return icp->errc = rv;
7213 		}
7214 	if ((rv = write_UInt32Number(p->channels,bp+12)) != 0) {
7215 			sprintf(icp->err,"icmScreening_write: write_UInt32NumberXYZumber() failed");
7216 			icp->al->free(icp->al, buf);
7217 			return icp->errc = rv;
7218 		}
7219 	bp = bp + 16;
7220 
7221 	/* Write all the data to the buffer */
7222 	for (i = 0; i < p->channels; i++, bp += 12) {
7223 		if ((rv = write_ScreeningData(&p->data[i],bp)) != 0) {
7224 			sprintf(icp->err,"icmScreening_write: write_ScreeningData() failed");
7225 			icp->al->free(icp->al, buf);
7226 			return icp->errc = rv;
7227 		}
7228 	}
7229 
7230 	/* Write to the file */
7231 	if (   icp->fp->seek(icp->fp, of) != 0
7232 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
7233 		sprintf(icp->err,"icmScreening_write fseek() or fwrite() failed");
7234 		icp->al->free(icp->al, buf);
7235 		return icp->errc = 2;
7236 	}
7237 	icp->al->free(icp->al, buf);
7238 	return 0;
7239 }
7240 
7241 /* Dump a text description of the object */
7242 static void icmScreening_dump(
7243 	icmBase *pp,
7244 	FILE *op,		/* Output to dump to */
7245 	int   verb		/* Verbosity level */
7246 ) {
7247 	icmScreening *p = (icmScreening *)pp;
7248 	if (verb <= 0)
7249 		return;
7250 
7251 	fprintf(op,"Screening:\n");
7252 	fprintf(op,"  Flags = %s\n", string_ScreenEncodings(p->screeningFlag));
7253 	fprintf(op,"  No. channels = %u\n",p->channels);
7254 	if (verb >= 2) {
7255 		unsigned long i;
7256 		for (i = 0; i < p->channels; i++) {
7257 			fprintf(op,"    %u:\n",i);
7258 			fprintf(op,"      Frequency:  %f\n",p->data[i].frequency);
7259 			fprintf(op,"      Angle:      %f\n",p->data[i].angle);
7260 			fprintf(op,"      Spot shape: %s\n", string_SpotShape(p->data[i].spotShape));
7261 		}
7262 	}
7263 }
7264 
7265 /* Allocate variable sized data elements */
7266 static int icmScreening_allocate(
7267 	icmBase *pp
7268 ) {
7269 	icmScreening *p = (icmScreening *)pp;
7270 	icc *icp = p->icp;
7271 
7272 	if (p->channels != p->_channels) {
7273 		if (p->data != NULL)
7274 			icp->al->free(icp->al, p->data);
7275 		if ((p->data = (icmScreeningData *) icp->al->malloc(icp->al, p->channels * sizeof(icmScreeningData))) == NULL) {
7276 			sprintf(icp->err,"icmScreening_alloc: malloc() of icmScreening data failed");
7277 			return icp->errc = 2;
7278 		}
7279 		p->_channels = p->channels;
7280 	}
7281 	return 0;
7282 }
7283 
7284 /* Free all storage in the object */
7285 static void icmScreening_delete(
7286 	icmBase *pp
7287 ) {
7288 	icmScreening *p = (icmScreening *)pp;
7289 	icc *icp = p->icp;
7290 
7291 	if (p->data != NULL)
7292 		icp->al->free(icp->al, p->data);
7293 	icp->al->free(icp->al, p);
7294 }
7295 
7296 /* Create an empty object. Return null on error */
7297 static icmBase *new_icmScreening(
7298 	icc *icp
7299 ) {
7300 	icmScreening *p;
7301 	if ((p = (icmScreening *) icp->al->calloc(icp->al,1,sizeof(icmScreening))) == NULL)
7302 		return NULL;
7303 	p->ttype    = icSigScreeningType;
7304 	p->refcount = 1;
7305 	p->get_size = icmScreening_get_size;
7306 	p->read     = icmScreening_read;
7307 	p->write    = icmScreening_write;
7308 	p->dump     = icmScreening_dump;
7309 	p->allocate = icmScreening_allocate;
7310 	p->del      = icmScreening_delete;
7311 	p->icp      = icp;
7312 
7313 	return (icmBase *)p;
7314 }
7315 
7316 /* ---------------------------------------------------------- */
7317 /* icmUcrBg object */
7318 
7319 /* Return the number of bytes needed to write this tag */
7320 static unsigned int icmUcrBg_get_size(
7321 	icmBase *pp
7322 ) {
7323 	icmUcrBg *p = (icmUcrBg *)pp;
7324 	unsigned int len = 0;
7325 	len += 8;			/* 8 bytes for tag and padding */
7326 	len += 4 + p->UCRcount * 2;	/* Undercolor Removal */
7327 	len += 4 + p->BGcount * 2;	/* Black Generation */
7328 	len += p->size;				/* Description string */
7329 	return len;
7330 }
7331 
7332 /* read the object, return 0 on success, error code on fail */
7333 static int icmUcrBg_read(
7334 	icmBase *pp,
7335 	unsigned long len,		/* tag length */
7336 	unsigned long of		/* start offset within file */
7337 ) {
7338 	icmUcrBg *p = (icmUcrBg *)pp;
7339 	icc *icp = p->icp;
7340 	unsigned long i;
7341 	int rv = 0;
7342 	char *bp, *buf, *end;
7343 
7344 	if (len < 16) {
7345 		sprintf(icp->err,"icmUcrBg_read: Tag too small to be legal");
7346 		return icp->errc = 1;
7347 	}
7348 
7349 	/* Allocate a file read buffer */
7350 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7351 		sprintf(icp->err,"icmUcrBg_read: malloc() failed");
7352 		return icp->errc = 2;
7353 	}
7354 	bp = buf;
7355 	end = buf + len;
7356 
7357 	/* Read portion of file into buffer */
7358 	if (   icp->fp->seek(icp->fp, of) != 0
7359 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
7360 		sprintf(icp->err,"icmUcrBg_read: fseek() or fread() failed");
7361 		icp->al->free(icp->al, buf);
7362 		return icp->errc = 1;
7363 	}
7364 
7365 	/* Read type descriptor from the buffer */
7366 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
7367 		sprintf(icp->err,"icmUcrBg_read: Wrong tag type for icmUcrBg");
7368 		icp->al->free(icp->al, buf);
7369 		return icp->errc = 1;
7370 	}
7371 	p->UCRcount = read_UInt32Number(bp+8);	/* First curve count */
7372 	bp = bp + 12;
7373 
7374 	if (p->UCRcount > 0) {
7375 		if ((rv = p->allocate((icmBase *)p)) != 0) {
7376 			icp->al->free(icp->al, buf);
7377 			return rv;
7378 		}
7379 		for (i = 0; i < p->UCRcount; i++, bp += 2) {
7380 			if ((bp + 2) > end) {
7381 				sprintf(icp->err,"icmUcrBg_read: Data too short to read UCR Data");
7382 				icp->al->free(icp->al, buf);
7383 				return icp->errc = 1;
7384 			}
7385 			if (p->UCRcount == 1)	/* % */
7386 				p->UCRcurve[i] = (double)read_UInt16Number(bp);
7387 			else					/* 0.0 - 1.0 */
7388 				p->UCRcurve[i] = read_DCS16Number(bp);
7389 		}
7390 	} else {
7391 		p->UCRcurve = NULL;
7392 	}
7393 
7394 	if ((bp + 4) > end) {
7395 		sprintf(icp->err,"icmData_read: Data too short to read Black Gen count");
7396 		icp->al->free(icp->al, buf);
7397 		return icp->errc = 1;
7398 	}
7399 	p->BGcount = read_UInt32Number(bp);	/* First curve count */
7400 	bp += 4;
7401 
7402 	if (p->BGcount > 0) {
7403 		if ((rv = p->allocate((icmBase *)p)) != 0) {
7404 			icp->al->free(icp->al, buf);
7405 			return rv;
7406 		}
7407 		for (i = 0; i < p->BGcount; i++, bp += 2) {
7408 			if ((bp + 2) > end) {
7409 				sprintf(icp->err,"icmUcrBg_read: Data too short to read BG Data");
7410 				icp->al->free(icp->al, buf);
7411 				return icp->errc = 1;
7412 			}
7413 			if (p->BGcount == 1)	/* % */
7414 				p->BGcurve[i] = (double)read_UInt16Number(bp);
7415 			else					/* 0.0 - 1.0 */
7416 				p->BGcurve[i] = read_DCS16Number(bp);
7417 		}
7418 	} else {
7419 		p->BGcurve = NULL;
7420 	}
7421 
7422 	p->size = end - bp;		/* Nominal string length */
7423 	if (p->size > 0) {
7424 		if (check_null_string(bp, p->size) != 0) {
7425 			sprintf(icp->err,"icmUcrBg_read: string is not null terminated");
7426 			icp->al->free(icp->al, buf);
7427 			return icp->errc = 1;
7428 		}
7429 		p->size = strlen(bp) + 1;
7430 		if ((rv = p->allocate((icmBase *)p)) != 0) {
7431 			icp->al->free(icp->al, buf);
7432 			return rv;
7433 		}
7434 		memcpy((void *)p->string, (void *)bp, p->size);
7435 		bp += p->size;
7436 	} else {
7437 		p->string = NULL;
7438 	}
7439 
7440 	icp->al->free(icp->al, buf);
7441 	return 0;
7442 }
7443 
7444 /* Write the contents of the object. Return 0 on sucess, error code on failure */
7445 static int icmUcrBg_write(
7446 	icmBase *pp,
7447 	unsigned long of			/* File offset to write from */
7448 ) {
7449 	icmUcrBg *p = (icmUcrBg *)pp;
7450 	icc *icp = p->icp;
7451 	unsigned long i;
7452 	unsigned int len;
7453 	char *bp, *buf;		/* Buffer to write from */
7454 	int rv = 0;
7455 
7456 	/* Allocate a file write buffer */
7457 	len = p->get_size((icmBase *)p);
7458 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7459 		sprintf(icp->err,"icmUcrBg_write malloc() failed");
7460 		return icp->errc = 2;
7461 	}
7462 	bp = buf;
7463 
7464 	/* Write type descriptor to the buffer */
7465 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
7466 		sprintf(icp->err,"icmUcrBg_write: write_SInt32Number() failed");
7467 		icp->al->free(icp->al, buf);
7468 		return icp->errc = rv;
7469 	}
7470 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
7471 	bp = bp + 8;
7472 
7473 	/* Write UCR curve */
7474 	if ((rv = write_UInt32Number(p->UCRcount,bp)) != 0) {
7475 		sprintf(icp->err,"icmUcrBg_write: write_UInt32Number() failed");
7476 		icp->al->free(icp->al, buf);
7477 		return icp->errc = rv;
7478 	}
7479 	bp += 4;
7480 
7481 	for (i = 0; i < p->UCRcount; i++, bp += 2) {
7482 		if (p->UCRcount == 1) { /* % */
7483 			if ((rv = write_UInt16Number((unsigned int)(p->UCRcurve[i]+0.5),bp)) != 0) {
7484 				sprintf(icp->err,"icmUcrBg_write: write_UInt16umber() failed");
7485 				icp->al->free(icp->al, buf);
7486 				return icp->errc = rv;
7487 			}
7488 		} else {
7489 			if ((rv = write_DCS16Number(p->UCRcurve[i],bp)) != 0) {
7490 				sprintf(icp->err,"icmUcrBg_write: write_DCS16umber(%f) failed",p->UCRcurve[i]);
7491 				icp->al->free(icp->al, buf);
7492 				return icp->errc = rv;
7493 			}
7494 		}
7495 	}
7496 
7497 	/* Write BG curve */
7498 	if ((rv = write_UInt32Number(p->BGcount,bp)) != 0) {
7499 		sprintf(icp->err,"icmUcrBg_write: write_UInt32Number() failed");
7500 		icp->al->free(icp->al, buf);
7501 		return icp->errc = rv;
7502 	}
7503 	bp += 4;
7504 
7505 	for (i = 0; i < p->BGcount; i++, bp += 2) {
7506 		if (p->BGcount == 1) { /* % */
7507 			if ((rv = write_UInt16Number((unsigned int)(p->BGcurve[i]+0.5),bp)) != 0) {
7508 				sprintf(icp->err,"icmUcrBg_write: write_UInt16umber() failed");
7509 				icp->al->free(icp->al, buf);
7510 				return icp->errc = rv;
7511 			}
7512 		} else {
7513 			if ((rv = write_DCS16Number(p->BGcurve[i],bp)) != 0) {
7514 				sprintf(icp->err,"icmUcrBg_write: write_DCS16umber(%f) failed",p->BGcurve[i]);
7515 				icp->al->free(icp->al, buf);
7516 				return icp->errc = rv;
7517 			}
7518 		}
7519 	}
7520 
7521 	if (p->string != NULL) {
7522 		if ((rv = check_null_string(p->string,p->size)) != 0) {
7523 			sprintf(icp->err,"icmUcrBg_write: text is not null terminated");
7524 			icp->al->free(icp->al, buf);
7525 			return icp->errc = 1;
7526 		}
7527 		memcpy((void *)bp, (void *)p->string, p->size);
7528 	}
7529 
7530 	/* Write to the file */
7531 	if (   icp->fp->seek(icp->fp, of) != 0
7532 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
7533 		sprintf(icp->err,"icmUcrBg_write fseek() or fwrite() failed");
7534 		icp->al->free(icp->al, buf);
7535 		return icp->errc = 2;
7536 	}
7537 	icp->al->free(icp->al, buf);
7538 	return 0;
7539 }
7540 
7541 /* Dump a text description of the object */
7542 static void icmUcrBg_dump(
7543 	icmBase *pp,
7544 	FILE *op,		/* Output to dump to */
7545 	int   verb		/* Verbosity level */
7546 ) {
7547 	icmUcrBg *p = (icmUcrBg *)pp;
7548 	if (verb <= 0)
7549 		return;
7550 
7551 	fprintf(op,"Undercolor Removal Curve & Black Generation:\n");
7552 
7553 	if (p->UCRcount == 0) {
7554 		fprintf(op,"  UCR: Not specified\n");
7555 	} else if (p->UCRcount == 1) {
7556 		fprintf(op,"  UCR: %f%%\n",p->UCRcurve[0]);
7557 	} else {
7558 		fprintf(op,"  UCR curve no. elements = %u\n",p->UCRcount);
7559 		if (verb >= 2) {
7560 			unsigned long i;
7561 			for (i = 0; i < p->UCRcount; i++)
7562 				fprintf(op,"  % 3u:  %f\n",i,p->UCRcurve[i]);
7563 		}
7564 	}
7565 	if (p->BGcount == 0) {
7566 		fprintf(op,"  BG: Not specified\n");
7567 	} else if (p->BGcount == 1) {
7568 		fprintf(op,"  BG: %f%%\n",p->BGcurve[0]);
7569 	} else {
7570 		fprintf(op,"  BG curve no. elements = %u\n",p->BGcount);
7571 		if (verb >= 2) {
7572 			unsigned long i;
7573 			for (i = 0; i < p->BGcount; i++)
7574 				fprintf(op,"  % 3u:  %f\n",i,p->BGcurve[i]);
7575 		}
7576 	}
7577 
7578 	{
7579 		unsigned long i, r, c, size;
7580 		fprintf(op,"  Description:\n");
7581 		fprintf(op,"    No. chars = %u\n",p->size);
7582 
7583 		size = p->size > 0 ? p->size-1 : 0;
7584 		i = 0;
7585 		for (r = 1;; r++) {		/* count rows */
7586 			if (i >= size) {
7587 				fprintf(op,"\n");
7588 				break;
7589 			}
7590 			if (r > 1 && verb < 2) {
7591 				fprintf(op,"...\n");
7592 				break;			/* Print 1 row if not verbose */
7593 			}
7594 			c = 1;
7595 			fprintf(op,"      0x%04x: ",i);
7596 			c += 10;
7597 			while (i < size && c < 73) {
7598 				if (isprint(p->string[i])) {
7599 					fprintf(op,"%c",p->string[i]);
7600 					c++;
7601 				} else {
7602 					fprintf(op,"\\%03o",p->string[i]);
7603 					c += 4;
7604 				}
7605 				i++;
7606 			}
7607 			if (i < size)
7608 				fprintf(op,"\n");
7609 		}
7610 	}
7611 }
7612 
7613 /* Allocate variable sized data elements */
7614 static int icmUcrBg_allocate(
7615 	icmBase *pp
7616 ) {
7617 	icmUcrBg *p = (icmUcrBg *)pp;
7618 	icc *icp = p->icp;
7619 
7620 	if (p->UCRcount != p->UCR_count) {
7621 		if (p->UCRcurve != NULL)
7622 			icp->al->free(icp->al, p->UCRcurve);
7623 		if ((p->UCRcurve = (double *) icp->al->malloc(icp->al, p->UCRcount * sizeof(double))) == NULL) {
7624 			sprintf(icp->err,"icmUcrBg_allocate: malloc() of UCR curve data failed");
7625 			return icp->errc = 2;
7626 		}
7627 		p->UCR_count = p->UCRcount;
7628 	}
7629 	if (p->BGcount != p->BG_count) {
7630 		if (p->BGcurve != NULL)
7631 			icp->al->free(icp->al, p->BGcurve);
7632 		if ((p->BGcurve = (double *) icp->al->malloc(icp->al, p->BGcount * sizeof(double))) == NULL) {
7633 			sprintf(icp->err,"icmUcrBg_allocate: malloc() of BG curve data failed");
7634 			return icp->errc = 2;
7635 		}
7636 		p->BG_count = p->BGcount;
7637 	}
7638 	if (p->size != p->_size) {
7639 		if (p->string != NULL)
7640 			icp->al->free(icp->al, p->string);
7641 		if ((p->string = (char *) icp->al->malloc(icp->al, p->size * sizeof(char))) == NULL) {
7642 			sprintf(icp->err,"icmUcrBg_allocate: malloc() of string data failed");
7643 			return icp->errc = 2;
7644 		}
7645 		p->_size = p->size;
7646 	}
7647 	return 0;
7648 }
7649 
7650 /* Free all storage in the object */
7651 static void icmUcrBg_delete(
7652 	icmBase *pp
7653 ) {
7654 	icmUcrBg *p = (icmUcrBg *)pp;
7655 	icc *icp = p->icp;
7656 
7657 	if (p->UCRcurve != NULL)
7658 		icp->al->free(icp->al, p->UCRcurve);
7659 	if (p->BGcurve != NULL)
7660 		icp->al->free(icp->al, p->BGcurve);
7661 	if (p->string != NULL)
7662 		icp->al->free(icp->al, p->string);
7663 	icp->al->free(icp->al, p);
7664 }
7665 
7666 /* Create an empty object. Return null on error */
7667 static icmBase *new_icmUcrBg(
7668 	icc *icp
7669 ) {
7670 	icmUcrBg *p;
7671 	if ((p = (icmUcrBg *) icp->al->calloc(icp->al,1,sizeof(icmUcrBg))) == NULL)
7672 		return NULL;
7673 	p->ttype    = icSigUcrBgType;
7674 	p->refcount = 1;
7675 	p->get_size = icmUcrBg_get_size;
7676 	p->read     = icmUcrBg_read;
7677 	p->write    = icmUcrBg_write;
7678 	p->dump     = icmUcrBg_dump;
7679 	p->allocate = icmUcrBg_allocate;
7680 	p->del      = icmUcrBg_delete;
7681 	p->icp      = icp;
7682 
7683 	return (icmBase *)p;
7684 }
7685 
7686 /* ---------------------------------------------------------- */
7687 /* VideoCardGamma (ColorSync 2.5 specific - c/o Neil Okamoto) */
7688 
7689 static unsigned int icmVideoCardGamma_get_size(
7690 	icmBase *pp
7691 ) {
7692 	icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
7693 	unsigned int len = 0;
7694 
7695 	len += 8;			/* 8 bytes for tag and padding */
7696 	len += 4;			/* 4 for gamma type */
7697 
7698 	/* compute size of remainder */
7699 	if (p->tagType == icmVideoCardGammaTableType) {
7700 		len += 2;       /* 2 bytes for channels */
7701 		len += 2;       /* 2 for entry count */
7702 		len += 2;       /* 2 for entry size */
7703 		len += ( p->u.table.channels *     /* compute table size */
7704 				 p->u.table.entryCount *
7705 				 p->u.table.entrySize );
7706 	}
7707 	else if (p->tagType == icmVideoCardGammaFormulaType) {
7708 		len += 12;		/* 4 bytes each for red gamma, min, & max */
7709 		len += 12;		/* 4 bytes each for green gamma, min & max */
7710 		len += 12;		/* 4 bytes each for blue gamma, min & max */
7711 	}
7712 	return len;
7713 }
7714 /* read the object, return 0 on success, error code on fail */
7715 static int icmVideoCardGamma_read(
7716 	icmBase *pp,
7717 	unsigned long len,		/* tag length */
7718 	unsigned long of		/* start offset within file */
7719 ) {
7720 	icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
7721 	icc *icp = p->icp;
7722 	int rv, c;
7723 	char *bp, *buf;
7724 	unsigned char *pchar;
7725 	unsigned short *pshort;
7726 
7727 	if (len < 18) {
7728 		sprintf(icp->err,"icmVideoCardGamma_read: Tag too small to be legal");
7729 		return icp->errc = 1;
7730 	}
7731 
7732 	/* Allocate a file read buffer */
7733 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7734 		sprintf(icp->err,"icmVideoCardGamma_read: malloc() failed");
7735 		return icp->errc = 2;
7736 	}
7737 	bp = buf;
7738 
7739 	/* Read portion of file into buffer */
7740 	if (   icp->fp->seek(icp->fp, of) != 0
7741 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
7742 		sprintf(icp->err,"icmVideoCardGamma_read: fseek() or fread() failed");
7743 		icp->al->free(icp->al, buf);
7744 		return icp->errc = 1;
7745 	}
7746 
7747 	/* Read type descriptor from the buffer */
7748 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
7749 		sprintf(icp->err,"icmVideoCardGamma_read: Wrong tag type for icmVideoCardGamma");
7750 		icp->al->free(icp->al, buf);
7751 		return icp->errc = 1;
7752 	}
7753 
7754 	/* Read gamma format (eg. table or formula) from the buffer */
7755 	p->tagType = read_UInt32Number(bp+8);
7756 
7757 	/* Read remaining gamma data based on format */
7758 	switch (p->tagType) {
7759 	case icmVideoCardGammaTableType:
7760 		p->u.table.channels   = read_UInt16Number(bp+12);
7761 		p->u.table.entryCount = read_UInt16Number(bp+14);
7762 		p->u.table.entrySize  = read_UInt16Number(bp+16);
7763 		if (len-18 < p->u.table.channels*p->u.table.entryCount*p->u.table.entrySize) {
7764 			sprintf(icp->err,"icmVideoCardGamma_read: Tag too small to be legal");
7765 			return icp->errc = 1;
7766 		}
7767 		if ((rv = pp->allocate(pp)) != 0) {  /* make space for table */
7768 			icp->al->free(icp->al, buf);
7769 			return icp->errc = rv;
7770 		}
7771 		pchar = (unsigned char*)p->u.table.data;
7772 		pshort = (unsigned short*)p->u.table.data;
7773 		for (c=0, bp=bp+18; c<p->u.table.channels*p->u.table.entryCount; c++) {
7774 			switch (p->u.table.entrySize) {
7775 			case 1:
7776 				*pchar++ = read_UInt8Number(bp);
7777 				bp++;
7778 				break;
7779 			case 2:
7780 				*pshort++ = read_UInt16Number(bp);
7781 				bp+=2;
7782 				break;
7783 			default:
7784 				sprintf(icp->err,"icmVideoCardGamma_read: unsupported table entry size");
7785 				pp->del(pp);
7786 				icp->al->free(icp->al, buf);
7787 				return icp->errc = 1;
7788 			}
7789 		}
7790 		break;
7791 	case icmVideoCardGammaFormulaType:
7792 		if (len < 48) {
7793 			sprintf(icp->err,"icmVideoCardGamma_read: Tag too small to be legal");
7794 			return icp->errc = 1;
7795 		}
7796 		p->u.formula.redGamma   = read_S15Fixed16Number(bp+12);
7797 		p->u.formula.redMin     = read_S15Fixed16Number(bp+16);
7798 		p->u.formula.redMax     = read_S15Fixed16Number(bp+20);
7799 		p->u.formula.greenGamma = read_S15Fixed16Number(bp+24);
7800 		p->u.formula.greenMin   = read_S15Fixed16Number(bp+28);
7801 		p->u.formula.greenMax   = read_S15Fixed16Number(bp+32);
7802 		p->u.formula.blueGamma  = read_S15Fixed16Number(bp+36);
7803 		p->u.formula.blueMin    = read_S15Fixed16Number(bp+40);
7804 		p->u.formula.blueMax    = read_S15Fixed16Number(bp+44);
7805 		break;
7806 	default:
7807 		sprintf(icp->err,"icmVideoCardGammaTable_read: Unknown gamma format for icmVideoCardGamma");
7808 		icp->al->free(icp->al, buf);
7809 		return icp->errc = 1;
7810 	}
7811 
7812 	icp->al->free(icp->al, buf);
7813 	return 0;
7814 }
7815 
7816 /* Write the contents of the object. Return 0 on sucess, error code on failure */
7817 static int icmVideoCardGamma_write(
7818 	icmBase *pp,
7819 	unsigned long of			/* File offset to write from */
7820 ) {
7821 	icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
7822 	icc *icp = p->icp;
7823 	unsigned int len;
7824 	char *bp, *buf;		/* Buffer to write from */
7825 	int rv = 0, c;
7826 	unsigned char *pchar;
7827 	unsigned short *pshort;
7828 
7829 	/* Allocate a file write buffer */
7830 	len = p->get_size((icmBase *)p);
7831 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
7832 		sprintf(icp->err,"icmViewingConditions_write malloc() failed");
7833 		return icp->errc = 2;
7834 	}
7835 	bp = buf;
7836 
7837 	/* Write type descriptor to the buffer */
7838 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
7839 		sprintf(icp->err,"icmVideoCardGamma_write: write_SInt32Number() failed");
7840 		icp->al->free(icp->al, buf);
7841 		return icp->errc = rv;
7842 	}
7843 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
7844 
7845 	/* Write gamma format (eg. table of formula) */
7846 	if ((rv = write_UInt32Number(p->tagType,bp+8)) != 0) {
7847 		sprintf(icp->err,"icmVideoCardGamma_write: write_UInt32Number() failed");
7848 		icp->al->free(icp->al, buf);
7849 		return icp->errc = rv;
7850 	}
7851 
7852 	/* Write remaining gamma data based on format */
7853 	switch (p->tagType) {
7854 	case icmVideoCardGammaTableType:
7855 		if ((rv = write_UInt16Number(p->u.table.channels,bp+12)) != 0) {
7856 			sprintf(icp->err,"icmVideoCardGamma_write: write_UInt16Number() failed");
7857 			icp->al->free(icp->al, buf);
7858 			return icp->errc = rv;
7859 		}
7860 		if ((rv = write_UInt16Number(p->u.table.entryCount,bp+14)) != 0) {
7861 			sprintf(icp->err,"icmVideoCardGamma_write: write_UInt16Number() failed");
7862 			icp->al->free(icp->al, buf);
7863 			return icp->errc = rv;
7864 		}
7865 		if ((rv = write_UInt16Number(p->u.table.entrySize,bp+16)) != 0) {
7866 			sprintf(icp->err,"icmVideoCardGamma_write: write_UInt16Number() failed");
7867 			icp->al->free(icp->al, buf);
7868 			return icp->errc = rv;
7869 		}
7870 		pchar = (unsigned char*)p->u.table.data;
7871 		pshort = (unsigned short*)p->u.table.data;
7872 		for (c=0, bp=bp+18; c<p->u.table.channels*p->u.table.entryCount; c++) {
7873 			switch (p->u.table.entrySize) {
7874 			case 1:
7875 				write_UInt8Number(*pchar++,bp);
7876 				bp++;
7877 				break;
7878 			case 2:
7879 				write_UInt16Number(*pshort++,bp);
7880 				bp+=2;
7881 				break;
7882 			default:
7883 				sprintf(icp->err,"icmVideoCardGamma_write: unsupported table entry size");
7884 				icp->al->free(icp->al, buf);
7885 				return icp->errc = 1;
7886 			}
7887 		}
7888 		break;
7889 	case icmVideoCardGammaFormulaType:
7890 		if ((rv = write_S15Fixed16Number(p->u.formula.redGamma,bp+12)) != 0) {
7891 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7892 			icp->al->free(icp->al, buf);
7893 			return icp->errc = rv;
7894 		}
7895 		if ((rv = write_S15Fixed16Number(p->u.formula.redMin,bp+16)) != 0) {
7896 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7897 			icp->al->free(icp->al, buf);
7898 			return icp->errc = rv;
7899 		}
7900 		if ((rv = write_S15Fixed16Number(p->u.formula.redMax,bp+20)) != 0) {
7901 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7902 			icp->al->free(icp->al, buf);
7903 			return icp->errc = rv;
7904 		}
7905 		if ((rv = write_S15Fixed16Number(p->u.formula.greenGamma,bp+24)) != 0) {
7906 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7907 			icp->al->free(icp->al, buf);
7908 			return icp->errc = rv;
7909 		}
7910 		if ((rv = write_S15Fixed16Number(p->u.formula.greenMin,bp+28)) != 0) {
7911 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7912 			icp->al->free(icp->al, buf);
7913 			return icp->errc = rv;
7914 		}
7915 		if ((rv = write_S15Fixed16Number(p->u.formula.greenMax,bp+32)) != 0) {
7916 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7917 			icp->al->free(icp->al, buf);
7918 			return icp->errc = rv;
7919 		}
7920 		if ((rv = write_S15Fixed16Number(p->u.formula.blueGamma,bp+36)) != 0) {
7921 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7922 			icp->al->free(icp->al, buf);
7923 			return icp->errc = rv;
7924 		}
7925 		if ((rv = write_S15Fixed16Number(p->u.formula.blueMin,bp+40)) != 0) {
7926 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7927 			icp->al->free(icp->al, buf);
7928 			return icp->errc = rv;
7929 		}
7930 		if ((rv = write_S15Fixed16Number(p->u.formula.blueMax,bp+44)) != 0) {
7931 			sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
7932 			icp->al->free(icp->al, buf);
7933 			return icp->errc = rv;
7934 		}
7935 		break;
7936 	default:
7937 		sprintf(icp->err,"icmVideoCardGammaTable_write: Unknown gamma format for icmVideoCardGamma");
7938 		icp->al->free(icp->al, buf);
7939 		return icp->errc = 1;
7940 	}
7941 
7942 	/* Write to the file */
7943 	if (   icp->fp->seek(icp->fp, of) != 0
7944 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
7945 		sprintf(icp->err,"icmViewingConditions_write fseek() or fwrite() failed");
7946 		icp->al->free(icp->al, buf);
7947 		return icp->errc = 2;
7948 	}
7949 	icp->al->free(icp->al, buf);
7950 	return 0;
7951 }
7952 
7953 /* Dump a text description of the object */
7954 static void icmVideoCardGamma_dump(
7955 	icmBase *pp,
7956 	FILE *op,		/* Output to dump to */
7957 	int   verb		/* Verbosity level */
7958 ) {
7959 	icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
7960 	int c,i;
7961 
7962 	if (verb <= 0)
7963 		return;
7964 
7965 	switch (p->tagType) {
7966 	case icmVideoCardGammaTableType:
7967 		fprintf(op,"VideoCardGammaTable:\n");
7968 		fprintf(op,"  channels  = %d\n", p->u.table.channels);
7969 		fprintf(op,"  entries   = %d\n", p->u.table.entryCount);
7970 		fprintf(op,"  entrysize = %d\n", p->u.table.entrySize);
7971 		if (verb >= 2) {
7972 			/* dump array contents also */
7973 			for (c=0; c<p->u.table.channels; c++) {
7974 				fprintf(op,"  channel #%d\n",c);
7975 				for (i=0; i<p->u.table.entryCount; i++) {
7976 					if (p->u.table.entrySize == 1) {
7977 						fprintf(op,"    %d: %d\n",i,((unsigned char*)p->u.table.data)[c*p->u.table.entryCount+i]);
7978 					}
7979 					else if (p->u.table.entrySize == 2) {
7980 						fprintf(op,"    %d: %d\n",i,((unsigned short*)p->u.table.data)[c*p->u.table.entryCount+i]);
7981 					}
7982 				}
7983 			}
7984 		}
7985 		break;
7986 	case icmVideoCardGammaFormulaType:
7987 		fprintf(op,"VideoCardGammaFormula:\n");
7988 		fprintf(op,"  red gamma   = %lf\n", p->u.formula.redGamma);
7989 		fprintf(op,"  red min     = %lf\n", p->u.formula.redMin);
7990 		fprintf(op,"  red max     = %lf\n", p->u.formula.redMax);
7991 		fprintf(op,"  green gamma = %lf\n", p->u.formula.greenGamma);
7992 		fprintf(op,"  green min   = %lf\n", p->u.formula.greenMin);
7993 		fprintf(op,"  green max   = %lf\n", p->u.formula.greenMax);
7994 		fprintf(op,"  blue gamma  = %lf\n", p->u.formula.blueGamma);
7995 		fprintf(op,"  blue min    = %lf\n", p->u.formula.blueMin);
7996 		fprintf(op,"  blue max    = %lf\n", p->u.formula.blueMax);
7997 		break;
7998 	default:
7999 		fprintf(op,"  Unknown tag format\n");
8000 	}
8001 }
8002 
8003 /* Allocate variable sized data elements */
8004 static int icmVideoCardGamma_allocate(
8005 	icmBase *pp
8006 ) {
8007 	icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
8008 	icc *icp = p->icp;
8009 	int size;
8010 
8011 	/* note: allocation is only relevant for table type
8012 	 * and in that case the channels, entryCount, and entrySize
8013 	 * fields must all be set prior to getting here
8014 	 */
8015 
8016 	if (p->tagType == icmVideoCardGammaTableType) {
8017 		if (p->u.table.data != NULL)
8018 			icp->al->free(icp->al, p->u.table.data);
8019 		size = (p->u.table.channels *
8020 				p->u.table.entryCount);
8021 		switch (p->u.table.entrySize) {
8022 		case 1:
8023 			size *= sizeof(unsigned char);
8024 			break;
8025 		case 2:
8026 			size *= sizeof(unsigned short);
8027 			break;
8028 		default:
8029 			sprintf(icp->err,"icmVideoCardGamma_alloc: unsupported table entry size");
8030 			return icp->errc = 1;
8031 		}
8032 		if ((p->u.table.data = (void*) icp->al->malloc(icp->al, size)) == NULL) {
8033 			sprintf(icp->err,"icmVideoCardGamma_alloc: malloc() of table data failed");
8034 			return icp->errc = 2;
8035 		}
8036 	}
8037 
8038 	return 0;
8039 }
8040 
8041 /* Free all storage in the object */
8042 static void icmVideoCardGamma_delete(
8043 	icmBase *pp
8044 ) {
8045 	icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
8046 	icc *icp = p->icp;
8047 
8048 	if (p->tagType == icmVideoCardGammaTableType && p->u.table.data != NULL)
8049 		icp->al->free(icp->al, p->u.table.data);
8050 
8051 	icp->al->free(icp->al, p);
8052 }
8053 
8054 /* Create an empty object. Return null on error */
8055 static icmBase *new_icmVideoCardGamma(
8056 	icc *icp
8057 ) {
8058 	icmVideoCardGamma *p;
8059 	if ((p = (icmVideoCardGamma *) icp->al->calloc(icp->al,1,sizeof(icmVideoCardGamma))) == NULL)
8060 		return NULL;
8061 	p->ttype    = icSigVideoCardGammaType;
8062 	p->refcount = 1;
8063 	p->get_size = icmVideoCardGamma_get_size;
8064 	p->read     = icmVideoCardGamma_read;
8065 	p->write    = icmVideoCardGamma_write;
8066 	p->dump     = icmVideoCardGamma_dump;
8067 	p->allocate = icmVideoCardGamma_allocate;
8068 	p->del      = icmVideoCardGamma_delete;
8069 	p->icp      = icp;
8070 
8071 	return (icmBase *)p;
8072 }
8073 
8074 /* ---------------------------------------------------------- */
8075 /* ViewingConditions */
8076 
8077 /* Return the number of bytes needed to write this tag */
8078 static unsigned int icmViewingConditions_get_size(
8079 	icmBase *pp
8080 ) {
8081 	icmViewingConditions *p = (icmViewingConditions *)pp;
8082 	unsigned int len = 0;
8083 	len += 8;			/* 8 bytes for tag and padding */
8084 	len += 12;			/* 12 for XYZ of illuminant */
8085 	len += 12;			/* 12 for XYZ of surround */
8086 	len += 4;			/* 4 for illuminant type */
8087 	return len;
8088 }
8089 
8090 /* read the object, return 0 on success, error code on fail */
8091 static int icmViewingConditions_read(
8092 	icmBase *pp,
8093 	unsigned long len,		/* tag length */
8094 	unsigned long of		/* start offset within file */
8095 ) {
8096 	icmViewingConditions *p = (icmViewingConditions *)pp;
8097 	icc *icp = p->icp;
8098 	int rv;
8099 	char *bp, *buf;
8100 
8101 	if (len < 36) {
8102 		sprintf(icp->err,"icmViewingConditions_read: Tag too small to be legal");
8103 		return icp->errc = 1;
8104 	}
8105 
8106 	/* Allocate a file read buffer */
8107 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
8108 		sprintf(icp->err,"icmViewingConditions_read: malloc() failed");
8109 		return icp->errc = 2;
8110 	}
8111 	bp = buf;
8112 
8113 	/* Read portion of file into buffer */
8114 	if (   icp->fp->seek(icp->fp, of) != 0
8115 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
8116 		sprintf(icp->err,"icmViewingConditions_read: fseek() or fread() failed");
8117 		icp->al->free(icp->al, buf);
8118 		return icp->errc = 1;
8119 	}
8120 
8121 	/* Read type descriptor from the buffer */
8122 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
8123 		sprintf(icp->err,"icmViewingConditions_read: Wrong tag type for icmViewingConditions");
8124 		icp->al->free(icp->al, buf);
8125 		return icp->errc = 1;
8126 	}
8127 
8128 	/* Read the XYZ values for the illuminant */
8129 	if ((rv = read_XYZNumber(&p->illuminant, bp+8)) != 0) {
8130 		sprintf(icp->err,"icmViewingConditions: read_XYZNumber error");
8131 		icp->al->free(icp->al, buf);
8132 		return icp->errc = rv;
8133 	}
8134 
8135 	/* Read the XYZ values for the surround */
8136 	if ((rv = read_XYZNumber(&p->surround, bp+20)) != 0) {
8137 		sprintf(icp->err,"icmViewingConditions: read_XYZNumber error");
8138 		icp->al->free(icp->al, buf);
8139 		return icp->errc = rv;
8140 	}
8141 
8142 	/* Read the encoded standard illuminant */
8143 	p->stdIlluminant = (icIlluminant)read_SInt32Number(bp + 32);
8144 
8145 	icp->al->free(icp->al, buf);
8146 	return 0;
8147 }
8148 
8149 /* Write the contents of the object. Return 0 on sucess, error code on failure */
8150 static int icmViewingConditions_write(
8151 	icmBase *pp,
8152 	unsigned long of			/* File offset to write from */
8153 ) {
8154 	icmViewingConditions *p = (icmViewingConditions *)pp;
8155 	icc *icp = p->icp;
8156 	unsigned int len;
8157 	char *bp, *buf;		/* Buffer to write from */
8158 	int rv = 0;
8159 
8160 	/* Allocate a file write buffer */
8161 	len = p->get_size((icmBase *)p);
8162 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
8163 		sprintf(icp->err,"icmViewingConditions_write malloc() failed");
8164 		return icp->errc = 2;
8165 	}
8166 	bp = buf;
8167 
8168 	/* Write type descriptor to the buffer */
8169 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
8170 		sprintf(icp->err,"icmViewingConditions_write: write_SInt32Number() failed");
8171 		icp->al->free(icp->al, buf);
8172 		return icp->errc = rv;
8173 	}
8174 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
8175 
8176 	/* Write the XYZ values for the illuminant */
8177 	if ((rv = write_XYZNumber(&p->illuminant, bp+8)) != 0) {
8178 		sprintf(icp->err,"icmViewingConditions: write_XYZNumber error");
8179 		icp->al->free(icp->al, buf);
8180 		return icp->errc = rv;
8181 	}
8182 
8183 	/* Write the XYZ values for the surround */
8184 	if ((rv = write_XYZNumber(&p->surround, bp+20)) != 0) {
8185 		sprintf(icp->err,"icmViewingConditions: write_XYZNumber error");
8186 		icp->al->free(icp->al, buf);
8187 		return icp->errc = rv;
8188 	}
8189 
8190 	/* Write the encoded standard illuminant */
8191 	if ((rv = write_SInt32Number((int)p->stdIlluminant, bp + 32)) != 0) {
8192 		sprintf(icp->err,"icmViewingConditionsa_write: write_SInt32Number() failed");
8193 		icp->al->free(icp->al, buf);
8194 		return icp->errc = rv;
8195 	}
8196 
8197 	/* Write to the file */
8198 	if (   icp->fp->seek(icp->fp, of) != 0
8199 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
8200 		sprintf(icp->err,"icmViewingConditions_write fseek() or fwrite() failed");
8201 		icp->al->free(icp->al, buf);
8202 		return icp->errc = 2;
8203 	}
8204 	icp->al->free(icp->al, buf);
8205 	return 0;
8206 }
8207 
8208 /* Dump a text description of the object */
8209 static void icmViewingConditions_dump(
8210 	icmBase *pp,
8211 	FILE *op,		/* Output to dump to */
8212 	int   verb		/* Verbosity level */
8213 ) {
8214 	icmViewingConditions *p = (icmViewingConditions *)pp;
8215 	if (verb <= 0)
8216 		return;
8217 
8218 	fprintf(op,"Viewing Conditions:\n");
8219 	fprintf(op,"  XYZ value of illuminant in cd/m^2 = %s\n", string_XYZNumber(&p->illuminant));
8220 	fprintf(op,"  XYZ value of surround in cd/m^2   = %s\n", string_XYZNumber(&p->surround));
8221 	fprintf(op,"  Illuminant type = %s\n", string_Illuminant(p->stdIlluminant));
8222 }
8223 
8224 /* Allocate variable sized data elements */
8225 static int icmViewingConditions_allocate(
8226 	icmBase *pp
8227 ) {
8228 	icmViewingConditions *p = (icmViewingConditions *)pp;
8229 
8230 	/* Nothing to do */
8231 	return 0;
8232 }
8233 
8234 /* Free all storage in the object */
8235 static void icmViewingConditions_delete(
8236 	icmBase *pp
8237 ) {
8238 	icmViewingConditions *p = (icmViewingConditions *)pp;
8239 	icc *icp = p->icp;
8240 
8241 	icp->al->free(icp->al, p);
8242 }
8243 
8244 /* Create an empty object. Return null on error */
8245 static icmBase *new_icmViewingConditions(
8246 	icc *icp
8247 ) {
8248 	icmViewingConditions *p;
8249 	if ((p = (icmViewingConditions *) icp->al->calloc(icp->al,1,sizeof(icmViewingConditions))) == NULL)
8250 		return NULL;
8251 	p->ttype    = icSigViewingConditionsType;
8252 	p->refcount = 1;
8253 	p->get_size = icmViewingConditions_get_size;
8254 	p->read     = icmViewingConditions_read;
8255 	p->write    = icmViewingConditions_write;
8256 	p->dump     = icmViewingConditions_dump;
8257 	p->allocate = icmViewingConditions_allocate;
8258 	p->del      = icmViewingConditions_delete;
8259 	p->icp      = icp;
8260 
8261 	return (icmBase *)p;
8262 }
8263 
8264 /* ---------------------------------------------------------- */
8265 /* icmCrdInfo object */
8266 
8267 /* Return the number of bytes needed to write this tag */
8268 static unsigned int icmCrdInfo_get_size(
8269 	icmBase *pp
8270 ) {
8271 	icmCrdInfo *p = (icmCrdInfo *)pp;
8272 	unsigned int len = 0, t;
8273 	len += 8;				/* 8 bytes for tag and padding */
8274 	len += 4 + p->ppsize;	/* Postscript product name */
8275 	for (t = 0; t < 4; t++) {	/* For all 4 intents */
8276 		len += 4 + p->crdsize[t];	/* crd names */
8277 	}
8278 	return len;
8279 }
8280 
8281 /* read the object, return 0 on success, error code on fail */
8282 static int icmCrdInfo_read(
8283 	icmBase *pp,
8284 	unsigned long len,		/* tag length */
8285 	unsigned long of		/* start offset within file */
8286 ) {
8287 	icmCrdInfo *p = (icmCrdInfo *)pp;
8288 	icc *icp = p->icp;
8289 	unsigned long t;
8290 	int rv = 0;
8291 	char *bp, *buf, *end;
8292 
8293 	if (len < 28) {
8294 		sprintf(icp->err,"icmCrdInfo_read: Tag too small to be legal");
8295 		return icp->errc = 1;
8296 	}
8297 
8298 	/* Allocate a file read buffer */
8299 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
8300 		sprintf(icp->err,"icmCrdInfo_read: malloc() failed");
8301 		return icp->errc = 2;
8302 	}
8303 	bp = buf;
8304 	end = buf + len;
8305 
8306 	/* Read portion of file into buffer */
8307 	if (   icp->fp->seek(icp->fp, of) != 0
8308 	    || icp->fp->read(icp->fp, bp, 1, len) != len) {
8309 		sprintf(icp->err,"icmCrdInfo_read: fseek() or fread() failed");
8310 		icp->al->free(icp->al, buf);
8311 		return icp->errc = 1;
8312 	}
8313 
8314 	/* Read type descriptor from the buffer */
8315 	if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
8316 		sprintf(icp->err,"icmCrdInfo_read: Wrong tag type for icmCrdInfo");
8317 		icp->al->free(icp->al, buf);
8318 		return icp->errc = 1;
8319 	}
8320 	bp = bp + 8;
8321 
8322 	/* Postscript product name */
8323 	if ((bp + 4) > end) {
8324 		sprintf(icp->err,"icmCrdInfo_read: Data too short to read Postscript product name");
8325 		icp->al->free(icp->al, buf);
8326 		return icp->errc = 1;
8327 	}
8328 	p->ppsize = read_UInt32Number(bp);
8329 	bp += 4;
8330 	if (p->ppsize > 0) {
8331 		if ((bp + p->ppsize) > end) {
8332 			sprintf(icp->err,"icmCrdInfo_read: Data to short to read Postscript product string");
8333 			icp->al->free(icp->al, buf);
8334 			return icp->errc = 1;
8335 		}
8336 		if (check_null_string(bp,p->ppsize)) {
8337 			sprintf(icp->err,"icmCrdInfo_read: Postscript product name is not terminated");
8338 			icp->al->free(icp->al, buf);
8339 			return icp->errc = 1;
8340 		}
8341 		if ((rv = p->allocate((icmBase *)p)) != 0) {
8342 			icp->al->free(icp->al, buf);
8343 			return rv;
8344 		}
8345 		memcpy((void *)p->ppname, (void *)bp, p->ppsize);
8346 		bp += p->ppsize;
8347 	}
8348 
8349 	/* CRD names for the four rendering intents */
8350 	for (t = 0; t < 4; t++) {	/* For all 4 intents */
8351 		if ((bp + 4) > end) {
8352 			sprintf(icp->err,"icmCrdInfo_read: Data too short to read CRD%d name",t);
8353 			icp->al->free(icp->al, buf);
8354 			return icp->errc = 1;
8355 		}
8356 		p->crdsize[t] = read_UInt32Number(bp);
8357 		bp += 4;
8358 		if (p->crdsize[t] > 0) {
8359 			if ((bp + p->crdsize[t]) > end) {
8360 				sprintf(icp->err,"icmCrdInfo_read: Data to short to read CRD%d string",t);
8361 				icp->al->free(icp->al, buf);
8362 				return icp->errc = 1;
8363 			}
8364 			if (check_null_string(bp,p->crdsize[t])) {
8365 				sprintf(icp->err,"icmCrdInfo_read: CRD%d name is not terminated",t);
8366 				icp->al->free(icp->al, buf);
8367 				return icp->errc = 1;
8368 			}
8369 			if ((rv = p->allocate((icmBase *)p)) != 0) {
8370 				icp->al->free(icp->al, buf);
8371 				return rv;
8372 			}
8373 			memcpy((void *)p->crdname[t], (void *)bp, p->crdsize[t]);
8374 			bp += p->crdsize[t];
8375 		}
8376 	}
8377 
8378 	icp->al->free(icp->al, buf);
8379 	return 0;
8380 }
8381 
8382 /* Write the contents of the object. Return 0 on sucess, error code on failure */
8383 static int icmCrdInfo_write(
8384 	icmBase *pp,
8385 	unsigned long of			/* File offset to write from */
8386 ) {
8387 	icmCrdInfo *p = (icmCrdInfo *)pp;
8388 	icc *icp = p->icp;
8389 	unsigned long t;
8390 	unsigned int len;
8391 	char *bp, *buf;		/* Buffer to write from */
8392 	int rv = 0;
8393 
8394 	/* Allocate a file write buffer */
8395 	len = p->get_size((icmBase *)p);
8396 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
8397 		sprintf(icp->err,"icmCrdInfo_write malloc() failed");
8398 		return icp->errc = 2;
8399 	}
8400 	bp = buf;
8401 
8402 	/* Write type descriptor to the buffer */
8403 	if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
8404 		sprintf(icp->err,"icmCrdInfo_write: write_SInt32Number() failed");
8405 		icp->al->free(icp->al, buf);
8406 		return icp->errc = rv;
8407 	}
8408 	write_SInt32Number(0,bp+4);			/* Set padding to 0 */
8409 	bp = bp + 8;
8410 
8411 	/* Postscript product name */
8412 	if ((rv = write_UInt32Number(p->ppsize,bp)) != 0) {
8413 		sprintf(icp->err,"icmCrdInfo_write: write_UInt32Number() failed");
8414 		icp->al->free(icp->al, buf);
8415 		return icp->errc = rv;
8416 	}
8417 	bp += 4;
8418 	if (p->ppsize > 0) {
8419 		if ((rv = check_null_string(p->ppname,p->ppsize)) != 0) {
8420 			sprintf(icp->err,"icmCrdInfo_write: Postscript product name is not terminated");
8421 			icp->al->free(icp->al, buf);
8422 			return icp->errc = 1;
8423 		}
8424 		memcpy((void *)bp, (void *)p->ppname, p->ppsize);
8425 		bp += p->ppsize;
8426 	}
8427 
8428 	/* CRD names for the four rendering intents */
8429 	for (t = 0; t < 4; t++) {	/* For all 4 intents */
8430 		if ((rv = write_UInt32Number(p->crdsize[t],bp)) != 0) {
8431 			sprintf(icp->err,"icmCrdInfo_write: write_UInt32Number() failed");
8432 			icp->al->free(icp->al, buf);
8433 			return icp->errc = rv;
8434 		}
8435 		bp += 4;
8436 		if (p->ppsize > 0) {
8437 			if ((rv = check_null_string(p->crdname[t],p->crdsize[t])) != 0) {
8438 				sprintf(icp->err,"icmCrdInfo_write: CRD%d name is not terminated",t);
8439 				icp->al->free(icp->al, buf);
8440 				return icp->errc = 1;
8441 			}
8442 			memcpy((void *)bp, (void *)p->crdname[t], p->crdsize[t]);
8443 			bp += p->crdsize[t];
8444 		}
8445 	}
8446 
8447 	/* Write to the file */
8448 	if (   icp->fp->seek(icp->fp, of) != 0
8449 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
8450 		sprintf(icp->err,"icmCrdInfo_write fseek() or fwrite() failed");
8451 		icp->al->free(icp->al, buf);
8452 		return icp->errc = 2;
8453 	}
8454 	icp->al->free(icp->al, buf);
8455 	return 0;
8456 }
8457 
8458 /* Dump a text description of the object */
8459 static void icmCrdInfo_dump(
8460 	icmBase *pp,
8461 	FILE *op,		/* Output to dump to */
8462 	int   verb		/* Verbosity level */
8463 ) {
8464 	icmCrdInfo *p = (icmCrdInfo *)pp;
8465 	unsigned long i, r, c, size, t;
8466 
8467 	if (verb <= 0)
8468 		return;
8469 
8470 	fprintf(op,"PostScript Product name and CRD names:\n");
8471 
8472 	fprintf(op,"  Product name:\n");
8473 	fprintf(op,"    No. chars = %u\n",p->ppsize);
8474 
8475 	size = p->ppsize > 0 ? p->ppsize-1 : 0;
8476 	i = 0;
8477 	for (r = 1;; r++) {		/* count rows */
8478 		if (i >= size) {
8479 			fprintf(op,"\n");
8480 			break;
8481 		}
8482 		if (r > 1 && verb < 2) {
8483 			fprintf(op,"...\n");
8484 			break;			/* Print 1 row if not verbose */
8485 		}
8486 		c = 1;
8487 		fprintf(op,"      0x%04x: ",i);
8488 		c += 10;
8489 		while (i < size && c < 73) {
8490 			if (isprint(p->ppname[i])) {
8491 				fprintf(op,"%c",p->ppname[i]);
8492 				c++;
8493 			} else {
8494 				fprintf(op,"\\%03o",p->ppname[i]);
8495 				c += 4;
8496 			}
8497 			i++;
8498 		}
8499 		if (i < size)
8500 			fprintf(op,"\n");
8501 	}
8502 
8503 	for (t = 0; t < 4; t++) {	/* For all 4 intents */
8504 		fprintf(op,"  CRD%d name:\n",t);
8505 		fprintf(op,"    No. chars = %u\n",p->crdsize[t]);
8506 
8507 		size = p->crdsize[t] > 0 ? p->crdsize[t]-1 : 0;
8508 		i = 0;
8509 		for (r = 1;; r++) {		/* count rows */
8510 			if (i >= size) {
8511 				fprintf(op,"\n");
8512 				break;
8513 			}
8514 			if (r > 1 && verb < 2) {
8515 				fprintf(op,"...\n");
8516 				break;			/* Print 1 row if not verbose */
8517 			}
8518 			c = 1;
8519 			fprintf(op,"      0x%04x: ",i);
8520 			c += 10;
8521 			while (i < size && c < 73) {
8522 				if (isprint(p->crdname[t][i])) {
8523 					fprintf(op,"%c",p->crdname[t][i]);
8524 					c++;
8525 				} else {
8526 					fprintf(op,"\\%03o",p->crdname[t][i]);
8527 					c += 4;
8528 				}
8529 				i++;
8530 			}
8531 			if (i < size)
8532 				fprintf(op,"\n");
8533 		}
8534 	}
8535 }
8536 
8537 /* Allocate variable sized data elements */
8538 static int icmCrdInfo_allocate(
8539 	icmBase *pp
8540 ) {
8541 	icmCrdInfo *p = (icmCrdInfo *)pp;
8542 	icc *icp = p->icp;
8543 	unsigned int t;
8544 
8545 	if (p->ppsize != p->_ppsize) {
8546 		if (p->ppname != NULL)
8547 			icp->al->free(icp->al, p->ppname);
8548 		if ((p->ppname = (char *) icp->al->malloc(icp->al, p->ppsize * sizeof(char))) == NULL) {
8549 			sprintf(icp->err,"icmCrdInfo_alloc: malloc() of string data failed");
8550 			return icp->errc = 2;
8551 		}
8552 		p->_ppsize = p->ppsize;
8553 	}
8554 	for (t = 0; t < 4; t++) {	/* For all 4 intents */
8555 		if (p->crdsize[t] != p->_crdsize[t]) {
8556 			if (p->crdname[t] != NULL)
8557 				icp->al->free(icp->al, p->crdname[t]);
8558 			if ((p->crdname[t] = (char *) icp->al->malloc(icp->al, p->crdsize[t] * sizeof(char))) == NULL) {
8559 				sprintf(icp->err,"icmCrdInfo_alloc: malloc() of CRD%d name string failed",t);
8560 				return icp->errc = 2;
8561 			}
8562 			p->_crdsize[t] = p->crdsize[t];
8563 		}
8564 	}
8565 	return 0;
8566 }
8567 
8568 /* Free all storage in the object */
8569 static void icmCrdInfo_delete(
8570 	icmBase *pp
8571 ) {
8572 	icmCrdInfo *p = (icmCrdInfo *)pp;
8573 	icc *icp = p->icp;
8574 	unsigned int t;
8575 
8576 	if (p->ppname != NULL)
8577 		icp->al->free(icp->al, p->ppname);
8578 	for (t = 0; t < 4; t++) {	/* For all 4 intents */
8579 		if (p->crdname[t] != NULL)
8580 			icp->al->free(icp->al, p->crdname[t]);
8581 	}
8582 	icp->al->free(icp->al, p);
8583 }
8584 
8585 /* Create an empty object. Return null on error */
8586 static icmBase *new_icmCrdInfo(
8587 	icc *icp
8588 ) {
8589 	icmCrdInfo *p;
8590 	if ((p = (icmCrdInfo *) icp->al->calloc(icp->al,1,sizeof(icmCrdInfo))) == NULL)
8591 		return NULL;
8592 	p->ttype    = icSigCrdInfoType;
8593 	p->refcount = 1;
8594 	p->get_size = icmCrdInfo_get_size;
8595 	p->read     = icmCrdInfo_read;
8596 	p->write    = icmCrdInfo_write;
8597 	p->dump     = icmCrdInfo_dump;
8598 	p->allocate = icmCrdInfo_allocate;
8599 	p->del      = icmCrdInfo_delete;
8600 	p->icp      = icp;
8601 
8602 	return (icmBase *)p;
8603 }
8604 
8605 /* ========================================================== */
8606 /* icmHeader object */
8607 /* ========================================================== */
8608 
8609 /* Return the number of bytes needed to write this tag */
8610 static unsigned int icmHeader_get_size(
8611 	icmHeader *p
8612 ) {
8613 	return 128;		/* By definition */
8614 }
8615 
8616 /* read the object, return 0 on success, error code on fail */
8617 static int icmHeader_read(
8618 	icmHeader *p,
8619 	unsigned long len,		/* tag length */
8620 	unsigned long of		/* start offset within file */
8621 ) {
8622 	icc *icp = p->icp;
8623 	char *buf;
8624 	unsigned int tt;
8625 	int rv = 0;
8626 
8627 	if (len != 128) {
8628 		sprintf(icp->err,"icmHeader_read: Length expected to be 128");
8629 		return icp->errc = 1;
8630 	}
8631 
8632 	if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
8633 		sprintf(icp->err,"icmHeader_read: malloc() failed");
8634 		return icp->errc = 2;
8635 	}
8636 	if (   icp->fp->seek(icp->fp, of) != 0
8637 	    || icp->fp->read(icp->fp, buf, 1, len) != len) {
8638 		sprintf(icp->err,"icmHeader_read: fseek() or fread() failed");
8639 		icp->al->free(icp->al, buf);
8640 		return icp->errc = 1;
8641 	}
8642 
8643 	/* Fill in the in-memory structure */
8644 	p->size  = read_UInt32Number(buf + 0);	/* Profile size in bytes */
8645     p->cmmId = read_SInt32Number(buf + 4);	/* CMM for profile */
8646 	tt       = read_UInt8Number(buf + 8);	/* Raw major version number */
8647     p->majv  = (tt >> 4) * 10 + (tt & 0xf);	/* Integer major version number */
8648 	tt       = read_UInt8Number(buf + 9);	/* Raw minor/bug fix version numbers */
8649     p->minv  = (tt >> 4);					/* Integer minor version number */
8650     p->bfv   = (tt & 0xf);					/* Integer bug fix version number */
8651 	p->deviceClass = (icProfileClassSignature)
8652 	           read_SInt32Number(buf + 12);	/* Type of profile */
8653     p->colorSpace = (icColorSpaceSignature)
8654 	           read_SInt32Number(buf + 16);	/* Color space of data */
8655     p->pcs = (icColorSpaceSignature)
8656 	           read_SInt32Number(buf + 20);	/* PCS: XYZ or Lab */
8657 	if ((rv = read_DateTimeNumber(&p->date, buf + 24)) != 0) {	/* Creation Date */
8658 		sprintf(icp->err,"icmHeader_read: read_DateTimeNumber corrupted");
8659 		icp->al->free(icp->al, buf);
8660 		return icp->errc = rv;
8661 	}
8662 	tt = read_SInt32Number(buf+36);
8663 	if (tt != icMagicNumber) {				/* Check magic number */
8664 		sprintf(icp->err,"icmHeader_read: wrong magic number 0x%x",tt);
8665 		icp->al->free(icp->al, buf);
8666 		return icp->errc = 1;
8667 	}
8668     p->platform = (icPlatformSignature)
8669 	           read_SInt32Number(buf + 40);			/* Primary platform */
8670     p->flags = read_UInt32Number(buf + 44);			/* Various bits */
8671     p->manufacturer = read_SInt32Number(buf + 48); /* Dev manufacturer */
8672     p->model = read_SInt32Number(buf + 52);			/* Dev model */
8673     read_UInt64Number(&p->attributes, buf + 56);	/* Device attributes */
8674 	p->renderingIntent = (icRenderingIntent)
8675 	           read_SInt32Number(buf + 64);	/* Rendering intent */
8676 	if ((rv = read_XYZNumber(&p->illuminant, buf + 68)) != 0) {	/* Profile illuminant */
8677 		sprintf(icp->err,"icmHeader_read: read_XYZNumber error");
8678 		icp->al->free(icp->al, buf);
8679 		return icp->errc = rv;
8680 	}
8681     p->creator = read_SInt32Number(buf + 80);	/* Profile creator */
8682 
8683 	icp->al->free(icp->al, buf);
8684 	return 0;
8685 }
8686 
8687 /* Write the contents of the object. Return 0 on sucess, error code on failure */
8688 static int icmHeader_write(
8689 	icmHeader *p,
8690 	unsigned long of			/* File offset to write from */
8691 ) {
8692 	icc *icp = p->icp;
8693 	char *buf;		/* Buffer to write from */
8694 	unsigned int len;
8695 	unsigned int tt;
8696 	int rv = 0;
8697 
8698 	len = p->get_size(p);
8699 	if ((buf = (char *) icp->al->calloc(icp->al,1,len)) == NULL) {			/* Zero it - some CMS are fussy */
8700 		sprintf(icp->err,"icmHeader_write calloc() failed");
8701 		return icp->errc = 2;
8702 	}
8703 
8704 	/* Fill in the write buffer */
8705 	if ((rv = write_UInt32Number(p->size, buf + 0)) != 0) {	/* Profile size in bytes */
8706 		sprintf(icp->err,"icmHeader_write: profile size");
8707 		icp->al->free(icp->al, buf);
8708 		return icp->errc = rv;
8709 	}
8710 
8711     if ((rv = write_SInt32Number(p->cmmId, buf + 4)) != 0) {	/* CMM for profile */
8712 		sprintf(icp->err,"icmHeader_write: cmmId");
8713 		icp->al->free(icp->al, buf);
8714 		return icp->errc = rv;
8715 	}
8716 	if (p->majv < 0 || p->majv > 99			/* Sanity check version numbers */
8717 	 || p->minv < 0 || p->minv > 9
8718 	 || p->bfv  < 0 || p->bfv  > 9) {
8719 		sprintf(icp->err,"icmHeader_write: version number");
8720 		icp->al->free(icp->al, buf);
8721 		return icp->errc = 1;
8722 	}
8723 	tt = ((p->majv/10) << 4) + (p->majv % 10);
8724 	if ((rv = write_UInt8Number(tt, buf + 8)) != 0) {	/* Raw major version number */
8725 		sprintf(icp->err,"icmHeader_write: Uint8Number major version");
8726 		icp->al->free(icp->al, buf);
8727 		return icp->errc = rv;
8728 	}
8729     tt = (p->minv << 4) + p->bfv;
8730 	if ((rv = write_UInt8Number(tt, buf + 9)) != 0) {	/* Raw minor/bug fix version numbers */
8731 		sprintf(icp->err,"icmHeader_write: Uint8Number minor/bug fix");
8732 		icp->al->free(icp->al, buf);
8733 		return icp->errc = rv;
8734 	}
8735 	if ((rv = write_SInt32Number((int)p->deviceClass, buf + 12)) != 0) {	/* Type of profile */
8736 		sprintf(icp->err,"icmHeader_write: SInt32Number deviceClass");
8737 		icp->al->free(icp->al, buf);
8738 		return icp->errc = rv;
8739 	}
8740 	if ((rv = write_SInt32Number((int)p->colorSpace, buf + 16)) != 0) {	/* Color space of data */
8741 		sprintf(icp->err,"icmHeader_write: SInt32Number data color space");
8742 		icp->al->free(icp->al, buf);
8743 		return icp->errc = rv;
8744 	}
8745 	if ((rv = write_SInt32Number((int)p->pcs, buf + 20)) != 0) {		/* PCS: XYZ or Lab */
8746 		sprintf(icp->err,"icmHeader_write: SInt32Number PCS");
8747 		icp->al->free(icp->al, buf);
8748 		return icp->errc = rv;
8749 	}
8750 	if ((rv = write_DateTimeNumber(&p->date, buf + 24)) != 0) {		/* Creation Date */
8751 		sprintf(icp->err,"icmHeader_write: DateTimeNumber creation");
8752 		icp->al->free(icp->al, buf);
8753 		return icp->errc = rv;
8754 	}
8755 	if ((rv = write_SInt32Number(icMagicNumber, buf+36)) != 0) {		/* Magic number */
8756 		sprintf(icp->err,"icmHeader_write: SInt32Number magic");
8757 		icp->al->free(icp->al, buf);
8758 		return icp->errc = rv;
8759 	}
8760 	if ((rv = write_SInt32Number((int)p->platform, buf + 40)) != 0) {	/* Primary platform */
8761 		sprintf(icp->err,"icmHeader_write: SInt32Number platform");
8762 		icp->al->free(icp->al, buf);
8763 		return icp->errc = rv;
8764 	}
8765     if ((rv = write_UInt32Number(p->flags, buf + 44)) != 0) {			/* Various bits */
8766 		sprintf(icp->err,"icmHeader_write: UInt32Number flags");
8767 		icp->al->free(icp->al, buf);
8768 		return icp->errc = rv;
8769 	}
8770     if ((rv = write_SInt32Number(p->manufacturer, buf + 48)) != 0) { /* Dev manufacturer */
8771 		sprintf(icp->err,"icmHeader_write: SInt32Number manufaturer");
8772 		icp->al->free(icp->al, buf);
8773 		return icp->errc = rv;
8774 	}
8775     if ((write_SInt32Number(p->model, buf + 52)) != 0) {				/* Dev model */
8776 		sprintf(icp->err,"icmHeader_write: SInt32Number model");
8777 		icp->al->free(icp->al, buf);
8778 		return icp->errc = rv;
8779 	}
8780     if ((rv = write_UInt64Number(&p->attributes, buf + 56)) != 0) {	/* Device attributes */
8781 		sprintf(icp->err,"icmHeader_write: UInt64Number attributes");
8782 		icp->al->free(icp->al, buf);
8783 		return icp->errc = rv;
8784 	}
8785 	if ((rv = write_SInt32Number((int)p->renderingIntent, buf + 64)) != 0) { /* Rendering intent */
8786 		sprintf(icp->err,"icmHeader_write: SInt32Number rendering intent");
8787 		icp->al->free(icp->al, buf);
8788 		return icp->errc = rv;
8789 	}
8790 	if ((rv = write_XYZNumber(&p->illuminant, buf + 68)) != 0) {		/* Profile illuminant */
8791 		sprintf(icp->err,"icmHeader_write: XYZNumber illuminant");
8792 		icp->al->free(icp->al, buf);
8793 		return icp->errc = rv;
8794 	}
8795     if ((rv = write_SInt32Number(p->creator, buf + 80)) != 0) {		/* Profile creator */
8796 		sprintf(icp->err,"icmHeader_write: SInt32Number creator");
8797 		icp->al->free(icp->al, buf);
8798 		return icp->errc = rv;
8799 	}
8800 
8801 	if (   icp->fp->seek(icp->fp, of) != 0
8802 	    || icp->fp->write(icp->fp, buf, 1, len) != len) {
8803 		sprintf(icp->err,"icmHeader_write fseek() or fwrite() failed");
8804 		icp->al->free(icp->al, buf);
8805 		return icp->errc = 2;
8806 	}
8807 
8808 	icp->al->free(icp->al, buf);
8809 	return rv;
8810 }
8811 
8812 static void icmHeader_dump(
8813 	icmHeader *p,
8814 	FILE *op,		/* Output to dump to */
8815 	int   verb		/* Verbosity level */
8816 ) {
8817 	if (verb <= 0)
8818 		return;
8819 
8820 	fprintf(op,"Header:\n");
8821 	fprintf(op,"  size         = %d bytes\n",p->size);
8822 	fprintf(op,"  CMM          = %s\n",tag2str(p->cmmId));
8823 	fprintf(op,"  Version      = %d.%d.%d\n",p->majv, p->minv, p->bfv);
8824 	fprintf(op,"  Device Class = %s\n", string_ProfileClassSignature(p->deviceClass));
8825 	fprintf(op,"  Color Space  = %s\n", string_ColorSpaceSignature(p->colorSpace));
8826 	fprintf(op,"  Conn. Space  = %s\n", string_ColorSpaceSignature(p->pcs));
8827 	fprintf(op,"  Date, Time   = %s\n", string_DateTimeNumber(&p->date));
8828 	fprintf(op,"  Platform     = %s\n", string_PlatformSignature(p->platform));
8829 	fprintf(op,"  Flags        = %s\n", string_ProfileHeaderFlags(p->flags));
8830 	fprintf(op,"  Dev. Mnfctr. = %s\n",tag2str(p->manufacturer));	/* ~~~ */
8831 	fprintf(op,"  Dev. Model   = %s\n",tag2str(p->model));	/* ~~~ */
8832 	fprintf(op,"  Dev. Attrbts = %s\n", string_DeviceAttributes(p->attributes.l));
8833 	fprintf(op,"  Rndrng Intnt = %s\n", string_RenderingIntent(p->renderingIntent));
8834 	fprintf(op,"  Illuminant   = %s\n", string_XYZNumber_and_Lab(&p->illuminant));
8835 	fprintf(op,"  Creator      = %s\n",tag2str(p->creator));	/* ~~~ */
8836 	fprintf(op,"\n");
8837 }
8838 
8839 static void icmHeader_delete(
8840 	icmHeader *p
8841 ) {
8842 	icc *icp = p->icp;
8843 
8844 	icp->al->free(icp->al, p);
8845 }
8846 
8847 /* Create an empty object. Return null on error */
8848 static icmHeader *new_icmHeader(
8849 	icc *icp
8850 ) {
8851 	icmHeader *p;
8852 	if ((p = (icmHeader *) icp->al->calloc(icp->al,1,sizeof(icmHeader))) == NULL)
8853 		return NULL;
8854 	p->icp      = icp;
8855 	p->get_size = icmHeader_get_size;
8856 	p->read     = icmHeader_read;
8857 	p->write    = icmHeader_write;
8858 	p->dump     = icmHeader_dump;
8859 	p->del      = icmHeader_delete;
8860 
8861 	return p;
8862 }
8863 
8864 /* ---------------------------------------------------------- */
8865 /* Type vector table. Match the Tag type against the object creator */
8866 static struct {
8867 	icTagTypeSignature  ttype;			/* The tag type signature */
8868 	icmBase *              (*new_obj)(icc *icp);
8869 } typetable[] = {
8870 	{icSigCrdInfoType,             new_icmCrdInfo},
8871 	{icSigCurveType,               new_icmCurve},
8872 	{icSigDataType,                new_icmData},
8873 	{icSigDateTimeType,            new_icmDateTimeNumber},
8874 	{icSigLut16Type,               new_icmLut},
8875 	{icSigLut8Type,                new_icmLut},
8876 	{icSigMeasurementType,         new_icmMeasurement},
8877 	{icSigNamedColorType,          new_icmNamedColor},
8878 	{icSigNamedColor2Type,         new_icmNamedColor},
8879 	{icSigProfileSequenceDescType, new_icmProfileSequenceDesc},
8880 	{icSigS15Fixed16ArrayType,     new_icmS15Fixed16Array},
8881 	{icSigScreeningType,           new_icmScreening},
8882 	{icSigSignatureType,           new_icmSignature},
8883 	{icSigTextDescriptionType,     new_icmTextDescription},
8884 	{icSigTextType,                new_icmText},
8885 	{icSigU16Fixed16ArrayType,     new_icmU16Fixed16Array},
8886 	{icSigUcrBgType,               new_icmUcrBg},
8887 	{icSigVideoCardGammaType,      new_icmVideoCardGamma},
8888 	{icSigUInt16ArrayType,         new_icmUInt16Array},
8889 	{icSigUInt32ArrayType,         new_icmUInt32Array},
8890 	{icSigUInt64ArrayType,         new_icmUInt64Array},
8891 	{icSigUInt8ArrayType,          new_icmUInt8Array},
8892 	{icSigViewingConditionsType,   new_icmViewingConditions},
8893 	{icSigXYZArrayType,            new_icmXYZArray},
8894 	{icMaxEnumType,                NULL}
8895 };
8896 
8897 /* Table that lists the legal Types for each Tag Signature */
8898 static struct {
8899 	icTagSignature      sig;
8900 	icTagTypeSignature  ttypes[4];			/* Arbitrary max of 4 */
8901 } sigtypetable[] = {
8902 	{icSigAToB0Tag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8903 	{icSigAToB1Tag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8904 	{icSigAToB2Tag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8905 	{icSigBlueColorantTag,			{icSigXYZType,icMaxEnumType}},
8906 	{icSigBlueTRCTag,				{icSigCurveType,icMaxEnumType}},
8907 	{icSigBToA0Tag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8908 	{icSigBToA1Tag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8909 	{icSigBToA2Tag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8910 	{icSigCalibrationDateTimeTag,	{icSigDateTimeType,icMaxEnumType}},
8911 	{icSigCharTargetTag,			{icSigTextType,icMaxEnumType}},
8912 	{icSigCopyrightTag,				{icSigTextType,icMaxEnumType}},
8913 	{icSigCrdInfoTag,				{icSigCrdInfoType,icMaxEnumType}},
8914 	{icSigDeviceMfgDescTag,			{icSigTextDescriptionType,icMaxEnumType}},
8915 	{icSigDeviceModelDescTag,		{icSigTextDescriptionType,icMaxEnumType}},
8916 	{icSigGamutTag,					{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8917 	{icSigGrayTRCTag,				{icSigCurveType,icMaxEnumType}},
8918 	{icSigGreenColorantTag,			{icSigXYZType,icMaxEnumType}},
8919 	{icSigGreenTRCTag,				{icSigCurveType,icMaxEnumType}},
8920 	{icSigLuminanceTag,				{icSigXYZType,icMaxEnumType}},
8921 	{icSigMeasurementTag,			{icSigMeasurementType,icMaxEnumType}},
8922 	{icSigMediaBlackPointTag,		{icSigXYZType,icMaxEnumType}},
8923 	{icSigMediaWhitePointTag,		{icSigXYZType,icMaxEnumType}},
8924 	{icSigNamedColorTag,			{icSigNamedColorType,icMaxEnumType}},
8925 	{icSigNamedColor2Tag,			{icSigNamedColor2Type,icMaxEnumType}},
8926 	{icSigPreview0Tag,				{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8927 	{icSigPreview1Tag,				{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8928 	{icSigPreview2Tag,				{icSigLut8Type,icSigLut16Type,icMaxEnumType}},
8929 	{icSigProfileDescriptionTag,	{icSigTextDescriptionType,icMaxEnumType}},
8930 	{icSigProfileSequenceDescTag,	{icSigProfileSequenceDescType,icMaxEnumType}},
8931 	{icSigPs2CRD0Tag,				{icSigDataType,icMaxEnumType}},
8932 	{icSigPs2CRD1Tag,				{icSigDataType,icMaxEnumType}},
8933 	{icSigPs2CRD2Tag,				{icSigDataType,icMaxEnumType}},
8934 	{icSigPs2CRD3Tag,				{icSigDataType,icMaxEnumType}},
8935 	{icSigPs2CSATag,				{icSigDataType,icMaxEnumType}},
8936 	{icSigPs2RenderingIntentTag,	{icSigDataType,icMaxEnumType}},
8937 	{icSigRedColorantTag,			{icSigXYZType,icMaxEnumType}},
8938 	{icSigRedTRCTag,				{icSigCurveType,icMaxEnumType}},
8939 	{icSigScreeningDescTag,			{icSigTextDescriptionType,icMaxEnumType}},
8940 	{icSigScreeningTag,				{icSigScreeningType,icMaxEnumType}},
8941 	{icSigTechnologyTag,			{icSigSignatureType,icMaxEnumType}},
8942 	{icSigUcrBgTag,					{icSigUcrBgType,icMaxEnumType}},
8943 	{icSigVideoCardGammaTag,		{icSigVideoCardGammaType,icMaxEnumType}},
8944 	{icSigViewingCondDescTag,		{icSigTextDescriptionType,icMaxEnumType}},
8945 	{icSigViewingConditionsTag,		{icSigViewingConditionsType,icMaxEnumType}},
8946 	{icMaxEnumType,					{icMaxEnumType}}
8947 };
8948 
8949 /* Fake color tag for specifying PCS */
8950 #define icSigPCSData  ((icColorSpaceSignature) 0x50435320L)
8951 
8952 /* Table that lists the required tags for various profiles */
8953 static struct {
8954 	icProfileClassSignature sig;		/* Profile signature */
8955 	int      			    chans;		/* Data Color channels, -ve for match but try next, */
8956 										/*          -100 for ignore, -200 for ignore and try next */
8957 	icColorSpaceSignature   colsig;		/* Data Color space signature, icMaxEnumData for ignore, */
8958 										/*                           icSigPCSData for XYZ of Lab */
8959 	icColorSpaceSignature   pcssig;		/* PCS Color space signature, icMaxEnumData for ignore, */
8960 										/*                          icSigPCSData for XYZ or Lab */
8961 	icTagSignature          tags[12];	/* Arbitrary max of 12 */
8962 } tagchecktable[] = {
8963     {icSigInputClass,      -1, icMaxEnumData, icSigPCSData,
8964 	 	{icSigProfileDescriptionTag,
8965 		 icSigGrayTRCTag,
8966 		 icSigMediaWhitePointTag,
8967 		 icSigCopyrightTag, icMaxEnumType}},
8968 
8969     {icSigInputClass,      -3, icMaxEnumData, icSigXYZData,
8970 	 	{icSigProfileDescriptionTag,
8971 		 icSigRedColorantTag,
8972 		 icSigGreenColorantTag,
8973 		 icSigBlueColorantTag,
8974 		 icSigRedTRCTag,
8975 		 icSigGreenTRCTag,
8976 		 icSigBlueTRCTag,
8977 		 icSigMediaWhitePointTag,
8978 		 icSigCopyrightTag, icMaxEnumType}},
8979 
8980     {icSigInputClass,     -100, icMaxEnumData, icSigPCSData,
8981 	 	{icSigProfileDescriptionTag,
8982 		 icSigAToB0Tag,
8983 		 icSigMediaWhitePointTag,
8984 		 icSigCopyrightTag, icMaxEnumType}},
8985 
8986     {icSigDisplayClass,     -1, icMaxEnumData, icSigPCSData,
8987 	 	{icSigProfileDescriptionTag,
8988 		 icSigGrayTRCTag,
8989 		 icSigMediaWhitePointTag,
8990 		 icSigCopyrightTag, icMaxEnumType}},
8991 
8992     {icSigDisplayClass,     -3, icSigRgbData, icSigXYZData,	/* Rgb or any 3 component space ?? */
8993 	 	{icSigProfileDescriptionTag,
8994 		 icSigRedColorantTag,
8995 		 icSigGreenColorantTag,
8996 		 icSigBlueColorantTag,
8997 		 icSigRedTRCTag,
8998 		 icSigGreenTRCTag,
8999 		 icSigBlueTRCTag,
9000 		 icSigMediaWhitePointTag,
9001 		 icSigCopyrightTag, icMaxEnumType}},
9002 
9003 	/* Non-3 component Display device */
9004     {icSigDisplayClass,     -100, icMaxEnumData, icSigPCSData,
9005 	 	{icSigProfileDescriptionTag,
9006 		 icSigAToB0Tag,					/* BToA doesn't seem to be required, which is strange... */
9007 		 icSigMediaWhitePointTag,
9008 		 icSigCopyrightTag, icMaxEnumType}},
9009 
9010     {icSigOutputClass,     -1, icMaxEnumData, icSigPCSData,
9011 	 	{icSigProfileDescriptionTag,
9012 		 icSigGrayTRCTag,
9013 		 icSigMediaWhitePointTag,
9014 		 icSigCopyrightTag, icMaxEnumType}},
9015 
9016     {icSigOutputClass,     -1, icMaxEnumData, icSigPCSData,
9017 	 	{icSigProfileDescriptionTag,
9018 		 icSigAToB0Tag,
9019 		 icSigBToA0Tag,
9020 		 icSigGamutTag,
9021 		 icSigAToB1Tag,
9022 		 icSigBToA1Tag,
9023 		 icSigAToB2Tag,
9024 		 icSigBToA2Tag,
9025 		 icSigMediaWhitePointTag,
9026 		 icSigCopyrightTag, icMaxEnumType}},
9027 
9028     {icSigOutputClass,     -2, icMaxEnumData, icSigPCSData,
9029 	 	{icSigProfileDescriptionTag,
9030 		 icSigAToB0Tag,
9031 		 icSigBToA0Tag,
9032 		 icSigGamutTag,
9033 		 icSigAToB1Tag,
9034 		 icSigBToA1Tag,
9035 		 icSigAToB2Tag,
9036 		 icSigBToA2Tag,
9037 		 icSigMediaWhitePointTag,
9038 		 icSigCopyrightTag, icMaxEnumType}},
9039 
9040     {icSigOutputClass,     -3, icMaxEnumData, icSigPCSData,
9041 	 	{icSigProfileDescriptionTag,
9042 		 icSigAToB0Tag,
9043 		 icSigBToA0Tag,
9044 		 icSigGamutTag,
9045 		 icSigAToB1Tag,
9046 		 icSigBToA1Tag,
9047 		 icSigAToB2Tag,
9048 		 icSigBToA2Tag,
9049 		 icSigMediaWhitePointTag,
9050 		 icSigCopyrightTag, icMaxEnumType}},
9051 
9052     {icSigOutputClass,     -4, icMaxEnumData, icSigPCSData,
9053 	 	{icSigProfileDescriptionTag,
9054 		 icSigAToB0Tag,
9055 		 icSigBToA0Tag,
9056 		 icSigGamutTag,
9057 		 icSigAToB1Tag,
9058 		 icSigBToA1Tag,
9059 		 icSigAToB2Tag,
9060 		 icSigBToA2Tag,
9061 		 icSigMediaWhitePointTag,
9062 		 icSigCopyrightTag, icMaxEnumType}},
9063 
9064     {icSigOutputClass,     -100, icMaxEnumData, icSigPCSData,	/* Assumed from Hexachrome examples */
9065 	 	{icSigProfileDescriptionTag,
9066 		 icSigBToA0Tag,
9067 		 icSigBToA1Tag,
9068 		 icSigBToA2Tag,
9069 		 icSigMediaWhitePointTag,
9070 		 icSigCopyrightTag, icMaxEnumType}},
9071 
9072     {icSigLinkClass,      -100, icMaxEnumData, icMaxEnumData,
9073 	 	{icSigProfileDescriptionTag,
9074 		 icSigAToB0Tag,
9075 		 icSigProfileSequenceDescTag,
9076 		 icSigCopyrightTag, icMaxEnumType}},
9077 
9078     {icSigColorSpaceClass,       -100, icMaxEnumData, icSigPCSData,
9079 	 	{icSigProfileDescriptionTag,
9080 		 icSigBToA0Tag,
9081 		 icSigAToB0Tag,
9082 		 icSigMediaWhitePointTag,
9083 		 icSigCopyrightTag, icMaxEnumType}},
9084 
9085     {icSigAbstractClass,      -100, icSigPCSData, icSigPCSData,
9086 	 	{icSigProfileDescriptionTag,
9087 		 icSigAToB0Tag,
9088 		 icSigMediaWhitePointTag,
9089 		 icSigCopyrightTag, icMaxEnumType}},
9090 
9091     {icSigNamedColorClass,        -200, icMaxEnumData, icMaxEnumData,
9092 	 	{icSigProfileDescriptionTag,
9093 		 icSigNamedColor2Tag,
9094 		 icSigMediaWhitePointTag,
9095 		 icSigCopyrightTag, icMaxEnumType}},
9096 
9097     {icSigNamedColorClass,        -100, icMaxEnumData, icMaxEnumData,
9098 	 	{icSigProfileDescriptionTag,
9099 		 icSigNamedColorTag,				/* Not strictly V3.4 */
9100 		 icSigMediaWhitePointTag,
9101 		 icSigCopyrightTag, icMaxEnumType}},
9102 
9103 	{icMaxEnumType,-1,icMaxEnumData, icMaxEnumData,		{icMaxEnumType}}
9104 };
9105 
9106 /* ------------------------------------------------------------- */
9107 /* Check that the ICC profile looks like it will be legal. */
9108 /* Return non-zero and set error string if not */
9109 static int check_icc_legal(
9110 	icc *p
9111 ) {
9112 	int i, j;
9113 	icProfileClassSignature  sig;
9114 	icColorSpaceSignature colsig;
9115 	icColorSpaceSignature pcssig;
9116 	int      	          dchans;
9117 
9118 	if (p->header == NULL) {
9119 		sprintf(p->err,"icc_check_legal: Header is missing");
9120 		return p->errc = 1;
9121 	}
9122 
9123 	sig = p->header->deviceClass;
9124 	colsig = p->header->colorSpace;
9125 	dchans = number_ColorSpaceSignature(colsig);
9126 	pcssig = p->header->pcs;
9127 
9128 	/* Find a matching table entry */
9129 	for (i = 0; tagchecktable[i].sig != icMaxEnumType; i++) {
9130 		if (    tagchecktable[i].sig   == sig
9131 		 && (   tagchecktable[i].chans == dchans	/* Exactly matches */
9132 		     || tagchecktable[i].chans == -dchans	/* Exactly matches, but can try next table */
9133 		     || tagchecktable[i].chans < -99)		/* Doesn't have to match or try next table */
9134 		 && (   tagchecktable[i].colsig == colsig
9135 		     || (tagchecktable[i].colsig == icSigPCSData
9136 		         && (colsig == icSigXYZData || colsig == icSigLabData))
9137 		     || tagchecktable[i].colsig == icMaxEnumData)
9138 		 && (   tagchecktable[i].pcssig == pcssig
9139 		     || (tagchecktable[i].pcssig == icSigPCSData
9140 		         && (pcssig == icSigXYZData || pcssig == icSigLabData))
9141 		     || tagchecktable[i].pcssig == icMaxEnumData)) {
9142 
9143 			/* Found entry, so now check that all the required tags are present. */
9144 			for (j = 0; tagchecktable[i].tags[j] != icMaxEnumType; j++) {
9145 				if (p->find_tag(p, tagchecktable[i].tags[j]) != 0) {	/* Not present! */
9146 					if (tagchecktable[i].chans == -200
9147 					 || tagchecktable[i].chans == -dchans) {	/* But can try next table */
9148 						break;
9149 					}
9150 					sprintf(p->err,"icc_check_legal: deviceClass %s is missing required tag %s",
9151 					               tag2str(sig), tag2str(tagchecktable[i].tags[j]));
9152 					return p->errc = 1;
9153 				}
9154 			}
9155 			if (tagchecktable[i].tags[j] == icMaxEnumType) {
9156 				break;		/* Fount all required tags */
9157 			}
9158 		}
9159 	}
9160 
9161 	/* According to the spec. if the deviceClass is:
9162 		an Abstract Class: both in and out color spaces should be PCS
9163 		an Link Class: both in and out color spaces can be any, and should
9164 		    be the input space of the first profile in the link, and the
9165 		    input space of the last profile in the link respectively.
9166 		a Named Class: in and out color spaces are not defined in the spec.
9167 		Input, Display, Output and ColorSpace Classes, input color
9168 		    space can be any, and the output space must be PCS.
9169 		~~ should check this here ???
9170 	*/
9171 
9172 	return 0;	/* Assume anything is ok */
9173 }
9174 
9175 
9176 /* read the object, return 0 on success, error code on fail */
9177 /* NOTE: this doesn't read the tag types, they should be read on demand. */
9178 static int icc_read(
9179 	icc *p,
9180 	icmFile *fp,			/* File to read from */
9181 	unsigned long of		/* File offset to read from */
9182 ) {
9183 	char tcbuf[4];			/* Tag count read buffer */
9184 	int i;
9185 	unsigned int len;
9186 	int er = 0;				/* Error code */
9187 
9188 	p->fp = fp;
9189 	p->of = of;
9190 	if (p->header == NULL) {
9191 		sprintf(p->err,"icc_read: No header defined");
9192 		return p->errc = 1;
9193 	}
9194 
9195 	/* Read the header */
9196 	if (p->header->read(p->header, 128, of)) {
9197 		return 1;
9198 	}
9199 
9200 	/* Read the tag count */
9201 	if (   p->fp->seek(p->fp, of + 128) != 0
9202 	    || p->fp->read(p->fp, tcbuf, 1, 4) != 4) {
9203 		sprintf(p->err,"icc_read: fseek() or fread() failed on tag count");
9204 		return p->errc = 1;
9205 	}
9206 
9207 	p->count = read_UInt32Number(tcbuf);		/* Tag count */
9208 	if (p->count > 0) {
9209 		char *bp, *buf;
9210 		if ((p->data = (icmTag *) p->al->malloc(p->al, p->count * sizeof(icmTag))) == NULL) {
9211 			sprintf(p->err,"icc_read: Tag table malloc() failed");
9212 			return p->errc = 2;
9213 		}
9214 
9215 		len = 4 + p->count * 12;
9216 		if ((buf = (char *) p->al->malloc(p->al, len)) == NULL) {
9217 			sprintf(p->err,"icc_read: Tag table read buffer malloc() failed");
9218 			p->al->free(p->al, p->data);
9219 			p->data = NULL;
9220 			return p->errc = 2;
9221 		}
9222 		if (   p->fp->seek(p->fp, of + 128) != 0
9223 		    || p->fp->read(p->fp, buf, 1, len) != len) {
9224 			sprintf(p->err,"icc_read: fseek() or fread() failed on tag table");
9225 			p->al->free(p->al, p->data);
9226 			p->data = NULL;
9227 			p->al->free(p->al, buf);
9228 			return p->errc = 1;
9229 		}
9230 
9231 		/* Fill in the tag table structure */
9232 		bp = buf+4;
9233 		for (i = 0; i < p->count; i++, bp += 12) {
9234 	    	p->data[i].sig = (icTagSignature)read_SInt32Number(bp + 0);
9235 	    	p->data[i].offset = read_UInt32Number(bp + 4);
9236 	    	p->data[i].size = read_UInt32Number(bp + 8);
9237 			if (   p->fp->seek(p->fp, of + p->data[i].offset) != 0
9238 			    || p->fp->read(p->fp, tcbuf, 1, 4) != 4) {
9239 				sprintf(p->err,"icc_read: fseek() or fread() failed on tag headers");
9240 				p->al->free(p->al, p->data);
9241 				p->data = NULL;
9242 				p->al->free(p->al, buf);
9243 				return p->errc = 1;
9244 			}
9245 	    	p->data[i].ttype = read_SInt32Number(tcbuf);		/* Tag type */
9246 	    	p->data[i].objp = NULL;							/* Read on demand */
9247 		}
9248 		p->al->free(p->al, buf);
9249 	}	/* p->count > 0 */
9250 
9251 	return er;
9252 }
9253 
9254 #define DO_ALIGN(val) (((val) + 3) & ~3)
9255 
9256 /* Return the total size needed for the profile */
9257 /* Return 0 on error. */
9258 static unsigned int icc_get_size(
9259 	icc *p
9260 ) {
9261 	unsigned int size = 0;
9262 	int i;
9263 
9264 	/* Check that the right tags etc. are present for a legal ICC profile */
9265 	if (check_icc_legal(p) != 0) {
9266 		return 0;
9267 	}
9268 
9269 	/* Compute the total size and tag element data offsets */
9270 	if (p->header == NULL) {
9271 		sprintf(p->err,"icc_get_size: No header defined");
9272 		p->errc = 1;
9273 		return 0;
9274 	}
9275 
9276 	size += p->header->get_size(p->header);
9277 
9278 	size = DO_ALIGN(size);
9279 	size += 4 + p->count * 12;	/* Tag table length */
9280 
9281 	/* Reset touched flag for each tag type */
9282 	for (i = 0; i < p->count; i++) {
9283 		if (p->data[i].objp == NULL) {
9284 			sprintf(p->err,"icc_get_size: Internal error - NULL tag element");
9285 			p->errc = 1;
9286 			return 0;
9287 		}
9288 		p->data[i].objp->touched = 0;
9289 	}
9290 	/* Get size for each tag type, skipping links */
9291 	for (i = 0; i < p->count; i++) {
9292 		if (p->data[i].objp->touched == 0) { /* Not alllowed for previously */
9293 			size = DO_ALIGN(size);
9294 			size += p->data[i].objp->get_size(p->data[i].objp);
9295 			p->data[i].objp->touched = 1;	/* Don't account for this again */
9296 		}
9297 	}
9298 
9299 	return size;	/* Total size needed */
9300 }
9301 
9302 /* Write the contents of the object. Return 0 on sucess, error code on failure */
9303 static int icc_write(
9304 	icc *p,
9305 	icmFile *fp,		/* File to write to */
9306 	unsigned long of	/* File offset to write to */
9307 ) {
9308 	char *bp, *buf;		/* Buffer to write to */
9309 	unsigned int len;
9310 	int rv = 0;
9311 	int i;
9312 	unsigned int size = 0;
9313 
9314 	/* Check that the right tags etc. are present for a legal ICC profile */
9315 	if ((rv = check_icc_legal(p)) != 0) {
9316 		return rv;
9317 	}
9318 
9319 	p->fp = fp;			/* Open file pointer */
9320 	p->of = of;			/* Offset of ICC profile */
9321 
9322 	/* Compute the total size and tag element data offsets */
9323 	if (p->header == NULL) {
9324 		sprintf(p->err,"icc_write: No header defined");
9325 		return p->errc = 1;
9326 	}
9327 
9328 	size += p->header->get_size(p->header);
9329 
9330 	len = 4 + p->count * 12;	/* Tag table length */
9331 	size = DO_ALIGN(size);
9332 	size += len;
9333 
9334 	/* Allocate memory buffer for tag table */
9335 	if ((buf = (char *) p->al->malloc(p->al, len)) == NULL) {
9336 		sprintf(p->err,"icc_write malloc() failed");
9337 		return p->errc = 2;
9338 	}
9339 	bp = buf;
9340 
9341     if ((rv = write_UInt32Number(p->count, bp)) != 0) {		/* Tag count */
9342 		sprintf(p->err,"icc_write: write_UInt32Number() failed on tag count");
9343 		p->al->free(p->al, buf);
9344 		return p->errc = rv;
9345 	}
9346 	bp += 4;
9347 	/* Reset touched flag for each tag type */
9348 	for (i = 0; i < p->count; i++) {
9349 		if (p->data[i].objp == NULL) {
9350 			sprintf(p->err,"icc_write: Internal error - NULL tag element");
9351 			p->al->free(p->al, buf);
9352 			return p->errc = 1;
9353 		}
9354 		p->data[i].objp->touched = 0;
9355 	}
9356 	/* Set the offset and size for each tag type */
9357 	for (i = 0; i < p->count; i++) {
9358 		if (p->data[i].objp->touched == 0) {	/* Allocate space for tag type */
9359 			size = DO_ALIGN(size);
9360 			p->data[i].offset = size;			/* Profile relative target */
9361 			p->data[i].size = p->data[i].objp->get_size(p->data[i].objp);
9362 			size += p->data[i].size;
9363 			p->data[i].objp->touched = 1;	/* Allocated space for it */
9364 		} else { /* must be linked - copy allocation */
9365 			int k;
9366 			for (k = 0; k < p->count; k++) {	/* Find linked tag */
9367 				if (p->data[k].objp == p->data[i].objp)
9368 					break;
9369 			}
9370 			if (k == p->count) {
9371 				sprintf(p->err,"icc_write: corrupted link");
9372 				return p->errc = 2;
9373 			}
9374 		    p->data[i].offset = p->data[k].offset;
9375 		    p->data[i].size   = p->data[k].size;
9376 		}
9377 		/* Write tag table entry for this tag */
9378 		if ((rv = write_SInt32Number((int)p->data[i].sig,bp + 0)) != 0) {
9379 			sprintf(p->err,"icc_write: write_SInt32Number() failed on tag signature");
9380 			p->al->free(p->al, buf);
9381 			return p->errc = rv;
9382 		}
9383 		if ((rv = write_UInt32Number(p->data[i].offset, bp + 4)) != 0) {
9384 			sprintf(p->err,"icc_write: write_UInt32Number() failed on tag offset");
9385 			p->al->free(p->al, buf);
9386 			return p->errc = rv;
9387 		}
9388 		if ((rv = write_UInt32Number(p->data[i].size, bp + 8)) != 0) {
9389 			sprintf(p->err,"icc_write: write_UInt32Number() failed on tag size");
9390 			p->al->free(p->al, buf);
9391 			return p->errc = rv;
9392 		}
9393 		bp += 12;
9394 	}
9395 	p->header->size = size;		/* Record total icc size */
9396 
9397 	/* Write the header */
9398 	if ((rv = p->header->write(p->header, of)) != 0) {
9399 		p->al->free(p->al, buf);
9400 		return rv;
9401 	}
9402 
9403 	/* Write the tag table */
9404 	if (   p->fp->seek(p->fp, of + 128) != 0
9405 	    || p->fp->write(p->fp, buf, 1, len) != len) {
9406 		sprintf(p->err,"icc_write: fseek() or fwrite() failed");
9407 		p->al->free(p->al, buf);
9408 		return p->errc = 1;
9409 	}
9410 	p->al->free(p->al, buf);
9411 
9412 	/* Write all the tag element data */
9413 	for (i = 0; i < p->count; i++) {	/* For all the tag element data */
9414 		if (p->data[i].objp->touched == 0)
9415 			continue;		/* Must be linked, and we've already written it */
9416 		if ((rv = p->data[i].objp->write(p->data[i].objp, of + p->data[i].offset)) != 0) {
9417 			return rv;
9418 		}
9419 		p->data[i].objp->touched = 0;	/* Written it */
9420 	}
9421 
9422 	if (p->fp->flush(p->fp) != 0) {
9423 		sprintf(p->err,"icc_write flush() failed");
9424 		p->al->free(p->al, buf);
9425 		return p->errc = 2;
9426 	}
9427 	return rv;
9428 }
9429 #undef DO_ALIGN
9430 
9431 /* Create and add a tag with the given signature. */
9432 /* Returns a pointer to the element object */
9433 /* Returns NULL if error - icc->errc will contain */
9434 /* 2 on system error, */
9435 /* 3 if unknown tag */
9436 /* NOTE: that we prevent tag duplication */
9437 static icmBase *icc_add_tag(
9438 	icc *p,
9439     icTagSignature sig,			/* Tag signature - may be unknown */
9440 	icTagTypeSignature  ttype	/* Tag type */
9441 ) {
9442 	icmBase *tp;
9443 	icmBase *nob;
9444 	int i, j, ok = 1;
9445 
9446 	/* Check that a known signature has an acceptable type */
9447 	for (i = 0; sigtypetable[i].sig != icMaxEnumType; i++) {
9448 		if (sigtypetable[i].sig == sig) {	/* recognized signature */
9449 			ok = 0;
9450 			for (j = 0; sigtypetable[i].ttypes[j] != icMaxEnumType; j++) {
9451 				if (sigtypetable[i].ttypes[j] == ttype)	/* recognized type */
9452 					ok = 1;
9453 			}
9454 			break;
9455 		}
9456 	}
9457 	if (!ok) {
9458 		sprintf(p->err,"icc_add_tag: wrong tag type for signature");
9459 		p->errc = 1;
9460 		return NULL;
9461 	}
9462 
9463 	/* Check that we know how to handle this type */
9464 	for (i = 0; typetable[i].ttype != icMaxEnumType; i++) {
9465 		if (typetable[i].ttype == ttype)
9466 			break;
9467 	}
9468 	if (typetable[i].ttype == icMaxEnumType) {
9469 		sprintf(p->err,"icc_add_tag: unsupported tag type");
9470 		p->errc = 1;
9471 		return NULL;
9472 	}
9473 
9474 	/* Check that this tag doesn't already exits */
9475 	/* (Perhaps we should simply replace it, rather than erroring ?) */
9476 	for (j = 0; j < p->count; j++) {
9477 		if (p->data[j].sig == sig) {
9478 			sprintf(p->err,"icc_add_tag: Already have tag '%s' in profile",tag2str(p->data[j].sig));
9479 			p->errc = 1;
9480 			return NULL;
9481 		}
9482 	}
9483 
9484 	/* Make space in tag table for new tag item */
9485 	if (p->data == NULL)
9486 		tp = p->al->malloc(p->al, (p->count+1) * sizeof(icmTag));
9487 	else
9488 		tp = p->al->realloc(p->al, (void *)p->data, (p->count+1) * sizeof(icmTag));
9489 	if (tp == NULL) {
9490 		sprintf(p->err,"icc_add_tag: Tag table realloc() failed");
9491 		p->errc = 2;
9492 		return NULL;
9493 	}
9494 	p->data = (icmTag *)tp;
9495 
9496 	/* Allocate the empty object */
9497 	if ((nob = typetable[i].new_obj(p)) == NULL)
9498 		return NULL;
9499 
9500 	/* Fill out our tag table entry */
9501     p->data[p->count].sig = sig;		/* The tag signature */
9502 	p->data[p->count].ttype = nob->ttype = ttype;	/* The tag type signature */
9503     p->data[p->count].offset = 0;		/* Unknown offset yet */
9504     p->data[p->count].size = 0;			/* Unknown size yet */
9505     p->data[p->count].objp = nob;		/* Empty object */
9506 	p->count++;
9507 
9508 	return nob;
9509 }
9510 
9511 /* Create and add a tag which is a link to an existing tag. */
9512 /* Returns a pointer to the element object */
9513 /* Returns NULL if error - icc->errc will contain */
9514 /* 3 if incompatible tag */
9515 /* NOTE: that we prevent tag duplication */
9516 static icmBase *icc_link_tag(
9517 	icc *p,
9518     icTagSignature sig,			/* Tag signature - may be unknown */
9519     icTagSignature ex_sig		/* Tag signature of tag to link to */
9520 ) {
9521 	icmBase *tp;
9522 	int i, j, exi, ok = 1;
9523 
9524 	/* Search for existing signature */
9525 	for (exi = 0; exi < p->count; exi++) {
9526 		if (p->data[exi].sig == ex_sig)		/* Found it */
9527 			break;
9528 	}
9529 	if (exi == p->count) {
9530 		sprintf(p->err,"icc_link_tag: Can't find existing tag '%s'",tag2str(ex_sig));
9531 		p->errc = 1;
9532 		return NULL;
9533 	}
9534 
9535     if (p->data[exi].objp == NULL) {
9536 		sprintf(p->err,"icc_link_tag: Existing tag '%s' isn't loaded",tag2str(ex_sig));
9537 		p->errc = 1;
9538 		return NULL;
9539 	}
9540 
9541 	/* Check that a known signature has an acceptable type */
9542 	for (i = 0; sigtypetable[i].sig != icMaxEnumType; i++) {
9543 		if (sigtypetable[i].sig == sig) {	/* recognized signature */
9544 			ok = 0;
9545 			for (j = 0; sigtypetable[i].ttypes[j] != icMaxEnumType; j++) {
9546 				if (sigtypetable[i].ttypes[j] == p->data[exi].ttype)	/* recognized type */
9547 					ok = 1;
9548 			}
9549 			break;
9550 		}
9551 	}
9552 	if (!ok) {
9553 		sprintf(p->err,"icc_link_tag: wrong tag type for signature");
9554 		p->errc = 1;
9555 		return NULL;
9556 	}
9557 
9558 	/* Check that this tag doesn't already exits */
9559 	for (j = 0; j < p->count; j++) {
9560 		if (p->data[j].sig == sig) {
9561 			sprintf(p->err,"icc_link_tag: Already have tag '%s' in profile",tag2str(p->data[j].sig));
9562 			p->errc = 1;
9563 			return NULL;
9564 		}
9565 	}
9566 
9567 	/* Make space in tag table for new tag item */
9568 	if (p->data == NULL)
9569 		tp = p->al->malloc(p->al, (p->count+1) * sizeof(icmTag));
9570 	else
9571 		tp = p->al->realloc(p->al, (void *)p->data, (p->count+1) * sizeof(icmTag));
9572 	if (tp == NULL) {
9573 		sprintf(p->err,"icc_link_tag: Tag table realloc() failed");
9574 		p->errc = 2;
9575 		return NULL;
9576 	}
9577 	p->data = (icmTag *)tp;
9578 
9579 	/* Fill out our tag table entry */
9580     p->data[p->count].sig  = sig;		/* The tag signature */
9581 	p->data[p->count].ttype  = p->data[exi].ttype;	/* The tag type signature */
9582     p->data[p->count].offset = p->data[exi].offset;	/* Same offset (may not be allocated yet) */
9583     p->data[p->count].size = p->data[exi].size;		/* Same size (may not be allocated yet) */
9584     p->data[p->count].objp = p->data[exi].objp;		/* Shared object */
9585 	p->data[exi].objp->refcount++;					/* Bump reference count on tag type */
9586 	p->count++;
9587 
9588 	return p->data[exi].objp;
9589 }
9590 
9591 /* Search for tag signature */
9592 /* return: */
9593 /* 0 if found */
9594 /* 1 if found but not handled type */
9595 /* 2 if not found */
9596 /* NOTE: doesn't set icc->errc or icc->err[] */
9597 /* NOTE: we don't handle tag duplication - you'll always get the first in the file. */
9598 static int icc_find_tag(
9599 	icc *p,
9600     icTagSignature sig			/* Tag signature - may be unknown */
9601 ) {
9602 	int i,j;
9603 
9604 	/* Search for signature */
9605 	for (i = 0; i < p->count; i++) {
9606 		if (p->data[i].sig == sig)		/* Found it */
9607 			break;
9608 	}
9609 	if (i == p->count)
9610 		return 2;
9611 
9612 	/* See if we can handle this type */
9613 	for (j = 0; typetable[j].ttype != icMaxEnumType; j++) {
9614 		if (typetable[j].ttype == p->data[i].ttype)
9615 			break;
9616 	}
9617 	if (typetable[j].ttype == icMaxEnumType)
9618 		return 1;
9619 
9620 	return 0;
9621 }
9622 
9623 /* Read the tag element data, and return a pointer to the object */
9624 /* Returns NULL if error - icc->errc will contain
9625 /* 1 if found but not handled type */
9626 /* 2 if not found */
9627 /* NOTE: we don't handle tag duplication - you'll always get the first in the file */
9628 static icmBase *icc_read_tag(
9629 	icc *p,
9630     icTagSignature sig			/* Tag signature - may be unknown */
9631 ) {
9632 	icmBase *nob;
9633 	int i,j,k;
9634 
9635 	/* Search for signature */
9636 	for (i = 0; i < p->count; i++) {
9637 		if (p->data[i].sig == sig)		/* Found it */
9638 			break;
9639 	}
9640 	if (i >= p->count) {
9641 		sprintf(p->err,"icc_read_tag: Tag '%s' not found",string_TagSignature(sig));
9642 		p->errc = 2;
9643 		return NULL;
9644 	}
9645 
9646 	/* See if it's already been read */
9647     if (p->data[i].objp != NULL) {
9648 		return p->data[i].objp;		/* Just return it */
9649 	}
9650 
9651 	/* See if this should be a link */
9652 	for (k = 0; k < p->count; k++) {
9653 		if (i == k)
9654 			continue;
9655 	    if (p->data[i].ttype  == p->data[k].ttype	/* Exact match and already read */
9656 	     && p->data[i].offset == p->data[k].offset
9657 	     && p->data[i].size   == p->data[k].size
9658 	     && p->data[k].objp != NULL)
9659 			break;
9660 	}
9661 	if (k < p->count) {		/* Make this a link */
9662 		p->data[i].objp = p->data[k].objp;
9663 		p->data[k].objp->refcount++;	/* Bump reference count */
9664 		return p->data[k].objp;			/* Done */
9665 	}
9666 
9667 	/* See if we can handle this type */
9668 	for (j = 0; typetable[j].ttype != icMaxEnumType; j++) {
9669 		if (typetable[j].ttype == p->data[i].ttype)
9670 			break;
9671 	}
9672 	if (typetable[j].ttype == icMaxEnumType) {
9673 		sprintf(p->err,"icc_read_tag: Unhandled tag type '%s'",string_TypeSignature(p->data[i].ttype));
9674 		p->errc = 1;
9675 		return NULL;
9676 	}
9677 
9678 	/* Creat and read in the object */
9679 	if ((nob = typetable[j].new_obj(p)) == NULL)
9680 		return NULL;
9681 	if ((nob->read(nob, p->data[i].size, p->of + p->data[i].offset)) != 0) {
9682 		nob->del(nob);		/* Failed, so destroy it */
9683 		return NULL;
9684 	}
9685     p->data[i].objp = nob;
9686 	return nob;
9687 }
9688 
9689 /* Rename a tag signature */
9690 static int icc_rename_tag(
9691 	icc *p,
9692     icTagSignature sig,			/* Existing Tag signature - may be unknown */
9693     icTagSignature sigNew		/* New Tag signature - may be unknown */
9694 ) {
9695 	int rv;
9696 	icmBase *nob;
9697 	int i, j, k, ok = 1;
9698 
9699 	/* Search for signature */
9700 	for (k = 0; k < p->count; k++) {
9701 		if (p->data[k].sig == sig)		/* Found it */
9702 			break;
9703 	}
9704 	if (k >= p->count) {
9705 		sprintf(p->err,"icc_rename_tag: Tag '%s' not found",string_TagSignature(sig));
9706 		return p->errc = 2;
9707 	}
9708 
9709 	/* Check that a known new signature has an acceptable type */
9710 	for (i = 0; sigtypetable[i].sig != icMaxEnumType; i++) {
9711 		if (sigtypetable[i].sig == sigNew) {	/* recognized signature */
9712 			ok = 0;
9713 			for (j = 0; sigtypetable[i].ttypes[j] != icMaxEnumType; j++) {
9714 				if (sigtypetable[i].ttypes[j] == p->data[k].ttype)	/* recognized type */
9715 					ok = 1;
9716 			}
9717 			break;
9718 		}
9719 	}
9720 
9721 	if (!ok) {
9722 		sprintf(p->err,"icc_rename_tag: wrong signature for tag type");
9723 		p->errc = 1;
9724 		return p->errc;
9725 	}
9726 
9727 	/* change its signature */
9728 	p->data[k].sig = sigNew;
9729 
9730 	return 0;
9731 }
9732 
9733 /* Unread the tag, and free the underlying tag type */
9734 /* if this was the last reference to it. */
9735 /* Returns non-zero on error: */
9736 /* tag not found - icc->errc will contain 2 */
9737 /* tag not read - icc->errc will contain 2 */
9738 static int icc_unread_tag(
9739 	icc *p,
9740     icTagSignature sig		/* Tag signature - may be unknown */
9741 ) {
9742 	int rv;
9743 	icmBase *nob;
9744 	int i;
9745 
9746 	/* Search for signature */
9747 	for (i = 0; i < p->count; i++) {
9748 		if (p->data[i].sig == sig)		/* Found it */
9749 			break;
9750 	}
9751 	if (i >= p->count) {
9752 		sprintf(p->err,"icc_unread_tag: Tag '%s' not found",string_TagSignature(sig));
9753 		return p->errc = 2;
9754 	}
9755 
9756 	/* See if it's been read */
9757     if (p->data[i].objp == NULL) {
9758 		sprintf(p->err,"icc_unread_tag: Tag '%s' not currently loaded",string_TagSignature(sig));
9759 		return p->errc = 2;
9760 	}
9761 
9762 	if (--(p->data[i].objp->refcount) == 0)			/* decrement reference count */
9763 			(p->data[i].objp->del)(p->data[i].objp);	/* Last reference */
9764   	p->data[i].objp = NULL;
9765 
9766 	return 0;
9767 }
9768 
9769 /* Delete the tag, and free the underlying tag type */
9770 /* if this was the last reference to it. */
9771 /* Returns non-zero on error: */
9772 /* tag not found - icc->errc will contain 2 */
9773 static int icc_delete_tag(
9774 	icc *p,
9775     icTagSignature sig		/* Tag signature - may be unknown */
9776 ) {
9777 	int rv;
9778 	icmBase *nob;
9779 	int i;
9780 
9781 	/* Search for signature */
9782 	for (i = 0; i < p->count; i++) {
9783 		if (p->data[i].sig == sig)		/* Found it */
9784 			break;
9785 	}
9786 	if (i >= p->count) {
9787 		sprintf(p->err,"icc_delete_tag: Tag '%s' not found",string_TagSignature(sig));
9788 		return p->errc = 2;
9789 	}
9790 
9791 	/* If it's been read into memory, decrement the reference count */
9792     if (p->data[i].objp != NULL) {
9793 		if (--(p->data[i].objp->refcount) == 0)			/* decrement reference count */
9794 			(p->data[i].objp->del)(p->data[i].objp);	/* Last reference */
9795   		p->data[i].objp = NULL;
9796 	}
9797 
9798 	/* Now remove it from the tag list */
9799 	for (; i < (p->count-1); i++)
9800 		p->data[i] = p->data[i+1];	/* Copy the structure down */
9801 
9802 	p->count--;		/* One less tag in list */
9803 
9804 	return 0;
9805 }
9806 
9807 
9808 /* Read all the tags into memory. */
9809 /* Returns non-zero on error. */
9810 static int icc_read_all_tags(
9811 	icc *p
9812 ) {
9813 	int i;
9814 
9815 	for (i = 0; i < p->count; i++) {	/* For all the tag element data */
9816 		icmBase *ob;
9817 		if ((ob = p->read_tag(p, p->data[i].sig)) == NULL) {
9818 			return p->errc;
9819 		}
9820 	}
9821 	return 0;
9822 }
9823 
9824 
9825 static void icc_dump(
9826 	icc *p,
9827 	FILE *op,		/* Output to dump to */
9828 	int   verb		/* Verbosity level */
9829 ) {
9830 	int i;
9831 	if (verb <= 0)
9832 		return;
9833 
9834 	fprintf(op,"icc:\n");
9835 
9836 	/* Dump the header */
9837 	if (p->header != NULL)
9838 		p->header->dump(p->header,op,verb);
9839 
9840 	/* Dump all the tag elements */
9841 	for (i = 0; i < p->count; i++) {	/* For all the tag element data */
9842 		icmBase *ob;
9843 		int tr;
9844 		fprintf(op,"tag %d:\n",i);
9845 		fprintf(op,"  sig      %s\n",tag2str(p->data[i].sig));
9846 		fprintf(op,"  type     %s\n",tag2str(p->data[i].ttype));
9847 		fprintf(op,"  offset   %d\n", p->data[i].offset);
9848 		fprintf(op,"  size     %d\n", p->data[i].size);
9849 		tr = 0;
9850 		if ((ob = p->data[i].objp) == NULL) {
9851 			/* The object is not loaded, so load it then free it */
9852 			if ((ob = p->read_tag(p, p->data[i].sig)) == NULL) {
9853 				fprintf(op,"Unable to read: %d, %s\n",p->errc,p->err);
9854 			}
9855 			tr = 1;
9856 		}
9857 		if (ob != NULL) {
9858 			/* fprintf(op,"  refcount %d\n", ob->refcount); */
9859 			ob->dump(ob,op,verb-1);
9860 
9861 			if (tr != 0) {		/* Cleanup if temporary */
9862 				ob->refcount--;
9863 				(ob->del)(ob);
9864 				p->data[i].objp = NULL;
9865 			}
9866 		}
9867 		fprintf(op,"\n");
9868 	}
9869 }
9870 
9871 static void icc_delete(
9872 	icc *p
9873 ) {
9874 	int i;
9875 	icmAlloc *al = p->al;
9876 	int del_al   = p->del_al;
9877 
9878 	/* Free up the header */
9879 	if (p->header != NULL)
9880 		(p->header->del)(p->header);
9881 
9882 	/* Free up the tag data objects */
9883 	for (i = 0; i < p->count; i++) {
9884 		if (p->data[i].objp != NULL) {
9885 			if (--(p->data[i].objp->refcount) == 0)	/* decrement reference count */
9886 				(p->data[i].objp->del)(p->data[i].objp);	/* Last reference */
9887 	  	  	p->data[i].objp = NULL;
9888 		}
9889 	}
9890 
9891 	/* Free tag table */
9892 	al->free(al, p->data);
9893 
9894 	/* This object */
9895 	al->free(al, p);
9896 
9897 	if (del_al)			/* We are responsible for deleting allocator */
9898 		al->del(al);
9899 }
9900 
9901 /* ================================================== */
9902 /* Lut Color normalizing and de-normalizing functions */
9903 
9904 /* As a rule, I am representing Lut in memory as values in machine form as real */
9905 /* numbers in the range 0.0 - 1.0. For many color spaces (ie. RGB, Gray, */
9906 /* hsv, hls, cmyk and other device coords), this is entirely appropriate. */
9907 /* For the PCS though, this is not correct, since (I assume!) the binary */
9908 /* representation will be consistent with the encoding in Annex A, page 74 */
9909 /* of the standard. Note that the standard doesn't specify the encoding of */
9910 /* many color spaces (ie. Yuv, Yxy etc.), and is unclear about PCS. */
9911 
9912 /* The following functions convert to and from the PCS spaces (XYZ or Lab) */
9913 /* and the real Lut input/output values. These are used to convert real color */
9914 /* space values into/out of the raw lut 0.0-1.0 representation. */
9915 
9916 /* This is used internally to support the Lut->lookup() function, */
9917 /* and can also be used by someone writing a Lut based profile to determine */
9918 /* the colorspace range that the input lut indexes cover, as well */
9919 /* as processing the output luts values into normalized form ready */
9920 /* for writing. */
9921 
9922 /* These functions should be accessed by calling icc.getNormFuncs() */
9923 
9924 /* - - - - - - - - - - - - - - - - */
9925 /* According to 6.5.5 and 6.5.6 of the spec., */
9926 /* XYZ index values are represented the same as their table */
9927 /* values, ie. as a u1.15 representation, with a value */
9928 /* range from 0.0 ->  1.999969482422 */
9929 
9930 /* Convert Lut index/value to XYZ coord. */
9931 static void Lut_Lut2XYZ(double *out, double *in) {
9932 	out[0] = in[0] * (1.0 + 32767.0/32768); /* X */
9933 	out[1] = in[1] * (1.0 + 32767.0/32768); /* Y */
9934 	out[2] = in[2] * (1.0 + 32767.0/32768); /* Z */
9935 }
9936 
9937 /* Convert XYZ coord to Lut index/value. */
9938 static void Lut_XYZ2Lut(double *out, double *in) {
9939 	out[0] = in[0] * (1.0/(1.0 + 32767.0/32768));
9940 	out[1] = in[1] * (1.0/(1.0 + 32767.0/32768));
9941 	out[2] = in[2] * (1.0/(1.0 + 32767.0/32768));
9942 }
9943 
9944 /* - - - - - - - - - - - - - - - - */
9945 /* Convert Lab to Lut numbers */
9946 /* Annex A specifies 8 and 16 bit encoding, but is */
9947 /* silent on the Lut index normalization. */
9948 /* Following Michael Bourgoin's 1998 SIGGRAPH course comment on this, */
9949 /* we assume here that the index encoding is the same as the */
9950 /* value encoding. */
9951 
9952 /* Convert Lut16 table index/value to Lab */
9953 static void Lut_Lut2Lab16(double *out, double *in) {
9954 	out[0] = in[0] * (100.0 * 65535.0)/65280.0;			/* L */
9955 	out[1] = (in[1] * (255.0 * 65535.0)/65280) - 128.0;	/* a */
9956 	out[2] = (in[2] * (255.0 * 65535.0)/65280) - 128.0;	/* b */
9957 }
9958 
9959 /* Convert Lab to Lut16 table index/value */
9960 static void Lut_Lab2Lut16(double *out, double *in) {
9961 	out[0] = in[0] * 65280.0/(100.0 * 65535.0);				/* L */
9962 	out[1] = (in[1] + 128.0) * 65280.0/(255.0 * 65535.0);	/* a */
9963 	out[2] = (in[2] + 128.0) * 65280.0/(255.0 * 65535.0);	/* b */
9964 }
9965 
9966 /* Convert Lut8 table index/value to Lab */
9967 static void Lut_Lut2Lab8(double *out, double *in) {
9968 	out[0] = in[0] * 100.0;				/* L */
9969 	out[1] = (in[1] * 255.0) - 128.0;	/* a */
9970 	out[2] = (in[2] * 255.0) - 128.0;	/* b */
9971 }
9972 
9973 /* Convert Lab to Lut8 table index/value */
9974 static void Lut_Lab2Lut8(double *out, double *in) {
9975 	out[0] = in[0] * 1.0/100.0;				/* L */
9976 	out[1] = (in[1] + 128.0) * 1.0/255.0;	/* a */
9977 	out[2] = (in[2] + 128.0) * 1.0/255.0;	/* b */
9978 }
9979 
9980 /* - - - - - - - - - - - - - - - - */
9981 /* Convert Luv to Lut number */
9982 /* This data normalization is taken from Apples */
9983 /* Colorsync specification. */
9984 /* As per other color spaces, we assume that the index */
9985 /* normalization is the same as the data normalization. */
9986 
9987 /* Convert Lut table index/value to Luv */
9988 static void Lut_Lut2Luv(double *out, double *in) {
9989 	out[0] = in[0] * 100.0;						/* L */
9990 	out[1] = (in[1] * 65535.0/256.0) - 128.0;	/* u */
9991 	out[2] = (in[2] * 65535.0/256.0) - 128.0;	/* v */
9992 }
9993 
9994 /* Convert Luv to Lut table index/value */
9995 static void Lut_Luv2Lut(double *out, double *in) {
9996 	out[0] = in[0] * 1.0/100.0;					/* L */
9997 	out[1] = (in[1] + 128.0) * 256.0/65535.0;	/* u */
9998 	out[2] = (in[2] + 128.0) * 256.0/65535.0;	/* v */
9999 }
10000 
10001 /* - - - - - - - - - - - - - - - - */
10002 /* Default N component conversions */
10003 static void Lut_N(double *out, double *in, int nc) {
10004 	for (--nc; nc >= 0; nc--)
10005 		out[nc] = in[nc];
10006 }
10007 
10008 /* 1 */
10009 static void Lut_1(double *out, double *in) {
10010 	out[0] = in[0];
10011 }
10012 
10013 /* 2 */
10014 static void Lut_2(double *out, double *in) {
10015 	out[0] = in[0];
10016 	out[1] = in[1];
10017 }
10018 
10019 /* 3 */
10020 static void Lut_3(double *out, double *in) {
10021 	out[0] = in[0];
10022 	out[1] = in[1];
10023 	out[2] = in[2];
10024 }
10025 
10026 /* 4 */
10027 static void Lut_4(double *out, double *in) {
10028 	out[0] = in[0];
10029 	out[1] = in[1];
10030 	out[2] = in[2];
10031 	out[3] = in[3];
10032 }
10033 
10034 /* 5 */
10035 static void Lut_5(double *out, double *in) {
10036 	out[0] = in[0];
10037 	out[1] = in[1];
10038 	out[2] = in[2];
10039 	out[3] = in[3];
10040 	out[4] = in[4];
10041 }
10042 
10043 /* 6 */
10044 static void Lut_6(double *out, double *in) {
10045 	out[0] = in[0];
10046 	out[1] = in[1];
10047 	out[2] = in[2];
10048 	out[3] = in[3];
10049 	out[4] = in[4];
10050 	out[5] = in[5];
10051 }
10052 
10053 /* 7 */
10054 static void Lut_7(double *out, double *in) {
10055 	Lut_N(out, in, 7);
10056 }
10057 
10058 /* 8 */
10059 static void Lut_8(double *out, double *in) {
10060 	Lut_N(out, in, 8);
10061 }
10062 
10063 /* 9 */
10064 static void Lut_9(double *out, double *in) {
10065 	Lut_N(out, in, 9);
10066 }
10067 
10068 /* 10 */
10069 static void Lut_10(double *out, double *in) {
10070 	Lut_N(out, in, 10);
10071 }
10072 
10073 /* 11 */
10074 static void Lut_11(double *out, double *in) {
10075 	Lut_N(out, in, 11);
10076 }
10077 
10078 /* 12 */
10079 static void Lut_12(double *out, double *in) {
10080 	Lut_N(out, in, 12);
10081 }
10082 
10083 /* 13 */
10084 static void Lut_13(double *out, double *in) {
10085 	Lut_N(out, in, 13);
10086 }
10087 
10088 /* 14 */
10089 static void Lut_14(double *out, double *in) {
10090 	Lut_N(out, in, 14);
10091 }
10092 
10093 /* 15 */
10094 static void Lut_15(double *out, double *in) {
10095 	Lut_N(out, in, 15);
10096 }
10097 
10098 /* Function table - match conversions to color spaces. */
10099 /* Anything not here, we don't know how to convert. */
10100 /* (ie. YCbCr) */
10101 static struct {
10102 	icColorSpaceSignature csig;
10103 	void (*fromLut8)(double *out, double *in);		/* 8  bit from Lut index/entry */
10104 	void (*fromLut16)(double *out, double *in);		/* 16 bit from Lut index/entry */
10105 	void (*toLut8)(double *out, double *in);		/* 8  bit to Lut index/entry */
10106 	void (*toLut16)(double *out, double *in);		/* 16 bit to Lut index/entry */
10107 } colnormtable[] = {
10108 	{icSigXYZData,     NULL,         Lut_Lut2XYZ,   NULL,         Lut_XYZ2Lut },
10109 	{icSigLabData,     Lut_Lut2Lab8, Lut_Lut2Lab16, Lut_Lab2Lut8, Lut_Lab2Lut16 },
10110 	{icSigLuvData,     Lut_Lut2Luv,  Lut_Lut2Luv,   Lut_Luv2Lut,  Lut_Luv2Lut },
10111 	{icSigYxyData,     Lut_3,        Lut_3,         Lut_3,        Lut_3 },
10112 	{icSigRgbData,     Lut_3,        Lut_3,         Lut_3,        Lut_3 },
10113 	{icSigGrayData,    Lut_1,        Lut_1,         Lut_1,        Lut_1 },
10114 	{icSigHsvData,     Lut_3,        Lut_3,         Lut_3,        Lut_3 },
10115 	{icSigHlsData,     Lut_3,        Lut_3,         Lut_3,        Lut_3 },
10116 	{icSigCmykData,    Lut_4,        Lut_4,         Lut_4,        Lut_4 },
10117 	{icSigCmyData,     Lut_3,        Lut_3,         Lut_3,        Lut_3 },
10118 	{icSigMch6Data,    Lut_6,        Lut_6,         Lut_6,        Lut_6 },
10119 	{icSig2colorData,  Lut_2,        Lut_2,         Lut_2,        Lut_2 },
10120 	{icSig3colorData,  Lut_3,        Lut_3,         Lut_3,        Lut_3 },
10121 	{icSig4colorData,  Lut_4,        Lut_4,         Lut_4,        Lut_4 },
10122 	{icSig5colorData,  Lut_5,        Lut_5,         Lut_5,        Lut_5 },
10123 	{icSig6colorData,  Lut_6,        Lut_6,         Lut_6,        Lut_6 },
10124 	{icSig7colorData,  Lut_7,        Lut_7,         Lut_7,        Lut_7 },
10125 	{icSig8colorData,  Lut_8,        Lut_8,         Lut_8,        Lut_8 },
10126 	{icSig9colorData,  Lut_9,        Lut_9,         Lut_9,        Lut_9 },
10127 	{icSig10colorData, Lut_10,       Lut_10,        Lut_10,       Lut_10 },
10128 	{icSig11colorData, Lut_11,       Lut_11,        Lut_11,       Lut_11 },
10129 	{icSig12colorData, Lut_12,       Lut_12,        Lut_12,       Lut_12 },
10130 	{icSig13colorData, Lut_13,       Lut_13,        Lut_13,       Lut_13 },
10131 	{icSig14colorData, Lut_14,       Lut_14,        Lut_14,       Lut_14 },
10132 	{icSig15colorData, Lut_15,       Lut_15,        Lut_15,       Lut_15 },
10133 	{icMaxEnumData,    NULL,         NULL,          NULL,         NULL   }
10134 };
10135 
10136 /* Find appropriate conversion functions */
10137 /* given the color space and Lut type */
10138 /* Return 0 on success, 1 on match failure */
10139 /* NOTE: doesn't set error value, message etc.! */
10140 static int getNormFunc(
10141 	icColorSpaceSignature csig,
10142 	icTagTypeSignature    tagSig,
10143 	icmNormFlag           flag,
10144 	void               (**nfunc)(double *out, double *in)
10145 ) {
10146 	int i;
10147 	for (i = 0; colnormtable[i].csig != icMaxEnumData; i++) {
10148 		if (colnormtable[i].csig == csig)
10149 			break;	/* Found it */
10150 	}
10151 	if (colnormtable[i].csig == icMaxEnumData) {	/* Oops */
10152 		*nfunc   = NULL;
10153 		return 1;
10154 	}
10155 
10156 	if (flag == icmFromLuti || flag == icmFromLutv) {	/* Table index/value decoding functions */
10157 		if (tagSig == icSigLut8Type) {
10158 			*nfunc = colnormtable[i].fromLut8;
10159 			return 0;
10160 		} else if (tagSig == icSigLut16Type) {
10161 			*nfunc = colnormtable[i].fromLut16;
10162 			return 0;
10163 		} else {
10164 			*nfunc   = NULL;
10165 			return 1;
10166 		}
10167 	} else if (flag == icmToLuti || flag == icmToLutv) {	/* Table index/value encoding functions */
10168 		if (tagSig == icSigLut8Type) {
10169 			*nfunc = colnormtable[i].toLut8;
10170 			return 0;
10171 		} else if (tagSig == icSigLut16Type) {
10172 			*nfunc = colnormtable[i].toLut16;
10173 			return 0;
10174 		} else {
10175 			*nfunc   = NULL;
10176 			return 1;
10177 		}
10178 	} else {
10179 		*nfunc   = NULL;
10180 		return 1;
10181 	}
10182 	return 0;
10183 }
10184 
10185 /* - - - - - - - - - - - - - - - - - - - - - - - - */
10186 
10187 /*
10188 	Matrix Inversion
10189 	by Richard Carling
10190 	from "Graphics Gems", Academic Press, 1990
10191 */
10192 
10193 /*
10194  *   adjoint( original_matrix, inverse_matrix )
10195  *
10196  *     calculate the adjoint of a 3x3 matrix
10197  *
10198  *      Let  a   denote the minor determinant of matrix A obtained by
10199  *           ij
10200  *
10201  *      deleting the ith row and jth column from A.
10202  *
10203  *                    i+j
10204  *     Let  b   = (-1)    a
10205  *          ij            ji
10206  *
10207  *    The matrix B = (b  ) is the adjoint of A
10208  *                     ij
10209  */
10210 
10211 #define det2x2(a, b, c, d) (a * d - b * c)
10212 
10213 void adjoint(
10214 double out[3][3],
10215 double in[3][3]
10216 ) {
10217     double a1, a2, a3, b1, b2, b3, c1, c2, c3;
10218 
10219     /* assign to individual variable names to aid  */
10220     /* selecting correct values  */
10221 
10222 	a1 = in[0][0]; b1 = in[0][1]; c1 = in[0][2];
10223 	a2 = in[1][0]; b2 = in[1][1]; c2 = in[1][2];
10224 	a3 = in[2][0]; b3 = in[2][1]; c3 = in[2][2];
10225 
10226     /* row column labeling reversed since we transpose rows & columns */
10227 
10228     out[0][0]  =   det2x2(b2, b3, c2, c3);
10229     out[1][0]  = - det2x2(a2, a3, c2, c3);
10230     out[2][0]  =   det2x2(a2, a3, b2, b3);
10231 
10232     out[0][1]  = - det2x2(b1, b3, c1, c3);
10233     out[1][1]  =   det2x2(a1, a3, c1, c3);
10234     out[2][1]  = - det2x2(a1, a3, b1, b3);
10235 
10236     out[0][2]  =   det2x2(b1, b2, c1, c2);
10237     out[1][2]  = - det2x2(a1, a2, c1, c2);
10238     out[2][2]  =   det2x2(a1, a2, b1, b2);
10239 }
10240 
10241 /*
10242  * double = det3x3(  a1, a2, a3, b1, b2, b3, c1, c2, c3 )
10243  *
10244  * calculate the determinant of a 3x3 matrix
10245  * in the form
10246  *
10247  *     | a1,  b1,  c1 |
10248  *     | a2,  b2,  c2 |
10249  *     | a3,  b3,  c3 |
10250  */
10251 
10252 double det3x3(double in[3][3]) {
10253     double a1, a2, a3, b1, b2, b3, c1, c2, c3;
10254     double ans;
10255 
10256 	a1 = in[0][0]; b1 = in[0][1]; c1 = in[0][2];
10257 	a2 = in[1][0]; b2 = in[1][1]; c2 = in[1][2];
10258 	a3 = in[2][0]; b3 = in[2][1]; c3 = in[2][2];
10259 
10260     ans = a1 * det2x2(b2, b3, c2, c3)
10261         - b1 * det2x2(a2, a3, c2, c3)
10262         + c1 * det2x2(a2, a3, b2, b3);
10263     return ans;
10264 }
10265 
10266 #define SMALL_NUMBER	1.e-8
10267 /*
10268  *   inverse( original_matrix, inverse_matrix )
10269  *
10270  *    calculate the inverse of a 4x4 matrix
10271  *
10272  *     -1
10273  *     A  = ___1__ adjoint A
10274  *         det A
10275  */
10276 
10277 /* Return non-zero if not invertable */
10278 int inverse3x3(
10279 double out[3][3],
10280 double in[3][3]
10281 ) {
10282     int i, j;
10283     double det;
10284 
10285     /*  calculate the 3x3 determinant
10286      *  if the determinant is zero,
10287      *  then the inverse matrix is not unique.
10288      */
10289     det = det3x3(in);
10290 
10291     if ( fabs(det) < SMALL_NUMBER)
10292         return 1;
10293 
10294     /* calculate the adjoint matrix */
10295     adjoint(out, in);
10296 
10297     /* scale the adjoint matrix to get the inverse */
10298     for (i = 0; i < 3; i++)
10299         for(j = 0; j < 3; j++)
10300 		    out[i][j] /= det;
10301 	return 0;
10302 }
10303 
10304 /* - - - - - - - - - - - - - - - - - - - - - - - - */
10305 
10306 /* Multuply XYZ array by 3x3 transform matrix */
10307 static void icmMulBy3x3(double out[3], double mat[3][3], double in[3]) {
10308 	double tt[3];
10309 
10310 	tt[0] = mat[0][0] * in[0] + mat[0][1] * in[1] + mat[0][2] * in[2];
10311 	tt[1] = mat[1][0] * in[0] + mat[1][1] * in[1] + mat[1][2] * in[2];
10312 	tt[2] = mat[2][0] * in[0] + mat[2][1] * in[1] + mat[2][2] * in[2];
10313 
10314 	out[0] = tt[0];
10315 	out[1] = tt[1];
10316 	out[2] = tt[2];
10317 }
10318 
10319 /* - - - - - - - - - - - - - - - - - - - - - - - - */
10320 /* CIE XYZ to perceptual Lab */
10321 void
10322 icmXYZ2Lab(icmXYZNumber *w, double *out, double *in) {
10323 	double X = in[0], Y = in[1], Z = in[2];
10324 	double x,y,z,fx,fy,fz;
10325 	double L;
10326 
10327 	x = X/w->X;
10328 	if (x > 0.008856451586)
10329 		fx = pow(x,1.0/3.0);
10330 	else
10331 		fx = 7.787036979 * x + 16.0/116.0;
10332 
10333 	y = Y/w->Y;
10334 	if (y > 0.008856451586) {
10335 		fy = pow(y,1.0/3.0);
10336 		L = 116.0 * fy - 16.0;
10337 	} else {
10338 		fy = 7.787036979 * y + 16.0/116.0;
10339 		L = 903.2963058 * y;
10340 	}
10341 
10342 	z = Z/w->Z;
10343 	if (z > 0.008856451586)
10344 		fz = pow(z,1.0/3.0);
10345 	else
10346 		fz = 7.787036979 * z + 16.0/116.0;
10347 
10348 	out[0] = L;
10349 	out[1] = 500.0 * (fx - fy);
10350 	out[2] = 200.0 * (fy - fz);
10351 }
10352 
10353 /* Perceptual Lab to CIE XYZ */
10354 void
10355 icmLab2XYZ(icmXYZNumber *w, double *out, double *in) {
10356 	double L = in[0], a = in[1], b = in[2];
10357 	double x,y,z,fx,fy,fz;
10358 
10359 	if (L > 8.0) {
10360 		fy = (L + 16.0)/116.0;
10361 		y = pow(fy,3.0);
10362 	} else {
10363 		y = L/903.2963058;
10364 		fy = 7.787036979 * y + 16.0/116.0;
10365 	}
10366 
10367 	fx = a/500.0 + fy;
10368 	if (fx > 24.0/116.0)
10369 		x = pow(fx,3.0);
10370 	else
10371 		x = (fx - 16.0/116.0)/7.787036979;
10372 
10373 	fz = fy - b/200.0;
10374 	if (fz > 24.0/116.0)
10375 		z = pow(fz,3.0);
10376 	else
10377 		z = (fz - 16.0/116.0)/7.787036979;
10378 
10379 	out[0] = x * w->X;
10380 	out[1] = y * w->Y;
10381 	out[2] = z * w->Z;
10382 }
10383 
10384 /* available D50 Illuminant */
10385 icmXYZNumber icmD50 = { 		/* Profile illuminant - D50 */
10386     0.9642, 1.0000, 0.8249
10387 };
10388 
10389 /* Default black point */
10390 icmXYZNumber icmBlack = {
10391     0.0000, 0.0000, 0.0000
10392 };
10393 
10394 /* - - - - - - - - - - - - - - - - - - - - - - - - */
10395 
10396 /* Multiply one 3x3 with another */
10397 static void mul3x3(double dst[3][3], double src[3][3]) {
10398 	int i, j, k;
10399 	double td[3][3];		/* Temporary dest */
10400 
10401 	for (j = 0; j < 3; j++) {
10402 		for (i = 0; i < 3; i++) {
10403 			double tt = 0.0;
10404 			for (k = 0; k < 3; k++)
10405 				tt += src[j][k] * dst[k][i];
10406 			td[j][i] = tt;
10407 		}
10408 	}
10409 
10410 	/* Copy result out */
10411 	for (j = 0; j < 3; j++)
10412 		for (i = 0; i < 3; i++)
10413 			dst[j][i] = td[j][i];
10414 }
10415 
10416 /* Chrmatic Adaption transform utility */
10417 /* Return a 3x3 chromatic adaption matrix */
10418 void icmChromAdaptMatrix(
10419 	int flags,
10420 	icmXYZNumber d_wp,		/* Destination white point */
10421 	icmXYZNumber s_wp,		/* Source white point */
10422 	double mat[3][3]		/* Destination matrix */
10423 ) {
10424 	double dst[3], src[3];			/* Source & destination white points */
10425 	double vkmat[3][3];				/* Von Kries matrix */
10426 	double bradford[3][3] = {		/* Bradford cone space matrix */
10427 		{  0.8951,  0.2664, -0.1614 },
10428 		{ -0.7502,  1.7135,  0.0367 },
10429 		{  0.0389, -0.0685,  1.0296 }
10430 	};
10431 	double ibradford[3][3];			/* Inverse Bradford */
10432 
10433 	/* Set initial matrix to unity */
10434 	if (!(flags & ICM_CAM_MULMATRIX)) {
10435 		mat[0][0] = mat[1][1] = mat[2][2] = 1.0;
10436 		mat[0][1] = mat[0][2] = 0.0;
10437 		mat[1][0] = mat[1][2] = 0.0;
10438 		mat[2][0] = mat[2][1] = 0.0;
10439 	}
10440 
10441 	icmXYZ2Ary(src, s_wp);
10442 	icmXYZ2Ary(dst, d_wp);
10443 
10444 	if (flags & ICM_CAM_BRADFORD) {
10445 		icmMulBy3x3(src, bradford, src);
10446 		icmMulBy3x3(dst, bradford, dst);
10447 	}
10448 
10449 	/* Setup the Von Kries white point adaption matrix */
10450 	vkmat[0][0] = dst[0]/src[0];
10451 	vkmat[1][1] = dst[1]/src[1];
10452 	vkmat[2][2] = dst[2]/src[2];
10453 	vkmat[0][1] = vkmat[0][2] = 0.0;
10454 	vkmat[1][0] = vkmat[1][2] = 0.0;
10455 	vkmat[2][0] = vkmat[2][1] = 0.0;
10456 
10457 	/* Transform to Bradford space if requested */
10458 	if (flags & ICM_CAM_BRADFORD) {
10459 		mul3x3(mat, bradford);
10460 	}
10461 
10462 	/* Apply chromatic adaption */
10463 	mul3x3(mat, vkmat);
10464 
10465 	/* Transform from Bradford space */
10466 	if (flags & ICM_CAM_BRADFORD) {
10467 		inverse3x3(ibradford, bradford);
10468 		mul3x3(mat, ibradford);
10469 	}
10470 
10471 	/* We're done */
10472 }
10473 
10474 /* - - - - - - - - - - - - - - - - - - - - - - - - */
10475 
10476 /* Return information about the native lut in/out colorspaces. */
10477 /* Any pointer may be NULL if value is not to be returned */
10478 static void
10479 icmLutSpaces(
10480 	struct _icmLuBase *p,			/* This */
10481 	icColorSpaceSignature *ins,		/* Return Native input color space */
10482 	int *inn,						/* Return number of input components */
10483 	icColorSpaceSignature *outs,	/* Return Native output color space */
10484 	int *outn						/* Return number of output components */
10485 ) {
10486 	if (ins != NULL)
10487 		*ins = p->inSpace;
10488 	if (inn != NULL)
10489 		*inn = (int)number_ColorSpaceSignature(p->inSpace);
10490 
10491 	if (outs != NULL)
10492 		*outs = p->outSpace;
10493 	if (outn != NULL)
10494 		*outn = (int)number_ColorSpaceSignature(p->outSpace);
10495 }
10496 
10497 /* Return information about the effective lookup in/out colorspaces, */
10498 /* including allowance for PCS overide. */
10499 /* Any pointer may be NULL if value is not to be returned */
10500 static void
10501 icmLuSpaces(
10502 	struct _icmLuBase *p,			/* This */
10503 	icColorSpaceSignature *ins,		/* Return effective input color space */
10504 	int *inn,						/* Return number of input components */
10505 	icColorSpaceSignature *outs,	/* Return effective output color space */
10506 	int *outn,						/* Return number of output components */
10507 	icmLuAlgType *alg,				/* Return type of lookup algorithm used */
10508     icRenderingIntent *intt,		/* Return the intent being implented */
10509     icmLookupFunc *fnc,				/* Return the profile function being implimented */
10510 	icColorSpaceSignature *pcs		/* Return the profile effective PCS */
10511 ) {
10512 	if (ins != NULL)
10513 		*ins = p->e_inSpace;
10514 	if (inn != NULL)
10515 		*inn = (int)number_ColorSpaceSignature(p->e_inSpace);
10516 
10517 	if (outs != NULL)
10518 		*outs = p->e_outSpace;
10519 	if (outn != NULL)
10520 		*outn = (int)number_ColorSpaceSignature(p->e_outSpace);
10521 
10522 	if (alg != NULL)
10523 		*alg = p->ttype;
10524 
10525     if (intt != NULL)
10526 		*intt = p->intent;
10527 
10528 	if (fnc != NULL)
10529 		*fnc = p->function;
10530 
10531 	if (pcs != NULL)
10532 		*pcs = p->e_pcs;
10533 }
10534 
10535 /* Return the media white and black points in XYZ space. */
10536 /* Note that if not in the icc, the black point will be returned as 0, 0, 0 */
10537 /* Any pointer may be NULL if value is not to be returned */
10538 static void icmLuWh_bk_points(
10539 struct _icmLuBase *p,
10540 icmXYZNumber *wht,
10541 icmXYZNumber *blk
10542 ) {
10543 	if (wht != NULL)
10544 		*wht = p->whitePoint;	/* Structure copy */
10545 
10546 	if (blk != NULL)
10547 		*blk = p->blackPoint;	/* Structure copy */
10548 }
10549 
10550 /* - - - - - - - - - - - - - - - - - - - - - - - - */
10551 /* Forward and Backward Monochrome type conversion */
10552 /* Return 0 on success, 1 if clipping occured, 2 on other error */
10553 
10554 /* Individual components of Fwd conversion: */
10555 
10556 /* Actual device to linearised device */
10557 static int
10558 icmLuMonoFwd_curve (
10559 icmLuMono *p,		/* This */
10560 double *out,		/* Vector of output values */
10561 double *in			/* Vector of input values */
10562 ) {
10563 	icc *icp = p->icp;
10564 	int rv = 0;
10565 
10566 	/* Translate from device to PCS scale */
10567 	if ((rv |= p->grayCurve->lookup_fwd(p->grayCurve,&out[0],&in[0])) > 1) {
10568 		sprintf(icp->err,"icc_lookup: Curve->lookup_fwd() failed");
10569 		icp->errc = rv;
10570 		return 2;
10571 	}
10572 
10573 	return rv;
10574 }
10575 
10576 /* Linearised device to relative PCS */
10577 static int
10578 icmLuMonoFwd_map (
10579 icmLuMono *p,		/* This */
10580 double *out,		/* Vector of output values (native space) */
10581 double *in			/* Vector of input values (native space) */
10582 ) {
10583 	int rv = 0;
10584 	double Y = in[0];		/* In case out == in */
10585 
10586 	out[0] = p->pcswht.X;
10587 	out[1] = p->pcswht.Y;
10588 	out[2] = p->pcswht.Z;
10589 	if (p->outSpace == icSigLabData)
10590 		icmXYZ2Lab(&p->pcswht, out, out);	/* in Lab */
10591 
10592 	/* Scale linearized device level to PCS white */
10593 	out[0] *= Y;
10594 	out[1] *= Y;
10595 	out[2] *= Y;
10596 
10597 	return rv;
10598 }
10599 
10600 /* relative PCS to absolute PCS (if required) */
10601 static int
10602 icmLuMonoFwd_abs (	/* Abs comes last in Fwd conversion */
10603 icmLuMono *p,		/* This */
10604 double *out,		/* Vector of output values in Effective PCS */
10605 double *in			/* Vector of input values in Native PCS */
10606 ) {
10607 	int rv = 0;
10608 
10609 	if (out != in) {
10610 		int i;
10611 		for (i = 0; i < 3; i++)		/* Don't alter input values */
10612 			out[i] = in[i];
10613 	}
10614 
10615 	/* Do absolute conversion */
10616 	if (p->intent == icAbsoluteColorimetric) {
10617 
10618 		if (p->outSpace == icSigLabData) 	/* Convert L to Y */
10619 			icmLab2XYZ(&p->pcswht, out, out);
10620 
10621 		/* Convert from Relative to Absolute colorometric */
10622 		icmMulBy3x3(out, p->toAbs, out);
10623 
10624 		if (p->e_outSpace == icSigLabData)
10625 			icmXYZ2Lab(&p->pcswht, out, out);
10626 
10627 	} else {
10628 
10629 		/* Convert from Native to Effective output space */
10630 		if (p->outSpace == icSigLabData && p->e_outSpace == icSigXYZData)
10631 			icmLab2XYZ(&p->pcswht, out, out);
10632 		else if (p->outSpace == icSigXYZData && p->e_outSpace == icSigLabData)
10633 			icmXYZ2Lab(&p->pcswht, out, out);
10634 	}
10635 
10636 	return rv;
10637 }
10638 
10639 
10640 /* Overall Fwd conversion routine */
10641 static int
10642 icmLuMonoFwd_lookup (
10643 icmLuBase *pp,		/* This */
10644 double *out,		/* Vector of output values */
10645 double *in			/* Input value */
10646 ) {
10647 	int rv = 0;
10648 	icmLuMono *p = (icmLuMono *)pp;
10649 	rv |= icmLuMonoFwd_curve(p, out, in);
10650 	rv |= icmLuMonoFwd_map(p, out, out);
10651 	rv |= icmLuMonoFwd_abs(p, out, out);
10652 	return rv;
10653 }
10654 
10655 
10656 /* Individual components of Bwd conversion: */
10657 
10658 /* Convert from relative PCS to absolute PCS (if required) */
10659 static int
10660 icmLuMonoBwd_abs (	/* Abs comes first in Bwd conversion */
10661 icmLuMono *p,		/* This */
10662 double *out,		/* Vector of output values in Native PCS */
10663 double *in			/* Vector of input values in Effective PCS */
10664 ) {
10665 	int rv = 0;
10666 
10667 	if (out != in) {
10668 		int i;
10669 		for (i = 0; i < 3; i++)		/* Don't alter input values */
10670 			out[i] = in[i];
10671 	}
10672 
10673 	/* Force to monochrome locus in correct space */
10674 	if (p->e_inSpace == icSigLabData) {
10675 		double wp[3];
10676 
10677 		if (p->intent == icAbsoluteColorimetric) {
10678 			wp[0] = p->whitePoint.X;
10679 			wp[1] = p->whitePoint.Y;
10680 			wp[2] = p->whitePoint.Z;
10681 		} else {
10682 			wp[0] = p->pcswht.X;
10683 			wp[1] = p->pcswht.Y;
10684 			wp[2] = p->pcswht.Z;
10685 		}
10686 		icmXYZ2Lab(&p->pcswht, wp, wp);	/* Convert to Lab white point */
10687 		out[1] = out[0]/wp[0] * wp[1];
10688 		out[2] = out[0]/wp[0] * wp[2];
10689 
10690 	} else {
10691 		if (p->intent == icAbsoluteColorimetric) {
10692 			out[0] = out[1]/p->whitePoint.Y * p->whitePoint.X;
10693 			out[2] = out[1]/p->whitePoint.Y * p->whitePoint.Z;
10694 		} else {
10695 			out[0] = out[1]/p->pcswht.Y * p->pcswht.X;
10696 			out[2] = out[1]/p->pcswht.Y * p->pcswht.Z;
10697 		}
10698 	}
10699 
10700 	/* Do absolute conversion to */
10701 	if (p->intent == icAbsoluteColorimetric) {
10702 
10703 		if (p->e_inSpace == icSigLabData)
10704 			icmLab2XYZ(&p->pcswht, out, out);
10705 
10706 		icmMulBy3x3(out, p->fromAbs, out);
10707 
10708 		/* Convert from Effective to Native input space */
10709 		if (p->inSpace == icSigLabData)
10710 			icmXYZ2Lab(&p->pcswht, out, out);
10711 
10712 	} else {
10713 
10714 		/* Convert from Effective to Native input space */
10715 		if (p->e_inSpace == icSigLabData && p->inSpace == icSigXYZData)
10716 			icmLab2XYZ(&p->pcswht, out, out);
10717 		else if (p->e_inSpace == icSigXYZData && p->inSpace == icSigLabData)
10718 			icmXYZ2Lab(&p->pcswht, out, out);
10719 	}
10720 
10721 	return rv;
10722 }
10723 
10724 /* Map from relative PCS to linearised device */
10725 static int
10726 icmLuMonoBwd_map (
10727 icmLuMono *p,		/* This */
10728 double *out,		/* Output value */
10729 double *in			/* Vector of input values (native space) */
10730 ) {
10731 	int i, rv = 0;
10732 	double pcsw[3];
10733 
10734 	pcsw[0] = p->pcswht.X;
10735 	pcsw[1] = p->pcswht.Y;
10736 	pcsw[2] = p->pcswht.Z;
10737 	if (p->inSpace == icSigLabData)
10738 		icmXYZ2Lab(&p->pcswht, pcsw, pcsw);	/* in Lab (should be 100.0!) */
10739 
10740 	/* Divide linearized device level into PCS white luminence */
10741 	if (p->inSpace == icSigLabData)
10742 		out[0] = in[0]/pcsw[0];
10743 	else
10744 		out[0] = in[1]/pcsw[1];
10745 
10746 	return rv;
10747 }
10748 
10749 /* Map from linearised device to actual device */
10750 static int
10751 icmLuMonoBwd_curve (
10752 icmLuMono *p,		/* This */
10753 double *out,		/* Output value */
10754 double *in			/* Input value */
10755 ) {
10756 	icc *icp = p->icp;
10757 	int i, rv = 0;
10758 
10759 	/* Convert to device value through curve */
10760 	if ((rv = p->grayCurve->lookup_bwd(p->grayCurve,&out[0],&in[0])) > 1) {
10761 		sprintf(icp->err,"icc_lookup: Curve->lookup_bwd() failed");
10762 		icp->errc = rv;
10763 		return 2;
10764 	}
10765 
10766 	return rv;
10767 }
10768 
10769 /* Overall Bwd conversion routine */
10770 static int
10771 icmLuMonoBwd_lookup (
10772 icmLuBase *pp,		/* This */
10773 double *out,		/* Output value */
10774 double *in			/* Vector of input values */
10775 ) {
10776 	double temp[3];
10777 	int rv = 0;
10778 	icmLuMono *p = (icmLuMono *)pp;
10779 	rv |= icmLuMonoBwd_abs(p, temp, in);
10780 	rv |= icmLuMonoBwd_map(p, out, temp);
10781 	rv |= icmLuMonoBwd_curve(p, out, out);
10782 	return rv;
10783 }
10784 
10785 static void
10786 icmLuMono_delete(
10787 icmLuBase *p
10788 ) {
10789 	icc *icp = p->icp;
10790 
10791 	icp->al->free(icp->al, p);
10792 }
10793 
10794 static icmLuBase *
10795 new_icmLuMono(
10796 	struct _icc          *icp,
10797     icColorSpaceSignature inSpace,		/* Native Input color space */
10798     icColorSpaceSignature outSpace,		/* Native Output color space */
10799     icColorSpaceSignature pcs,			/* Native PCS */
10800     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
10801     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
10802     icColorSpaceSignature e_pcs,		/* Effective PCS */
10803 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
10804 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
10805 	icRenderingIntent     intent,		/* Rendering intent */
10806 	icmLookupFunc         func,			/* Functionality requested */
10807 	int dir								/* 0 = fwd, 1 = bwd */
10808 ) {
10809 	icmLuMono *p;
10810 	icmXYZArray *redColrnt, *greenColrnt, *blueColrnt;
10811 
10812 	if ((p = (icmLuMono *) icp->al->calloc(icp->al,1,sizeof(icmLuMono))) == NULL)
10813 		return NULL;
10814 	p->icp      = icp;
10815 	p->del      = icmLuMono_delete;
10816 	p->lutspaces= icmLutSpaces;
10817 	p->spaces   = icmLuSpaces;
10818 	p->wh_bk_points = icmLuWh_bk_points;
10819 	p->fwd_lookup = icmLuMonoFwd_lookup;
10820 	p->fwd_curve  = icmLuMonoFwd_curve;
10821 	p->fwd_map    = icmLuMonoFwd_map;
10822 	p->fwd_abs    = icmLuMonoFwd_abs;
10823 	p->bwd_lookup = icmLuMonoBwd_lookup;
10824 	p->bwd_abs    = icmLuMonoFwd_abs;
10825 	p->bwd_map    = icmLuMonoFwd_map;
10826 	p->bwd_curve  = icmLuMonoFwd_curve;
10827 	if (dir) {
10828 		p->ttype      = icmMonoBwdType;
10829 		p->lookup     = icmLuMonoBwd_lookup;
10830 	} else {
10831 		p->ttype      = icmMonoFwdType;
10832 		p->lookup     = icmLuMonoFwd_lookup;
10833 	}
10834 
10835 	/* See if the color spaces are appropriate for the mono type */
10836 	if (number_ColorSpaceSignature(icp->header->colorSpace) != 1
10837 	  || ( icp->header->pcs != icSigXYZData && icp->header->pcs != icSigLabData)) {
10838 		p->del((icmLuBase *)p);
10839 		return NULL;
10840 	}
10841 
10842 	/* Find the appropriate tags */
10843 	if ((p->grayCurve = (icmCurve *)icp->read_tag(icp, icSigGrayTRCTag)) == NULL
10844          || p->grayCurve->ttype != icSigCurveType) {
10845 		p->del((icmLuBase *)p);
10846 		return NULL;
10847 	}
10848 
10849 	p->pcswht = icp->header->illuminant;
10850 	p->whitePoint = whitePoint;
10851 	p->blackPoint = blackPoint;
10852 	p->intent   = intent;
10853 	p->function = func;
10854 	p->inSpace  = inSpace;
10855 	p->outSpace = outSpace;
10856 	p->pcs      = pcs;
10857 	p->e_inSpace  = e_inSpace;
10858 	p->e_outSpace = e_outSpace;
10859 	p->e_pcs      = e_pcs;
10860 
10861 	/* Create absolute <-> relative conversion matricies */
10862 	icmChromAdaptMatrix(ICM_CAM_BRADFORD, whitePoint, icmD50, p->toAbs);
10863 	icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, whitePoint,  p->fromAbs);
10864 
10865 	return (icmLuBase *)p;
10866 }
10867 
10868 static icmLuBase *
10869 new_icmLuMonoFwd(
10870 	struct _icc          *icp,
10871     icColorSpaceSignature inSpace,		/* Native Input color space */
10872     icColorSpaceSignature outSpace,		/* Native Output color space */
10873     icColorSpaceSignature pcs,			/* Native PCS */
10874     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
10875     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
10876     icColorSpaceSignature e_pcs,		/* Effective PCS */
10877 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
10878 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
10879 	icRenderingIntent     intent,		/* Rendering intent */
10880 	icmLookupFunc         func			/* Functionality requested */
10881 ) {
10882 	return new_icmLuMono(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
10883 	                     whitePoint, blackPoint, intent, func, 0);
10884 }
10885 
10886 
10887 static icmLuBase *
10888 new_icmLuMonoBwd(
10889 	struct _icc          *icp,
10890     icColorSpaceSignature inSpace,		/* Native Input color space */
10891     icColorSpaceSignature outSpace,		/* Native Output color space */
10892     icColorSpaceSignature pcs,			/* Native PCS */
10893     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
10894     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
10895     icColorSpaceSignature e_pcs,		/* Effective PCS */
10896 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
10897 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
10898 	icRenderingIntent     intent,		/* Rendering intent */
10899 	icmLookupFunc         func			/* Functionality requested */
10900 ) {
10901 	return new_icmLuMono(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
10902 	                     whitePoint, blackPoint, intent, func, 1);
10903 }
10904 
10905 /* - - - - - - - - - - - - - - - - - - - - - - - */
10906 /* Forward and Backward Matrix type conversion */
10907 /* Return 0 on success, 1 if clipping occured, 2 on other error */
10908 
10909 /* Individual components of Fwd conversion: */
10910 static int
10911 icmLuMatrixFwd_curve (
10912 icmLuMatrix *p,		/* This */
10913 double *out,		/* Vector of output values */
10914 double *in			/* Vector of input values */
10915 ) {
10916 	icc *icp = p->icp;
10917 	int rv = 0;
10918 
10919 	/* Curve lookups */
10920 	if ((rv |= p->redCurve->lookup_fwd(  p->redCurve,  &out[0],&in[0])) > 1
10921 	 || (rv |= p->greenCurve->lookup_fwd(p->greenCurve,&out[1],&in[1])) > 1
10922 	 || (rv |= p->blueCurve->lookup_fwd( p->blueCurve, &out[2],&in[2])) > 1) {
10923 		sprintf(icp->err,"icc_lookup: Curve->lookup_fwd() failed");
10924 		icp->errc = rv;
10925 		return 2;
10926 	}
10927 
10928 	return rv;
10929 }
10930 
10931 static int
10932 icmLuMatrixFwd_matrix (
10933 icmLuMatrix *p,		/* This */
10934 double *out,		/* Vector of output values */
10935 double *in			/* Vector of input values */
10936 ) {
10937 	int rv = 0;
10938 	double tt[3];
10939 
10940 	/* Matrix */
10941 	tt[0] = p->mx[0][0] * in[0] + p->mx[0][1] * in[1] + p->mx[0][2] * in[2];
10942 	tt[1] = p->mx[1][0] * in[0] + p->mx[1][1] * in[1] + p->mx[1][2] * in[2];
10943 	tt[2] = p->mx[2][0] * in[0] + p->mx[2][1] * in[1] + p->mx[2][2] * in[2];
10944 
10945 	out[0] = tt[0];
10946 	out[1] = tt[1];
10947 	out[2] = tt[2];
10948 
10949 	return rv;
10950 }
10951 
10952 static int
10953 icmLuMatrixFwd_abs (/* Abs comes last in Fwd conversion */
10954 icmLuMatrix *p,		/* This */
10955 double *out,		/* Vector of output values */
10956 double *in			/* Vector of input values */
10957 ) {
10958 	int rv = 0;
10959 
10960 	if (out != in) {
10961 		int i;
10962 		for (i = 0; i < 3; i++)		/* Don't alter input values */
10963 			out[i] = in[i];
10964 	}
10965 
10966 	/* If required, convert from Relative to Absolute colorometric */
10967 	if (p->intent == icAbsoluteColorimetric) {
10968 		icmMulBy3x3(out, p->toAbs, out);
10969 	}
10970 
10971 	/* If e_outSpace is Lab (==e_PCS), then convert XYZ to Lab */
10972 	if (p->e_outSpace == icSigLabData)
10973 		icmXYZ2Lab(&p->pcswht, out, out);
10974 
10975 	return rv;
10976 }
10977 
10978 
10979 /* Overall Fwd conversion */
10980 static int
10981 icmLuMatrixFwd_lookup (
10982 icmLuBase *pp,		/* This */
10983 double *out,		/* Vector of output values */
10984 double *in			/* Vector of input values */
10985 ) {
10986 	int rv = 0;
10987 	icmLuMatrix *p = (icmLuMatrix *)pp;
10988 	rv |= icmLuMatrixFwd_curve(p, out, in);
10989 	rv |= icmLuMatrixFwd_matrix(p, out, out);
10990 	rv |= icmLuMatrixFwd_abs(p, out, out);
10991 	return rv;
10992 }
10993 
10994 /* Individual components of Bwd conversion: */
10995 
10996 static int
10997 icmLuMatrixBwd_abs (/* Abs comes first in Bwd conversion */
10998 icmLuMatrix *p,		/* This */
10999 double *out,		/* Vector of output values */
11000 double *in			/* Vector of input values */
11001 ) {
11002 	int rv = 0;
11003 
11004 	if (out != in) {
11005 		int i;
11006 		for (i = 0; i < 3; i++)		/* Don't alter input values */
11007 			out[i] = in[i];
11008 	}
11009 
11010 	/* If e_inSpace is Lab (==PCS), then convert Lab to XYZ */
11011 	if (p->e_inSpace == icSigLabData)
11012 		icmLab2XYZ(&p->pcswht, out, out);
11013 
11014 	/* If required, convert from Absolute to Relative colorometric */
11015 	if (p->intent == icAbsoluteColorimetric) {
11016 		icmMulBy3x3(out, p->fromAbs, out);
11017 	}
11018 
11019 	return rv;
11020 }
11021 
11022 static int
11023 icmLuMatrixBwd_matrix (
11024 icmLuMatrix *p,		/* This */
11025 double *out,		/* Vector of output values */
11026 double *in			/* Vector of input values */
11027 ) {
11028 	int rv = 0;
11029 	double tt[3];
11030 
11031 	/* Matrix */
11032 	tt[0] = p->bmx[0][0] * in[0] + p->bmx[0][1] * in[1] + p->bmx[0][2] * in[2];
11033 	tt[1] = p->bmx[1][0] * in[0] + p->bmx[1][1] * in[1] + p->bmx[1][2] * in[2];
11034 	tt[2] = p->bmx[2][0] * in[0] + p->bmx[2][1] * in[1] + p->bmx[2][2] * in[2];
11035 
11036 	out[0] = tt[0];
11037 	out[1] = tt[1];
11038 	out[2] = tt[2];
11039 
11040 	return rv;
11041 }
11042 
11043 static int
11044 icmLuMatrixBwd_curve (
11045 icmLuMatrix *p,		/* This */
11046 double *out,		/* Vector of output values */
11047 double *in			/* Vector of input values */
11048 ) {
11049 	icc *icp = p->icp;
11050 	int rv = 0;
11051 
11052 	/* Curves */
11053 	if ((rv |= p->redCurve->lookup_bwd(p->redCurve,&out[0],&out[0])) > 1
11054 	 ||	(rv |= p->greenCurve->lookup_bwd(p->greenCurve,&out[1],&out[1])) > 1
11055 	 || (rv |= p->blueCurve->lookup_bwd(p->blueCurve,&out[2],&out[2])) > 1) {
11056 		sprintf(icp->err,"icc_lookup: Curve->lookup_bwd() failed");
11057 		icp->errc = rv;
11058 		return 2;
11059 	}
11060 	return rv;
11061 }
11062 
11063 /* Overall Bwd conversion */
11064 static int
11065 icmLuMatrixBwd_lookup (
11066 icmLuBase *pp,		/* This */
11067 double *out,		/* Vector of output values */
11068 double *in			/* Vector of input values */
11069 ) {
11070 	int rv = 0;
11071 	icmLuMatrix *p = (icmLuMatrix *)pp;
11072 	rv |= icmLuMatrixBwd_abs(p, out, in);
11073 	rv |= icmLuMatrixBwd_matrix(p, out, out);
11074 	rv |= icmLuMatrixBwd_curve(p, out, out);
11075 	return rv;
11076 }
11077 
11078 static void
11079 icmLuMatrix_delete(
11080 icmLuBase *p
11081 ) {
11082 	icc *icp = p->icp;
11083 
11084 	icp->al->free(icp->al, p);
11085 }
11086 
11087 /* We setup valid fwd and bwd component conversions, */
11088 /* but setup only the asked for overal conversion. */
11089 static icmLuBase *
11090 new_icmLuMatrix(
11091 	struct _icc          *icp,
11092     icColorSpaceSignature inSpace,		/* Native Input color space */
11093     icColorSpaceSignature outSpace,		/* Native Output color space */
11094     icColorSpaceSignature pcs,			/* Native PCS */
11095     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
11096     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
11097     icColorSpaceSignature e_pcs,		/* Effective PCS */
11098 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
11099 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
11100 	icRenderingIntent     intent,		/* Rendering intent */
11101 	icmLookupFunc         func,			/* Functionality requested */
11102 	int dir								/* 0 = fwd, 1 = bwd */
11103 ) {
11104 	icmLuMatrix *p;
11105 
11106 	if ((p = (icmLuMatrix *) icp->al->calloc(icp->al,1,sizeof(icmLuMatrix))) == NULL)
11107 		return NULL;
11108 	p->icp      = icp;
11109 	p->del      = icmLuMatrix_delete;
11110 	p->lutspaces= icmLutSpaces;
11111 	p->spaces   = icmLuSpaces;
11112 	p->wh_bk_points = icmLuWh_bk_points;
11113 	p->fwd_lookup = icmLuMatrixFwd_lookup;
11114 	p->fwd_curve  = icmLuMatrixFwd_curve;
11115 	p->fwd_matrix = icmLuMatrixFwd_matrix;
11116 	p->fwd_abs    = icmLuMatrixFwd_abs;
11117 	p->bwd_lookup = icmLuMatrixBwd_lookup;
11118 	p->bwd_abs    = icmLuMatrixBwd_abs;
11119 	p->bwd_matrix = icmLuMatrixBwd_matrix;
11120 	p->bwd_curve  = icmLuMatrixBwd_curve;
11121 	if (dir) {
11122 		p->ttype      = icmMatrixBwdType;
11123 		p->lookup     = icmLuMatrixBwd_lookup;
11124 	} else {
11125 		p->ttype      = icmMatrixFwdType;
11126 		p->lookup     = icmLuMatrixFwd_lookup;
11127 	}
11128 
11129 	/* Note that we can use matrix type even if PCS is Lab, */
11130 	/* by simply converting it. */
11131 
11132 	/* Find the appropriate tags */
11133 	if ((p->redCurve = (icmCurve *)icp->read_tag(icp, icSigRedTRCTag)) == NULL
11134      || p->redCurve->ttype != icSigCurveType
11135 	 || (p->greenCurve = (icmCurve *)icp->read_tag(icp, icSigGreenTRCTag)) == NULL
11136      || p->greenCurve->ttype != icSigCurveType
11137 	 || (p->blueCurve = (icmCurve *)icp->read_tag(icp, icSigBlueTRCTag)) == NULL
11138      || p->blueCurve->ttype != icSigCurveType
11139 	 || (p->redColrnt = (icmXYZArray *)icp->read_tag(icp, icSigRedColorantTag)) == NULL
11140      || p->redColrnt->ttype != icSigXYZType || p->redColrnt->size < 1
11141 	 || (p->greenColrnt = (icmXYZArray *)icp->read_tag(icp, icSigGreenColorantTag)) == NULL
11142      || p->greenColrnt->ttype != icSigXYZType || p->greenColrnt->size < 1
11143 	 || (p->blueColrnt = (icmXYZArray *)icp->read_tag(icp, icSigBlueColorantTag)) == NULL
11144      || p->blueColrnt->ttype != icSigXYZType || p->blueColrnt->size < 1) {
11145 		p->del((icmLuBase *)p);
11146 		return NULL;
11147 	}
11148 
11149 	/* Setup the matrix */
11150 	p->mx[0][0] = p->redColrnt->data[0].X;
11151 	p->mx[0][1] = p->greenColrnt->data[0].X;
11152 	p->mx[0][2] = p->blueColrnt->data[0].X;
11153 	p->mx[1][1] = p->greenColrnt->data[0].Y;
11154 	p->mx[1][0] = p->redColrnt->data[0].Y;
11155 	p->mx[1][2] = p->blueColrnt->data[0].Y;
11156 	p->mx[2][1] = p->greenColrnt->data[0].Z;
11157 	p->mx[2][0] = p->redColrnt->data[0].Z;
11158 	p->mx[2][2] = p->blueColrnt->data[0].Z;
11159 
11160 	if (inverse3x3(p->bmx, p->mx) != 0) {	/* Compute inverse */
11161 		sprintf(icp->err,"icc_new_iccLuMatrix: Matrix wasn't invertable");
11162 		icp->errc = 2;
11163 		p->del((icmLuBase *)p);
11164 		return NULL;
11165 	}
11166 
11167 	p->pcswht = icp->header->illuminant;
11168 	p->whitePoint = whitePoint;
11169 	p->blackPoint = blackPoint;
11170 	p->intent   = intent;
11171 	p->function = func;
11172 	p->inSpace  = inSpace;
11173 	p->outSpace = outSpace;
11174 	p->pcs      = pcs;
11175 	p->e_inSpace  = e_inSpace;
11176 	p->e_outSpace = e_outSpace;
11177 	p->e_pcs      = e_pcs;
11178 
11179 	/* Create absolute <-> relative conversion matricies */
11180 	icmChromAdaptMatrix(ICM_CAM_BRADFORD, whitePoint, icmD50, p->toAbs);
11181 	icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, whitePoint,  p->fromAbs);
11182 
11183 	return (icmLuBase *)p;
11184 }
11185 
11186 static icmLuBase *
11187 new_icmLuMatrixFwd(
11188 	struct _icc          *icp,
11189     icColorSpaceSignature inSpace,		/* Native Input color space */
11190     icColorSpaceSignature outSpace,		/* Native Output color space */
11191     icColorSpaceSignature pcs,			/* Native PCS */
11192     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
11193     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
11194     icColorSpaceSignature e_pcs,		/* Effective PCS */
11195 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
11196 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
11197 	icRenderingIntent     intent,		/* Rendering intent */
11198 	icmLookupFunc         func			/* Functionality requested */
11199 ) {
11200 	return new_icmLuMatrix(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
11201 	                       whitePoint, blackPoint, intent, func, 0);
11202 }
11203 
11204 
11205 static icmLuBase *
11206 new_icmLuMatrixBwd(
11207 	struct _icc          *icp,
11208     icColorSpaceSignature inSpace,		/* Native Input color space */
11209     icColorSpaceSignature outSpace,		/* Native Output color space */
11210     icColorSpaceSignature pcs,			/* Native PCS */
11211     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
11212     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
11213     icColorSpaceSignature e_pcs,		/* Effective PCS */
11214 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
11215 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
11216 	icRenderingIntent     intent,		/* Rendering intent */
11217 	icmLookupFunc         func			/* Functionality requested */
11218 ) {
11219 	return new_icmLuMatrix(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
11220 	                       whitePoint, blackPoint, intent, func, 1);
11221 }
11222 
11223 /* - - - - - - - - - - - - - - - - - - - - - - - */
11224 /* Forward and Backward Multi-Dimensional Interpolation type conversion */
11225 /* Return 0 on success, 1 if clipping occured, 2 on other error */
11226 
11227 /* Components of overall lookup, in order */
11228 int icmLuLut_in_abs(icmLuLut *p, double *out, double *in) {
11229 	icmLut *lut = p->lut;
11230 	int rv = 0;
11231 
11232 	if (out != in) {
11233 		int i;
11234 		for (i = 0; i < lut->inputChan; i++)		/* Don't alter input values */
11235 			out[i] = in[i];
11236 	}
11237 
11238 	/* If Bwd Lut, take care of Absolute color space and effective input space */
11239 	if ((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
11240 		&& p->intent == icAbsoluteColorimetric) {
11241 
11242 		if (p->e_inSpace == icSigLabData)
11243 			icmLab2XYZ(&p->pcswht, out, out);
11244 
11245 		/* Convert from Absolute to Relative colorometric */
11246 		icmMulBy3x3(out, p->fromAbs, out);
11247 
11248 		if (p->inSpace == icSigLabData)
11249 			icmXYZ2Lab(&p->pcswht, out, out);
11250 
11251 	} else {
11252 
11253 		/* Convert from Effective to Native input space */
11254 		if (p->e_inSpace == icSigLabData && p->inSpace == icSigXYZData)
11255 			icmLab2XYZ(&p->pcswht, out, out);
11256 		else if (p->e_inSpace == icSigXYZData && p->inSpace == icSigLabData)
11257 			icmXYZ2Lab(&p->pcswht, out, out);
11258 	}
11259 
11260 	return rv;
11261 }
11262 
11263 /* Possible matrix lookup */
11264 int icmLuLut_matrix(icmLuLut *p, double *out, double *in) {
11265 	icmLut *lut = p->lut;
11266 	int rv = 0;
11267 
11268 	if (p->usematrix)
11269 		rv |= lut->lookup_matrix(lut,out,in);
11270 	else if (out != in) {
11271 		int i;
11272 		for (i = 0; i < lut->inputChan; i++)
11273 			out[i] = in[i];
11274 	}
11275 	return rv;
11276 }
11277 
11278 /* Do input -> input' lookup */
11279 int icmLuLut_input(icmLuLut *p, double *out, double *in) {
11280 	icmLut *lut = p->lut;
11281 	int rv = 0;
11282 
11283 	p->in_normf(out, in); 						/* Normalize from input color space */
11284 	rv |= lut->lookup_input(lut,out,out);		/* Lookup though input tables */
11285 	p->in_denormf(out,out);						/* De-normalize to input color space */
11286 	return rv;
11287 }
11288 
11289 /* Do input'->output' lookup */
11290 int icmLuLut_clut(icmLuLut *p, double *out, double *in) {
11291 	icmLut *lut = p->lut;
11292 	double temp[MAX_CHAN];
11293 	int rv = 0;
11294 
11295 	p->in_normf(temp, in); 						/* Normalize from input color space */
11296 	rv |= p->lookup_clut(lut,out,temp);			/* Lookup though clut tables */
11297 	p->out_denormf(out,out);					/* De-normalize to output color space */
11298 	return rv;
11299 }
11300 
11301 /* Do output'->output lookup */
11302 int icmLuLut_output(icmLuLut *p, double *out, double *in) {
11303 	icmLut *lut = p->lut;
11304 	int rv = 0;
11305 
11306 	p->out_normf(out,in);						/* Normalize from output color space */
11307 	rv |= lut->lookup_output(lut,out,out);		/* Lookup though output tables */
11308 	p->out_denormf(out, out);					/* De-normalize to output color space */
11309 	return rv;
11310 }
11311 
11312 int icmLuLut_out_abs(icmLuLut *p, double *out, double *in) {
11313 	icmLut *lut = p->lut;
11314 	int rv = 0;
11315 
11316 	if (out != in) {
11317 		int i;
11318 		for (i = 0; i < lut->inputChan; i++)		/* Don't alter input values */
11319 			out[i] = in[i];
11320 	}
11321 
11322 	/* If Fwd Lut, take care of Absolute color space */
11323 	/* and convert from native to effective out PCS */
11324 	if ((p->function == icmFwd || p->function == icmPreview)
11325 		&& p->intent == icAbsoluteColorimetric) {
11326 
11327 		if (p->outSpace == icSigLabData)
11328 			icmLab2XYZ(&p->pcswht, out, out);
11329 
11330 		/* Convert from Relative to Absolute colorometric XYZ */
11331 		icmMulBy3x3(out, p->toAbs, out);
11332 
11333 		if (p->e_outSpace == icSigLabData)
11334 			icmXYZ2Lab(&p->pcswht, out, out);
11335 	} else {
11336 
11337 		/* Convert from Native to Effective output space */
11338 		if (p->outSpace == icSigLabData && p->e_outSpace == icSigXYZData)
11339 			icmLab2XYZ(&p->pcswht, out, out);
11340 		else if (p->outSpace == icSigXYZData && p->e_outSpace == icSigLabData)
11341 			icmXYZ2Lab(&p->pcswht, out, out);
11342 	}
11343 	return rv;
11344 }
11345 
11346 
11347 /* Overall lookup */
11348 static int
11349 icmLuLut_lookup (
11350 icmLuBase *pp,		/* This */
11351 double *out,		/* Vector of output values */
11352 double *in			/* Vector of input values */
11353 ) {
11354 	int i, rv = 0;
11355 	icmLuLut *p = (icmLuLut *)pp;
11356 	icmLut *lut = p->lut;
11357 	double temp[MAX_CHAN];
11358 
11359 	rv |= p->in_abs(p,temp,in);						/* Possible absolute conversion */
11360 	if (p->usematrix)
11361 		rv |= lut->lookup_matrix(lut,temp,temp);/* If XYZ, multiply by non-unity matrix */
11362 	p->in_normf(temp, temp);					/* Normalize for input color space */
11363 	rv |= lut->lookup_input(lut,temp,temp);		/* Lookup though input tables */
11364 	rv |= p->lookup_clut(lut,out,temp);			/* Lookup though clut tables */
11365 	rv |= lut->lookup_output(lut,out,out);		/* Lookup though output tables */
11366 	p->out_denormf(out,out);					/* Normalize for output color space */
11367 	rv |= p->out_abs(p,out,out);				/* Possible absolute conversion */
11368 
11369 	return rv;
11370 }
11371 
11372 #ifdef NEVER	/* The following should be identical in effect to the above. */
11373 
11374 /* Overall lookup */
11375 static int
11376 icmLuLut_lookup (
11377 icmLuBase *pp,		/* This */
11378 double *out,		/* Vector of output values */
11379 double *in			/* Vector of input values */
11380 ) {
11381 	int i, rv = 0;
11382 	icmLuLut *p = (icmLuLut *)pp;
11383 	icmLut *lut = p->lut;
11384 	double temp[MAX_CHAN];
11385 
11386 	rv |= p->in_abs(p,temp,in);
11387 	rv |= p->matrix(p,temp,temp);
11388 	rv |= p->input(p,temp,temp);
11389 	rv |= p->clut(p,out,temp);
11390 	rv |= p->output(p,out,out);
11391 	rv |= p->out_abs(p,out,out);
11392 
11393 	return rv;
11394 }
11395 #endif	/* NEVER */
11396 /* - - - - - - - - - - - - - - - - - - - - - - - - - - */
11397 /* Some components of inverse lookup, in order */
11398 /* ~~ should these be in icmLut (like all the fwd transforms)? */
11399 
11400 int icmLuLut_inv_out_abs(icmLuLut *p, double *out, double *in) {
11401 	icmLut *lut = p->lut;
11402 	int rv = 0;
11403 
11404 	if (out != in) {
11405 		int i;
11406 		for (i = 0; i < lut->inputChan; i++)		/* Don't alter input values */
11407 			out[i] = in[i];
11408 	}
11409 
11410 	/* If Fwd Lut, take care of Absolute color space */
11411 	/* and convert from effective to native inverse output PCS */
11412 	/* OutSpace must be PCS: XYZ or Lab */
11413 	if ((p->function == icmFwd || p->function == icmPreview)
11414 		&& p->intent == icAbsoluteColorimetric) {
11415 
11416 		if (p->e_outSpace == icSigLabData)
11417 			icmLab2XYZ(&p->pcswht, out, out);
11418 
11419 		/* Convert from Absolute to Relative colorometric */
11420 		icmMulBy3x3(out, p->fromAbs, out);
11421 
11422 		if (p->outSpace == icSigLabData)
11423 			icmXYZ2Lab(&p->pcswht, out, out);
11424 
11425 	} else {
11426 
11427 		/* Convert from Effective to Native output space */
11428 		if (p->e_outSpace == icSigLabData && p->outSpace == icSigXYZData)
11429 			icmLab2XYZ(&p->pcswht, out, out);
11430 		else if (p->e_outSpace == icSigXYZData && p->outSpace == icSigLabData)
11431 			icmXYZ2Lab(&p->pcswht, out, out);
11432 	}
11433 	return rv;
11434 }
11435 
11436 /* Do output->output' inverse lookup */
11437 int icmLuLut_inv_output(icmLuLut *p, double *out, double *in) {
11438 	icc *icp = p->icp;
11439 	icmLut *lut = p->lut;
11440 	int rv = 0;
11441 
11442 	if (lut->rot.inited == 0) {
11443 		rv = icmTable_setup_bwd(icp, &lut->rot, lut->outputEnt, lut->outputTable);
11444 		if (rv != 0) {
11445 			sprintf(icp->err,"icc_Lut_inv_input: Malloc failure in inverse lookup init.");
11446 			return icp->errc = rv;
11447 		}
11448 	}
11449 
11450 	p->out_normf(out,in);						/* Normalize from output color space */
11451 	rv |= icmTable_lookup_bwd(&lut->rot, out, out); /* Reverse lookup though output tables */
11452 	p->out_denormf(out, out);					/* De-normalize to output color space */
11453 	return rv;
11454 }
11455 
11456 /* No output' -> input inverse lookup. */
11457 /* This is non-trivial ! */
11458 
11459 /* Do input' -> input inverse lookup */
11460 int icmLuLut_inv_input(icmLuLut *p, double *out, double *in) {
11461 	icc *icp = p->icp;
11462 	icmLut *lut = p->lut;
11463 	int rv = 0;
11464 
11465 	if (lut->rit.inited == 0) {
11466 		rv = icmTable_setup_bwd(icp, &lut->rit, lut->inputEnt, lut->inputTable);
11467 		if (rv != 0) {
11468 			sprintf(icp->err,"icc_Lut_inv_input: Malloc failure in inverse lookup init.");
11469 			return icp->errc = rv;
11470 		}
11471 	}
11472 
11473 	p->in_normf(out, in); 						/* Normalize from input color space */
11474 	rv |= icmTable_lookup_bwd(&lut->rit, out, out); /* Reverse lookup though input tables */
11475 	p->in_denormf(out,out);						/* De-normalize to input color space */
11476 	return rv;
11477 }
11478 
11479 /* Possible inverse matrix lookup */
11480 int icmLuLut_inv_matrix(icmLuLut *p, double *out, double *in) {
11481 	icc *icp = p->icp;
11482 	icmLut *lut = p->lut;
11483 	int rv = 0;
11484 
11485 	if (p->usematrix) {
11486 		double tt[3];
11487 		if (p->imx_valid == 0) {
11488 			if (inverse3x3(p->imx, lut->e) != 0) {	/* Compute inverse */
11489 				sprintf(icp->err,"icc_new_iccLuMatrix: Matrix wasn't invertable");
11490 				icp->errc = 2;
11491 				return 2;
11492 			}
11493 			p->imx_valid = 1;
11494 		}
11495 		/* Matrix multiply */
11496 		tt[0] = p->imx[0][0] * in[0] + p->imx[0][1] * in[1] + p->imx[0][2] * in[2];
11497 		tt[1] = p->imx[1][0] * in[0] + p->imx[1][1] * in[1] + p->imx[1][2] * in[2];
11498 		tt[2] = p->imx[2][0] * in[0] + p->imx[2][1] * in[1] + p->imx[2][2] * in[2];
11499 		out[0] = tt[0], out[1] = tt[1], out[2] = tt[2];
11500 	} else if (out != in) {
11501 		int i;
11502 		for (i = 0; i < lut->inputChan; i++)
11503 			out[i] = in[i];
11504 	}
11505 	return rv;
11506 }
11507 
11508 int icmLuLut_inv_in_abs(icmLuLut *p, double *out, double *in) {
11509 	icmHeader *header = p->icp->header;
11510 	icmLut *lut = p->lut;
11511 	int rv = 0;
11512 
11513 	if (out != in) {
11514 		int i;
11515 		for (i = 0; i < lut->inputChan; i++)		/* Don't alter input values */
11516 			out[i] = in[i];
11517 	}
11518 
11519 	/* If Bwd Lut, take care of Absolute color space, and */
11520 	/* convert from native to effective input space */
11521 	if ((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
11522 		&& p->intent == icAbsoluteColorimetric) {
11523 
11524 		if (p->inSpace == icSigLabData)
11525 			icmLab2XYZ(&p->pcswht, out, out);
11526 
11527 		/* Convert from Relative to Absolute colorometric XYZ */
11528 		icmMulBy3x3(out, p->toAbs, out);
11529 
11530 		if (p->e_inSpace == icSigLabData)
11531 			icmXYZ2Lab(&p->pcswht, out, out);
11532 	} else {
11533 
11534 		/* Convert from Native to Effective input space */
11535 		if (p->inSpace == icSigLabData && p->e_inSpace == icSigXYZData)
11536 			icmLab2XYZ(&p->pcswht, out, out);
11537 		else if (p->inSpace == icSigXYZData && p->e_inSpace == icSigLabData)
11538 			icmXYZ2Lab(&p->pcswht, out, out);
11539 	}
11540 	return rv;
11541 }
11542 
11543 /* - - - - - - - - - - - - - - - - - - - - - - - - - - */
11544 
11545 /* Return LuLut information */
11546 static void icmLuLut_get_info(
11547 	icmLuLut     *p,		/* this */
11548 	icmLut       **lutp,	/* Pointer to icc lut type */
11549 	icmXYZNumber *pcswhtp,	/* Pointer to profile PCS white point */
11550 	icmXYZNumber *whitep,	/* Pointer to profile absolute white point */
11551 	icmXYZNumber *blackp	/* Pointer to profile absolute black point */
11552 ) {
11553 	if (lutp != NULL)
11554 		*lutp = p->lut;
11555 	if (pcswhtp != NULL)
11556 		*pcswhtp = p->pcswht;
11557 	if (whitep != NULL)
11558 		*whitep = p->whitePoint;
11559 	if (blackp != NULL)
11560 		*blackp = p->blackPoint;
11561 }
11562 
11563 /* Get the native ranges for the LuLut */
11564 static void
11565 icmLuLut_get_lutranges (
11566 	struct _icmLuLut *p,
11567 	double *inmin, double *inmax,		/* Return maximum range of inspace values */
11568 	double *outmin, double *outmax		/* Return maximum range of outspace values */
11569 ) {
11570 	int i;
11571 
11572 	for (i = 0; i < p->lut->inputChan; i++) {
11573 		inmin[i] = 0.0;	/* Normalized range of input space values */
11574 		inmax[i] = 1.0;
11575 	}
11576 	p->in_denormf(inmin,inmin);	/* Convert to real colorspace range */
11577 	p->in_denormf(inmax,inmax);
11578 
11579 	/* Make sure min and max are so. */
11580 	for (i = 0; i < p->lut->inputChan; i++) {
11581 		if (inmin[i] > inmax[i]) {
11582 			double tt;
11583 			tt = inmin[i];
11584 			inmin[i] = inmax[i];
11585 			inmax[i] = tt;
11586 		}
11587 	}
11588 
11589 	for (i = 0; i < p->lut->outputChan; i++) {
11590 		outmin[i] = 0.0;	/* Normalized range of output space values */
11591 		outmax[i] = 1.0;
11592 	}
11593 	p->out_denormf(outmin,outmin);	/* Convert to real colorspace range */
11594 	p->out_denormf(outmax,outmax);
11595 
11596 	/* Make sure min and max are so. */
11597 	for (i = 0; i < p->lut->outputChan; i++) {
11598 		if (outmin[i] > outmax[i]) {
11599 			double tt;
11600 			tt = outmin[i];
11601 			outmin[i] = outmax[i];
11602 			outmax[i] = tt;
11603 		}
11604 	}
11605 }
11606 
11607 /* Get the effective ranges for the LuLut */
11608 static void
11609 icmLuLut_get_ranges (
11610 	struct _icmLuLut *p,
11611 	double *inmin, double *inmax,		/* Return maximum range of inspace values */
11612 	double *outmin, double *outmax		/* Return maximum range of outspace values */
11613 ) {
11614 	int i;
11615 
11616 	for (i = 0; i < p->lut->inputChan; i++) {
11617 		inmin[i] = 0.0;	/* Normalized range of input space values */
11618 		inmax[i] = 1.0;
11619 	}
11620 	p->e_in_denormf(inmin,inmin);	/* Convert to real colorspace range */
11621 	p->e_in_denormf(inmax,inmax);
11622 
11623 	/* Make sure min and max are so. */
11624 	for (i = 0; i < p->lut->inputChan; i++) {
11625 		if (inmin[i] > inmax[i]) {
11626 			double tt;
11627 			tt = inmin[i];
11628 			inmin[i] = inmax[i];
11629 			inmax[i] = tt;
11630 		}
11631 	}
11632 
11633 	for (i = 0; i < p->lut->outputChan; i++) {
11634 		outmin[i] = 0.0;	/* Normalized range of output space values */
11635 		outmax[i] = 1.0;
11636 	}
11637 	p->e_out_denormf(outmin,outmin);	/* Convert to real colorspace range */
11638 	p->e_out_denormf(outmax,outmax);
11639 
11640 	/* Make sure min and max are so. */
11641 	for (i = 0; i < p->lut->outputChan; i++) {
11642 		if (outmin[i] > outmax[i]) {
11643 			double tt;
11644 			tt = outmin[i];
11645 			outmin[i] = outmax[i];
11646 			outmax[i] = tt;
11647 		}
11648 	}
11649 }
11650 
11651 /* Return the underlying Lut matrix */
11652 static void
11653 icmLuLut_get_matrix (
11654 	struct _icmLuLut *p,
11655 	double m[3][3]
11656 ) {
11657 	int i, j;
11658 	icmLut *lut = p->lut;
11659 
11660 	if (p->usematrix) {
11661 		for (i = 0; i < 3; i++)
11662 			for (j = 0; j < 3; j++)
11663 				m[i][j] = lut->e[i][j];	/* Copy from Lut */
11664 
11665 	} else {							/* return unity matrix */
11666 		for (i = 0; i < 3; i++) {
11667 			for (j = 0; j < 3; j++) {
11668 				if (i == j)
11669 					m[i][j] = 1.0;
11670 				else
11671 					m[i][j] = 0.0;
11672 			}
11673 		}
11674 	}
11675 }
11676 
11677 
11678 static void
11679 icmLuLut_delete(
11680 icmLuBase *p
11681 ) {
11682 	icc *icp = p->icp;
11683 
11684 	icp->al->free(icp->al, p);
11685 }
11686 
11687 static icmLuBase *
11688 new_icmLuLut(
11689 	icc                   *icp,
11690 	icTagSignature        ttag,			/* Target Lut tag */
11691     icColorSpaceSignature inSpace,		/* Native Input color space */
11692     icColorSpaceSignature outSpace,		/* Native Output color space */
11693     icColorSpaceSignature pcs,			/* Native PCS */
11694     icColorSpaceSignature e_inSpace,	/* Effective Input color space */
11695     icColorSpaceSignature e_outSpace,	/* Effective Output color space */
11696     icColorSpaceSignature e_pcs,		/* Effective PCS */
11697 	icmXYZNumber          whitePoint,	/* Profile absolute white point */
11698 	icmXYZNumber          blackPoint,	/* Profile absolute black point */
11699 	icRenderingIntent     intent,		/* Rendering intent */
11700 	icmLookupFunc         func			/* Functionality requested */
11701 ) {
11702 	icmLuLut *p;
11703 
11704 	if ((p = (icmLuLut *) icp->al->calloc(icp->al,1,sizeof(icmLuLut))) == NULL)
11705 		return NULL;
11706 	p->ttype    = icmLutType;
11707 	p->icp      = icp;
11708 	p->del      = icmLuLut_delete;
11709 	p->lutspaces= icmLutSpaces;
11710 	p->spaces   = icmLuSpaces;
11711 	p->wh_bk_points = icmLuWh_bk_points;
11712 
11713 	p->lookup   = icmLuLut_lookup;
11714 	p->in_abs   = icmLuLut_in_abs;
11715 	p->matrix   = icmLuLut_matrix;
11716 	p->input    = icmLuLut_input;
11717 	p->clut     = icmLuLut_clut;
11718 	p->output   = icmLuLut_output;
11719 	p->out_abs  = icmLuLut_out_abs;
11720 
11721 	p->inv_in_abs   = icmLuLut_inv_in_abs;
11722 	p->inv_matrix   = icmLuLut_inv_matrix;
11723 	p->inv_input    = icmLuLut_inv_input;
11724 	p->inv_output   = icmLuLut_inv_output;
11725 	p->inv_out_abs  = icmLuLut_inv_out_abs;
11726 
11727 	p->pcswht   = icp->header->illuminant;
11728 	p->whitePoint = whitePoint;
11729 	p->blackPoint = blackPoint;
11730 	p->intent   = intent;
11731 	p->function = func;
11732 	p->inSpace  = inSpace;
11733 	p->outSpace = outSpace;
11734 	p->pcs      = pcs;
11735 	p->e_inSpace  = e_inSpace;
11736 	p->e_outSpace = e_outSpace;
11737 	p->e_pcs      = e_pcs;
11738 	p->get_info = icmLuLut_get_info;
11739 	p->get_lutranges = icmLuLut_get_lutranges;
11740 	p->get_ranges = icmLuLut_get_ranges;
11741 	p->get_matrix = icmLuLut_get_matrix;
11742 
11743 	/* Create absolute <-> relative conversion matricies */
11744 	icmChromAdaptMatrix(ICM_CAM_BRADFORD, whitePoint, icmD50, p->toAbs);
11745 	icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, whitePoint,  p->fromAbs);
11746 
11747 	/* Get the Lut tag, & check that it is expected type */
11748 	if ((p->lut = (icmLut *)icp->read_tag(icp, ttag)) == NULL
11749 	 || (p->lut->ttype != icSigLut8Type && p->lut->ttype != icSigLut16Type)) {
11750 		p->del((icmLuBase *)p);
11751 		return NULL;
11752 	}
11753 
11754 	/* Check if matrix should be used */
11755 	if (inSpace == icSigXYZData && p->lut->nu_matrix(p->lut))
11756 		p->usematrix = 1;
11757 	else
11758 		p->usematrix = 0;
11759 
11760 	/* Lookup input color space to normalized index function */
11761 	if (getNormFunc(inSpace, p->lut->ttype, icmToLuti, &p->in_normf)) {
11762 		sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
11763 		icp->errc = 1;
11764 		p->del((icmLuBase *)p);
11765 		return NULL;
11766 	}
11767 
11768 	/* Lookup normalized index to input color space function */
11769 	if (getNormFunc(inSpace, p->lut->ttype, icmFromLuti, &p->in_denormf)) {
11770 		sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
11771 		icp->errc = 1;
11772 		p->del((icmLuBase *)p);
11773 		return NULL;
11774 	}
11775 
11776 	/* Lookup output color space to normalized Lut entry value function */
11777 	if (getNormFunc(outSpace, p->lut->ttype, icmToLutv, &p->out_normf)) {
11778 		sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
11779 		icp->errc = 1;
11780 		p->del((icmLuBase *)p);
11781 		return NULL;
11782 	}
11783 
11784 	/* Lookup normalized Lut entry value to output color space function */
11785 	if (getNormFunc(outSpace, p->lut->ttype, icmFromLutv, &p->out_denormf)) {
11786 		sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
11787 		icp->errc = 1;
11788 		p->del((icmLuBase *)p);
11789 		return NULL;
11790 	}
11791 
11792 	/* Lookup normalized index to effective input color space function */
11793 	if (getNormFunc(e_inSpace, p->lut->ttype, icmFromLuti, &p->e_in_denormf)) {
11794 		sprintf(icp->err,"icc_get_luobj: Unknown effective colorspace");
11795 		icp->errc = 1;
11796 		p->del((icmLuBase *)p);
11797 		return NULL;
11798 	}
11799 
11800 	/* Lookup normalized Lut entry value to effective output color space function */
11801 	if (getNormFunc(e_outSpace, p->lut->ttype, icmFromLutv, &p->e_out_denormf)) {
11802 		sprintf(icp->err,"icc_get_luobj: Unknown effective colorspace");
11803 		icp->errc = 1;
11804 		p->del((icmLuBase *)p);
11805 		return NULL;
11806 	}
11807 
11808 	/* Determine appropriate clut lookup algorithm */
11809 	{
11810 		int use_sx = -1;			/* -1 = undecided, 0 = N-linear, 1 = Simplex lookup */
11811 		icColorSpaceSignature ins, outs;	/* In and out Lut color spaces */
11812 		int inn, outn;		/* in and out number of Lut components */
11813 
11814 		p->lutspaces((icmLuBase *)p, &ins, &inn, &outs, &outn);
11815 
11816 		/* Determine if the input space is "Device" like, */
11817 		/* ie. luminance will be expected to vary most strongly */
11818 		/* with the diagonal change in input coordinates. */
11819 		switch(ins) {
11820 
11821 			/* Luminence is carried by the sum of all the output channels, */
11822 			/* so output luminence will dominantly be in diagonal direction. */
11823 			case icSigRgbData:
11824 			case icSigGrayData:
11825 			case icSigCmykData:
11826 			case icSigCmyData:
11827 			case icSigMch6Data:
11828 				use_sx = 1;		/* Simplex interpolation is appropriate */
11829 				break;
11830 
11831 			/* A single chanel carries the luminence information */
11832 			case icSigLabData:
11833 			case icSigLuvData:
11834 			case icSigYCbCrData:
11835 			case icSigYxyData:
11836 			case icSigXYZData:
11837 			case icSigHlsData:
11838 			case icSigHsvData:
11839 				use_sx = 0;		/* N-linear interpolation is appropriate */
11840 				break;
11841 		}
11842 
11843 		/* If we couldn't figure it out from the input space, */
11844 		/* check output luminance variation with a diagonal input */
11845 		/* change. */
11846 		if (use_sx == -1) {
11847 			int lc;		/* Luminance channel */
11848 
11849 			/* Determine where the luminence is carried in the output */
11850 			switch(outs) {
11851 
11852 				/* Luminence is carried by the sum of all the output channels */
11853 				case icSigRgbData:
11854 				case icSigGrayData:
11855 				case icSigCmykData:
11856 				case icSigCmyData:
11857 				case icSigMch6Data:
11858 					lc = -1;		/* Average all chanels */
11859 					break;
11860 
11861 				/* A single chanel carries the luminence information */
11862 				case icSigLabData:
11863 				case icSigLuvData:
11864 				case icSigYCbCrData:
11865 				case icSigYxyData:
11866 					lc = 0;
11867 					break;
11868 
11869 				case icSigXYZData:
11870 				case icSigHlsData:
11871 					lc = 1;
11872 					break;
11873 
11874 				case icSigHsvData:
11875 					lc = 2;
11876 					break;
11877 
11878 				/* default means give up and use N-linear type lookup */
11879 				default:
11880 					lc = -2;
11881 					break;
11882 			}
11883 
11884 			/* If we know how luminance is represented in output space */
11885 			if (lc != -2) {
11886 				double tout1[MAX_CHAN];		/* Test output values */
11887 				double tout2[MAX_CHAN];
11888 				double tt, diag;
11889 				int n;
11890 
11891 				/* Determine input space location of min and max of */
11892 				/* given output chanel (chan = -1 means average of all) */
11893 				p->lut->min_max(p->lut, tout1, tout2, lc);
11894 
11895 				/* Convert to vector and then calculate normalized */
11896 				/* dot product with diagonal vector (1,1,1...) */
11897 				for (tt = 0.0, n = 0; n < inn; n++) {
11898 					tout1[n] = tout2[n] - tout1[n];
11899 					tt += tout1[n] * tout1[n];
11900 				}
11901 				if (tt > 0.0)
11902 					tt = sqrt(tt);			/* normalizing factor for maximum delta */
11903 				else
11904 					tt = 1.0;				/* Hmm. */
11905 				tt *= sqrt((double)inn);	/* Normalizing factor for diagonal vector */
11906 				for (diag = 0.0, n = 0; n < outn; n++)
11907 					diag += tout1[n] / tt;
11908 				diag = fabs(diag);
11909 
11910 				/* I'm not really convinced that this is a reliable */
11911 				/* indicator of whether simplex interpolation should be used ... */
11912 				/* It does seem to do the right thing with YCC space though. */
11913 				if (diag > 0.8)	/* Diagonal is dominant ? */
11914 					use_sx = 1;
11915 
11916 				/* If we couldn't figure it out, use N-linear interpolation */
11917 				if (use_sx == -1)
11918 					use_sx = 0;
11919 			}
11920 		}
11921 
11922 		if (use_sx) {
11923 			p->lookup_clut = p->lut->lookup_clut_sx;
11924 		} else
11925 			p->lookup_clut = p->lut->lookup_clut_nl;
11926 	}
11927 	return (icmLuBase *)p;
11928 }
11929 
11930 /* - - - - - - - - - - - - - - - - - - - - - - - */
11931 
11932 /* Return an appropriate lookup object */
11933 /* Return NULL on error, and detailed error in icc */
11934 icmLuBase* icc_get_luobj (
11935 	icc *p,						/* ICC */
11936 	icmLookupFunc func,			/* Conversion functionality */
11937 	icRenderingIntent intent,	/* Rendering intent, including icmAbsoluteColorimetricXYZ */
11938 	icColorSpaceSignature pcsor,/* PCS overide (0 = def) */
11939 	icmLookupOrder order		/* Conversion representation search Order */
11940 ) {
11941 	int rv;
11942 	icmLuBase *luobj = NULL;	/* Lookup object to return */
11943 	icmXYZNumber whitePoint, blackPoint;
11944 	icColorSpaceSignature pcs, e_pcs;	/* PCS and effective PCS */
11945 
11946 	/* Check that the profile is legal, since we depend on it */
11947 	if ((rv = check_icc_legal(p)) != 0)
11948 		return NULL;
11949 
11950 	/* Figure out the native and effective PCS */
11951 	e_pcs = pcs = p->header->pcs;
11952 	if (pcsor != icmSigDefaultData)
11953 		e_pcs = pcsor;			/* Overide */
11954 
11955 	/* Get White and Black points from the profile */
11956 	{
11957 		icmXYZArray *whitePointTag, *blackPointTag;
11958 
11959 		if ((whitePointTag = (icmXYZArray *)p->read_tag(p, icSigMediaWhitePointTag)) == NULL
11960          || whitePointTag->ttype != icSigXYZType || whitePointTag->size < 1) {
11961 			if (intent == icAbsoluteColorimetric) {
11962 				sprintf(p->err,"icc_lookup: Profile is missing Media White Point Tag");
11963 				p->errc = 1;
11964 				return NULL;
11965 			}
11966 			whitePoint = icmD50;						/* safe value */
11967 		} else
11968 			whitePoint = whitePointTag->data[0];	/* Copy structure */
11969 
11970 		if ((blackPointTag = (icmXYZArray *)p->read_tag(p, icSigMediaBlackPointTag)) == NULL
11971          || blackPointTag->ttype != icSigXYZType || blackPointTag->size < 1) {
11972 			blackPoint = icmBlack;						/* default */
11973 		} else
11974 			blackPoint = blackPointTag->data[0];	/* Copy structure */
11975 	}
11976 
11977 	/* How we expect to execute the request depends firstly on the type of profile */
11978 	switch (p->header->deviceClass) {
11979     	case icSigInputClass:
11980     	case icSigDisplayClass:
11981 			/* Look for AToB0 based profile + optional BToA0 reverse */
11982 			/* or three component matrix profile (reversable) */
11983 			/* or momochrome table profile (reversable) */
11984 			/* No intent */
11985 			/* Device <-> PCS */
11986 			/* Determine the algorithm and set its parameters */
11987 
11988 			if (intent != icmDefaultIntent
11989 			 && intent != icAbsoluteColorimetric) {
11990 				sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Input/Display profile");
11991 				p->errc = 1;
11992 				return NULL;
11993 			}
11994 
11995 			switch (func) {
11996 		    	case icmFwd:	/* Device to PCS */
11997 					if (order != icmLuOrdRev) {
11998 						/* Try Lut type lookup first */
11999 						if ((luobj = new_icmLuLut(p, icSigAToB0Tag,
12000 						     p->header->colorSpace, pcs, pcs,
12001 						     p->header->colorSpace, e_pcs, e_pcs,
12002 						     whitePoint, blackPoint, intent, func)) != NULL)
12003 							break;
12004 
12005 						/* See if it could be a matrix lookup */
12006 						if ((luobj = new_icmLuMatrixFwd(p,
12007 						     p->header->colorSpace, pcs, pcs,
12008 						     p->header->colorSpace, e_pcs, e_pcs,
12009 						     whitePoint, blackPoint, intent, func)) != NULL)
12010 							break;
12011 
12012 						/* See if it could be a monochrome lookup */
12013 						if ((luobj = new_icmLuMonoFwd(p,
12014 						     p->header->colorSpace, pcs, pcs,
12015 						     p->header->colorSpace, e_pcs, e_pcs,
12016 						     whitePoint, blackPoint, intent, func)) != NULL)
12017 							break;
12018 
12019 					} else {
12020 						/* See if it could be a monochrome lookup */
12021 						if ((luobj = new_icmLuMonoFwd(p,
12022 						     p->header->colorSpace, pcs, pcs,
12023 						     p->header->colorSpace, e_pcs, e_pcs,
12024 						     whitePoint, blackPoint, intent, func)) != NULL)
12025 							break;
12026 
12027 						/* See if it could be a matrix lookup */
12028 						if ((luobj = new_icmLuMatrixFwd(p,
12029 						     p->header->colorSpace, pcs, pcs,
12030 						     p->header->colorSpace, e_pcs, e_pcs,
12031 						     whitePoint, blackPoint, intent, func)) != NULL)
12032 							break;
12033 
12034 						/* Try Lut type lookup last */
12035 						if ((luobj = new_icmLuLut(p, icSigAToB0Tag,
12036 						     p->header->colorSpace, pcs, pcs,
12037 						     p->header->colorSpace, e_pcs, e_pcs,
12038 						     whitePoint, blackPoint, intent, func)) != NULL)
12039 							break;
12040 					}
12041 					break;
12042 
12043 		    	case icmBwd:	/* PCS to Device */
12044 					if (order != icmLuOrdRev) {
12045 						/* Try Lut type lookup first */
12046 						if ((luobj = new_icmLuLut(p, icSigBToA0Tag,
12047 						     pcs, p->header->colorSpace, pcs,
12048 						     e_pcs, p->header->colorSpace, e_pcs,
12049 						     whitePoint, blackPoint, intent, func)) != NULL)
12050 							break;
12051 
12052 						/* See if it could be a matrix lookup */
12053 						if ((luobj = new_icmLuMatrixBwd(p,
12054 						     pcs, p->header->colorSpace, pcs,
12055 						     e_pcs, p->header->colorSpace, e_pcs,
12056 						     whitePoint, blackPoint, intent, func)) != NULL)
12057 							break;
12058 
12059 						/* See if it could be a monochrome lookup */
12060 						if ((luobj = new_icmLuMonoBwd(p,
12061 						     pcs, p->header->colorSpace, pcs,
12062 						     e_pcs, p->header->colorSpace, e_pcs,
12063 						     whitePoint, blackPoint, intent, func)) != NULL)
12064 							break;
12065 					} else {
12066 						/* See if it could be a monochrome lookup */
12067 						if ((luobj = new_icmLuMonoBwd(p,
12068 						     pcs, p->header->colorSpace, pcs,
12069 						     e_pcs, p->header->colorSpace, e_pcs,
12070 						     whitePoint, blackPoint, intent, func)) != NULL)
12071 							break;
12072 
12073 						/* See if it could be a matrix lookup */
12074 						if ((luobj = new_icmLuMatrixBwd(p,
12075 						     pcs, p->header->colorSpace, pcs,
12076 						     e_pcs, p->header->colorSpace, e_pcs,
12077 						     whitePoint, blackPoint, intent, func)) != NULL)
12078 							break;
12079 
12080 						/* Try Lut type lookup last */
12081 						if ((luobj = new_icmLuLut(p, icSigBToA0Tag,
12082 						     pcs, p->header->colorSpace, pcs,
12083 						     e_pcs, p->header->colorSpace, e_pcs,
12084 						     whitePoint, blackPoint, intent, func)) != NULL)
12085 							break;
12086 					}
12087 					break;
12088 
12089 				default:
12090 					sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
12091 					p->errc = 1;
12092 					return NULL;
12093 				}
12094 			break;
12095 
12096     	case icSigOutputClass:
12097 			/* Expect BToA Lut and optional AToB Lut, All intents, expect gamut */
12098 			/* or momochrome table profile (reversable) */
12099 			/* Device <-> PCS */
12100 			/* Gamut Lut - no intent */
12101 			/* Optional preview links PCS <-> PCS */
12102 
12103 			/* Determine the algorithm and set its parameters */
12104 			switch (func) {
12105 				icTagSignature ttag;
12106 
12107 		    	case icmFwd:	/* Device to PCS */
12108 
12109 					if (intent == icmDefaultIntent)
12110 						intent = icRelativeColorimetric;	/* Make this the default */
12111 
12112 					switch (intent) {
12113 		    			case icRelativeColorimetric:
12114 		    			case icAbsoluteColorimetric:
12115 								ttag = icSigAToB1Tag;
12116 							break;
12117 		    			case icPerceptual:
12118 								ttag = icSigAToB0Tag;
12119 							break;
12120 		    			case icSaturation:
12121 								ttag = icSigAToB2Tag;
12122 							break;
12123 						default:
12124 							sprintf(p->err,"icc_get_luobj: Unknown intent");
12125 							p->errc = 1;
12126 							return NULL;
12127 					}
12128 
12129 					if (order != icmLuOrdRev) {
12130 						/* Try Lut type lookup first */
12131 						if ((luobj = new_icmLuLut(p, ttag,
12132 						     p->header->colorSpace, pcs, pcs,
12133 						     p->header->colorSpace, e_pcs, e_pcs,
12134 						     whitePoint, blackPoint, intent, func)) != NULL)
12135 							break;
12136 
12137 						/* See if it could be a matrix lookup */
12138 						if ((luobj = new_icmLuMatrixFwd(p,
12139 						     p->header->colorSpace, pcs, pcs,
12140 						     p->header->colorSpace, e_pcs, e_pcs,
12141 						     whitePoint, blackPoint, intent, func)) != NULL)
12142 							break;
12143 
12144 						/* See if it could be a monochrome lookup */
12145 						if ((luobj = new_icmLuMonoFwd(p,
12146 						     p->header->colorSpace, pcs, pcs,
12147 						     p->header->colorSpace, e_pcs, e_pcs,
12148 						     whitePoint, blackPoint, intent, func)) != NULL)
12149 							break;
12150 					} else {
12151 						/* See if it could be a monochrome lookup */
12152 						if ((luobj = new_icmLuMonoFwd(p,
12153 						     p->header->colorSpace, pcs, pcs,
12154 						     p->header->colorSpace, e_pcs, e_pcs,
12155 						     whitePoint, blackPoint, intent, func)) != NULL)
12156 							break;
12157 
12158 						/* See if it could be a matrix lookup */
12159 						if ((luobj = new_icmLuMatrixFwd(p,
12160 						     p->header->colorSpace, pcs, pcs,
12161 						     p->header->colorSpace, e_pcs, e_pcs,
12162 						     whitePoint, blackPoint, intent, func)) != NULL)
12163 							break;
12164 
12165 						/* Try Lut type lookup last */
12166 						if ((luobj = new_icmLuLut(p, ttag,
12167 						     p->header->colorSpace, pcs, pcs,
12168 						     p->header->colorSpace, e_pcs, e_pcs,
12169 						     whitePoint, blackPoint, intent, func)) != NULL)
12170 							break;
12171 					}
12172 					break;
12173 
12174 		    	case icmBwd:	/* PCS to Device */
12175 
12176 					if (intent == icmDefaultIntent)
12177 						intent = icRelativeColorimetric;	/* Make this the default */
12178 
12179 					switch (intent) {
12180 		    			case icRelativeColorimetric:
12181 		    			case icAbsoluteColorimetric:
12182 								ttag = icSigBToA1Tag;
12183 							break;
12184 		    			case icPerceptual:
12185 								ttag = icSigBToA0Tag;
12186 							break;
12187 		    			case icSaturation:
12188 								ttag = icSigBToA2Tag;
12189 							break;
12190 						default:
12191 							sprintf(p->err,"icc_get_luobj: Unknown intent");
12192 							p->errc = 1;
12193 							return NULL;
12194 					}
12195 
12196 					if (order != icmLuOrdRev) {
12197 						/* Try Lut type lookup first */
12198 						if ((luobj = new_icmLuLut(p, ttag,
12199 						     pcs, p->header->colorSpace, pcs,
12200 						     e_pcs, p->header->colorSpace, e_pcs,
12201 						     whitePoint, blackPoint, intent, func)) != NULL)
12202 							break;
12203 
12204 						/* See if it could be a matrix lookup */
12205 						if ((luobj = new_icmLuMatrixBwd(p,
12206 						     pcs, p->header->colorSpace, pcs,
12207 						     e_pcs, p->header->colorSpace, e_pcs,
12208 						     whitePoint, blackPoint, intent, func)) != NULL)
12209 							break;
12210 
12211 						/* See if it could be a monochrome lookup */
12212 						if ((luobj = new_icmLuMonoBwd(p,
12213 						     pcs, p->header->colorSpace, pcs,
12214 						     e_pcs, p->header->colorSpace, e_pcs,
12215 						     whitePoint, blackPoint, intent, func)) != NULL)
12216 							break;
12217 					} else {
12218 						/* See if it could be a monochrome lookup */
12219 						if ((luobj = new_icmLuMonoBwd(p,
12220 						     pcs, p->header->colorSpace, pcs,
12221 						     e_pcs, p->header->colorSpace, e_pcs,
12222 						     whitePoint, blackPoint, intent, func)) != NULL)
12223 							break;
12224 
12225 						/* See if it could be a matrix lookup */
12226 						if ((luobj = new_icmLuMatrixBwd(p,
12227 						     pcs, p->header->colorSpace, pcs,
12228 						     e_pcs, p->header->colorSpace, e_pcs,
12229 						     whitePoint, blackPoint, intent, func)) != NULL)
12230 							break;
12231 
12232 						/* Try Lut type lookup last */
12233 						if ((luobj = new_icmLuLut(p, ttag,
12234 						     pcs, p->header->colorSpace, pcs,
12235 						     e_pcs, p->header->colorSpace, e_pcs,
12236 						     whitePoint, blackPoint, intent, func)) != NULL)
12237 							break;
12238 					}
12239 					break;
12240 
12241 		    	case icmGamut:	/* PCS to 1D */
12242 
12243 					if (intent != icmDefaultIntent) {
12244 						sprintf(p->err,"icc_get_luobj: Intent is inappropriate for type of function");
12245 						p->errc = 1;
12246 						return NULL;
12247 					}
12248 
12249 					/* If the target tag exists, and it is a Lut */
12250 					luobj = new_icmLuLut(p, icSigGamutTag,
12251 					     pcs, icSigGrayData, pcs,
12252 					     e_pcs, icSigGrayData, e_pcs,
12253 					     whitePoint, blackPoint, intent, func);
12254 					break;
12255 
12256 		    	case icmPreview:	/* PCS to PCS */
12257 
12258 					switch (intent)  {
12259 		    			case icRelativeColorimetric:
12260 								ttag = icSigPreview1Tag;
12261 							break;
12262 		    			case icPerceptual:
12263 								ttag = icSigPreview0Tag;
12264 							break;
12265 		    			case icSaturation:
12266 								ttag = icSigPreview2Tag;
12267 							break;
12268 		    			case icAbsoluteColorimetric:
12269 							sprintf(p->err,"icc_get_luobj: Intent is inappropriate for type of function");
12270 							p->errc = 1;
12271 							return NULL;
12272 						default:
12273 							sprintf(p->err,"icc_get_luobj: Unknown intent");
12274 							p->errc = 1;
12275 							return NULL;
12276 					}
12277 
12278 					/* If the target tag exists, and it is a Lut */
12279 					luobj = new_icmLuLut(p, ttag,
12280 					     pcs, pcs, pcs,
12281 					     e_pcs, e_pcs, e_pcs,
12282 					     whitePoint, blackPoint, intent, func);
12283 					break;
12284 
12285 				default:
12286 					sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
12287 					p->errc = 1;
12288 					return NULL;
12289 			}
12290 			break;
12291 
12292     	case icSigLinkClass:
12293 			/* Expect AToB0 Lut and optional BToA0 Lut, One intent in header */
12294 			/* Device <-> Device */
12295 
12296 			if (intent != p->header->renderingIntent
12297 			 && intent != icmDefaultIntent) {
12298 				sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Link profile");
12299 				p->errc = 1;
12300 				return NULL;
12301 			}
12302 			intent = p->header->renderingIntent;
12303 
12304 			/* Determine the algorithm and set its parameters */
12305 			switch (func) {
12306 		    	case icmFwd:	/* Device to PCS (== Device) */
12307 
12308 					luobj = new_icmLuLut(p, icSigAToB0Tag,
12309 					     p->header->colorSpace, pcs, pcs,
12310 					     p->header->colorSpace, pcs, pcs,
12311 					     whitePoint, blackPoint, intent, func);
12312 					break;
12313 
12314 		    	case icmBwd:	/* PCS (== Device) to Device */
12315 
12316 					luobj = new_icmLuLut(p, icSigBToA0Tag,
12317 					     pcs, p->header->colorSpace, pcs,
12318 					     pcs, p->header->colorSpace, pcs,
12319 					     whitePoint, blackPoint, intent, func);
12320 					break;
12321 
12322 				default:
12323 					sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
12324 					p->errc = 1;
12325 					return NULL;
12326 			}
12327 			break;
12328 
12329     	case icSigAbstractClass:
12330 			/* Expect AToB0 Lut and BToA Lut, no intents */
12331 			/* PCS <-> PCS */
12332 			/* Determine the algorithm and set its parameters */
12333 
12334 			if (intent != icmDefaultIntent) {
12335 				sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Abstract profile");
12336 				p->errc = 1;
12337 				return NULL;
12338 			}
12339 
12340 			switch (func) {
12341 		    	case icmFwd:	/* PCS (== Device) to PCS */
12342 
12343 					luobj = new_icmLuLut(p, icSigAToB0Tag,
12344 					     p->header->colorSpace, pcs, pcs,
12345 					     e_pcs, e_pcs, e_pcs,
12346 					     whitePoint, blackPoint, intent, func);
12347 					break;
12348 
12349 		    	case icmBwd:	/* PCS to PCS (== Device) */
12350 
12351 					luobj = new_icmLuLut(p, icSigBToA0Tag,
12352 					     pcs, p->header->colorSpace, pcs,
12353 					     e_pcs, e_pcs, e_pcs,
12354 					     whitePoint, blackPoint, intent, func);
12355 					break;
12356 
12357 				default:
12358 					sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
12359 					p->errc = 1;
12360 					return NULL;
12361 			}
12362 			break;
12363 
12364     	case icSigColorSpaceClass:
12365 			/* Expect AToB0 Lut and BToA0 Lut, no intents, */
12366 			/* Device <-> PCS */
12367 
12368 			if (intent != icmDefaultIntent) {
12369 				sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Colorspace profile");
12370 				p->errc = 1;
12371 				return NULL;
12372 			}
12373 
12374 			/* Determine the algorithm and set its parameters */
12375 			switch (func) {
12376 		    	case icmFwd:	/* Device to PCS */
12377 
12378 					luobj = new_icmLuLut(p, icSigAToB0Tag,
12379 					     p->header->colorSpace, pcs, pcs,
12380 					     p->header->colorSpace, e_pcs, e_pcs,
12381 					     whitePoint, blackPoint, intent, func);
12382 					break;
12383 
12384 		    	case icmBwd:	/* PCS to Device */
12385 
12386 					luobj = new_icmLuLut(p, icSigBToA0Tag,
12387 					     pcs, p->header->colorSpace, pcs,
12388 					     e_pcs, p->header->colorSpace, e_pcs,
12389 					     whitePoint, blackPoint, intent, func);
12390 					break;
12391 
12392 				default:
12393 					sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
12394 					p->errc = 1;
12395 					return NULL;
12396 			}
12397 			break;
12398 
12399     	case icSigNamedColorClass:
12400 			/* Expect Name -> Device, Optional PCS */
12401 			/* and a reverse lookup would be useful */
12402 			/* (ie. PCS or Device coords to closest named color) */
12403 			/* ~~ to be implimented ~~ */
12404 
12405 			/* ~~ Absolute intent is valid for processing of */
12406 			/* PCS from named Colors. Also allow for e_pcs */
12407 			if (intent != icmDefaultIntent) {
12408 				sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Named Color profile");
12409 				p->errc = 1;
12410 				return NULL;
12411 			}
12412 
12413 			sprintf(p->err,"icc_get_luobj: Named Colors not handled yet");
12414 			p->errc = 1;
12415 			return NULL;
12416 
12417     	default:
12418 			sprintf(p->err,"icc_get_luobj: Unknown profile class");
12419 			p->errc = 1;
12420 			return NULL;
12421 	}
12422 
12423 	return luobj;
12424 }
12425 
12426 /* - - - - - - - - - - - - - - - - - - - - - - - - */
12427 
12428 /* Create an empty object. Return null on error */
12429 icc *new_icc_a(
12430 icmAlloc *al			/* Optional memory allocator. NULL for default */
12431 ) {
12432 	icc *p;
12433 	int del_al = 0;
12434 
12435 	if (al == NULL) {	/* None provided, create default */
12436 		if ((al = new_icmAllocStd()) == NULL)
12437 			return NULL;
12438 		del_al = 1;		/* We need to delete it */
12439 	}
12440 
12441 	if ((p = (icc *) al->calloc(al, 1,sizeof(icc))) == NULL)
12442 		return NULL;
12443 	p->al = al;				/* Heap allocator */
12444 	p->del_al = del_al;		/* Flag noting whether we delete it */
12445 
12446 	p->get_size      = icc_get_size;
12447 	p->read          = icc_read;
12448 	p->write         = icc_write;
12449 	p->dump          = icc_dump;
12450 	p->del           = icc_delete;
12451 	p->add_tag       = icc_add_tag;
12452 	p->link_tag      = icc_link_tag;
12453 	p->find_tag      = icc_find_tag;
12454 	p->read_tag      = icc_read_tag;
12455 	p->rename_tag    = icc_rename_tag;
12456 	p->unread_tag    = icc_unread_tag;
12457 	p->read_all_tags = icc_read_all_tags;
12458 	p->delete_tag    = icc_delete_tag;
12459 
12460 	p->get_luobj  = icc_get_luobj;
12461 
12462 #if defined(__IBMC__) && defined(_M_IX86)
12463 	_control87(EM_UNDERFLOW, EM_UNDERFLOW);
12464 #endif
12465 
12466 	/* Allocate a header object */
12467 	if ((p->header = new_icmHeader(p)) == NULL) {
12468 		al->free(al, p);
12469 		if (del_al)
12470 			al->del(al);
12471 		return NULL;
12472 	}
12473 
12474 	/* Values that must be set before writing */
12475 	p->header->deviceClass = icMaxEnumClass;/* Type of profile - must be set! */
12476     p->header->colorSpace = icMaxEnumData;	/* Clr space of data - must be set! */
12477     p->header->pcs = icMaxEnumData;			/* PCS: XYZ or Lab - must be set! */
12478     p->header->renderingIntent = icMaxEnumIntent;	/* Rendering intent - must be set ! */
12479 
12480 	/* Values that should be set before writing */
12481 	p->header->manufacturer = -1;			/* Dev manufacturer - should be set ! */
12482     p->header->model = -1;					/* Dev model number - should be set ! */
12483     p->header->attributes.l = 0;			/* ICC Device attributes - should set ! */
12484     p->header->flags = 0;					/* Embedding flags - should be set ! */
12485 
12486 	/* Values that may be set before writing */
12487     p->header->attributes.h = 0;			/* Dev Device attributes - may be set ! */
12488     p->header->creator = str2tag("argl");	/* Profile creator - Argyll - may be set ! */
12489 
12490 	/* Init default values in header */
12491 	p->header->cmmId = str2tag("argl");		/* CMM for profile - Argyll CMM */
12492     p->header->majv = 2;					/* Current version 2.1.0 */
12493 	p->header->minv = 1;
12494 	p->header->bfv  = 0;
12495 	setcur_DateTimeNumber(&p->header->date);/* Creation Date */
12496     p->header->platform = icSigMicrosoft;	/* Primary Platform */
12497     p->header->illuminant = icmD50;			/* Profile illuminant - D50 */
12498 
12499 	return p;
12500 }
12501 
12502 
12503 /* For backwards compatibility - a NULL allocator version */
12504 icc *new_icc(void) {
12505 	return new_icc_a(NULL);
12506 }
12507 
12508 
12509 /* ---------------------------------------------------------- */
12510