summaryrefslogtreecommitdiff
path: root/.pc/04_CVE-2012-4405.diff
diff options
context:
space:
mode:
Diffstat (limited to '.pc/04_CVE-2012-4405.diff')
-rw-r--r--.pc/04_CVE-2012-4405.diff/icc/icc.c17833
1 files changed, 17833 insertions, 0 deletions
diff --git a/.pc/04_CVE-2012-4405.diff/icc/icc.c b/.pc/04_CVE-2012-4405.diff/icc/icc.c
new file mode 100644
index 0000000..68668d0
--- /dev/null
+++ b/.pc/04_CVE-2012-4405.diff/icc/icc.c
@@ -0,0 +1,17833 @@
+
+/*
+ * International Color Consortium Format Library (icclib)
+ * For ICC profile version 3.4
+ *
+ * Author: Graeme W. Gill
+ * Date: 2002/04/22
+ * Version: 2.15
+ *
+ * Copyright 1997 - 2012 Graeme W. Gill
+ *
+ * This material is licensed with an "MIT" free use license:-
+ * see the License.txt file in this directory for licensing details.
+ */
+
+/*
+ * TTBD:
+ *
+ * Add a "warning mode" to file reading, in which file format
+ * errors are ignored where possible, rather than generating
+ * a fatal error (see ICM_STRICT #define).
+ *
+ * NameColor Dump doesn't handle device space correctly -
+ * should use appropriate interpretation in case device is Lab etc.
+ *
+ * Should recognise & honour unicode 0xFFFE endian marker.
+ * Should generate it on writing too ?
+ *
+ * Add support for copying tags from one icc to another.
+ *
+ * Should fix all write_number failure errors to indicate failed value.
+ * (Partially implemented - need to check all write_number functions)
+ *
+ * Make write fail error messages be specific on which element failed.
+ *
+ * Should add named color space lookup function support.
+ *
+ * Would be nice to add generic ability to add new tag type handling,
+ * so that the base library doesn't need to be modified (ie. VideoCardGamma) ?
+ *
+ * Need to add DeviceSettings and OutputResponse tags to bring up to
+ * ICC.1:1998-09 [started but not complete]
+ *
+ */
+
+#undef ICM_STRICT /* Not fully implimented - switch off strict checking of file format */
+
+/* Make the default grid points of the Lab clut be symetrical about */
+/* a/b 0.0, and also make L = 100.0 fall on a grid point. */
+#define SYMETRICAL_DEFAULT_LAB_RANGE
+
+#define _ICC_C_ /* Turn on implimentation code */
+
+#undef DEBUG_SETLUT /* Show each value being set in setting lut contents */
+#undef DEBUG_SETLUT_CLIP /* Show clipped values when setting LUT */
+#undef DEBUG_LULUT /* Show each value being looked up from lut contents */
+#undef DEBUG_LLULUT /* Debug individual lookup steps (not fully implemented) */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <time.h>
+#ifdef __sun
+#include <unistd.h>
+#endif
+#if defined(__IBMC__) && defined(_M_IX86)
+#include <float.h>
+#endif
+#include "icc.h"
+
+#ifdef _MSC_VER
+#define vsnprintf _vsnprintf
+#define snprintf _snprintf
+#endif
+
+/* ========================================================== */
+/* Default system interface object implementations */
+
+#ifndef SEPARATE_STD
+#define COMBINED_STD
+
+#include "iccstd.c"
+
+#undef COMBINED_STD
+#endif /* SEPARATE_STD */
+
+/* Forced byte alignment for tag table and tags */
+#define ALIGN_SIZE 4
+
+/* =========================================================== */
+
+#ifdef DEBUG_SETLUT
+#undef DBGSL
+#define DBGSL(xxx) printf xxx ;
+#else
+#undef DBGSL
+#define DBGSL(xxx)
+#endif
+
+#if defined(DEBUG_SETLUT) || defined(DEBUG_SETLUT_CLIP)
+#undef DBGSLC
+#define DBGSLC(xxx) printf xxx ;
+#else
+#undef DBGSLC
+#define DBGSLC(xxx)
+#endif
+
+#ifdef DEBUG_LULUT
+#undef DBGLL
+#define DBGLL(xxx) printf xxx ;
+#else
+#undef DBGLL
+#define DBGLL(xxx)
+#endif
+
+#ifdef DEBUG_LLULUT
+#undef DBLLL
+#define DBLLL(xxx) printf xxx ;
+#else
+#undef DBLLL
+#define DBLLL(xxx)
+#endif
+
+/* =========================================================== */
+/* Overflow protected unsigned int arithmatic functions. */
+/* These functions saturate rather than wrapping around. */
+/* (Divide doesn't need protection) */
+/* They return UINT_MAX if there was an overflow */
+
+/* a + b */
+static unsigned int sat_add(unsigned int a, unsigned int b) {
+ if (b > (UINT_MAX - a))
+ return UINT_MAX;
+ return a + b;
+}
+
+/* a - b */
+static unsigned int sat_sub(unsigned int a, unsigned int b) {
+ if (a < b)
+ return UINT_MAX;
+ return a - b;
+}
+
+/* a * b */
+static unsigned int sat_mul(unsigned int a, unsigned int b) {
+ unsigned int c;
+
+ if (a == 0 || b == 0)
+ return 0;
+
+ if (a > (UINT_MAX/b))
+ return UINT_MAX;
+ else
+ return a * b;
+}
+
+/* A + B + C */
+#define sat_addadd(A, B, C) sat_add(A, sat_add(B, C))
+
+/* A + B * C */
+#define sat_addmul(A, B, C) sat_add(A, sat_mul(B, C))
+
+/* A + B + C * D */
+#define sat_addaddmul(A, B, C, D) sat_add(A, sat_add(B, sat_mul(C, D)))
+
+/* A * B * C */
+#define sat_mul3(A, B, C) sat_mul(A, sat_mul(B, C))
+
+/* a ^ b */
+static unsigned int sat_pow(unsigned int a, unsigned int b) {
+ unsigned int c = 1;
+ for (; b > 0; b--) {
+ c = sat_mul(c, a);
+ if (c == UINT_MAX)
+ break;
+ }
+ return c;
+}
+
+/* Alignment */
+static unsigned int sat_align(unsigned int align_size, unsigned int a) {
+ align_size--;
+
+ if (align_size > (UINT_MAX - a))
+ return UINT_MAX;
+
+ return (a + align_size) & ~align_size;
+}
+
+/* These test functions detect whether an overflow would occur */
+
+/* Return nz if add would overflow */
+static int ovr_add(unsigned int a, unsigned int b) {
+
+ if (b > (UINT_MAX - a))
+ return 1;
+ return 0;
+}
+
+/* Return nz if sub would overflow */
+static int ovr_sub(unsigned int a, unsigned int b) {
+ if (a < b)
+ return 1;
+ return 0;
+}
+
+/* Return nz if mult would overflow */
+static int ovr_mul(unsigned int a, unsigned int b) {
+ if (a > (UINT_MAX/b))
+ return 1;
+ return 0;
+}
+
+
+/* size_t versions of saturating arithmatic */
+
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t)(-1))
+#endif
+
+/* a + b */
+static size_t ssat_add(size_t a, size_t b) {
+ if (b > (SIZE_MAX - a))
+ return SIZE_MAX;
+ return a + b;
+}
+
+/* a - b */
+static size_t ssat_sub(size_t a, size_t b) {
+ if (a < b)
+ return SIZE_MAX;
+ return a - b;
+}
+
+/* a * b */
+static size_t ssat_mul(size_t a, size_t b) {
+ size_t c;
+
+ if (a == 0 || b == 0)
+ return 0;
+
+ if (a > (SIZE_MAX/b))
+ return SIZE_MAX;
+ else
+ return a * b;
+}
+
+/* ------------------------------------------------- */
+/* Memory image icmFile compatible class */
+/* Buffer is assumed to have been allocated by the given allocator, */
+/* and will be expanded on write. */
+
+/* Get the size of the file */
+static size_t icmFileMem_get_size(icmFile *pp) {
+ icmFileMem *p = (icmFileMem *)pp;
+
+ return p->end - p->start;
+}
+
+/* Set current position to offset. Return 0 on success, nz on failure. */
+static int icmFileMem_seek(
+icmFile *pp,
+unsigned int offset
+) {
+ icmFileMem *p = (icmFileMem *)pp;
+ unsigned char *np;
+
+ np = p->start + offset;
+ if (np < p->start || np >= p->end)
+ return 1;
+ p->cur = np;
+ return 0;
+}
+
+/* Read count items of size length. Return number of items successfully read. */
+static size_t icmFileMem_read(
+icmFile *pp,
+void *buffer,
+size_t size,
+size_t count
+) {
+ icmFileMem *p = (icmFileMem *)pp;
+ size_t len;
+
+ len = ssat_mul(size, count);
+ if (len > (p->end - p->cur)) { /* Too much */
+ if (size > 0)
+ count = (p->end - p->cur)/size;
+ else
+ count = 0;
+ }
+ len = size * count;
+ if (len > 0)
+ memmove(buffer, p->cur, len);
+ p->cur += len;
+ return count;
+}
+
+/* Expand the memory buffer file to hold up to pointer ep */
+/* Don't expand if realloc fails */
+static void icmFileMem_filemem_resize(icmFileMem *p, unsigned char *ep) {
+ size_t na, co, ce;
+ unsigned char *nstart;
+
+ /* No need to realloc */
+ if (ep <= p->aend) {
+ return;
+ }
+
+ co = p->cur - p->start; /* Current offset */
+ ce = p->end - p->start; /* Current end */
+ na = ep - p->start; /* new allocated size */
+
+ /* Round new allocation up */
+ if (na <= 1024)
+ na += 1024;
+ else
+ na += 4096;
+
+ if ((nstart = p->al->realloc(p->al, p->start, na)) != NULL) {
+ p->start = nstart;
+ p->cur = nstart + co;
+ p->end = nstart + ce;
+ p->aend = nstart + na;
+ }
+}
+
+/* write count items of size length. Return number of items successfully written. */
+static size_t icmFileMem_write(
+icmFile *pp,
+void *buffer,
+size_t size,
+size_t count
+) {
+ icmFileMem *p = (icmFileMem *)pp;
+ size_t len;
+
+ len = ssat_mul(size, count);
+ if (len > (size_t)(p->aend - p->cur)) /* Try and expand buffer */
+ icmFileMem_filemem_resize(p, p->start + len);
+
+ if (len > (size_t)(p->aend - p->cur)) {
+ if (size > 0)
+ count = (p->aend - p->cur)/size;
+ else
+ count = 0;
+ }
+ len = size * count;
+ if (len > 0)
+ memmove(p->cur, buffer, len);
+ p->cur += len;
+ if (p->end < p->cur)
+ p->end = p->cur;
+ return count;
+}
+
+/* do a printf */
+static int icmFileMem_printf(
+icmFile *pp,
+const char *format,
+...
+) {
+ int rv;
+ va_list args;
+ icmFileMem *p = (icmFileMem *)pp;
+ int len;
+
+ va_start(args, format);
+
+ rv = 1;
+ len = 100; /* Initial allocation for printf */
+ icmFileMem_filemem_resize(p, p->cur + len);
+
+ /* We have to use the available printf functions to resize the buffer if needed. */
+ for (;rv != 0;) {
+ /* vsnprintf() either returns -1 if it doesn't fit, or */
+ /* returns the size-1 needed in order to fit. */
+ len = vsnprintf((char *)p->cur, (p->aend - p->cur), format, args);
+
+ if (len > -1 && ((p->cur + len +1) <= p->aend)) /* Fitted in current allocation */
+ break;
+
+ if (len > -1) /* vsnprintf returned needed size-1 */
+ len = len+2; /* (In case vsnprintf returned 1 less than it needs) */
+ else
+ len *= 2; /* We just have to guess */
+
+ /* Attempt to resize */
+ icmFileMem_filemem_resize(p, p->cur + len);
+
+ /* If resize failed */
+ if ((p->aend - p->cur) < len) {
+ rv = 0;
+ break;
+ }
+ }
+ if (rv != 0) {
+ /* Figure out where end of printf is */
+ len = strlen((char *)p->cur); /* Length excluding nul */
+ p->cur += len;
+ if (p->cur > p->end)
+ p->end = p->cur;
+ rv = len;
+ }
+ va_end(args);
+ return rv;
+}
+
+/* flush all write data out to secondary storage. Return nz on failure. */
+static int icmFileMem_flush(
+icmFile *pp
+) {
+ return 0;
+}
+
+/* Return the memory buffer. Error if not icmFileMem */
+static int icmFileMem_get_buf(
+icmFile *pp,
+unsigned char **buf,
+size_t *len
+) {
+ icmFileMem *p = (icmFileMem *)pp;
+ if (buf != NULL)
+ *buf = p->start;
+ if (len != NULL)
+ *len = p->end - p->start;
+ return 0;
+}
+
+/* we're done with the file object, return nz on failure */
+static int icmFileMem_delete(
+icmFile *pp
+) {
+ icmFileMem *p = (icmFileMem *)pp;
+ icmAlloc *al = p->al;
+ int del_al = p->del_al;
+
+ if (p->del_buf) /* Free the memory buffer */
+ al->free(al, p->start);
+ al->free(al, p); /* Free object */
+ if (del_al) /* We are responsible for deleting allocator */
+ al->del(al);
+ return 0;
+}
+
+/* Create a memory image file access class with allocator */
+/* Buffer is used as is. */
+icmFile *new_icmFileMem_a(
+void *base, /* Pointer to base of memory buffer */
+size_t length, /* Number of bytes in buffer */
+icmAlloc *al /* heap allocator */
+) {
+ icmFileMem *p;
+
+ if ((p = (icmFileMem *) al->calloc(al, 1, sizeof(icmFileMem))) == NULL) {
+ return NULL;
+ }
+ p->al = al; /* Heap allocator */
+ p->get_size = icmFileMem_get_size;
+ p->seek = icmFileMem_seek;
+ p->read = icmFileMem_read;
+ p->write = icmFileMem_write;
+ p->gprintf = icmFileMem_printf;
+ p->flush = icmFileMem_flush;
+ p->get_buf = icmFileMem_get_buf;
+ p->del = icmFileMem_delete;
+
+ p->start = (unsigned char *)base;
+ p->cur = p->start;
+ p->aend = p->end = p->start + length;
+
+ return (icmFile *)p;
+}
+
+/* Create a memory image file access class with given allocator */
+/* and delete base when icmFile is deleted. */
+icmFile *new_icmFileMem_ad(void *base, size_t length, icmAlloc *al) {
+ icmFile *fp;
+
+ if ((fp = new_icmFileMem_a(base, length, al)) != NULL) {
+ ((icmFileMem *)fp)->del_buf = 1;
+ }
+
+ return fp;
+}
+
+/* ========================================================== */
+/* Conversion support functions */
+/* Convert between ICC storage types and native C types */
+/* Write routine return non-zero if numbers can't be represented */
+
+/* Unsigned */
+static unsigned int read_UInt8Number(char *p) {
+ unsigned int rv;
+ rv = (unsigned int)((ORD8 *)p)[0];
+ return rv;
+}
+
+static int write_UInt8Number(unsigned int d, char *p) {
+ if (d > 255)
+ return 1;
+ ((ORD8 *)p)[0] = (ORD8)d;
+ return 0;
+}
+
+static unsigned int read_UInt16Number(char *p) {
+ unsigned int rv;
+ rv = 256 * (unsigned int)((ORD8 *)p)[0]
+ + (unsigned int)((ORD8 *)p)[1];
+ return rv;
+}
+
+static int write_UInt16Number(unsigned int d, char *p) {
+ if (d > 65535)
+ return 1;
+ ((ORD8 *)p)[0] = (ORD8)(d >> 8);
+ ((ORD8 *)p)[1] = (ORD8)(d);
+ return 0;
+}
+
+static unsigned int read_UInt32Number(char *p) {
+ unsigned int rv;
+ rv = 16777216 * (unsigned int)((ORD8 *)p)[0]
+ + 65536 * (unsigned int)((ORD8 *)p)[1]
+ + 256 * (unsigned int)((ORD8 *)p)[2]
+ + (unsigned int)((ORD8 *)p)[3];
+ return rv;
+}
+
+static int write_UInt32Number(unsigned int d, char *p) {
+ ((ORD8 *)p)[0] = (ORD8)(d >> 24);
+ ((ORD8 *)p)[1] = (ORD8)(d >> 16);
+ ((ORD8 *)p)[2] = (ORD8)(d >> 8);
+ ((ORD8 *)p)[3] = (ORD8)(d);
+ return 0;
+}
+
+static void read_UInt64Number(icmUint64 *d, char *p) {
+ d->h = 16777216 * (unsigned int)((ORD8 *)p)[0]
+ + 65536 * (unsigned int)((ORD8 *)p)[1]
+ + 256 * (unsigned int)((ORD8 *)p)[2]
+ + (unsigned int)((ORD8 *)p)[3];
+ d->l = 16777216 * (unsigned int)((ORD8 *)p)[4]
+ + 65536 * (unsigned int)((ORD8 *)p)[5]
+ + 256 * (unsigned int)((ORD8 *)p)[6]
+ + (unsigned int)((ORD8 *)p)[7];
+}
+
+static int write_UInt64Number(icmUint64 *d, char *p) {
+ ((ORD8 *)p)[0] = (ORD8)(d->h >> 24);
+ ((ORD8 *)p)[1] = (ORD8)(d->h >> 16);
+ ((ORD8 *)p)[2] = (ORD8)(d->h >> 8);
+ ((ORD8 *)p)[3] = (ORD8)(d->h);
+ ((ORD8 *)p)[4] = (ORD8)(d->l >> 24);
+ ((ORD8 *)p)[5] = (ORD8)(d->l >> 16);
+ ((ORD8 *)p)[6] = (ORD8)(d->l >> 8);
+ ((ORD8 *)p)[7] = (ORD8)(d->l);
+ return 0;
+}
+
+static double read_U8Fixed8Number(char *p) {
+ ORD32 o32;
+ o32 = 256 * (ORD32)((ORD8 *)p)[0] /* Read big endian 16 bit unsigned */
+ + (ORD32)((ORD8 *)p)[1];
+ return (double)o32/256.0;
+}
+
+static int write_U8Fixed8Number(double d, char *p) {
+ ORD32 o32;
+ d = d * 256.0 + 0.5;
+ if (d >= 65536.0)
+ return 1;
+ if (d < 0.0)
+ return 1;
+ o32 = (ORD32)d;
+ ((ORD8 *)p)[0] = (ORD8)((o32) >> 8);
+ ((ORD8 *)p)[1] = (ORD8)((o32));
+ return 0;
+}
+
+static double read_U16Fixed16Number(char *p) {
+ ORD32 o32;
+ o32 = 16777216 * (ORD32)((ORD8 *)p)[0] /* Read big endian 32 bit unsigned */
+ + 65536 * (ORD32)((ORD8 *)p)[1]
+ + 256 * (ORD32)((ORD8 *)p)[2]
+ + (ORD32)((ORD8 *)p)[3];
+ return (double)o32/65536.0;
+}
+
+static int write_U16Fixed16Number(double d, char *p) {
+ ORD32 o32;
+ d = d * 65536.0 + 0.5;
+ if (d >= 4294967296.0)
+ return 1;
+ if (d < 0.0)
+ return 1;
+ o32 = (ORD32)d;
+ ((ORD8 *)p)[0] = (ORD8)((o32) >> 24);
+ ((ORD8 *)p)[1] = (ORD8)((o32) >> 16);
+ ((ORD8 *)p)[2] = (ORD8)((o32) >> 8);
+ ((ORD8 *)p)[3] = (ORD8)((o32));
+ return 0;
+}
+
+
+/* Signed numbers */
+static int read_SInt8Number(char *p) {
+ int rv;
+ rv = (int)((INR8 *)p)[0];
+ return rv;
+}
+
+static int write_SInt8Number(int d, char *p) {
+ if (d > 127)
+ return 1;
+ else if (d < -128)
+ return 1;
+ ((INR8 *)p)[0] = (INR8)d;
+ return 0;
+}
+
+static int read_SInt16Number(char *p) {
+ int rv;
+ rv = 256 * (int)((INR8 *)p)[0]
+ + (int)((ORD8 *)p)[1];
+ return rv;
+}
+
+static int write_SInt16Number(int d, char *p) {
+ if (d > 32767)
+ return 1;
+ else if (d < -32768)
+ return 1;
+ ((INR8 *)p)[0] = (INR8)(d >> 8);
+ ((ORD8 *)p)[1] = (ORD8)(d);
+ return 0;
+}
+
+static int read_SInt32Number(char *p) {
+ int rv;
+ rv = 16777216 * (int)((INR8 *)p)[0]
+ + 65536 * (int)((ORD8 *)p)[1]
+ + 256 * (int)((ORD8 *)p)[2]
+ + (int)((ORD8 *)p)[3];
+ return rv;
+}
+
+static int write_SInt32Number(int d, char *p) {
+ ((INR8 *)p)[0] = (INR8)(d >> 24);
+ ((ORD8 *)p)[1] = (ORD8)(d >> 16);
+ ((ORD8 *)p)[2] = (ORD8)(d >> 8);
+ ((ORD8 *)p)[3] = (ORD8)(d);
+ return 0;
+}
+
+static void read_SInt64Number(icmInt64 *d, char *p) {
+ d->h = 16777216 * (int)((INR8 *)p)[0]
+ + 65536 * (int)((ORD8 *)p)[1]
+ + 256 * (int)((ORD8 *)p)[2]
+ + (int)((ORD8 *)p)[3];
+ d->l = 16777216 * (unsigned int)((ORD8 *)p)[4]
+ + 65536 * (unsigned int)((ORD8 *)p)[5]
+ + 256 * (unsigned int)((ORD8 *)p)[6]
+ + (unsigned int)((ORD8 *)p)[7];
+}
+
+static int write_SInt64Number(icmInt64 *d, char *p) {
+ ((INR8 *)p)[0] = (INR8)(d->h >> 24);
+ ((ORD8 *)p)[1] = (ORD8)(d->h >> 16);
+ ((ORD8 *)p)[2] = (ORD8)(d->h >> 8);
+ ((ORD8 *)p)[3] = (ORD8)(d->h);
+ ((ORD8 *)p)[4] = (ORD8)(d->l >> 24);
+ ((ORD8 *)p)[5] = (ORD8)(d->l >> 16);
+ ((ORD8 *)p)[6] = (ORD8)(d->l >> 8);
+ ((ORD8 *)p)[7] = (ORD8)(d->l);
+ return 0;
+}
+
+static double read_S15Fixed16Number(char *p) {
+ INR32 i32;
+ i32 = 16777216 * (INR32)((INR8 *)p)[0] /* Read big endian 32 bit signed */
+ + 65536 * (INR32)((ORD8 *)p)[1]
+ + 256 * (INR32)((ORD8 *)p)[2]
+ + (INR32)((ORD8 *)p)[3];
+ return (double)i32/65536.0;
+}
+
+static int write_S15Fixed16Number(double d, char *p) {
+ INR32 i32;
+ d = floor(d * 65536.0 + 0.5); /* Beware! (int)(d + 0.5) doesn't work! */
+ if (d >= 2147483648.0)
+ return 1;
+ if (d < -2147483648.0)
+ return 1;
+ i32 = (INR32)d;
+ ((INR8 *)p)[0] = (INR8)((i32) >> 24); /* Write big endian 32 bit signed */
+ ((ORD8 *)p)[1] = (ORD8)((i32) >> 16);
+ ((ORD8 *)p)[2] = (ORD8)((i32) >> 8);
+ ((ORD8 *)p)[3] = (ORD8)((i32));
+ return 0;
+}
+
+/* Device coordinate as 8 bit value range 0.0 - 1.0 */
+static double read_DCS8Number(char *p) {
+ unsigned int rv;
+ rv = (unsigned int)((ORD8 *)p)[0];
+ return (double)rv/255.0;
+}
+
+static int write_DCS8Number(double d, char *p) {
+ ORD32 o32;
+ d = d * 255.0 + 0.5;
+ if (d >= 256.0)
+ return 1;
+ if (d < 0.0)
+ return 1;
+ o32 = (ORD32)d;
+ ((ORD8 *)p)[0] = (ORD8)(o32);
+ return 0;
+}
+
+/* Device coordinate as 16 bit value range 0.0 - 1.0 */
+static double read_DCS16Number(char *p) {
+ unsigned int rv;
+ rv = 256 * (unsigned int)((ORD8 *)p)[0]
+ + (unsigned int)((ORD8 *)p)[1];
+ return (double)rv/65535.0;
+}
+
+static int write_DCS16Number(double d, char *p) {
+ ORD32 o32;
+ d = d * 65535.0 + 0.5;
+ if (d >= 65536.0)
+ return 1;
+ if (d < 0.0)
+ return 1;
+ o32 = (ORD32)d;
+ ((ORD8 *)p)[0] = (ORD8)(o32 >> 8);
+ ((ORD8 *)p)[1] = (ORD8)(o32);
+ return 0;
+}
+
+static void Lut_Lut2XYZ(double *out, double *in);
+static void Lut_XYZ2Lut(double *out, double *in);
+static void Lut_Lut2Lab_8(double *out, double *in);
+static void Lut_Lab2Lut_8(double *out, double *in);
+static void Lut_Lut2LabV2_16(double *out, double *in);
+static void Lut_Lab2LutV2_16(double *out, double *in);
+static void Lut_Lut2LabV4_16(double *out, double *in);
+static void Lut_Lab2LutV4_16(double *out, double *in);
+
+static void Lut_Lut2Y(double *out, double *in);
+static void Lut_Y2Lut(double *out, double *in);
+static void Lut_Lut2L_8(double *out, double *in);
+static void Lut_L2Lut_8(double *out, double *in);
+static void Lut_Lut2LV2_16(double *out, double *in);
+static void Lut_L2LutV2_16(double *out, double *in);
+static void Lut_Lut2LV4_16(double *out, double *in);
+static void Lut_L2LutV4_16(double *out, double *in);
+
+//~~~~888888
+/* read a PCS number. PCS can be profile PCS, profile version Lab, */
+/* or a specific type of Lab, depending on the value of csig: */
+/* icmSigPCSData, icSigXYZData, icmSigLab8Data, icSigLabData, */
+/* icmSigLabV2Data or icmSigLabV4Data */
+/* Do nothing if not one of the above. */
+static void read_PCSNumber(icc *icp, icColorSpaceSignature csig, double pcs[3], char *p) {
+
+ if (csig == icmSigPCSData)
+ csig = icp->header->pcs;
+ if (csig == icSigLabData) {
+ if (icp->ver != 0)
+ csig = icmSigLabV4Data;
+ else
+ csig = icmSigLabV2Data;
+ }
+
+ if (csig == icmSigLab8Data) {
+ pcs[0] = read_DCS8Number(p);
+ pcs[1] = read_DCS8Number(p+1);
+ pcs[2] = read_DCS8Number(p+2);
+ } else {
+ pcs[0] = read_DCS16Number(p);
+ pcs[1] = read_DCS16Number(p+2);
+ pcs[2] = read_DCS16Number(p+4);
+ }
+ switch (csig) {
+ case icSigXYZData:
+ Lut_Lut2XYZ(pcs, pcs);
+ break;
+ case icmSigLab8Data:
+ Lut_Lut2Lab_8(pcs, pcs);
+ break;
+ case icmSigLabV2Data:
+ Lut_Lut2LabV2_16(pcs, pcs);
+ break;
+ case icmSigLabV4Data:
+ Lut_Lut2LabV4_16(pcs, pcs);
+ break;
+ default:
+ break;
+ }
+}
+
+/* write a PCS number. PCS can be profile PCS, profile version Lab, */
+/* or a specific type of Lab, depending on the value of csig: */
+/* icmSigPCSData, icSigXYZData, icmSigLab8Data, icSigLabData, */
+/* icmSigLabV2Data or icmSigLabV4Data */
+/* Return 1 if error */
+static int write_PCSNumber(icc *icp, icColorSpaceSignature csig, double pcs[3], char *p) {
+ double v[3];
+ int j;
+
+ if (csig == icmSigPCSData)
+ csig = icp->header->pcs;
+ if (csig == icSigLabData) {
+ if (icp->ver != 0)
+ csig = icmSigLabV4Data;
+ else
+ csig = icmSigLabV2Data;
+ }
+
+ switch (csig) {
+ case icSigXYZData:
+ Lut_XYZ2Lut(v, pcs);
+ break;
+ case icmSigLab8Data:
+ Lut_Lab2Lut_8(v, pcs);
+ break;
+ case icmSigLabV2Data:
+ Lut_Lab2LutV2_16(v, pcs);
+ break;
+ case icmSigLabV4Data:
+ Lut_Lab2LutV4_16(v, pcs);
+ break;
+ default:
+ return 1;
+ }
+ if (csig == icmSigLab8Data) {
+ for (j = 0; j < 3; j++) {
+ if (write_DCS8Number(v[j], p+j))
+ return 1;
+ }
+ } else {
+ for (j = 0; j < 3; j++) {
+ if (write_DCS16Number(v[j], p+(2 * j)))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Read a given primitive type. Return non-zero on error */
+/* (Not currently used internaly ?) */
+/* Public: */
+int read_Primitive(icc *icp, icmPrimType ptype, void *prim, char *p) {
+
+ switch(ptype) {
+ case icmUInt8Number:
+ *((unsigned int *)prim) = read_UInt8Number(p);
+ return 0;
+ case icmUInt16Number:
+ *((unsigned int *)prim) = read_UInt16Number(p);
+ return 0;
+ case icmUInt32Number:
+ *((unsigned int *)prim) = read_UInt32Number(p);
+ return 0;
+ case icmUInt64Number:
+ read_UInt64Number((icmUint64 *)prim, p);
+ return 0;
+ case icmU8Fixed8Number:
+ *((double *)prim) = read_U8Fixed8Number(p);
+ return 0;
+ case icmU16Fixed16Number:
+ *((double *)prim) = read_U16Fixed16Number(p);
+ return 0;
+ case icmSInt8Number:
+ *((int *)prim) = read_SInt8Number(p);
+ return 0;
+ case icmSInt16Number:
+ *((int *)prim) = read_SInt16Number(p);
+ return 0;
+ case icmSInt32Number:
+ *((int *)prim) = read_SInt32Number(p);
+ return 0;
+ case icmSInt64Number:
+ read_SInt64Number((icmInt64 *)prim, p);
+ return 0;
+ case icmS15Fixed16Number:
+ *((double *)prim) = read_S15Fixed16Number(p);
+ return 0;
+ case icmDCS8Number:
+ *((double *)prim) = read_DCS8Number(p);
+ return 0;
+ case icmDCS16Number:
+ *((double *)prim) = read_DCS16Number(p);
+ return 0;
+ case icmPCSNumber:
+ read_PCSNumber(icp, icmSigPCSData, ((double *)prim), p);
+ return 0;
+ case icmPCSXYZNumber:
+ read_PCSNumber(icp, icSigXYZData, ((double *)prim), p);
+ return 0;
+ case icmPCSLab8Number:
+ read_PCSNumber(icp, icmSigLab8Data, ((double *)prim), p);
+ return 0;
+ case icmPCSLabNumber:
+ read_PCSNumber(icp, icSigLabData, ((double *)prim), p);
+ return 0;
+ case icmPCSLabV2Number:
+ read_PCSNumber(icp, icmSigLabV2Data, ((double *)prim), p);
+ return 0;
+ case icmPCSLabV4Number:
+ read_PCSNumber(icp, icmSigLabV4Data, ((double *)prim), p);
+ return 0;
+ }
+
+ return 2;
+}
+
+/* Write a given primitive type. Return non-zero on error */
+/* (Not currently used internaly ?) */
+/* Public: */
+int write_Primitive(icc *icp, icmPrimType ptype, char *p, void *prim) {
+
+ switch(ptype) {
+ case icmUInt8Number:
+ return write_UInt8Number(*((unsigned int *)prim), p);
+ case icmUInt16Number:
+ return write_UInt16Number(*((unsigned int *)prim), p);
+ case icmUInt32Number:
+ return write_UInt32Number(*((unsigned int *)prim), p);
+ case icmUInt64Number:
+ return write_UInt64Number((icmUint64 *)prim, p);
+ case icmU8Fixed8Number:
+ return write_U8Fixed8Number(*((double *)prim), p);
+ case icmU16Fixed16Number:
+ return write_U16Fixed16Number(*((double *)prim), p);
+ case icmSInt8Number:
+ return write_SInt8Number(*((int *)prim), p);
+ case icmSInt16Number:
+ return write_SInt16Number(*((int *)prim), p);
+ case icmSInt32Number:
+ return write_SInt32Number(*((int *)prim), p);
+ case icmSInt64Number:
+ return write_SInt64Number((icmInt64 *)prim, p);
+ case icmS15Fixed16Number:
+ return write_S15Fixed16Number(*((double *)prim), p);
+ case icmDCS8Number:
+ return write_DCS8Number(*((double *)prim), p);
+ case icmDCS16Number:
+ return write_DCS16Number(*((double *)prim), p);
+ case icmPCSNumber:
+ return write_PCSNumber(icp, icmSigPCSData, ((double *)prim), p);
+ case icmPCSXYZNumber:
+ return write_PCSNumber(icp, icSigXYZData, ((double *)prim), p);
+ case icmPCSLab8Number:
+ return write_PCSNumber(icp, icmSigLab8Data, ((double *)prim), p);
+ case icmPCSLabNumber:
+ return write_PCSNumber(icp, icSigLabData, ((double *)prim), p);
+ case icmPCSLabV2Number:
+ return write_PCSNumber(icp, icmSigLabV2Data, ((double *)prim), p);
+ case icmPCSLabV4Number:
+ return write_PCSNumber(icp, icmSigLabV4Data, ((double *)prim), p);
+ }
+
+ return 2;
+}
+
+/* ---------------------------------------------------------- */
+/* Auiliary function - return a string that represents a tag */
+/* Note - returned buffers are static, can only be used 5 */
+/* times before buffers get reused. */
+char *tag2str(
+ int tag
+) {
+ int i;
+ static int si = 0; /* String buffer index */
+ static char buf[5][20]; /* String buffers */
+ char *bp;
+ unsigned char c[4];
+
+ bp = buf[si++];
+ si %= 5; /* Rotate through buffers */
+
+ c[0] = 0xff & (tag >> 24);
+ c[1] = 0xff & (tag >> 16);
+ c[2] = 0xff & (tag >> 8);
+ c[3] = 0xff & (tag >> 0);
+ for (i = 0; i < 4; i++) { /* Can we represent it as a string ? */
+ if (!isprint(c[i]))
+ break;
+ }
+ if (i < 4) { /* Not printable - use hex */
+ sprintf(bp,"0x%x",tag);
+ } else { /* Printable */
+ sprintf(bp,"'%c%c%c%c'",c[0],c[1],c[2],c[3]);
+ }
+ return bp;
+}
+
+/* Auiliary function - return a tag created from a string */
+/* Note there is also the icmMakeTag() macro */
+unsigned int str2tag(
+ const char *str
+) {
+ unsigned int tag;
+ tag = (((unsigned int)str[0]) << 24)
+ + (((unsigned int)str[1]) << 16)
+ + (((unsigned int)str[2]) << 8)
+ + (((unsigned int)str[3]));
+ return tag;
+}
+
+/* helper - return 1 if the string doesn't have a */
+/* null terminator within len, return 0 has null at exactly len, */
+/* and 2 if it has null before len. */
+/* Note: will return 1 if len == 0 */
+static int check_null_string(char *cp, int len) {
+ for (; len > 0; len--) {
+ if (cp[0] == '\000')
+ break;
+ cp++;
+ }
+ if (len == 0)
+ return 1;
+ if (len > 1)
+ return 2;
+ return 0;
+}
+
+/* helper - return 1 if the string doesn't have a */
+/* null terminator within len, return 0 has null at exactly len, */
+/* and 2 if it has null before len. */
+/* Note: will return 1 if len == 0 */
+/* Unicode version */
+static int check_null_string16(char *cp, int len) {
+ for (; len > 0; len--) { /* Length is in characters */
+ if (cp[0] == 0 && cp[1] == 0)
+ break;
+ cp += 2;
+ }
+ if (len == 0)
+ return 1;
+ if (len > 1)
+ return 2;
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Color Space to number of component conversion */
+/* Return 0 on error */
+static unsigned int number_ColorSpaceSignature(icColorSpaceSignature sig) {
+ switch(sig) {
+ case icSigXYZData:
+ return 3;
+ case icSigLabData:
+ return 3;
+ case icSigLuvData:
+ return 3;
+ case icSigYCbCrData:
+ return 3;
+ case icSigYxyData:
+ return 3;
+ case icSigRgbData:
+ return 3;
+ case icSigGrayData:
+ return 1;
+ case icSigHsvData:
+ return 3;
+ case icSigHlsData:
+ return 3;
+ case icSigCmykData:
+ return 4;
+ case icSigCmyData:
+ return 3;
+ case icSig2colorData:
+ return 2;
+ case icSig3colorData:
+ return 3;
+ case icSig4colorData:
+ return 4;
+ case icSig5colorData:
+ case icSigMch5Data:
+ return 5;
+ case icSig6colorData:
+ case icSigMch6Data:
+ return 6;
+ case icSig7colorData:
+ case icSigMch7Data:
+ return 7;
+ case icSig8colorData:
+ case icSigMch8Data:
+ return 8;
+ case icSig9colorData:
+ return 9;
+ case icSig10colorData:
+ return 10;
+ case icSig11colorData:
+ return 11;
+ case icSig12colorData:
+ return 12;
+ case icSig13colorData:
+ return 13;
+ case icSig14colorData:
+ return 14;
+ case icSig15colorData:
+ return 15;
+
+ /* Non-standard and Pseudo spaces */
+ case icmSigYData:
+ return 1;
+ case icmSigLData:
+ return 1;
+ case icmSigL8Data:
+ return 1;
+ case icmSigLV2Data:
+ return 1;
+ case icmSigLV4Data:
+ return 1;
+ case icmSigPCSData:
+ return 3;
+ case icmSigLab8Data:
+ return 3;
+ case icmSigLabV2Data:
+ return 3;
+ case icmSigLabV4Data:
+ return 3;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* Public version of above */
+
+/* Return the number of channels for the given color space. Return 0 if unknown. */
+ICCLIB_API unsigned int icmCSSig2nchan(icColorSpaceSignature sig) {
+ return number_ColorSpaceSignature(sig);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Return the individual channel names and number of channels give a colorspace signature. */
+/* Return 0 if it is not a colorspace that itself defines particular channels, */
+/* 1 if it is a colorant based colorspace, and 2 if it is not a colorant based space */
+static int chnames_ColorSpaceSignature(
+icColorSpaceSignature sig,
+char *cvals[] /* Pointers to return for each channel */
+) {
+ switch (sig) {
+ case icSigXYZData:
+ cvals[0] = "CIE X";
+ cvals[1] = "CIE Y";
+ cvals[2] = "CIE Z";
+ return 2;
+
+ case icSigLabData:
+ cvals[0] = "CIE L*";
+ cvals[1] = "CIE a*";
+ cvals[2] = "CIE b*";
+ return 2;
+
+ case icSigLuvData:
+ cvals[0] = "CIE L*";
+ cvals[1] = "CIE u*";
+ cvals[2] = "CIE v*";
+ return 2;
+
+ /* Usually ITU-R BT.601 (was CCIR 601) */
+ case icSigYCbCrData:
+ cvals[0] = "ITU Y";
+ cvals[1] = "ITU Cb";
+ cvals[2] = "ITU Cr";
+ return 2;
+
+ case icSigYxyData:
+ cvals[0] = "CIE Y";
+ cvals[1] = "CIE x";
+ cvals[2] = "CIE y";
+ return 2;
+
+ /* Alvy Ray Smith ? */
+ case icSigHsvData:
+ cvals[0] = "RGB Hue";
+ cvals[1] = "RGB Saturation";
+ cvals[2] = "RGB Value";
+ return 2;
+
+ /* GSPC ? */
+ case icSigHlsData:
+ cvals[0] = "RGB Hue";
+ cvals[1] = "RGB Lightness";
+ cvals[2] = "RGB Saturation";
+ return 2;
+
+ case icSigCmyData:
+ cvals[0] = "Cyan";
+ cvals[1] = "Magenta";
+ cvals[2] = "Yellow";
+ return 1;
+
+ case icSigRgbData:
+ cvals[0] = "Red";
+ cvals[1] = "Green";
+ cvals[2] = "Blue";
+ return 1;
+
+ case icSigCmykData:
+ cvals[0] = "Cyan";
+ cvals[1] = "Magenta";
+ cvals[2] = "Yellow";
+ cvals[3] = "Black";
+ return 1;
+
+
+ /* Non-standard and Pseudo spaces */
+ case icmSigYData:
+ cvals[0] = "CIE Y";
+ return 2;
+
+ case icmSigLData:
+ cvals[0] = "CIE L*";
+ return 2;
+
+ default:
+ break;
+
+ }
+ return 0;
+}
+
+/* Public version of above */
+
+/* Return the individual channel names and number of channels give a colorspace signature. */
+/* Return 0 if it is not a colorspace that itself defines particular channels, */
+/* 1 if it is a colorant based colorspace, and 2 if it is not a colorant based space */
+ICCLIB_API unsigned int icmCSSig2chanNames(icColorSpaceSignature sig, char *cvals[]) {
+
+ return chnames_ColorSpaceSignature(sig, cvals);
+}
+
+/* ------------------------------------------------------- */
+/* Flag dump functions */
+/* Note - returned buffers are static, can only be used 5 */
+/* times before buffers get reused. */
+
+/* Screening Encodings */
+static char *string_ScreenEncodings(unsigned int flags) {
+ static int si = 0; /* String buffer index */
+ static char buf[5][80]; /* String buffers */
+ char *bp, *cp;
+
+ cp = bp = buf[si++];
+ si %= 5; /* Rotate through buffers */
+
+ if (flags & icPrtrDefaultScreensTrue) {
+ sprintf(cp,"Default Screen");
+ } else {
+ sprintf(cp,"No Default Screen");
+ }
+ cp = cp + strlen(cp);
+ if (flags & icLinesPerInch) {
+ sprintf(cp,", Lines Per Inch");
+ } else {
+ sprintf(cp,", Lines Per cm");
+ }
+ cp = cp + strlen(cp);
+
+ return bp;
+}
+
+/* Device attributes */
+static char *string_DeviceAttributes(unsigned int flags) {
+ static int si = 0; /* String buffer index */
+ static char buf[5][80]; /* String buffers */
+ char *bp, *cp;
+
+ cp = bp = buf[si++];
+ si %= 5; /* Rotate through buffers */
+
+ if (flags & icTransparency) {
+ sprintf(cp,"Transparency");
+ } else {
+ sprintf(cp,"Reflective");
+ }
+ cp = cp + strlen(cp);
+ if (flags & icMatte) {
+ sprintf(cp,", Matte");
+ } else {
+ sprintf(cp,", Glossy");
+ }
+ cp = cp + strlen(cp);
+ if (flags & icNegative) {
+ sprintf(cp,", Negative");
+ } else {
+ sprintf(cp,", Positive");
+ }
+ cp = cp + strlen(cp);
+ if (flags & icBlackAndWhite) {
+ sprintf(cp,", BlackAndWhite");
+ } else {
+ sprintf(cp,", Color");
+ }
+ cp = cp + strlen(cp);
+
+ return bp;
+}
+
+/* Profile header flags */
+static char *string_ProfileHeaderFlags(unsigned int flags) {
+ static int si = 0; /* String buffer index */
+ static char buf[5][80]; /* String buffers */
+ char *bp, *cp;
+
+ cp = bp = buf[si++];
+ si %= 5; /* Rotate through buffers */
+
+ if (flags & icEmbeddedProfileTrue) {
+ sprintf(cp,"Embedded Profile");
+ } else {
+ sprintf(cp,"Not Embedded Profile");
+ }
+ cp = cp + strlen(cp);
+ if (flags & icUseWithEmbeddedDataOnly) {
+ sprintf(cp,", Use with embedded data only");
+ } else {
+ sprintf(cp,", Use anywhere");
+ }
+ cp = cp + strlen(cp);
+
+ return bp;
+}
+
+
+static char *string_AsciiOrBinaryData(unsigned int flags) {
+ static int si = 0; /* String buffer index */
+ static char buf[5][80]; /* String buffers */
+ char *bp, *cp;
+
+ cp = bp = buf[si++];
+ si %= 5; /* Rotate through buffers */
+
+ if (flags & icBinaryData) {
+ sprintf(cp,"Binary");
+ } else {
+ sprintf(cp,"Ascii");
+ }
+ cp = cp + strlen(cp);
+
+ return bp;
+}
+
+/* ------------------------------------------------------------ */
+/* Enumeration dump functions */
+/* Note - returned buffers are static, can only be used once */
+/* before buffers get reused if type is unknown. */
+
+/* public tags and sizes */
+static const char *string_TagSignature(icTagSignature sig) {
+ static char buf[80];
+ switch(sig) {
+ case icSigAToB0Tag:
+ return "AToB0 Multidimentional Transform";
+ case icSigAToB1Tag:
+ return "AToB1 Multidimentional Transform";
+ case icSigAToB2Tag:
+ return "AToB2 Multidimentional Transform";
+ case icSigBlueColorantTag:
+ return "Blue Colorant";
+ case icSigBlueTRCTag:
+ return "Blue Tone Reproduction Curve";
+ case icSigBToA0Tag:
+ return "BToA0 Multidimentional Transform";
+ case icSigBToA1Tag:
+ return "BToA1 Multidimentional Transform";
+ case icSigBToA2Tag:
+ return "BToA2 Multidimentional Transform";
+ case icSigCalibrationDateTimeTag:
+ return "Calibration Date & Time";
+ case icSigCharTargetTag:
+ return "Characterization Target";
+ case icSigCopyrightTag:
+ return "Copyright";
+ case icSigCrdInfoTag:
+ return "CRD Info";
+ case icSigDeviceMfgDescTag:
+ return "Device Manufacturer Description";
+ case icSigDeviceModelDescTag:
+ return "Device Model Description";
+ case icSigGamutTag:
+ return "Gamut";
+ case icSigGrayTRCTag:
+ return "Gray Tone Reproduction Curve";
+ case icSigGreenColorantTag:
+ return "Green Colorant";
+ case icSigGreenTRCTag:
+ return "Green Tone Reproduction Curve";
+ case icSigLuminanceTag:
+ return "Luminance";
+ case icSigMeasurementTag:
+ return "Measurement";
+ case icSigMediaBlackPointTag:
+ return "Media Black Point";
+ case icSigMediaWhitePointTag:
+ return "Media White Point";
+ case icSigNamedColorTag:
+ return "Named Color";
+ case icSigNamedColor2Tag:
+ return "Named Color 2";
+ case icSigPreview0Tag:
+ return "Preview0";
+ case icSigPreview1Tag:
+ return "Preview1";
+ case icSigPreview2Tag:
+ return "Preview2";
+ case icSigProfileDescriptionTag:
+ return "Profile Description";
+ case icSigProfileSequenceDescTag:
+ return "Profile Sequence";
+ case icSigPs2CRD0Tag:
+ return "PS Level 2 CRD perceptual";
+ case icSigPs2CRD1Tag:
+ return "PS Level 2 CRD colorimetric";
+ case icSigPs2CRD2Tag:
+ return "PS Level 2 CRD saturation";
+ case icSigPs2CRD3Tag:
+ return "PS Level 2 CRD absolute";
+ case icSigPs2CSATag:
+ return "PS Level 2 color space array";
+ case icSigPs2RenderingIntentTag:
+ return "PS Level 2 Rendering Intent";
+ case icSigRedColorantTag:
+ return "Red Colorant";
+ case icSigRedTRCTag:
+ return "Red Tone Reproduction Curve";
+ case icSigScreeningDescTag:
+ return "Screening Description";
+ case icSigScreeningTag:
+ return "Screening Attributes";
+ case icSigTechnologyTag:
+ return "Device Technology";
+ case icSigUcrBgTag:
+ return "Under Color Removal & Black Generation";
+ case icSigVideoCardGammaTag:
+ return "Video Card Gamma Curve";
+ case icSigViewingCondDescTag:
+ return "Viewing Condition Description";
+ case icSigViewingConditionsTag:
+ return "Viewing Condition Paramaters";
+ default:
+ sprintf(buf,"Unrecognized - %s",tag2str(sig));
+ return buf;
+ }
+}
+
+/* technology signature descriptions */
+static const char *string_TechnologySignature(icTechnologySignature sig) {
+ static char buf[80];
+ switch(sig) {
+ case icSigDigitalCamera:
+ return "Digital Camera";
+ case icSigFilmScanner:
+ return "Film Scanner";
+ case icSigReflectiveScanner:
+ return "Reflective Scanner";
+ case icSigInkJetPrinter:
+ return "InkJet Printer";
+ case icSigThermalWaxPrinter:
+ return "Thermal WaxPrinter";
+ case icSigElectrophotographicPrinter:
+ return "Electrophotographic Printer";
+ case icSigElectrostaticPrinter:
+ return "Electrostatic Printer";
+ case icSigDyeSublimationPrinter:
+ return "DyeSublimation Printer";
+ case icSigPhotographicPaperPrinter:
+ return "Photographic Paper Printer";
+ case icSigFilmWriter:
+ return "Film Writer";
+ case icSigVideoMonitor:
+ return "Video Monitor";
+ case icSigVideoCamera:
+ return "Video Camera";
+ case icSigProjectionTelevision:
+ return "Projection Television";
+ case icSigCRTDisplay:
+ return "Cathode Ray Tube Display";
+ case icSigPMDisplay:
+ return "Passive Matrix Display";
+ case icSigAMDisplay:
+ return "Active Matrix Display";
+ case icSigPhotoCD:
+ return "Photo CD";
+ case icSigPhotoImageSetter:
+ return "Photo ImageSetter";
+ case icSigGravure:
+ return "Gravure";
+ case icSigOffsetLithography:
+ return "Offset Lithography";
+ case icSigSilkscreen:
+ return "Silkscreen";
+ case icSigFlexography:
+ return "Flexography";
+ default:
+ sprintf(buf,"Unrecognized - %s",tag2str(sig));
+ return buf;
+ }
+}
+
+/* type signatures */
+static const char *string_TypeSignature(icTagTypeSignature sig) {
+ static char buf[80];
+ switch(sig) {
+ case icSigCurveType:
+ return "Curve";
+ case icSigDataType:
+ return "Data";
+ case icSigDateTimeType:
+ return "DateTime";
+ case icSigLut16Type:
+ return "Lut16";
+ case icSigLut8Type:
+ return "Lut8";
+ case icSigMeasurementType:
+ return "Measurement";
+ case icSigNamedColorType:
+ return "Named Color";
+ case icSigProfileSequenceDescType:
+ return "Profile Sequence Desc";
+ case icSigS15Fixed16ArrayType:
+ return "S15Fixed16 Array";
+ case icSigScreeningType:
+ return "Screening";
+ case icSigSignatureType:
+ return "Signature";
+ case icSigTextType:
+ return "Text";
+ case icSigTextDescriptionType:
+ return "Text Description";
+ case icSigU16Fixed16ArrayType:
+ return "U16Fixed16 Array";
+ case icSigUcrBgType:
+ return "Under Color Removal & Black Generation";
+ case icSigUInt16ArrayType:
+ return "UInt16 Array";
+ case icSigUInt32ArrayType:
+ return "UInt32 Array";
+ case icSigUInt64ArrayType:
+ return "UInt64 Array";
+ case icSigUInt8ArrayType:
+ return "UInt8 Array";
+ case icSigVideoCardGammaType:
+ return "Video Card Gamma";
+ case icSigViewingConditionsType:
+ return "Viewing Conditions";
+ case icSigXYZType:
+ return "XYZ (Array?)";
+ case icSigNamedColor2Type:
+ return "Named Color 2";
+ case icSigCrdInfoType:
+ return "CRD Info";
+ default:
+ sprintf(buf,"Unrecognized - %s",tag2str(sig));
+ return buf;
+ }
+}
+
+/* Color Space Signatures */
+static const char *string_ColorSpaceSignature(icColorSpaceSignature sig) {
+ static char buf[80];
+ switch(sig) {
+ case icSigXYZData:
+ return "XYZ";
+ case icSigLabData:
+ return "Lab";
+ case icSigLuvData:
+ return "Luv";
+ case icSigYCbCrData:
+ return "YCbCr";
+ case icSigYxyData:
+ return "Yxy";
+ case icSigRgbData:
+ return "RGB";
+ case icSigGrayData:
+ return "Gray";
+ case icSigHsvData:
+ return "HSV";
+ case icSigHlsData:
+ return "HLS";
+ case icSigCmykData:
+ return "CMYK";
+ case icSigCmyData:
+ return "CMY";
+ case icSig2colorData:
+ return "2 Color";
+ case icSig3colorData:
+ return "3 Color";
+ case icSig4colorData:
+ return "4 Color";
+ case icSig5colorData:
+ case icSigMch5Data:
+ return "5 Color";
+ case icSig6colorData:
+ case icSigMch6Data:
+ return "6 Color";
+ case icSig7colorData:
+ case icSigMch7Data:
+ return "7 Color";
+ case icSig8colorData:
+ case icSigMch8Data:
+ return "8 Color";
+ case icSig9colorData:
+ return "9 Color";
+ case icSig10colorData:
+ return "10 Color";
+ case icSig11colorData:
+ return "11 Color";
+ case icSig12colorData:
+ return "12 Color";
+ case icSig13colorData:
+ return "13 Color";
+ case icSig14colorData:
+ return "14 Color";
+ case icSig15colorData:
+ return "15 Color";
+
+ /* Non-standard and Pseudo spaces */
+ case icmSigYData:
+ return "Y";
+ case icmSigLData:
+ return "L";
+ case icmSigL8Data:
+ return "L";
+ case icmSigLV2Data:
+ return "L";
+ case icmSigLV4Data:
+ return "L";
+ case icmSigPCSData:
+ return "PCS";
+ case icmSigLab8Data:
+ return "Lab";
+ case icmSigLabV2Data:
+ return "Lab";
+ case icmSigLabV4Data:
+ return "Lab";
+
+ default:
+ sprintf(buf,"Unrecognized - %s",tag2str(sig));
+ return buf;
+ }
+}
+
+#ifdef NEVER
+/* Public version of above */
+char *ColorSpaceSignature2str(icColorSpaceSignature sig) {
+ return string_ColorSpaceSignature(sig);
+}
+#endif
+
+
+/* profileClass enumerations */
+static const char *string_ProfileClassSignature(icProfileClassSignature sig) {
+ static char buf[80];
+ switch(sig) {
+ case icSigInputClass:
+ return "Input";
+ case icSigDisplayClass:
+ return "Display";
+ case icSigOutputClass:
+ return "Output";
+ case icSigLinkClass:
+ return "Link";
+ case icSigAbstractClass:
+ return "Abstract";
+ case icSigColorSpaceClass:
+ return "Color Space";
+ case icSigNamedColorClass:
+ return "Named Color";
+ default:
+ sprintf(buf,"Unrecognized - %s",tag2str(sig));
+ return buf;
+ }
+}
+
+/* Platform Signatures */
+static const char *string_PlatformSignature(icPlatformSignature sig) {
+ static char buf[80];
+ switch(sig) {
+ case icSigMacintosh:
+ return "Macintosh";
+ case icSigMicrosoft:
+ return "Microsoft";
+ case icSigSolaris:
+ return "Solaris";
+ case icSigSGI:
+ return "SGI";
+ case icSigTaligent:
+ return "Taligent";
+ case icmSig_nix:
+ return "*nix";
+ default:
+ sprintf(buf,"Unrecognized - %s",tag2str(sig));
+ return buf;
+ }
+}
+
+/* Measurement Geometry, used in the measurmentType tag */
+static const char *string_MeasurementGeometry(icMeasurementGeometry sig) {
+ static char buf[30];
+ switch(sig) {
+ case icGeometryUnknown:
+ return "Unknown";
+ case icGeometry045or450:
+ return "0/45 or 45/0";
+ case icGeometry0dord0:
+ return "0/d or d/0";
+ default:
+ sprintf(buf,"Unrecognized - 0x%x",sig);
+ return buf;
+ }
+}
+
+/* Rendering Intents, used in the profile header */
+static const char *string_RenderingIntent(icRenderingIntent sig) {
+ static char buf[30];
+ switch(sig) {
+ case icPerceptual:
+ return "Perceptual";
+ case icRelativeColorimetric:
+ return "Relative Colorimetric";
+ case icSaturation:
+ return "Saturation";
+ case icAbsoluteColorimetric:
+ return "Absolute Colorimetric";
+ case icmAbsolutePerceptual: /* icclib specials */
+ return "Absolute Perceptual";
+ case icmAbsoluteSaturation: /* icclib specials */
+ return "Absolute Saturation";
+ case icmDefaultIntent: /* icclib specials */
+ return "Default Intent";
+ default:
+ sprintf(buf,"Unrecognized - 0x%x",sig);
+ return buf;
+ }
+}
+
+/* Transform Lookup function */
+static const char *string_LookupFunc(icmLookupFunc sig) {
+ static char buf[30];
+ switch(sig) {
+ case icmFwd:
+ return "Forward";
+ case icmBwd:
+ return "Backward";
+ case icmGamut:
+ return "Gamut";
+ case icmPreview:
+ return "Preview";
+ default:
+ sprintf(buf,"Unrecognized - 0x%x",sig);
+ return buf;
+ }
+}
+
+
+/* Different Spot Shapes currently defined, used for screeningType */
+static const char *string_SpotShape(icSpotShape sig) {
+ static char buf[30];
+ switch(sig) {
+ case icSpotShapeUnknown:
+ return "Unknown";
+ case icSpotShapePrinterDefault:
+ return "Printer Default";
+ case icSpotShapeRound:
+ return "Round";
+ case icSpotShapeDiamond:
+ return "Diamond";
+ case icSpotShapeEllipse:
+ return "Ellipse";
+ case icSpotShapeLine:
+ return "Line";
+ case icSpotShapeSquare:
+ return "Square";
+ case icSpotShapeCross:
+ return "Cross";
+ default:
+ sprintf(buf,"Unrecognized - 0x%x",sig);
+ return buf;
+ }
+}
+
+/* Standard Observer, used in the measurmentType tag */
+static const char *string_StandardObserver(icStandardObserver sig) {
+ static char buf[30];
+ switch(sig) {
+ case icStdObsUnknown:
+ return "Unknown";
+ case icStdObs1931TwoDegrees:
+ return "1931 Two Degrees";
+ case icStdObs1964TenDegrees:
+ return "1964 Ten Degrees";
+ default:
+ sprintf(buf,"Unrecognized - 0x%x",sig);
+ return buf;
+ }
+}
+
+/* Pre-defined illuminants, used in measurement and viewing conditions type */
+static const char *string_Illuminant(icIlluminant sig) {
+ static char buf[30];
+ switch(sig) {
+ case icIlluminantUnknown:
+ return "Unknown";
+ case icIlluminantD50:
+ return "D50";
+ case icIlluminantD65:
+ return "D65";
+ case icIlluminantD93:
+ return "D93";
+ case icIlluminantF2:
+ return "F2";
+ case icIlluminantD55:
+ return "D55";
+ case icIlluminantA:
+ return "A";
+ case icIlluminantEquiPowerE:
+ return "Equi-Power(E)";
+ case icIlluminantF8:
+ return "F8";
+ default:
+ sprintf(buf,"Unrecognized - 0x%x",sig);
+ return buf;
+ }
+}
+
+/* Return a text abreviation of a color lookup algorithm */
+static const char *string_LuAlg(icmLuAlgType alg) {
+ static char buf[80];
+
+ switch(alg) {
+ case icmMonoFwdType:
+ return "MonoFwd";
+ case icmMonoBwdType:
+ return "MonoBwd";
+ case icmMatrixFwdType:
+ return "MatrixFwd";
+ case icmMatrixBwdType:
+ return "MatrixBwd";
+ case icmLutType:
+ return "Lut";
+ default:
+ sprintf(buf,"Unrecognized - %d",alg);
+ return buf;
+ }
+}
+
+/* Return a string description of the given enumeration value */
+/* Public: */
+const char *icm2str(icmEnumType etype, int enumval) {
+
+ switch(etype) {
+ case icmScreenEncodings:
+ return string_ScreenEncodings((unsigned int) enumval);
+ case icmDeviceAttributes:
+ return string_DeviceAttributes((unsigned int) enumval);
+ case icmProfileHeaderFlags:
+ return string_ProfileHeaderFlags((unsigned int) enumval);
+ case icmAsciiOrBinaryData:
+ return string_AsciiOrBinaryData((unsigned int) enumval);
+ case icmTagSignature:
+ return string_TagSignature((icTagSignature) enumval);
+ case icmTechnologySignature:
+ return string_TechnologySignature((icTechnologySignature) enumval);
+ case icmTypeSignature:
+ return string_TypeSignature((icTagTypeSignature) enumval);
+ case icmColorSpaceSignature:
+ return string_ColorSpaceSignature((icColorSpaceSignature) enumval);
+ case icmProfileClassSignature:
+ return string_ProfileClassSignature((icProfileClassSignature) enumval);
+ case icmPlatformSignature:
+ return string_PlatformSignature((icPlatformSignature) enumval);
+ case icmMeasurementGeometry:
+ return string_MeasurementGeometry((icMeasurementGeometry) enumval);
+ case icmRenderingIntent:
+ return string_RenderingIntent((icRenderingIntent) enumval);
+ case icmTransformLookupFunc:
+ return string_LookupFunc((icmLookupFunc) enumval);
+ case icmSpotShape:
+ return string_SpotShape((icSpotShape) enumval);
+ case icmStandardObserver:
+ return string_StandardObserver((icStandardObserver) enumval);
+ case icmIlluminant:
+ return string_Illuminant((icIlluminant) enumval);
+ case icmLuAlg:
+ return string_LuAlg((icmLuAlgType) enumval);
+ default:
+ return "enum2str got unknown type";
+ }
+}
+
+/* ========================================================== */
+/* Object I/O routines */
+/* ========================================================== */
+/* icmUnknown object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmUnknown_get_size(
+ icmBase *pp
+) {
+ icmUnknown *p = (icmUnknown *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 1); /* 1 byte for each unknown data */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmUnknown_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmUnknown *p = (icmUnknown *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmUnknown_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUnknown_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmUnknown_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/1; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ p->uttype = (icTagTypeSignature)read_SInt32Number(bp);
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 1) {
+ p->data[i] = read_UInt8Number(bp);
+ }
+ icp->al->free(p->icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmUnknown_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmUnknown *p = (icmUnknown *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmUnknown_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUnknown_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->uttype,bp)) != 0) {
+ sprintf(icp->err,"icmUnknown_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ bp += 8; /* Skip padding */
+
+ /* Write all the data to the buffer */
+ for (i = 0; i < p->size; i++, bp += 1) {
+ if ((rv = write_UInt8Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmUnknown_write: write_UInt8umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmUnknown_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmUnknown_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmUnknown *p = (icmUnknown *)pp;
+ unsigned int i, ii, r, ph;
+
+ if (verb <= 1)
+ return;
+
+ op->gprintf(op,"Unknown:\n");
+ op->gprintf(op," Payload size in bytes = %u\n",p->size);
+
+ /* Print one row of binary and ASCII interpretation if verb == 2, All if == 3 */
+ /* else print all of it. */
+ ii = i = ph = 0;
+ for (r = 1;; r++) { /* count rows */
+ int c = 1; /* Character location */
+
+ c = 1;
+ if (ph != 0) { /* Print ASCII under binary */
+ op->gprintf(op," ");
+ i = ii; /* Swap */
+ c += 12;
+ } else {
+ op->gprintf(op," 0x%04lx: ",i);
+ ii = i; /* Swap */
+ c += 12;
+ }
+ while (i < p->size && c < 60) {
+ if (ph == 0)
+ op->gprintf(op,"%02x ",p->data[i]);
+ else {
+ if (isprint(p->data[i]))
+ op->gprintf(op,"%c ",p->data[i]);
+ else
+ op->gprintf(op," ",p->data[i]);
+ }
+ c += 3;
+ i++;
+ }
+ if (ph == 0 || i < p->size)
+ op->gprintf(op,"\n");
+
+ if (ph == 1 && i >= p->size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (ph == 1 && r > 1 && verb < 3) {
+ op->gprintf(op," ...\n");
+ break; /* Print 1 row if not verbose */
+ }
+
+ if (ph == 0)
+ ph = 1;
+ else
+ ph = 0;
+
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmUnknown_allocate(
+ icmBase *pp
+) {
+ icmUnknown *p = (icmUnknown *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(unsigned char))) {
+ sprintf(icp->err,"icmUnknown_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (unsigned char *) icp->al->calloc(icp->al, p->size, sizeof(unsigned char)))
+ == NULL) {
+ sprintf(icp->err,"icmUnknown_alloc: malloc() of icmUnknown data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmUnknown_delete(
+ icmBase *pp
+) {
+ icmUnknown *p = (icmUnknown *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmUnknown(
+ icc *icp
+) {
+ icmUnknown *p;
+ if ((p = (icmUnknown *) icp->al->calloc(icp->al,1,sizeof(icmUnknown))) == NULL)
+ return NULL;
+ p->ttype = icmSigUnknownType;
+ p->uttype = icmSigUnknownType;
+ p->refcount = 1;
+ p->get_size = icmUnknown_get_size;
+ p->read = icmUnknown_read;
+ p->write = icmUnknown_write;
+ p->dump = icmUnknown_dump;
+ p->allocate = icmUnknown_allocate;
+ p->del = icmUnknown_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmUInt8Array object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmUInt8Array_get_size(
+ icmBase *pp
+) {
+ icmUInt8Array *p = (icmUInt8Array *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 1); /* 1 byte for each UInt8 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmUInt8Array_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmUInt8Array *p = (icmUInt8Array *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmUInt8Array_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt8Array_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmUInt8Array_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/1; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ icp->al->free(icp->al, buf);
+ sprintf(icp->err,"icmUInt8Array_read: Wrong tag type for icmUInt8Array");
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 1) {
+ p->data[i] = read_UInt8Number(bp);
+ }
+ icp->al->free(p->icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmUInt8Array_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmUInt8Array *p = (icmUInt8Array *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmUInt8Array_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt8Array_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmUInt8Array_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ bp += 8; /* Skip padding */
+
+ /* Write all the data to the buffer */
+ for (i = 0; i < p->size; i++, bp += 1) {
+ if ((rv = write_UInt8Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmUInt8Array_write: write_UInt8umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmUInt8Array_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmUInt8Array_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmUInt8Array *p = (icmUInt8Array *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"UInt8Array:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %lu: %u\n",i,p->data[i]);
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmUInt8Array_allocate(
+ icmBase *pp
+) {
+ icmUInt8Array *p = (icmUInt8Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(unsigned int))) {
+ sprintf(icp->err,"icmUInt8Array_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (unsigned int *) icp->al->calloc(icp->al, p->size, sizeof(unsigned int)))
+ == NULL) {
+ sprintf(icp->err,"icmUInt8Array_alloc: malloc() of icmUInt8Array data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmUInt8Array_delete(
+ icmBase *pp
+) {
+ icmUInt8Array *p = (icmUInt8Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmUInt8Array(
+ icc *icp
+) {
+ icmUInt8Array *p;
+ if ((p = (icmUInt8Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt8Array))) == NULL)
+ return NULL;
+ p->ttype = icSigUInt8ArrayType;
+ p->refcount = 1;
+ p->get_size = icmUInt8Array_get_size;
+ p->read = icmUInt8Array_read;
+ p->write = icmUInt8Array_write;
+ p->dump = icmUInt8Array_dump;
+ p->allocate = icmUInt8Array_allocate;
+ p->del = icmUInt8Array_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmUInt16Array object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmUInt16Array_get_size(
+ icmBase *pp
+) {
+ icmUInt16Array *p = (icmUInt16Array *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 2); /* 2 bytes for each UInt16 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmUInt16Array_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmUInt16Array *p = (icmUInt16Array *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmUInt16Array_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt16Array_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmUInt16Array_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/2; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmUInt16Array_read: Wrong tag type for icmUInt16Array");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 2) {
+ p->data[i] = read_UInt16Number(bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmUInt16Array_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmUInt16Array *p = (icmUInt16Array *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmUInt16Array_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt16Array_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmUInt16Array_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ for (i = 0; i < p->size; i++, bp += 2) {
+ if ((rv = write_UInt16Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmUInt16Array_write: write_UInt16umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmUInt16Array_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmUInt16Array_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmUInt16Array *p = (icmUInt16Array *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"UInt16Array:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %lu: %u\n",i,p->data[i]);
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmUInt16Array_allocate(
+ icmBase *pp
+) {
+ icmUInt16Array *p = (icmUInt16Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(unsigned int))) {
+ sprintf(icp->err,"icmUInt16Array_alloc:: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (unsigned int *) icp->al->calloc(icp->al, p->size, sizeof(unsigned int)))
+ == NULL) {
+ sprintf(icp->err,"icmUInt16Array_alloc: malloc() of icmUInt16Array data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmUInt16Array_delete(
+ icmBase *pp
+) {
+ icmUInt16Array *p = (icmUInt16Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmUInt16Array(
+ icc *icp
+) {
+ icmUInt16Array *p;
+ if ((p = (icmUInt16Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt16Array))) == NULL)
+ return NULL;
+ p->ttype = icSigUInt16ArrayType;
+ p->refcount = 1;
+ p->get_size = icmUInt16Array_get_size;
+ p->read = icmUInt16Array_read;
+ p->write = icmUInt16Array_write;
+ p->dump = icmUInt16Array_dump;
+ p->allocate = icmUInt16Array_allocate;
+ p->del = icmUInt16Array_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmUInt32Array object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmUInt32Array_get_size(
+ icmBase *pp
+) {
+ icmUInt32Array *p = (icmUInt32Array *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 4); /* 4 bytes for each UInt32 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmUInt32Array_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmUInt32Array *p = (icmUInt32Array *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmUInt32Array_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt32Array_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmUInt32Array_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/4; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmUInt32Array_read: Wrong tag type for icmUInt32Array");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 4) {
+ p->data[i] = read_UInt32Number(bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmUInt32Array_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmUInt32Array *p = (icmUInt32Array *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmUInt32Array_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt32Array_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmUInt32Array_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ for (i = 0; i < p->size; i++, bp += 4) {
+ if ((rv = write_UInt32Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmUInt32Array_write: write_UInt32umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmUInt32Array_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmUInt32Array_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmUInt32Array *p = (icmUInt32Array *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"UInt32Array:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %lu: %u\n",i,p->data[i]);
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmUInt32Array_allocate(
+ icmBase *pp
+) {
+ icmUInt32Array *p = (icmUInt32Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(unsigned int))) {
+ sprintf(icp->err,"icmUInt32Array_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (unsigned int *) icp->al->calloc(icp->al, p->size, sizeof(unsigned int)))
+ == NULL) {
+ sprintf(icp->err,"icmUInt32Array_alloc: malloc() of icmUInt32Array data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmUInt32Array_delete(
+ icmBase *pp
+) {
+ icmUInt32Array *p = (icmUInt32Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmUInt32Array(
+ icc *icp
+) {
+ icmUInt32Array *p;
+ if ((p = (icmUInt32Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt32Array))) == NULL)
+ return NULL;
+ p->ttype = icSigUInt32ArrayType;
+ p->refcount = 1;
+ p->get_size = icmUInt32Array_get_size;
+ p->read = icmUInt32Array_read;
+ p->write = icmUInt32Array_write;
+ p->dump = icmUInt32Array_dump;
+ p->allocate = icmUInt32Array_allocate;
+ p->del = icmUInt32Array_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmUInt64Array object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmUInt64Array_get_size(
+ icmBase *pp
+) {
+ icmUInt64Array *p = (icmUInt64Array *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 8); /* 8 bytes for each UInt64 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmUInt64Array_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmUInt64Array *p = (icmUInt64Array *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmUInt64Array_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt64Array_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmUInt64Array_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/8; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmUInt64Array_read: Wrong tag type for icmUInt64Array");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 8) {
+ read_UInt64Number(&p->data[i], bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmUInt64Array_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmUInt64Array *p = (icmUInt64Array *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmUInt64Array_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt64Array_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmUInt64Array_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ for (i = 0; i < p->size; i++, bp += 8) {
+ if ((rv = write_UInt64Number(&p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmUInt64Array_write: write_UInt64umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmUInt64Array_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmUInt64Array_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmUInt64Array *p = (icmUInt64Array *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"UInt64Array:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %lu: h=%lu, l=%lu\n",i,p->data[i].h,p->data[i].l);
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmUInt64Array_allocate(
+ icmBase *pp
+) {
+ icmUInt64Array *p = (icmUInt64Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(icmUint64))) {
+ sprintf(icp->err,"icmUInt64Array_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (icmUint64 *) icp->al->calloc(icp->al, p->size, sizeof(icmUint64)))
+ == NULL) {
+ sprintf(icp->err,"icmUInt64Array_alloc: malloc() of icmUInt64Array data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmUInt64Array_delete(
+ icmBase *pp
+) {
+ icmUInt64Array *p = (icmUInt64Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmUInt64Array(
+ icc *icp
+) {
+ icmUInt64Array *p;
+ if ((p = (icmUInt64Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt64Array))) == NULL)
+ return NULL;
+ p->ttype = icSigUInt64ArrayType;
+ p->refcount = 1;
+ p->get_size = icmUInt64Array_get_size;
+ p->read = icmUInt64Array_read;
+ p->write = icmUInt64Array_write;
+ p->dump = icmUInt64Array_dump;
+ p->allocate = icmUInt64Array_allocate;
+ p->del = icmUInt64Array_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmU16Fixed16Array object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmU16Fixed16Array_get_size(
+ icmBase *pp
+) {
+ icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 4); /* 4 byte for each U16Fixed16 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmU16Fixed16Array_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmU16Fixed16Array_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmU16Fixed16Array_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmU16Fixed16Array_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/4; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmU16Fixed16Array_read: Wrong tag type for icmU16Fixed16Array");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 4) {
+ p->data[i] = read_U16Fixed16Number(bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmU16Fixed16Array_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmU16Fixed16Array_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmU16Fixed16Array_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmU16Fixed16Array_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ for (i = 0; i < p->size; i++, bp += 4) {
+ if ((rv = write_U16Fixed16Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmU16Fixed16Array_write: write_U16Fixed16umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmU16Fixed16Array_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmU16Fixed16Array_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"U16Fixed16Array:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %lu: %f\n",i,p->data[i]);
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmU16Fixed16Array_allocate(
+ icmBase *pp
+) {
+ icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(double))) {
+ sprintf(icp->err,"icmU16Fixed16Array_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (double *) icp->al->calloc(icp->al, p->size, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmU16Fixed16Array_alloc: malloc() of icmU16Fixed16Array data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmU16Fixed16Array_delete(
+ icmBase *pp
+) {
+ icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmU16Fixed16Array(
+ icc *icp
+) {
+ icmU16Fixed16Array *p;
+ if ((p = (icmU16Fixed16Array *) icp->al->calloc(icp->al,1,sizeof(icmU16Fixed16Array))) == NULL)
+ return NULL;
+ p->ttype = icSigU16Fixed16ArrayType;
+ p->refcount = 1;
+ p->get_size = icmU16Fixed16Array_get_size;
+ p->read = icmU16Fixed16Array_read;
+ p->write = icmU16Fixed16Array_write;
+ p->dump = icmU16Fixed16Array_dump;
+ p->allocate = icmU16Fixed16Array_allocate;
+ p->del = icmU16Fixed16Array_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmS15Fixed16Array object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmS15Fixed16Array_get_size(
+ icmBase *pp
+) {
+ icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 4); /* 4 byte for each S15Fixed16 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmS15Fixed16Array_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmS15Fixed16Array_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmS15Fixed16Array_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmS15Fixed16Array_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/4; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmS15Fixed16Array_read: Wrong tag type for icmS15Fixed16Array");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 4) {
+ p->data[i] = read_S15Fixed16Number(bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmS15Fixed16Array_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmS15Fixed16Array_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmS15Fixed16Array_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmS15Fixed16Array_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ for (i = 0; i < p->size; i++, bp += 4) {
+ if ((rv = write_S15Fixed16Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmS15Fixed16Array_write: write_S15Fixed16umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmS15Fixed16Array_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmS15Fixed16Array_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"S15Fixed16Array:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %lu: %f\n",i,p->data[i]);
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmS15Fixed16Array_allocate(
+ icmBase *pp
+) {
+ icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(double))) {
+ sprintf(icp->err,"icmS15Fixed16Array_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (double *) icp->al->calloc(icp->al, p->size, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmS15Fixed16Array_alloc: malloc() of icmS15Fixed16Array data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmS15Fixed16Array_delete(
+ icmBase *pp
+) {
+ icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmS15Fixed16Array(
+ icc *icp
+) {
+ icmS15Fixed16Array *p;
+ if ((p = (icmS15Fixed16Array *) icp->al->calloc(icp->al,1,sizeof(icmS15Fixed16Array))) == NULL)
+ return NULL;
+ p->ttype = icSigS15Fixed16ArrayType;
+ p->refcount = 1;
+ p->get_size = icmS15Fixed16Array_get_size;
+ p->read = icmS15Fixed16Array_read;
+ p->write = icmS15Fixed16Array_write;
+ p->dump = icmS15Fixed16Array_dump;
+ p->allocate = icmS15Fixed16Array_allocate;
+ p->del = icmS15Fixed16Array_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+
+/* Data conversion support functions */
+static int write_XYZNumber(icmXYZNumber *p, char *d) {
+ int rv;
+ if ((rv = write_S15Fixed16Number(p->X, d + 0)) != 0)
+ return rv;
+ if ((rv = write_S15Fixed16Number(p->Y, d + 4)) != 0)
+ return rv;
+ if ((rv = write_S15Fixed16Number(p->Z, d + 8)) != 0)
+ return rv;
+ return 0;
+}
+
+static int read_XYZNumber(icmXYZNumber *p, char *d) {
+ p->X = read_S15Fixed16Number(d + 0);
+ p->Y = read_S15Fixed16Number(d + 4);
+ p->Z = read_S15Fixed16Number(d + 8);
+ return 0;
+}
+
+
+/* Helper: Return a string that shows the XYZ number value */
+static char *string_XYZNumber(icmXYZNumber *p) {
+ static char buf[40];
+
+ sprintf(buf,"%f, %f, %f", p->X, p->Y, p->Z);
+ return buf;
+}
+
+/* Helper: Return a string that shows the XYZ number value, */
+/* and the Lab D50 number in paren. Note the buffer will be re-used on every call. */
+static char *string_XYZNumber_and_Lab(icmXYZNumber *p) {
+ static char buf[100];
+ double lab[3];
+ lab[0] = p->X;
+ lab[1] = p->Y;
+ lab[2] = p->Z;
+ icmXYZ2Lab(&icmD50, lab, lab);
+ snprintf(buf,sizeof(buf),"%f, %f, %f [Lab %f, %f, %f]", p->X, p->Y, p->Z, lab[0], lab[1], lab[2]);
+ return buf;
+}
+
+/* icmXYZArray object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmXYZArray_get_size(
+ icmBase *pp
+) {
+ icmXYZArray *p = (icmXYZArray *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 12); /* 12 bytes for each XYZ */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmXYZArray_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmXYZArray *p = (icmXYZArray *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmXYZArray_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmXYZArray_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmXYZArray_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/12; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmXYZArray_read: Wrong tag type for icmXYZArray");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 12) {
+ read_XYZNumber(&p->data[i], bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmXYZArray_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmXYZArray *p = (icmXYZArray *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmXYZArray_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmXYZArray_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmXYZArray_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ for (i = 0; i < p->size; i++, bp += 12) {
+ if ((rv = write_XYZNumber(&p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmXYZArray_write: write_XYZumber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmXYZArray_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmXYZArray_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmXYZArray *p = (icmXYZArray *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"XYZArray:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++) {
+ op->gprintf(op," %lu: %s\n",i,string_XYZNumber_and_Lab(&p->data[i]));
+
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmXYZArray_allocate(
+ icmBase *pp
+) {
+ icmXYZArray *p = (icmXYZArray *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(icmXYZNumber))) {
+ sprintf(icp->err,"icmXYZArray_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (icmXYZNumber *) icp->al->malloc(icp->al, sat_mul(p->size, sizeof(icmXYZNumber)))) == NULL) {
+ sprintf(icp->err,"icmXYZArray_alloc: malloc() of icmXYZArray data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmXYZArray_delete(
+ icmBase *pp
+) {
+ icmXYZArray *p = (icmXYZArray *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmXYZArray(
+ icc *icp
+) {
+ icmXYZArray *p;
+ if ((p = (icmXYZArray *) icp->al->calloc(icp->al,1,sizeof(icmXYZArray))) == NULL)
+ return NULL;
+ p->ttype = icSigXYZArrayType;
+ p->refcount = 1;
+ p->get_size = icmXYZArray_get_size;
+ p->read = icmXYZArray_read;
+ p->write = icmXYZArray_write;
+ p->dump = icmXYZArray_dump;
+ p->allocate = icmXYZArray_allocate;
+ p->del = icmXYZArray_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmCurve object */
+
+/* Do a forward lookup through the curve */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+static int icmCurve_lookup_fwd(
+ icmCurve *p,
+ double *out,
+ double *in
+) {
+ int rv = 0;
+ if (p->flag == icmCurveLin) {
+ *out = *in;
+ } else if (p->flag == icmCurveGamma) {
+ double val = *in;
+ if (val <= 0.0)
+ *out = 0.0;
+ else
+ *out = pow(val, p->data[0]);
+ } else if (p->size == 0) { /* Table of 0 size */
+ *out = *in;
+ } else { /* Use linear interpolation */
+ unsigned int ix;
+ double val, w;
+ double inputEnt_1 = (double)(p->size-1);
+
+ val = *in * inputEnt_1;
+ if (val < 0.0) {
+ val = 0.0;
+ rv |= 1;
+ } else if (val > inputEnt_1) {
+ val = inputEnt_1;
+ rv |= 1;
+ }
+ ix = (unsigned int)floor(val); /* Coordinate */
+ if (ix > (p->size-2))
+ ix = (p->size-2);
+ w = val - (double)ix; /* weight */
+ val = p->data[ix];
+ *out = val + w * (p->data[ix+1] - val);
+ }
+ return rv;
+}
+
+/* - - - - - - - - - - - - */
+/* Support for reverse interpolation of 1D lookup tables */
+
+/* Create a reverse curve lookup acceleration table */
+/* return non-zero on error, 2 = malloc error. */
+static int icmTable_setup_bwd(
+ icc *icp, /* Base icc object */
+ icmRevTable *rt, /* Reverse table data to setup */
+ unsigned int size, /* Size of fwd table */
+ double *data /* Table */
+) {
+ unsigned int i;
+
+ rt->size = size; /* Stash pointers to these away */
+ rt->data = data;
+
+ /* Find range of output values */
+ rt->rmin = 1e300;
+ rt->rmax = -1e300;
+ for (i = 0; i < rt->size; i++) {
+ if (rt->data[i] > rt->rmax)
+ rt->rmax = rt->data[i];
+ if (rt->data[i] < rt->rmin)
+ rt->rmin = rt->data[i];
+ }
+
+ /* Decide on reverse granularity */
+ rt->rsize = sat_add(rt->size,2)/2;
+ rt->qscale = (double)rt->rsize/(rt->rmax - rt->rmin); /* Scale factor to quantize to */
+
+ if (ovr_mul(rt->size, sizeof(unsigned int *))) {
+ return 2;
+ }
+ /* Initialize the reverse lookup structures, and get overall min/max */
+ if ((rt->rlists = (unsigned int **) icp->al->calloc(icp->al, rt->rsize, sizeof(unsigned int *))) == NULL) {
+ return 2;
+ }
+
+ /* Assign each output value range bucket lists it intersects */
+ for (i = 0; i < (rt->size-1); i++) {
+ unsigned int s, e, j; /* Start and end indexes (inclusive) */
+ s = (unsigned int)((rt->data[i] - rt->rmin) * rt->qscale);
+ e = (unsigned int)((rt->data[i+1] - rt->rmin) * rt->qscale);
+ if (s >= rt->rsize)
+ s = rt->rsize-1;
+ if (e >= rt->rsize)
+ e = rt->rsize-1;
+ if (s > e) { /* swap */
+ unsigned int t;
+ t = s; s = e; e = t;
+ }
+
+ /* For all buckets that may contain this output range, add index of this output */
+ for (j = s; j <= e; j++) {
+ unsigned int as; /* Allocation size */
+ unsigned int nf; /* Next free slot */
+ if (rt->rlists[j] == NULL) { /* No allocation */
+ as = 5; /* Start with space for 5 */
+ if ((rt->rlists[j] = (unsigned int *) icp->al->calloc(icp->al, as, sizeof(unsigned int))) == NULL) {
+ return 2;
+ }
+ rt->rlists[j][0] = as;
+ nf = rt->rlists[j][1] = 2;
+ } else {
+ as = rt->rlists[j][0]; /* Allocate space for this list */
+ nf = rt->rlists[j][1]; /* Next free location in list */
+ if (nf >= as) { /* need to expand space */
+ if ((as = sat_mul(as, 2)) == UINT_MAX
+ || ovr_mul(as, sizeof(unsigned int))) {
+ return 2;
+ }
+ rt->rlists[j] = (unsigned int *) icp->al->realloc(icp->al,rt->rlists[j], as * sizeof(unsigned int));
+ if (rt->rlists[j] == NULL) {
+ return 2;
+ }
+ rt->rlists[j][0] = as;
+ }
+ }
+ rt->rlists[j][nf++] = i;
+ rt->rlists[j][1] = nf;
+ }
+ }
+ rt->inited = 1;
+ return 0;
+}
+
+/* Free up any data */
+static void icmTable_delete_bwd(
+ icc *icp, /* Base icc */
+ icmRevTable *rt /* Reverse table data to setup */
+) {
+ if (rt->inited != 0) {
+ while (rt->rsize > 0)
+ icp->al->free(icp->al, rt->rlists[--rt->rsize]);
+ icp->al->free(icp->al, rt->rlists);
+ rt->size = 0; /* Don't keep these */
+ rt->data = NULL;
+ }
+}
+
+/* Do a reverse lookup through the curve */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+static int icmTable_lookup_bwd(
+ icmRevTable *rt,
+ double *out,
+ double *in
+) {
+ int rv = 0;
+ unsigned int ix, k, i;
+ double oval, ival = *in, val;
+ double rsize_1;
+
+ /* Find appropriate reverse list */
+ rsize_1 = (double)(rt->rsize-1);
+ val = ((ival - rt->rmin) * rt->qscale);
+ if (val < 0.0)
+ val = 0.0;
+ else if (val > rsize_1)
+ val = rsize_1;
+ ix = (unsigned int)floor(val); /* Coordinate */
+
+ if (ix > (rt->size-2))
+ ix = (rt->size-2);
+ if (rt->rlists[ix] != NULL) { /* There is a list of fwd candidates */
+ /* For each candidate forward range */
+ for (i = 2; i < rt->rlists[ix][1]; i++) { /* For all fwd indexes */
+ double lv,hv;
+ k = rt->rlists[ix][i]; /* Base index */
+ lv = rt->data[k];
+ hv = rt->data[k+1];
+ if ((ival >= lv && ival <= hv) /* If this slot contains output value */
+ || (ival >= hv && ival <= lv)) {
+ /* Reverse linear interpolation */
+ if (hv == lv) { /* Technically non-monotonic - due to quantization ? */
+ oval = (k + 0.5)/(rt->size-1.0);
+ } else
+ oval = (k + ((ival - lv)/(hv - lv)))/(rt->size-1.0);
+ /* If we kept looking, we would find multiple */
+ /* solution for non-monotonic curve */
+ *out = oval;
+ return rv;
+ }
+ }
+ }
+
+ /* We have failed to find an exact value, so return the nearest value */
+ /* (This is slow !) */
+ val = fabs(ival - rt->data[0]);
+ for (k = 0, i = 1; i < rt->size; i++) {
+ double er;
+ er = fabs(ival - rt->data[i]);
+ if (er < val) { /* new best */
+ val = er;
+ k = i;
+ }
+ }
+ *out = k/(rt->size-1.0);
+ rv |= 1;
+ return rv;
+}
+
+
+/* - - - - - - - - - - - - */
+
+/* Do a reverse lookup through the curve */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+static int icmCurve_lookup_bwd(
+ icmCurve *p,
+ double *out,
+ double *in
+) {
+ icc *icp = p->icp;
+ int rv = 0;
+ if (p->flag == icmCurveLin) {
+ *out = *in;
+ } else if (p->flag == icmCurveGamma) {
+ double val = *in;
+ if (val <= 0.0)
+ *out = 0.0;
+ else
+ *out = pow(val, 1.0/p->data[0]);
+ } else if (p->size == 0) { /* Table of 0 size */
+ *out = *in;
+ } else { /* Use linear interpolation */
+ if (p->rt.inited == 0) {
+ rv = icmTable_setup_bwd(icp, &p->rt, p->size, p->data);
+ if (rv != 0) {
+ sprintf(icp->err,"icmCurve_lookup: Malloc failure in reverse lookup init.");
+ return icp->errc = rv;
+ }
+ }
+ rv = icmTable_lookup_bwd(&p->rt, out, in);
+ }
+ return rv;
+}
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmCurve_get_size(
+ icmBase *pp
+) {
+ icmCurve *p = (icmCurve *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 12); /* 12 bytes for tag, padding and count */
+ len = sat_addmul(len, p->size, 2); /* 2 bytes for each UInt16 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmCurve_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmCurve *p = (icmCurve *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i;
+ char *bp, *buf, *end;
+
+ if (len < 12) {
+ sprintf(icp->err,"icmCurve_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmCurve_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmCurve_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmCurve_read: Wrong tag type for icmCurve");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ p->size = read_UInt32Number(bp+8);
+ bp = bp + 12;
+
+ /* Set flag up before allocating */
+ if (p->size == 0) { /* Linear curve */
+ p->flag = icmCurveLin;
+ } else if (p->size == 1) { /* Gamma curve */
+ p->flag = icmCurveGamma;
+ } else {
+ p->flag = icmCurveSpec;
+ if (p->size > (len - 12)/2) {
+ sprintf(icp->err,"icmCurve_read: size overflow");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ }
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ if (p->flag == icmCurveGamma) { /* Gamma curve */
+ if (bp > end || 1 > (end - bp)) {
+ sprintf(icp->err,"icmCurve_read: Data too short for curve gamma");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->data[0] = read_U8Fixed8Number(bp);
+ } else if (p->flag == icmCurveSpec) {
+ /* Read all the data from the buffer */
+ for (i = 0; i < p->size; i++, bp += 2) {
+ if (bp > end || 2 > (end - bp)) {
+ sprintf(icp->err,"icmCurve_read: Data too short for curve value");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->data[i] = read_DCS16Number(bp);
+ }
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmCurve_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmCurve *p = (icmCurve *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmCurve_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmCurve_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmCurve_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write count */
+ if ((rv = write_UInt32Number(p->size,bp+8)) != 0) {
+ sprintf(icp->err,"icmCurve_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write all the data to the buffer */
+ bp += 12; /* Skip padding */
+ if (p->flag == icmCurveLin) {
+ if (p->size != 0) {
+ sprintf(icp->err,"icmCurve_write: Must be exactly 0 entry for Linear");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ } else if (p->flag == icmCurveGamma) {
+ if (p->size != 1) {
+ sprintf(icp->err,"icmCurve_write: Must be exactly 1 entry for Gamma");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if ((rv = write_U8Fixed8Number(p->data[0],bp)) != 0) {
+ sprintf(icp->err,"icmCurve_write: write_U8Fixed8umber(%f) failed",p->data[0]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ } else if (p->flag == icmCurveSpec) {
+ if (p->size < 2) {
+ sprintf(icp->err,"icmCurve_write: Must be 2 or more entries for Specified curve");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ for (i = 0; i < p->size; i++, bp += 2) {
+ if ((rv = write_DCS16Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmCurve_write: write_UInt16umber(%f) failed",p->data[i]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmCurve_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmCurve_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmCurve *p = (icmCurve *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Curve:\n");
+
+ if (p->flag == icmCurveLin) {
+ op->gprintf(op," Curve is linear\n");
+ } else if (p->flag == icmCurveGamma) {
+ op->gprintf(op," Curve is gamma of %f\n",p->data[0]);
+ } else {
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %3lu: %f\n",i,p->data[i]);
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmCurve_allocate(
+ icmBase *pp
+) {
+ icmCurve *p = (icmCurve *)pp;
+ icc *icp = p->icp;
+
+ if (p->flag == icmCurveUndef) {
+ sprintf(icp->err,"icmCurve_alloc: flag not set");
+ return icp->errc = 1;
+ } else if (p->flag == icmCurveLin) {
+ p->size = 0;
+ } else if (p->flag == icmCurveGamma) {
+ p->size = 1;
+ }
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(double))) {
+ sprintf(icp->err,"icmCurve_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (double *) icp->al->calloc(icp->al, p->size, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmCurve_alloc: malloc() of icmCurve data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmCurve_delete(
+ icmBase *pp
+) {
+ icmCurve *p = (icmCurve *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icmTable_delete_bwd(icp, &p->rt); /* Free reverse table info */
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmCurve(
+ icc *icp
+) {
+ icmCurve *p;
+ if ((p = (icmCurve *) icp->al->calloc(icp->al,1,sizeof(icmCurve))) == NULL)
+ return NULL;
+ p->ttype = icSigCurveType;
+ p->refcount = 1;
+ p->get_size = icmCurve_get_size;
+ p->read = icmCurve_read;
+ p->write = icmCurve_write;
+ p->dump = icmCurve_dump;
+ p->allocate = icmCurve_allocate;
+ p->del = icmCurve_delete;
+ p->icp = icp;
+
+ p->lookup_fwd = icmCurve_lookup_fwd;
+ p->lookup_bwd = icmCurve_lookup_bwd;
+
+ p->rt.inited = 0;
+
+ p->flag = icmCurveUndef;
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmData object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmData_get_size(
+ icmBase *pp
+) {
+ icmData *p = (icmData *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 12); /* 12 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 1); /* 1 byte for each data element */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmData_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmData *p = (icmData *)pp;
+ icc *icp = p->icp;
+ int rv;
+ unsigned size, f;
+ char *bp, *buf;
+
+ if (len < 12) {
+ sprintf(icp->err,"icmData_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmData_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmData_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 12)/1; /* Number of elements in the array */
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmData_read: Wrong tag type for icmData");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Read the data type flag */
+ f = read_UInt32Number(bp+8);
+ if (f == 0) {
+ p->flag = icmDataASCII;
+ } else if (f == 1) {
+ p->flag = icmDataBin;
+#ifndef ICM_STRICT /* Profile maker sometimes has a problem */
+ } else if (f == 0x01000000) {
+ p->flag = icmDataBin;
+#endif
+ } else {
+ sprintf(icp->err,"icmData_read: Unknown flag value 0x%x",f);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 12; /* Skip padding and flag */
+
+ if (p->size > 0) {
+ if (p->flag == icmDataASCII) {
+ if ((rv = check_null_string(bp,p->size)) == 1) {
+ sprintf(icp->err,"icmData_read: ACSII is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ }
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ memmove((void *)p->data, (void *)bp, p->size);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmData_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmData *p = (icmData *)pp;
+ icc *icp = p->icp;
+ unsigned int len, f;
+ char *bp, *buf; /* Buffer to write from */
+ int rv;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmData_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmData_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmData_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ switch(p->flag) {
+ case icmDataASCII:
+ f = 0;
+ break;
+ case icmDataBin:
+ f = 1;
+ break;
+ default:
+ sprintf(icp->err,"icmData_write: Unknown Data Flag value");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Write data flag descriptor to the buffer */
+ if ((rv = write_UInt32Number(f,bp+8)) != 0) {
+ sprintf(icp->err,"icmData_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp += 12; /* Skip padding */
+
+ if (p->data != NULL) {
+ if (p->flag == icmDataASCII) {
+ if ((rv = check_null_string((char *)p->data, p->size)) == 1) {
+ sprintf(icp->err,"icmData_write: ASCII is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ }
+ memmove((void *)bp, (void *)p->data, p->size);
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmData_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmData_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmData *p = (icmData *)pp;
+ unsigned int i, r, c, ii, size = 0;
+ int ph = 0; /* Phase */
+
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Data:\n");
+ switch(p->flag) {
+ case icmDataASCII:
+ op->gprintf(op," ASCII data\n");
+ size = p->size > 0 ? p->size-1 : 0;
+ break;
+ case icmDataBin:
+ op->gprintf(op," Binary data\n");
+ size = p->size;
+ break;
+ case icmDataUndef:
+ op->gprintf(op," Undefined data\n");
+ size = p->size;
+ break;
+ }
+ op->gprintf(op," No. elements = %lu\n",p->size);
+
+ ii = i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+
+ c = 1;
+ if (ph != 0) { /* Print ASCII under binary */
+ op->gprintf(op," ");
+ i = ii;
+ c += 11;
+ } else {
+ op->gprintf(op," 0x%04lx: ",i);
+ ii = i;
+ c += 10;
+ }
+ while (i < size && c < 75) {
+ if (p->flag == icmDataASCII) {
+ if (isprint(p->data[i])) {
+ op->gprintf(op,"%c",p->data[i]);
+ c++;
+ } else {
+ op->gprintf(op,"\\%03o",p->data[i]);
+ c += 4;
+ }
+ } else {
+ if (ph == 0)
+ op->gprintf(op,"%02x ",p->data[i]);
+ else {
+ if (isprint(p->data[i]))
+ op->gprintf(op," %c ",p->data[i]);
+ else
+ op->gprintf(op," ",p->data[i]);
+ }
+ c += 3;
+ }
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ if (verb > 2 && p->flag != icmDataASCII && ph == 0)
+ ph = 1;
+ else
+ ph = 0;
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmData_allocate(
+ icmBase *pp
+) {
+ icmData *p = (icmData *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(unsigned char))) {
+ sprintf(icp->err,"icmData_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (unsigned char *) icp->al->calloc(icp->al, p->size, sizeof(unsigned char))) == NULL) {
+ sprintf(icp->err,"icmData_alloc: malloc() of icmData data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmData_delete(
+ icmBase *pp
+) {
+ icmData *p = (icmData *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmData(
+ icc *icp
+) {
+ icmData *p;
+ if ((p = (icmData *) icp->al->calloc(icp->al,1,sizeof(icmData))) == NULL)
+ return NULL;
+ p->ttype = icSigDataType;
+ p->refcount = 1;
+ p->get_size = icmData_get_size;
+ p->read = icmData_read;
+ p->write = icmData_write;
+ p->dump = icmData_dump;
+ p->allocate = icmData_allocate;
+ p->del = icmData_delete;
+ p->icp = icp;
+
+ p->flag = icmDataUndef;
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmText object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmText_get_size(
+ icmBase *pp
+) {
+ icmText *p = (icmText *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 1); /* 1 byte for each character element (inc. null) */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmText_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmText *p = (icmText *)pp;
+ icc *icp = p->icp;
+ int rv;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmText_read: Tag too short to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmText_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmText_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = (len - 8)/1; /* Number of elements in the array */
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmText_read: Wrong tag type for icmText");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp = bp + 8;
+
+ if (p->size > 0) {
+ if ((rv = check_null_string(bp,p->size)) == 1) {
+ sprintf(icp->err,"icmText_read: text is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ memmove((void *)p->data, (void *)bp, p->size);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmText_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmText *p = (icmText *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmText_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmText_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmText_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ bp = bp + 8;
+
+ if (p->data != NULL) {
+ if ((rv = check_null_string(p->data, p->size)) == 1) {
+ sprintf(icp->err,"icmText_write: text is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ memmove((void *)bp, (void *)p->data, p->size);
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmText_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmText_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmText *p = (icmText *)pp;
+ unsigned int i, r, c, size;
+
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Text:\n");
+ op->gprintf(op," No. chars = %lu\n",p->size);
+
+ size = p->size > 0 ? p->size-1 : 0;
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 75) {
+ if (isprint(p->data[i])) {
+ op->gprintf(op,"%c",p->data[i]);
+ c++;
+ } else {
+ op->gprintf(op,"\\%03o",p->data[i]);
+ c += 4;
+ }
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmText_allocate(
+ icmBase *pp
+) {
+ icmText *p = (icmText *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(char))) {
+ sprintf(icp->err,"icmText_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (char *) icp->al->calloc(icp->al, p->size, sizeof(char))) == NULL) {
+ sprintf(icp->err,"icmText_alloc: malloc() of icmText data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmText_delete(
+ icmBase *pp
+) {
+ icmText *p = (icmText *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmText(
+ icc *icp
+) {
+ icmText *p;
+ if ((p = (icmText *) icp->al->calloc(icp->al,1,sizeof(icmText))) == NULL)
+ return NULL;
+ p->ttype = icSigTextType;
+ p->refcount = 1;
+ p->get_size = icmText_get_size;
+ p->read = icmText_read;
+ p->write = icmText_write;
+ p->dump = icmText_dump;
+ p->allocate = icmText_allocate;
+ p->del = icmText_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+
+/* Data conversion support functions */
+static int write_DateTimeNumber(icmDateTimeNumber *p, char *d) {
+ int rv;
+ if (p->year < 1900 || p->year > 3000
+ || p->month == 0 || p->month > 12
+ || p->day == 0 || p->day > 31
+ || p->hours > 23
+ || p->minutes > 59
+ || p->seconds > 59)
+ return 1;
+
+ if ((rv = write_UInt16Number(p->year, d + 0)) != 0)
+ return rv;
+ if ((rv = write_UInt16Number(p->month, d + 2)) != 0)
+ return rv;
+ if ((rv = write_UInt16Number(p->day, d + 4)) != 0)
+ return rv;
+ if ((rv = write_UInt16Number(p->hours, d + 6)) != 0)
+ return rv;
+ if ((rv = write_UInt16Number(p->minutes, d + 8)) != 0)
+ return rv;
+ if ((rv = write_UInt16Number(p->seconds, d + 10)) != 0)
+ return rv;
+ return 0;
+}
+
+static int read_DateTimeNumber(icmDateTimeNumber *p, char *d) {
+
+ p->year = read_UInt16Number(d + 0);
+ p->month = read_UInt16Number(d + 2);
+ p->day = read_UInt16Number(d + 4);
+ p->hours = read_UInt16Number(d + 6);
+ p->minutes = read_UInt16Number(d + 8);
+ p->seconds = read_UInt16Number(d + 10);
+
+ /* Sanity check the date and time */
+ if (p->year >= 1900 && p->year <= 3000
+ && p->month != 0 && p->month <= 12
+ && p->day != 0 && p->day <= 31
+ && p->hours <= 23
+ && p->minutes <= 59
+ && p->seconds <= 59)
+ return 0;
+
+#ifdef NEVER
+ printf("Raw year = %d, month = %d, day = %d\n",p->year, p->month, p->day);
+ printf("Raw hour = %d, minutes = %d, seconds = %d\n", p->hours, p->minutes, p->seconds);
+#endif /* NEVER */
+
+#ifdef ICM_STRICT
+ return 1; /* Not legal */
+
+#else
+ /* Be more forgiving */
+
+ /* Check for Adobe problem */
+ if (p->month >= 1900 && p->month <= 3000
+ && p->year != 0 && p->year <= 12
+ && p->hours != 0 && p->hours <= 31
+ && p->day <= 23
+ && p->seconds <= 59
+ && p->minutes <= 59) {
+ unsigned int tt;
+
+ /* Correct Adobe's faulty profile */
+ tt = p->month; p->month = p->year; p->year = tt;
+ tt = p->hours; p->hours = p->day; p->day = tt;
+ tt = p->seconds; p->seconds = p->minutes; p->minutes = tt;
+
+ return 0;
+ }
+
+ /* Hmm. some other sort of corruption. Limit values to sane */
+ if (p->year < 1900) {
+ if (p->year < 100) /* Hmm. didn't use 4 digit year, guess it's 19xx ? */
+ p->year += 1900;
+ else
+ p->year = 1900;
+ } else if (p->year > 3000)
+ p->year = 3000;
+
+ if (p->month == 0)
+ p->month = 1;
+ else if (p->month > 12)
+ p->month = 12;
+
+ if (p->day == 0)
+ p->day = 1;
+ else if (p->day > 31)
+ p->day = 31;
+
+ if (p->hours > 23)
+ p->hours = 23;
+
+ if (p->minutes > 59)
+ p->minutes = 59;
+
+ if (p->seconds > 59)
+ p->seconds = 59;
+
+ return 0;
+#endif
+}
+
+/* Return a string that shows the given date and time */
+static char *string_DateTimeNumber(icmDateTimeNumber *p) {
+ static const char *mstring[13] = {"Bad", "Jan","Feb","Mar","Apr","May","Jun",
+ "Jul","Aug","Sep","Oct","Nov","Dec"};
+ static char buf[80];
+
+ sprintf(buf,"%d %s %4d, %d:%02d:%02d",
+ p->day, mstring[p->month > 12 ? 0 : p->month], p->year,
+ p->hours, p->minutes, p->seconds);
+ return buf;
+}
+
+/* Set the DateTime structure to the current date and time */
+static void setcur_DateTimeNumber(icmDateTimeNumber *p) {
+ time_t cclk;
+ struct tm *ctm;
+
+ cclk = time(NULL);
+ ctm = localtime(&cclk);
+
+ p->year = ctm->tm_year + 1900;
+ p->month = ctm->tm_mon + 1;
+ p->day = ctm->tm_mday;
+ p->hours = ctm->tm_hour;
+ p->minutes = ctm->tm_min;
+ p->seconds = ctm->tm_sec;
+}
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmDateTimeNumber_get_size(
+ icmBase *pp
+) {
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 12); /* 12 bytes for Date & Time */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmDateTimeNumber_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
+ icc *icp = p->icp;
+ int rv;
+ char *bp, *buf;
+
+ if (len < 20) {
+ sprintf(icp->err,"icmDateTimeNumber_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmDateTimeNumber_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmDateTimeNumber_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmDateTimeNumber_read: Wrong tag type for icmDateTimeNumber");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read the time and date from buffer */
+ if((rv = read_DateTimeNumber(p, bp)) != 0) {
+ sprintf(icp->err,"icmDateTimeNumber_read: Corrupted DateTime");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmDateTimeNumber_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmDateTimeNumber_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmDateTimeNumber_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmDateTimeNumber_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ if ((rv = write_DateTimeNumber(p, bp)) != 0) {
+ sprintf(icp->err,"icmDateTimeNumber_write: write_DateTimeNumber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmDateTimeNumber_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmDateTimeNumber_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"DateTimeNumber:\n");
+ op->gprintf(op," Date = %s\n", string_DateTimeNumber(p));
+}
+
+/* Allocate variable sized data elements */
+static int icmDateTimeNumber_allocate(
+ icmBase *pp
+) {
+ /* Nothing to do */
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmDateTimeNumber_delete(
+ icmBase *pp
+) {
+ icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmDateTimeNumber(
+ icc *icp
+) {
+ icmDateTimeNumber *p;
+ if ((p = (icmDateTimeNumber *) icp->al->calloc(icp->al,1,sizeof(icmDateTimeNumber))) == NULL)
+ return NULL;
+ p->ttype = icSigDateTimeType;
+ p->refcount = 1;
+ p->get_size = icmDateTimeNumber_get_size;
+ p->read = icmDateTimeNumber_read;
+ p->write = icmDateTimeNumber_write;
+ p->dump = icmDateTimeNumber_dump;
+ p->allocate = icmDateTimeNumber_allocate;
+ p->del = icmDateTimeNumber_delete;
+ p->icp = icp;
+
+ setcur_DateTimeNumber(p); /* Default to current date and time */
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmLut object */
+
+/* Check if the matrix is non-zero */
+static int icmLut_nu_matrix(
+ icmLut *p /* Pointer to Lut object */
+) {
+ int i, j;
+
+ for (j = 0; j < 3; j++) { /* Rows */
+ for (i = 0; i < 3; i++) { /* Columns */
+ if ( (i == j && p->e[j][i] != 1.0)
+ || (i != j && p->e[j][i] != 0.0))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* return the locations of the minimum and */
+/* maximum values of the given channel, in the clut */
+static void icmLut_min_max(
+ icmLut *p, /* Pointer to Lut object */
+ double *minp, /* Return position of min/max */
+ double *maxp,
+ int chan /* Channel, -1 for average of all */
+) {
+ double *tp;
+ double minv, maxv; /* Values */
+ unsigned int e, ee, f;
+ int gc[MAX_CHAN]; /* Grid coordinate */
+
+ minv = 1e6;
+ maxv = -1e6;
+
+ for (e = 0; e < p->inputChan; e++)
+ gc[e] = 0; /* init coords */
+
+ /* Search the whole table */
+ for (tp = p->clutTable, e = 0; e < p->inputChan; tp += p->outputChan) {
+ double v;
+ if (chan == -1) {
+ for (v = 0.0, f = 0; f < p->outputChan; f++)
+ v += tp[f];
+ } else {
+ v = tp[chan];
+ }
+ if (v < minv) {
+ minv = v;
+ for (ee = 0; ee < p->inputChan; ee++)
+ minp[ee] = gc[ee]/(p->clutPoints-1.0);
+ }
+ if (v > maxv) {
+ maxv = v;
+ for (ee = 0; ee < p->inputChan; ee++)
+ maxp[ee] = gc[ee]/(p->clutPoints-1.0);
+ }
+
+ /* Increment coord */
+ for (e = 0; e < p->inputChan; e++) {
+ if (++gc[e] < p->clutPoints)
+ break; /* No carry */
+ gc[e] = 0;
+ }
+ }
+}
+
+/* Convert XYZ throught Luts matrix */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+static int icmLut_lookup_matrix(
+icmLut *p, /* Pointer to Lut object */
+double *out, /* Output array[outputChan] in ICC order - see Table 39 in 6.5.5 */
+double *in /* Input array[inputChan] */
+) {
+ double t0,t1; /* Take care if out == in */
+ t0 = p->e[0][0] * in[0] + p->e[0][1] * in[1] + p->e[0][2] * in[2];
+ t1 = p->e[1][0] * in[0] + p->e[1][1] * in[1] + p->e[1][2] * in[2];
+ out[2] = p->e[2][0] * in[0] + p->e[2][1] * in[1] + p->e[2][2] * in[2];
+ out[0] = t0;
+ out[1] = t1;
+
+ return 0;
+}
+
+/* Convert normalized numbers though this Luts input tables. */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+static int icmLut_lookup_input(
+icmLut *p, /* Pointer to Lut object */
+double *out, /* Output array[inputChan] */
+double *in /* Input array[inputChan] */
+) {
+ int rv = 0;
+ unsigned int ix, n;
+ double inputEnt_1 = (double)(p->inputEnt-1);
+ double *table = p->inputTable;
+
+ if (p->inputEnt == 0) { /* Hmm. */
+ for (n = 0; n < p->inputChan; n++)
+ out[n] = in[n];
+ } else {
+ /* Use linear interpolation */
+ for (n = 0; n < p->inputChan; n++, table += p->inputEnt) {
+ double val, w;
+ val = in[n] * inputEnt_1;
+ if (val < 0.0) {
+ val = 0.0;
+ rv |= 1;
+ } else if (val > inputEnt_1) {
+ val = inputEnt_1;
+ rv |= 1;
+ }
+ ix = (unsigned int)floor(val); /* Grid coordinate */
+ if (ix > (p->inputEnt-2))
+ ix = (p->inputEnt-2);
+ w = val - (double)ix; /* weight */
+ val = table[ix];
+ out[n] = val + w * (table[ix+1] - val);
+ }
+ }
+ return rv;
+}
+
+/* Convert normalized numbers though this Luts multi-dimensional table. */
+/* using multi-linear interpolation. */
+static int icmLut_lookup_clut_nl(
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+icmLut *p, /* Pointer to Lut object */
+double *out, /* Output array[inputChan] */
+double *in /* Input array[outputChan] */
+) {
+ icc *icp = p->icp;
+ int rv = 0;
+ double *gp; /* Pointer to grid cube base */
+ double co[MAX_CHAN]; /* Coordinate offset with the grid cell */
+ double *gw, GW[1 << 8]; /* weight for each grid cube corner */
+
+ if (p->inputChan <= 8) {
+ gw = GW; /* Use stack allocation */
+ } else {
+ if ((gw = (double *) icp->al->malloc(icp->al, sat_mul((1 << p->inputChan), sizeof(double)))) == NULL) {
+ sprintf(icp->err,"icmLut_lookup_clut: malloc() failed");
+ return icp->errc = 2;
+ }
+ }
+
+ /* We are using an multi-linear (ie. Trilinear for 3D input) interpolation. */
+ /* The implementation here uses more multiplies that some other schemes, */
+ /* (for instance, see "Tri-Linear Interpolation" by Steve Hill, */
+ /* Graphics Gems IV, page 521), but has less involved bookeeping, */
+ /* needs less local storage for intermediate output values, does fewer */
+ /* output and intermediate value reads, and fp multiplies are fast on */
+ /* todays processors! */
+
+ /* Compute base index into grid and coordinate offsets */
+ {
+ unsigned int e;
+ double clutPoints_1 = (double)(p->clutPoints-1);
+ int clutPoints_2 = p->clutPoints-2;
+ gp = p->clutTable; /* Base of grid array */
+
+ for (e = 0; e < p->inputChan; e++) {
+ unsigned int x;
+ double val;
+ val = in[e] * clutPoints_1;
+ if (val < 0.0) {
+ val = 0.0;
+ rv |= 1;
+ } else if (val > clutPoints_1) {
+ val = clutPoints_1;
+ rv |= 1;
+ }
+ x = (unsigned int)floor(val); /* Grid coordinate */
+ if (x > clutPoints_2)
+ x = clutPoints_2;
+ co[e] = val - (double)x; /* 1.0 - weight */
+ gp += x * p->dinc[e]; /* Add index offset for base of cube */
+ }
+ }
+ /* Compute corner weights needed for interpolation */
+ {
+ unsigned int e;
+ int i, g = 1;
+ gw[0] = 1.0;
+ for (e = 0; e < p->inputChan; e++) {
+ for (i = 0; i < g; i++) {
+ gw[g+i] = gw[i] * co[e];
+ gw[i] *= (1.0 - co[e]);
+ }
+ g *= 2;
+ }
+ }
+ /* Now compute the output values */
+ {
+ int i;
+ unsigned int f;
+ double w = gw[0];
+ double *d = gp + p->dcube[0];
+ for (f = 0; f < p->outputChan; f++) /* Base of cube */
+ out[f] = w * d[f];
+ for (i = 1; i < (1 << p->inputChan); i++) { /* For all other corners of cube */
+ w = gw[i]; /* Strength reduce */
+ d = gp + p->dcube[i];
+ for (f = 0; f < p->outputChan; f++)
+ out[f] += w * d[f];
+ }
+ }
+ if (gw != GW)
+ icp->al->free(icp->al, (void *)gw);
+ return rv;
+}
+
+/* Convert normalized numbers though this Luts multi-dimensional table */
+/* using simplex interpolation. */
+static int icmLut_lookup_clut_sx(
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+icmLut *p, /* Pointer to Lut object */
+double *out, /* Output array[inputChan] */
+double *in /* Input array[outputChan] */
+) {
+ int rv = 0;
+ double *gp; /* Pointer to grid cube base */
+ double co[MAX_CHAN]; /* Coordinate offset with the grid cell */
+ int si[MAX_CHAN]; /* co[] Sort index, [0] = smalest */
+
+ /* We are using a simplex (ie. tetrahedral for 3D input) interpolation. */
+ /* This method is more appropriate for XYZ/RGB/CMYK input spaces, */
+
+ /* Compute base index into grid and coordinate offsets */
+ {
+ unsigned int e;
+ double clutPoints_1 = (double)(p->clutPoints-1);
+ int clutPoints_2 = p->clutPoints-2;
+ gp = p->clutTable; /* Base of grid array */
+
+ for (e = 0; e < p->inputChan; e++) {
+ unsigned int x;
+ double val;
+ val = in[e] * clutPoints_1;
+ if (val < 0.0) {
+ val = 0.0;
+ rv |= 1;
+ } else if (val > clutPoints_1) {
+ val = clutPoints_1;
+ rv |= 1;
+ }
+ x = (unsigned int)floor(val); /* Grid coordinate */
+ if (x > clutPoints_2)
+ x = clutPoints_2;
+ co[e] = val - (double)x; /* 1.0 - weight */
+ gp += x * p->dinc[e]; /* Add index offset for base of cube */
+ }
+ }
+#ifdef NEVER
+ /* Do selection sort on coordinates, smallest to largest. */
+ {
+ int e, f;
+ for (e = 0; e < p->inputChan; e++)
+ si[e] = e; /* Initial unsorted indexes */
+ for (e = 0; e < (p->inputChan-1); e++) {
+ double cosn;
+ cosn = co[si[e]]; /* Current smallest value */
+ for (f = e+1; f < p->inputChan; f++) { /* Check against rest */
+ int tt;
+ tt = si[f];
+ if (cosn > co[tt]) {
+ si[f] = si[e]; /* Exchange */
+ si[e] = tt;
+ cosn = co[tt];
+ }
+ }
+ }
+ }
+#else
+ /* Do insertion sort on coordinates, smallest to largest. */
+ {
+ int f, vf;
+ unsigned int e;
+ double v;
+ for (e = 0; e < p->inputChan; e++)
+ si[e] = e; /* Initial unsorted indexes */
+
+ for (e = 1; e < p->inputChan; e++) {
+ f = e;
+ v = co[si[f]];
+ vf = f;
+ while (f > 0 && co[si[f-1]] > v) {
+ si[f] = si[f-1];
+ f--;
+ }
+ si[f] = vf;
+ }
+ }
+#endif
+ /* Now compute the weightings, simplex vertices and output values */
+ {
+ unsigned int e, f;
+ double w; /* Current vertex weight */
+
+ w = 1.0 - co[si[p->inputChan-1]]; /* Vertex at base of cell */
+ for (f = 0; f < p->outputChan; f++)
+ out[f] = w * gp[f];
+
+ for (e = p->inputChan-1; e > 0; e--) { /* Middle verticies */
+ w = co[si[e]] - co[si[e-1]];
+ gp += p->dinc[si[e]]; /* Move to top of cell in next largest dimension */
+ for (f = 0; f < p->outputChan; f++)
+ out[f] += w * gp[f];
+ }
+
+ w = co[si[0]];
+ gp += p->dinc[si[0]]; /* Far corner from base of cell */
+ for (f = 0; f < p->outputChan; f++)
+ out[f] += w * gp[f];
+ }
+ return rv;
+}
+
+#ifdef NEVER // ~~~99 development code
+
+/* Convert normalized numbers though this Luts multi-dimensional table */
+/* using optimised simplex interpolation. */
+/* This version optimses the simplex split axis depending on the input */
+/* colorspace. */
+static int icmLut_lookup_clut_osx(
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+icmLut *p, /* Pointer to Lut object */
+double *out, /* Output array[inputChan] */
+double *in /* Input array[outputChan] */
+) {
+ int rv = 0;
+ double *gp; /* Pointer to grid cube base */
+ double co[MAX_CHAN]; /* Coordinate offset with the grid cell */
+ int si[MAX_CHAN]; /* co[] Sort index, [0] = smalest */
+ char xflip[MAX_CHAN]; /* Optimised simplex axis flip flags */
+
+ /* Compute base index into grid and coordinate offsets */
+ {
+ unsigned int e;
+ double clutPoints_1 = (double)(p->clutPoints-1);
+ int clutPoints_2 = p->clutPoints-2;
+ gp = p->clutTable; /* Base of grid array */
+
+ for (e = 0; e < p->inputChan; e++) {
+ unsigned int x;
+ double val;
+// ~~~999
+#ifdef NEVER
+ xflip[e] = p->finfo[e].bthff;
+ if (in[e] >= p->finfo[e].fth)
+ xflip[e] = p->finfo[e].athff;
+#else
+
+ xflip[e] = 0;
+ if (e == 0)
+ xflip[e] = 1;
+#endif
+ val = in[e] * clutPoints_1;
+ if (val < 0.0) {
+ val = 0.0;
+ rv |= 1;
+ } else if (val > clutPoints_1) {
+ val = clutPoints_1;
+ rv |= 1;
+ }
+ x = (unsigned int)floor(val); /* Grid coordinate */
+ if (x > clutPoints_2)
+ x = clutPoints_2;
+ co[e] = val - (double)x; /* 1.0 - weight */
+ gp += x * p->dinc[e]; /* Add index offset for base of cube */
+ if (xflip[e]) { /* Reverse sense of direction for this axis */
+ co[e] = 1.0 - co[e];
+ gp += p->dinc[e];
+ }
+ }
+ }
+//printf("*");fflush(stdout);
+#ifdef NEVER
+ /* Do selection sort on coordinates, smallest to largest. */
+ {
+ int e, f;
+ for (e = 0; e < p->inputChan; e++)
+ si[e] = e; /* Initial unsorted indexes */
+ for (e = 0; e < (p->inputChan-1); e++) {
+ double cosn;
+ cosn = co[si[e]]; /* Current smallest value */
+ for (f = e+1; f < p->inputChan; f++) { /* Check against rest */
+ int tt;
+ tt = si[f];
+ if (cosn > co[tt]) {
+ si[f] = si[e]; /* Exchange */
+ si[e] = tt;
+ cosn = co[tt];
+ }
+ }
+ }
+ }
+#else
+ /* Do insertion sort on coordinates, smallest to largest. */
+ {
+ int f, vf;
+ unsigned int e;
+ double v;
+ for (e = 0; e < p->inputChan; e++)
+ si[e] = e; /* Initial unsorted indexes */
+
+ for (e = 1; e < p->inputChan; e++) {
+ f = e;
+ v = co[si[f]];
+ vf = f;
+ while (f > 0 && co[si[f-1]] > v) {
+ si[f] = si[f-1];
+ f--;
+ }
+ si[f] = vf;
+ }
+ }
+#endif
+ /* Now compute the weightings, simplex vertices and output values */
+ {
+ unsigned int e, f;
+ double w; /* Current vertex weight */
+
+ w = 1.0 - co[si[p->inputChan-1]]; /* Vertex at base of cell */
+ for (f = 0; f < p->outputChan; f++)
+ out[f] = w * gp[f];
+
+ for (e = p->inputChan-1; e > 0; e--) { /* Middle verticies */
+ w = co[si[e]] - co[si[e-1]];
+ if (xflip[e])
+ gp -= p->dinc[si[e]]; /* Move to top of cell in next largest dimension */
+ else
+ gp += p->dinc[si[e]]; /* Move to top of cell in next largest dimension */
+ for (f = 0; f < p->outputChan; f++)
+ out[f] += w * gp[f];
+ }
+
+ w = co[si[0]];
+ if (xflip[0])
+ gp -= p->dinc[si[0]]; /* Far corner from base of cell */
+ else
+ gp += p->dinc[si[0]]; /* Far corner from base of cell */
+ for (f = 0; f < p->outputChan; f++)
+ out[f] += w * gp[f];
+ }
+ return rv;
+}
+
+#endif /* NEVER */ // ~~~99 development code
+
+
+/* Convert normalized numbers though this Luts output tables. */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+static int icmLut_lookup_output(
+icmLut *p, /* Pointer to Lut object */
+double *out, /* Output array[outputChan] */
+double *in /* Input array[outputChan] */
+) {
+ int rv = 0;
+ unsigned int ix, n;
+ double outputEnt_1 = (double)(p->outputEnt-1);
+ double *table = p->outputTable;
+
+ if (p->outputEnt == 0) { /* Hmm. */
+ for (n = 0; n < p->outputChan; n++)
+ out[n] = in[n];
+ } else {
+ /* Use linear interpolation */
+ for (n = 0; n < p->outputChan; n++, table += p->outputEnt) {
+ double val, w;
+ val = in[n] * outputEnt_1;
+ if (val < 0.0) {
+ val = 0.0;
+ rv |= 1;
+ } else if (val > outputEnt_1) {
+ val = outputEnt_1;
+ rv |= 1;
+ }
+ ix = (unsigned int)floor(val); /* Grid coordinate */
+ if (ix > (p->outputEnt-2))
+ ix = (p->outputEnt-2);
+ w = val - (double)ix; /* weight */
+ val = table[ix];
+ out[n] = val + w * (table[ix+1] - val);
+ }
+ }
+ return rv;
+}
+
+/* ----------------------------------------------- */
+/* Pseudo - Hilbert count sequencer */
+
+/* This multi-dimensional count sequence is a distributed */
+/* Gray code sequence, with direction reversal on every */
+/* alternate power of 2 scale. */
+/* It is intended to aid cache coherence in multi-dimensional */
+/* regular sampling. It approximates the Hilbert curve sequence. */
+
+/* Initialise, returns total usable count */
+unsigned
+psh_init(
+psh *p, /* Pointer to structure to initialise */
+int di, /* Dimensionality */
+unsigned int res, /* Size per coordinate */
+int co[] /* Coordinates to initialise (May be NULL) */
+) {
+ int e;
+
+ p->di = di;
+ p->res = res;
+
+ /* Compute bits */
+ for (p->bits = 0; (1u << p->bits) < res; p->bits++)
+ ;
+
+ /* Compute the total count mask */
+ p->tmask = ((((unsigned)1) << (p->bits * di))-1);
+
+ /* Compute usable count */
+ p->count = 1;
+ for (e = 0; e < di; e++)
+ p->count *= res;
+
+ p->ix = 0;
+
+ if (co != NULL) {
+ for (e = 0; e < di; e++)
+ co[e] = 0;
+ }
+
+ return p->count;
+}
+
+/* Reset the counter */
+void
+psh_reset(
+psh *p /* Pointer to structure */
+) {
+ p->ix = 0;
+}
+
+/* Increment pseudo-hilbert coordinates */
+/* Return non-zero if count rolls over to 0 */
+int
+psh_inc(
+psh *p, /* Pointer to structure */
+int co[] /* Coordinates to return */
+) {
+ int di = p->di;
+ unsigned int res = p->res;
+ unsigned int bits = p->bits;
+ int e;
+
+ do {
+ unsigned int b;
+ int gix; /* Gray code index */
+
+ p->ix = (p->ix + 1) & p->tmask;
+
+ gix = p->ix ^ (p->ix >> 1); /* Convert to gray code index */
+
+ for (e = 0; e < di; e++)
+ co[e] = 0;
+
+ for (b = 0; b < bits; b++) { /* Distribute bits */
+ if (b & 1) {
+ for (e = di-1; e >= 0; e--) { /* In reverse order */
+ co[e] |= (gix & 1) << b;
+ gix >>= 1;
+ }
+ } else {
+ for (e = 0; e < di; e++) { /* In normal order */
+ co[e] |= (gix & 1) << b;
+ gix >>= 1;
+ }
+ }
+ }
+
+ /* Convert from Gray to binary coordinates */
+ for (e = 0; e < di; e++) {
+ unsigned int sh, tv;
+
+ for(sh = 1, tv = co[e];; sh <<= 1) {
+ unsigned ptv = tv;
+ tv ^= (tv >> sh);
+ if (ptv <= 1 || sh == 16)
+ break;
+ }
+ if (tv >= res) /* Dumbo filter - increment again if outside cube range */
+ break;
+ co[e] = tv;
+ }
+
+ } while (e < di);
+
+ return (p->ix == 0);
+}
+
+/* ------------------------------------------------------- */
+
+#ifndef COUNTERS_H
+
+/* Macros for a multi-dimensional counter. */
+
+/* Declare the counter name nn, maximum di mxdi, dimensions di, & count */
+/* This counter can have each dimension range clipped */
+
+#define FCOUNT(nn, mxdi, di) \
+ int nn[mxdi]; /* counter value */ \
+ int nn##_di = (di); /* Number of dimensions */ \
+ int nn##_stt[mxdi]; /* start count value */ \
+ int nn##_res[mxdi]; /* last count +1 */ \
+ int nn##_e /* dimension index */
+
+#define FRECONF(nn, start, endp1) \
+ for (nn##_e = 0; nn##_e < nn##_di; nn##_e++) { \
+ nn##_stt[nn##_e] = (start); /* start count value */ \
+ nn##_res[nn##_e] = (endp1); /* last count +1 */ \
+ }
+
+/* Set the counter value to 0 */
+#define FC_INIT(nn) \
+{ \
+ for (nn##_e = 0; nn##_e < nn##_di; nn##_e++) \
+ nn[nn##_e] = nn##_stt[nn##_e]; \
+ nn##_e = 0; \
+}
+
+/* Increment the counter value */
+#define FC_INC(nn) \
+{ \
+ for (nn##_e = 0; nn##_e < nn##_di; nn##_e++) { \
+ nn[nn##_e]++; \
+ if (nn[nn##_e] < nn##_res[nn##_e]) \
+ break; /* No carry */ \
+ nn[nn##_e] = nn##_stt[nn##_e]; \
+ } \
+}
+
+/* After increment, expression is TRUE if counter is done */
+#define FC_DONE(nn) \
+ (nn##_e >= nn##_di)
+
+#endif /* COUNTERS_H */
+
+/* Parameter to getNormFunc function */
+typedef enum {
+ icmFromLuti = 0, /* return "fromo Lut normalized index" conversion function */
+ icmToLuti = 1, /* return "to Lut normalized index" conversion function */
+ icmFromLutv = 2, /* return "from Lut normalized value" conversion function */
+ icmToLutv = 3 /* return "to Lut normalized value" conversion function */
+} icmNormFlag;
+
+/* Return an appropriate color space normalization function, */
+/* given the color space and Lut type */
+/* Return 0 on success, 1 on match failure */
+static int getNormFunc(
+ icc *icp,
+ icColorSpaceSignature csig,
+ icTagTypeSignature tagSig,
+ icmNormFlag flag,
+ void (**nfunc)(double *out, double *in)
+);
+
+#define CLIP_MARGIN 0.005 /* Margine to allow before reporting clipping = 0.5% */
+
+/* Helper function to set multiple Lut tables simultaneously. */
+/* Note that these tables all have to be compatible in */
+/* having the same configuration and resolution. */
+/* Set errc and return error number in underlying icc */
+/* Set warnc if there is clipping in the output values */
+/* 1 = input table, 2 = main clut, 3 = clut midpoin, 4 = midpoint interp, 5 = output table */
+int icmSetMultiLutTables(
+ int ntables, /* Number of tables to be set, 1..n */
+ icmLut **pp, /* Pointer to array of Lut objects */
+ int flags, /* Setting flags */
+ void *cbctx, /* Opaque callback context pointer value */
+ icColorSpaceSignature insig, /* Input color space */
+ icColorSpaceSignature outsig, /* Output color space */
+ void (*infunc)(void *cbctx, double *out, double *in),
+ /* Input transfer function, inspace->inspace' (NULL = default) */
+ /* Will be called ntables times for each input grid value */
+ double *inmin, double *inmax, /* Maximum range of inspace' values */
+ /* (NULL = default) */
+ void (*clutfunc)(void *cbntx, double *out, double *in),
+ /* inspace' -> outspace[ntables]' transfer function */
+ /* will be called once for each input' grid value, and */
+ /* ntables output values should be written consecutively */
+ /* to out[]. */
+ double *clutmin, double *clutmax, /* Maximum range of outspace' values */
+ /* (NULL = default) */
+ void (*outfunc)(void *cbntx, double *out, double *in))
+ /* Output transfer function, outspace'->outspace (NULL = deflt) */
+ /* Will be called ntables times on each output value */
+{
+ icmLut *p, *pn; /* Pointer to 0'th nd tn'th Lut object */
+ icc *icp; /* Pointer to common icc */
+ int tn;
+ unsigned int e, f, i, n;
+ double **clutTable2 = NULL; /* Cell center values for ICM_CLUT_SET_APXLS */
+ double *clutTable3 = NULL; /* Vertex smoothing radius values [ntables] per entry */
+ int dinc3[MAX_CHAN]; /* Dimensional increment through clut3 (in doubles) */
+ int dcube3[1 << MAX_CHAN]; /* Hyper cube offsets throught clut3 (in doubles) */
+ int ii[MAX_CHAN]; /* Index value */
+ psh counter; /* Pseudo-Hilbert counter */
+// double _iv[4 * MAX_CHAN], *iv = &_iv[MAX_CHAN], *ivn; /* Real index value/table value */
+ int maxchan; /* Actual max of input and output */
+ double *_iv, *iv, *ivn; /* Real index value/table value */
+ double imin[MAX_CHAN], imax[MAX_CHAN];
+ double omin[MAX_CHAN], omax[MAX_CHAN];
+ void (*ifromindex)(double *out, double *in); /* Index to input color space function */
+ void (*itoentry)(double *out, double *in); /* Input color space to entry function */
+ void (*ifromentry)(double *out, double *in); /* Entry to input color space function */
+ void (*otoentry)(double *out, double *in); /* Output colorspace to table value function */
+ void (*ofromentry)(double *out, double *in); /* Table value to output color space function */
+ int clip = 0;
+
+ /* Check that everything is OK to proceed */
+ if (ntables < 1 || ntables > MAX_CHAN) {
+ if (ntables >= 1) {
+ icp = pp[0]->icp;
+ sprintf(icp->err,"icmSetMultiLutTables has illegal number of tables %d",ntables);
+ return icp->errc = 1;
+ } else {
+ /* Can't write error message anywhere */
+ return 1;
+ }
+ }
+
+ p = pp[0];
+ icp = p->icp;
+
+ for (tn = 1; tn < ntables; tn++) {
+ if (pp[tn]->icp != icp) {
+ sprintf(icp->err,"icmSetMultiLutTables Tables base icc is different");
+ return icp->errc = 1;
+ }
+ if (pp[tn]->ttype != p->ttype) {
+ sprintf(icp->err,"icmSetMultiLutTables Tables have different Tage Type");
+ return icp->errc = 1;
+ }
+
+ if (pp[tn]->inputChan != p->inputChan) {
+ sprintf(icp->err,"icmSetMultiLutTables Tables have different inputChan");
+ return icp->errc = 1;
+ }
+ if (pp[tn]->outputChan != p->outputChan) {
+ sprintf(icp->err,"icmSetMultiLutTables Tables have different outputChan");
+ return icp->errc = 1;
+ }
+ if (pp[tn]->clutPoints != p->clutPoints) {
+ sprintf(icp->err,"icmSetMultiLutTables Tables have different clutPoints");
+ return icp->errc = 1;
+ }
+ }
+
+ if (getNormFunc(icp, insig, p->ttype, icmFromLuti, &ifromindex) != 0) {
+ sprintf(icp->err,"icmLut_set_tables index to input colorspace function lookup failed");
+ return icp->errc = 1;
+ }
+ if (getNormFunc(icp, insig, p->ttype, icmToLutv, &itoentry) != 0) {
+ sprintf(icp->err,"icmLut_set_tables input colorspace to table entry function lookup failed");
+ return icp->errc = 1;
+ }
+ if (getNormFunc(icp, insig, p->ttype, icmFromLutv, &ifromentry) != 0) {
+ sprintf(icp->err,"icmLut_set_tables table entry to input colorspace function lookup failed");
+ return icp->errc = 1;
+ }
+
+ if (getNormFunc(icp, outsig, p->ttype, icmToLutv, &otoentry) != 0) {
+ sprintf(icp->err,"icmLut_set_tables output colorspace to table entry function lookup failed");
+ return icp->errc = 1;
+ }
+ if (getNormFunc(icp, outsig, p->ttype, icmFromLutv, &ofromentry) != 0) {
+ sprintf(icp->err,"icmLut_set_tables table entry to output colorspace function lookup failed");
+ return icp->errc = 1;
+ }
+
+ /* Allocate an array to hold the input and output values. */
+ /* It needs to be able to hold di "index under valus as in[], */
+ /* and ntables ICM_CLUT_SET_FILTER values as out[], so we assume maxchan >= di */
+ maxchan = p->inputChan > p->outputChan ? p->inputChan : p->outputChan;
+ if ((_iv = (double *) icp->al->malloc(icp->al, sizeof(double) * maxchan * (ntables+1)))
+ == NULL) {
+ sprintf(icp->err,"icmLut_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ iv = _iv + maxchan; /* Allow for "index under" and smoothing radius values */
+
+ /* Setup input table value min-max */
+ if (inmin == NULL || imax == NULL) {
+#ifdef SYMETRICAL_DEFAULT_LAB_RANGE /* Symetrical default range. */
+ /* We are assuming V2 Lab16 encoding, since this is a lut16type that always uses */
+ /* this encoding */
+ if (insig == icSigLabData) { /* Special case Lab */
+ double mn[3], mx[3];
+ /* This is to ensure that Lab 100,0,0 maps exactly to a clut grid point. */
+ /* This should work well if there is an odd grid resolution, */
+ /* and icclib is being used, as input lookup will */
+ /* be computed using floating point, so that the CLUT input value */
+ /* 0.5 can be represented exactly. */
+ /* Because the symetric range will cause slight clipping, */
+ /* only do it if the input table has sufficient resolution */
+ /* to represent the clipping faithfuly. */
+ if (p->inputEnt >= 64) {
+ if (p->ttype == icSigLut8Type) {
+ mn[0] = 0.0, mn[1] = mn[2] = -127.0;
+ mx[0] = 100.0, mx[1] = mx[2] = 127.0;
+ } else {
+ mn[0] = 0.0, mn[1] = mn[2] = -127.0 - 255.0/256.0;
+ mx[0] = 100.0, mx[1] = mx[2] = 127.0 + 255.0/256.0;
+ }
+ itoentry(imin, mn); /* Convert from input color space to table representation */
+ itoentry(imax, mx);
+ } else {
+ for (e = 0; e < p->inputChan; e++) {
+ imin[e] = 0.0;
+ imax[e] = 1.0;
+ }
+ }
+ } else
+#endif
+ {
+ for (e = 0; e < p->inputChan; e++) {
+ imin[e] = 0.0; /* We are assuming this is true for all other color spaces. */
+ imax[e] = 1.0;
+ }
+ }
+ } else {
+ itoentry(imin, inmin); /* Convert from input color space to table representation */
+ itoentry(imax, inmax);
+ }
+
+ /* Setup output table value min-max */
+ if (clutmin == NULL || clutmax == NULL) {
+#ifdef SYMETRICAL_DEFAULT_LAB_RANGE
+ /* This really isn't doing much, since the full range encoding doesn't need */
+ /* any adjustment to map a*b* 0 to an integer value. */
+ /* We are tweaking the 16 bit L* = 100 to the last index into */
+ /* the output table, which may help its accuracy slightly. */
+ /* We are assuming V2 Lab16 encoding, since this is a lut16type that always uses */
+ /* this encoding */
+ if (outsig == icSigLabData) { /* Special case Lab */
+ double mn[3], mx[3];
+ /* The output of the CLUT will be an 8 or 16 bit value, and we want to */
+ /* adjust the range so that the input grid point holding the white */
+ /* point can encode 0.0 exactly. */
+ /* Note that in the case of the a & b values, the range equates to */
+ /* normalised 0.0 .. 1.0, since 0 can be represented exactly in it. */
+ if (p->outputEnt >= 64) {
+ if (p->ttype == icSigLut8Type) {
+ mn[0] = 0.0, mn[1] = mn[2] = -128.0;
+ mx[0] = 100.0, mx[1] = mx[2] = 127.0;
+ } else {
+ mn[0] = 0.0, mn[1] = mn[2] = -128.0;
+ mx[0] = 100.0, mx[1] = mx[2] = (65535.0 * 255.0)/65280.0 - 128.0;
+ }
+ otoentry(omin, mn);/* Convert from output color space to table representation */
+ otoentry(omax, mx);
+ } else {
+ for (e = 0; e < p->inputChan; e++) {
+ omin[e] = 0.0;
+ omax[e] = 1.0;
+ }
+ }
+ } else
+#endif
+ {
+ for (f = 0; f < p->outputChan; f++) {
+ omin[f] = 0.0; /* We are assuming this is true for all other color spaces. */
+ omax[f] = 1.0;
+ }
+ }
+ } else {
+ otoentry(omin, clutmin);/* Convert from output color space to table representation */
+ otoentry(omax, clutmax);
+ }
+
+ /* Create the input table entry values */
+ for (tn = 0; tn < ntables; tn++) {
+ pn = pp[tn];
+ for (n = 0; n < pn->inputEnt; n++) {
+ double fv;
+ fv = n/(pn->inputEnt-1.0);
+ for (e = 0; e < pn->inputChan; e++)
+ iv[e] = fv;
+
+ ifromindex(iv,iv); /* Convert from index value to input color space value */
+
+ if (infunc != NULL)
+ infunc(cbctx, iv, iv); /* In colorspace -> input table -> In colorspace. */
+
+ itoentry(iv,iv); /* Convert from input color space value to table value */
+
+ /* Expand used range to 0.0 - 1.0, and clip to legal values */
+ /* Note that if the range is reduced, and clipping occurs, */
+ /* then there should be enough resolution within the input */
+ /* table, to represent the sharp edges of the clipping. */
+ for (e = 0; e < pn->inputChan; e++) {
+ double tt;
+ tt = (iv[e] - imin[e])/(imax[e] - imin[e]);
+ if (tt < 0.0) {
+ DBGSLC(("iclip: tt = %f, iv = %f, omin = %f, omax = %f\n",tt,iv[e],omin[e],omax[e]));
+ if (tt < -CLIP_MARGIN)
+ clip = 1;
+ tt = 0.0;
+ } else if (tt > 1.0) {
+ DBGSLC(("iclip: tt = %f, iv = %f, omin = %f, omax = %f\n",tt,iv[e],omin[e],omax[e]));
+ if (tt > (1.0 + CLIP_MARGIN))
+ clip = 1;
+ tt = 1.0;
+ }
+ iv[e] = tt;
+ }
+
+ for (e = 0; e < pn->inputChan; e++) /* Input tables */
+ pn->inputTable[e * pn->inputEnt + n] = iv[e];
+ }
+ }
+
+ /* Allocate space for cell center value lookup */
+ if (flags & ICM_CLUT_SET_APXLS) {
+ if ((clutTable2 = (double **) icp->al->calloc(icp->al,sizeof(double *), ntables)) == NULL) {
+ sprintf(icp->err,"icmLut_set_tables malloc of cube center array failed");
+ icp->al->free(icp->al, _iv);
+ return icp->errc = 1;
+ }
+ for (tn = 0; tn < ntables; tn++) {
+ if ((clutTable2[tn] = (double *) icp->al->calloc(icp->al,sizeof(double),
+ p->clutTable_size)) == NULL) {
+ for (--tn; tn >= 0; tn--)
+ icp->al->free(icp->al, clutTable2[tn]);
+ icp->al->free(icp->al, _iv);
+ icp->al->free(icp->al, clutTable2);
+ sprintf(icp->err,"icmLut_set_tables malloc of cube center array failed");
+ return icp->errc = 1;
+ }
+ }
+ }
+
+ /* Allocate space for smoothing radius values */
+ if (flags & ICM_CLUT_SET_FILTER) {
+ unsigned int j, g, size;
+
+ /* Private: compute dimensional increment though clut3 */
+ i = p->inputChan-1;
+ dinc3[i--] = ntables;
+ for (; i < p->inputChan; i--)
+ dinc3[i] = dinc3[i+1] * p->clutPoints;
+
+ /* Private: compute offsets from base of cube to other corners */
+ for (dcube3[0] = 0, g = 1, j = 0; j < p->inputChan; j++) {
+ for (i = 0; i < g; i++)
+ dcube3[g+i] = dcube3[i] + dinc3[j];
+ g *= 2;
+ }
+
+ if ((size = sat_mul(ntables, sat_pow(p->clutPoints,p->inputChan))) == UINT_MAX) {
+ sprintf(icp->err,"icmLut_alloc size overflow");
+ if (flags & ICM_CLUT_SET_APXLS) {
+ for (tn = 0; tn < ntables; tn++)
+ icp->al->free(icp->al, clutTable2[tn]);
+ }
+ icp->al->free(icp->al, clutTable2);
+ icp->al->free(icp->al, _iv);
+ return icp->errc = 1;
+ }
+
+ if ((clutTable3 = (double *) icp->al->calloc(icp->al,sizeof(double),
+ size)) == NULL) {
+ if (flags & ICM_CLUT_SET_APXLS) {
+ for (tn = 0; tn < ntables; tn++)
+ icp->al->free(icp->al, clutTable2[tn]);
+ }
+ icp->al->free(icp->al, clutTable2);
+ icp->al->free(icp->al, _iv);
+ sprintf(icp->err,"icmLut_set_tables malloc of vertex smoothing value array failed");
+ return icp->errc = 1;
+ }
+ }
+
+ /* Create the multi-dimensional lookup table values */
+
+ /* To make this clut function cache friendly, we use the pseudo-hilbert */
+ /* count sequence. This keeps each point close to the last in the */
+ /* multi-dimensional space. This is the point of setting multiple Luts at */
+ /* once too - the assumption is that these tables are all related (different */
+ /* gamut compressions for instance), and hence calling the clutfunc() with */
+ /* close values will maximise reverse lookup cache hit rate. */
+
+ psh_init(&counter, p->inputChan, p->clutPoints, ii); /* Initialise counter */
+
+ /* Itterate through all verticies in the grid */
+ for (;;) {
+ int ti, ti3; /* Table indexes */
+
+ for (ti = e = 0; e < p->inputChan; e++) { /* Input tables */
+ ti += ii[e] * p->dinc[e]; /* Clut index */
+ iv[e] = ii[e]/(p->clutPoints-1.0); /* Vertex coordinates */
+ iv[e] = iv[e] * (imax[e] - imin[e]) + imin[e]; /* Undo expansion to 0.0 - 1.0 */
+ *((int *)&iv[-((int)e)-1]) = ii[e]; /* Trick to supply grid index in iv[] */
+ }
+
+ if (flags & ICM_CLUT_SET_FILTER) {
+ for (ti3 = e = 0; e < p->inputChan; e++) /* Input tables */
+ ti3 += ii[e] * dinc3[e]; /* Clut3 index */
+ }
+
+ DBGSL(("\nix %s\n",icmPiv(p->inputChan, ii)));
+ DBGSL(("raw itv %s to iv'",icmPdv(p->inputChan, iv)));
+ ifromentry(iv,iv); /* Convert from table value to input color space */
+ DBGSL((" %s\n",icmPdv(p->inputChan, iv)));
+
+ /* Apply incolor -> outcolor function we want to represent for all tables */
+ DBGSL(("iv: %s to ov'",icmPdv(p->inputChan, iv)));
+ clutfunc(cbctx, iv, iv);
+ DBGSL((" %s\n",icmPdv(p->outputChan, iv)));
+
+ /* Save the results to the output tables */
+ for (tn = 0, ivn = iv; tn < ntables; ivn += p->outputChan, tn++) {
+ pn = pp[tn];
+
+ DBGSL(("tn %d, ov' %s -> otv",tn,icmPdv(p->outputChan, ivn)));
+ otoentry(ivn,ivn); /* Convert from output color space value to table value */
+ DBGSL((" %s\n -> oval",icmPdv(p->outputChan, ivn)));
+
+ /* Expand used range to 0.0 - 1.0, and clip to legal values */
+ for (f = 0; f < pn->outputChan; f++) {
+ double tt;
+ tt = (ivn[f] - omin[f])/(omax[f] - omin[f]);
+ if (tt < 0.0) {
+ DBGSLC(("lclip: tt = %f, ivn= %f, omin = %f, omax = %f\n",tt,ivn[f],omin[f],omax[f]));
+ if (tt < -CLIP_MARGIN)
+ clip = 2;
+ tt = 0.0;
+ } else if (tt > 1.0) {
+ DBGSLC(("lclip: tt = %f, ivn= %f, omin = %f, omax = %f\n",tt,ivn[f],omin[f],omax[f]));
+ if (tt > (1.0 + CLIP_MARGIN))
+ clip = 2;
+ tt = 1.0;
+ }
+ ivn[f] = tt;
+ }
+
+ for (f = 0; f < pn->outputChan; f++) /* Output chans */
+ pn->clutTable[ti + f] = ivn[f];
+ DBGSL((" %s\n",icmPdv(pn->outputChan, ivn)));
+
+ if (flags & ICM_CLUT_SET_FILTER) {
+ clutTable3[ti3 + tn] = iv[-1 -tn]; /* Filter radiuses */
+ }
+ }
+
+ /* Lookup cell center value if ICM_CLUT_SET_APXLS */
+ if (clutTable2 != NULL) {
+
+ for (e = 0; e < p->inputChan; e++) {
+ if (ii[e] >= (p->clutPoints-1))
+ break; /* Don't lookup beyond last */
+ iv[e] = (ii[e] + 0.5)/(p->clutPoints-1.0); /* Vertex coordinates + 0.5 */
+ iv[e] = iv[e] * (imax[e] - imin[e]) + imin[e]; /* Undo expansion to 0.0 - 1.0 */
+ *((int *)&iv[-((int)e)-1]) = ii[e]; /* Trick to supply grid index in iv[] */
+ /* (Not this is only the base for +0.5) */
+ }
+
+ if (e >= p->inputChan) { /* We're not on the last row */
+
+ ifromentry(iv,iv); /* Convert from table value to input color space */
+
+ /* Apply incolor -> outcolor function we want to represent */
+ clutfunc(cbctx, iv, iv);
+
+ /* Save the results to the output tables */
+ for (tn = 0, ivn = iv; tn < ntables; ivn += p->outputChan, tn++) {
+ pn = pp[tn];
+
+ otoentry(ivn,ivn); /* Convert from output color space value to table value */
+
+ /* Expand used range to 0.0 - 1.0, and clip to legal values */
+ for (f = 0; f < pn->outputChan; f++) {
+ double tt;
+ tt = (ivn[f] - omin[f])/(omax[f] - omin[f]);
+ if (tt < 0.0) {
+ DBGSLC(("lclip: tt = %f, ivn= %f, omin = %f, omax = %f\n",tt,ivn[f],omin[f],omax[f]));
+ if (tt < -CLIP_MARGIN)
+ clip = 3;
+ tt = 0.0;
+ } else if (tt > 1.0) {
+ DBGSLC(("lclip: tt = %f, ivn= %f, omin = %f, omax = %f\n",tt,ivn[f],omin[f],omax[f]));
+ if (tt > (1.0 + CLIP_MARGIN))
+ clip = 3;
+ tt = 1.0;
+ }
+ ivn[f] = tt;
+ }
+
+ for (f = 0; f < pn->outputChan; f++) /* Output chans */
+ clutTable2[tn][ti + f] = ivn[f];
+ }
+ }
+ }
+
+ /* Increment index within block (Reverse index significancd) */
+ if (psh_inc(&counter, ii))
+ break;
+ }
+
+#define APXLS_WHT 0.5
+#define APXLS_DIFF_THRHESH 0.2
+ /* Deal with cell center value, aproximate least squares adjustment. */
+ /* Subtract some of the mean of the surrounding center values from each grid value. */
+ /* Skip the edges so that things like the white point are not changed. */
+ /* Avoid modifying the value if the difference between the */
+ /* interpolated value and the current value is too great, */
+ /* and there is the possibility of different color aliases. */
+ if (clutTable2 != NULL) {
+ int ti; /* cube vertex table index */
+ int ti2; /* cube center table2 index */
+ int ee;
+ double cw = 1.0/(double)(1 << p->inputChan); /* Weight for each cube corner */
+
+ /* For each cell center point except last row */
+ for (e = 0; e < p->inputChan; e++)
+ ii[e] = 0; /* init coords */
+
+ /* Compute linear interpolated value from center values */
+ for (ee = 0; ee < p->inputChan;) {
+
+ /* Compute base index for table2 */
+ for (ti2 = e = 0; e < p->inputChan; e++) /* Input tables */
+ ti2 += ii[e] * p->dinc[e]; /* Clut index */
+
+ ti = ti2 + p->dcube[(1 << p->inputChan)-1]; /* +1 to each coord for vertex index */
+
+ for (tn = 0; tn < ntables; tn++) {
+ double mval[MAX_CHAN], vv;
+ double maxd = 0.0;
+
+ pn = pp[tn];
+
+ /* Compute mean of center values */
+ for (f = 0; f < pn->outputChan; f++) { /* Output chans */
+
+ mval[f] = 0.0;
+ for (i = 0; i < (1 << p->inputChan); i++) { /* For surrounding center values */
+ mval[f] += clutTable2[tn][ti2 + p->dcube[i] + f];
+ }
+ mval[f] = pn->clutTable[ti + f] - mval[f] * cw; /* Diff to mean */
+ vv = fabs(mval[f]);
+ if (vv > maxd)
+ maxd = vv;
+ }
+
+ if (pn->outputChan <= 3 || maxd < APXLS_DIFF_THRHESH) {
+ for (f = 0; f < pn->outputChan; f++) { /* Output chans */
+
+ vv = pn->clutTable[ti + f] + APXLS_WHT * mval[f];
+
+ /* Hmm. This is a bit crude. How do we know valid range is 0-1 ? */
+ /* What about an ink limit ? */
+ if (vv < 0.0) {
+ vv = 0.0;
+ } else if (vv > 1.0) {
+ vv = 1.0;
+ }
+ pn->clutTable[ti + f] = vv;
+ }
+ }
+ }
+
+ /* Increment coord */
+ for (ee = 0; ee < p->inputChan; ee++) {
+ if (++ii[ee] < (p->clutPoints-2)) /* Don't go through upper edge */
+ break; /* No carry */
+ ii[ee] = 0;
+ }
+ }
+
+ /* Done with center values */
+ for (tn = 0; tn < ntables; tn++)
+ icp->al->free(icp->al, clutTable2[tn]);
+ icp->al->free(icp->al, clutTable2);
+ }
+
+ /* Apply any smoothing in the clipped region to the resulting clutTable */
+ if (clutTable3 != NULL) {
+ double *clutTable1; /* Copy of current unfilted values */
+ FCOUNT(cc, MAX_CHAN, p->inputChan); /* Surrounding counter */
+
+ if ((clutTable1 = (double *) icp->al->calloc(icp->al,sizeof(double),
+ p->clutTable_size)) == NULL) {
+ icp->al->free(icp->al, clutTable3);
+ icp->al->free(icp->al, _iv);
+ sprintf(icp->err,"icmLut_set_tables malloc of grid copy failed");
+ return icp->errc = 1;
+ }
+
+ for (tn = 0; tn < ntables; tn++) {
+ int aa;
+ int ee;
+ int ti, ti3; /* Table indexes */
+
+ pn = pp[tn];
+
+ /* For each pass */
+ for (aa = 0; aa < 2; aa++) {
+
+ /* Copy current values */
+ memcpy(clutTable1, pn->clutTable, sizeof(double) * pn->clutTable_size);
+
+ /* Filter each point */
+ for (e = 0; e < pn->inputChan; e++)
+ ii[e] = 0; /* init coords */
+
+ /* Compute linear interpolated error to actual cell center value */
+ for (ee = 0; ee < pn->inputChan;) {
+ double rr; /* Filter radius */
+ int ir; /* Integer radius */
+ double tw; /* Total weight */
+
+ /* Compute base index for this cell */
+ for (ti3 = ti = e = 0; e < pn->inputChan; e++) { /* Input tables */
+ ti += ii[e] * pn->dinc[e]; /* Clut index */
+ ti3 += ii[e] * dinc3[e]; /* Clut3 index */
+ }
+ rr = clutTable3[ti3 + tn] * (pn->clutPoints-1.0);
+ ir = (int)floor(rr + 0.5); /* Don't bother unless 1/2 over vertex */
+
+ if (ir < 1)
+ goto next_vert;
+
+ //FRECONF(cc, -ir, ir + 1); /* Set size of surroundign grid */
+
+ /* Clip scanning cube to be within grid */
+ for (e = 0; e < pn->inputChan; e++) {
+ int cr = ir;
+ if ((ii[e] - ir) < 0)
+ cr = ii[e];
+ if ((ii[e] + ir) >= pn->clutPoints)
+ cr = pn->clutPoints -1 -ii[e];
+
+ cc_stt[e] = -cr;
+ cc_res[e] = cr + 1;
+ }
+
+ for (f = 0; f < pn->outputChan; f++)
+ pn->clutTable[ti + f] = 0.0;
+ tw = 0.0;
+
+ FC_INIT(cc)
+ for (tw = 0.0; !FC_DONE(cc);) {
+ double r;
+ int tti;
+
+ /* Radius of this cell */
+ for (r = 0.0, tti = e = 0; e < pn->inputChan; e++) {
+ int ix;
+ r += cc[e] * cc[e];
+ tti += (ii[e] + cc[e]) * p->dinc[e];
+ }
+ r = sqrt(r);
+
+ if (r <= rr && e >= pn->inputChan) {
+ double w = (rr - r)/rr; /* Triangle weighting */
+ w = sqrt(w);
+ for (f = 0; f < pn->outputChan; f++)
+ pn->clutTable[ti+f] += w * clutTable1[tti + f];
+ tw += w;
+ }
+ FC_INC(cc);
+ }
+ for (f = 0; f < pn->outputChan; f++) {
+ double vv = pn->clutTable[ti+f] / tw;
+ if (vv < 0.0) {
+ vv = 0.0;
+ } else if (vv > 1.0) {
+ vv = 1.0;
+ }
+ pn->clutTable[ti+f] = vv;
+ }
+
+ /* Increment coord */
+ next_vert:;
+ for (ee = 0; ee < pn->inputChan; ee++) {
+ if (++ii[ee] < (pn->clutPoints-1)) /* Don't go through upper edge */
+ break; /* No carry */
+ ii[ee] = 0;
+ }
+ } /* Next grid point to filter */
+ } /* Next pass */
+ } /* Next table */
+
+ free(clutTable1);
+ free(clutTable3);
+ }
+
+ /* Create the output table entry values */
+ for (tn = 0; tn < ntables; tn++) {
+ pn = pp[tn];
+ for (n = 0; n < pn->outputEnt; n++) {
+ double fv;
+ fv = n/(pn->outputEnt-1.0);
+ for (f = 0; f < pn->outputChan; f++)
+ iv[f] = fv;
+
+ /* Undo expansion to 0.0 - 1.0 */
+ for (f = 0; f < pn->outputChan; f++) /* Output tables */
+ iv[f] = iv[f] * (omax[f] - omin[f]) + omin[f];
+
+ ofromentry(iv,iv); /* Convert from table value to output color space value */
+
+ if (outfunc != NULL)
+ outfunc(cbctx, iv, iv); /* Out colorspace -> output table -> out colorspace. */
+
+ otoentry(iv,iv); /* Convert from output color space value to table value */
+
+ /* Clip to legal values */
+ for (f = 0; f < pn->outputChan; f++) {
+ double tt;
+ tt = iv[f];
+ if (tt < 0.0) {
+ DBGSLC(("oclip: tt = %f\n",tt));
+ if (tt < -CLIP_MARGIN)
+ clip = 5;
+ tt = 0.0;
+ } else if (tt > 1.0) {
+ DBGSLC(("oclip: tt = %f\n",tt));
+ if (tt > (1.0 + CLIP_MARGIN))
+ clip = 5;
+ tt = 1.0;
+ }
+ iv[f] = tt;
+ }
+
+ for (f = 0; f < pn->outputChan; f++) /* Input tables */
+ pn->outputTable[f * pn->outputEnt + n] = iv[f];
+ }
+ }
+
+ icp->al->free(icp->al, _iv);
+
+ icp->warnc = 0;
+ if (clip) {
+ DBGSLC(("Returning clip status = %d\n",clip));
+ icp->warnc = clip;
+ }
+
+ return 0;
+}
+
+/* Helper function to initialize a Lut tables contents */
+/* from supplied transfer functions. */
+/* Set errc and return error number */
+/* Set warnc if there is clipping in the output values */
+static int icmLut_set_tables (
+icmLut *p, /* Pointer to Lut object */
+int flags, /* Setting flags */
+void *cbctx, /* Opaque callback context pointer value */
+icColorSpaceSignature insig, /* Input color space */
+icColorSpaceSignature outsig, /* Output color space */
+void (*infunc)(void *cbcntx, double *out, double *in),
+ /* Input transfer function, inspace->inspace' (NULL = default) */
+double *inmin, double *inmax, /* Maximum range of inspace' values (NULL = default) */
+void (*clutfunc)(void *cbctx, double *out, double *in),
+ /* inspace' -> outspace' transfer function */
+double *clutmin, double *clutmax, /* Maximum range of outspace' values (NULL = default) */
+void (*outfunc)(void *cbctx, double *out, double *in)
+ /* Output transfer function, outspace'->outspace (NULL = deflt) */
+) {
+ struct _icmLut *pp[3];
+
+ /* Simply call the multiple table function with one table */
+ pp[0] = p;
+ return icmSetMultiLutTables(1, pp, flags,
+ cbctx, insig, outsig,
+ infunc,
+ inmin, inmax,
+ clutfunc,
+ clutmin, clutmax,
+ outfunc);
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmLut_get_size(
+ icmBase *pp
+) {
+ icmLut *p = (icmLut *)pp;
+ unsigned int len = 0;
+
+ if (p->ttype == icSigLut8Type) {
+ len = sat_add(len, 48); /* tag and header */
+ len = sat_add(len, sat_mul3(1, p->inputChan, p->inputEnt));
+ len = sat_add(len, sat_mul3(1, p->outputChan, sat_pow(p->clutPoints,p->inputChan)));
+ len = sat_add(len, sat_mul3(1, p->outputChan, p->outputEnt));
+ } else {
+ len = sat_add(len, 52); /* tag and header */
+ len = sat_add(len, sat_mul3(2, p->inputChan, p->inputEnt));
+ len = sat_add(len, sat_mul3(2, p->outputChan, sat_pow(p->clutPoints,p->inputChan)));
+ len = sat_add(len, sat_mul3(2, p->outputChan, p->outputEnt));
+ }
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmLut_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmLut *p = (icmLut *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, j, g, size;
+ char *bp, *buf;
+
+ if (len < 4) {
+ sprintf(icp->err,"icmLut_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmLut_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmLut_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ p->ttype = (icTagTypeSignature)read_SInt32Number(bp);
+ if (p->ttype != icSigLut8Type && p->ttype != icSigLut16Type) {
+ sprintf(icp->err,"icmLut_read: Wrong tag type for icmLut");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ if (p->ttype == icSigLut8Type) {
+ if (len < 48) {
+ sprintf(icp->err,"icmLut_read: Tag too small to be legal");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ } else {
+ if (len < 52) {
+ sprintf(icp->err,"icmLut_read: Tag too small to be legal");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ }
+
+ /* Read in the info common to 8 and 16 bit Lut */
+ p->inputChan = read_UInt8Number(bp+8);
+ p->outputChan = read_UInt8Number(bp+9);
+ p->clutPoints = read_UInt8Number(bp+10);
+
+ /* Sanity check */
+ if (p->inputChan > MAX_CHAN) {
+ sprintf(icp->err,"icmLut_read: Can't handle > %d input channels\n",MAX_CHAN);
+ return icp->errc = 1;
+ }
+
+ if (p->outputChan > MAX_CHAN) {
+ sprintf(icp->err,"icmLut_read: Can't handle > %d output channels\n",MAX_CHAN);
+ return icp->errc = 1;
+ }
+
+ /* Read 3x3 transform matrix */
+ for (j = 0; j < 3; j++) { /* Rows */
+ for (i = 0; i < 3; i++) { /* Columns */
+ p->e[j][i] = read_S15Fixed16Number(bp + 12 + ((j * 3 + i) * 4));
+ }
+ }
+ /* Read 16 bit specific stuff */
+ if (p->ttype == icSigLut8Type) {
+ p->inputEnt = 256; /* By definition */
+ p->outputEnt = 256; /* By definition */
+ bp = buf+48;
+ } else {
+ p->inputEnt = read_UInt16Number(bp+48);
+ p->outputEnt = read_UInt16Number(bp+50);
+ bp = buf+52;
+ }
+
+ /* Sanity check dimensions. This protects against */
+ /* subsequent integer overflows involving the dimensions. */
+ if ((size = icmLut_get_size((icmBase *)p)) == UINT_MAX
+ || size > len) {
+ sprintf(icp->err,"icmLut_read: Tag wrong size for contents");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read the input tables */
+ size = (p->inputChan * p->inputEnt);
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ if (p->ttype == icSigLut8Type) {
+ for (i = 0; i < size; i++, bp += 1)
+ p->inputTable[i] = read_DCS8Number(bp);
+ } else {
+ for (i = 0; i < size; i++, bp += 2)
+ p->inputTable[i] = read_DCS16Number(bp);
+ }
+
+ /* Read the clut table */
+ size = (p->outputChan * sat_pow(p->clutPoints,p->inputChan));
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ if (p->ttype == icSigLut8Type) {
+ for (i = 0; i < size; i++, bp += 1)
+ p->clutTable[i] = read_DCS8Number(bp);
+ } else {
+ for (i = 0; i < size; i++, bp += 2)
+ p->clutTable[i] = read_DCS16Number(bp);
+ }
+
+ /* Read the output tables */
+ size = (p->outputChan * p->outputEnt);
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ if (p->ttype == icSigLut8Type) {
+ for (i = 0; i < size; i++, bp += 1)
+ p->outputTable[i] = read_DCS8Number(bp);
+ } else {
+ for (i = 0; i < size; i++, bp += 2)
+ p->outputTable[i] = read_DCS16Number(bp);
+ }
+
+ /* Private: compute dimensional increment though clut */
+ /* Note that first channel varies least rapidly. */
+ i = p->inputChan-1;
+ p->dinc[i--] = p->outputChan;
+ for (; i < p->inputChan; i--)
+ p->dinc[i] = p->dinc[i+1] * p->clutPoints;
+
+ /* Private: compute offsets from base of cube to other corners */
+ for (p->dcube[0] = 0, g = 1, j = 0; j < p->inputChan; j++) {
+ for (i = 0; i < g; i++)
+ p->dcube[g+i] = p->dcube[i] + p->dinc[j];
+ g *= 2;
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmLut_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmLut *p = (icmLut *)pp;
+ icc *icp = p->icp;
+ unsigned int i,j;
+ unsigned int len, size;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmLut_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmLut_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write the info common to 8 and 16 bit Lut */
+ if ((rv = write_UInt8Number(p->inputChan, bp+8)) != 0) {
+ sprintf(icp->err,"icmLut_write: write_UInt8Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt8Number(p->outputChan, bp+9)) != 0) {
+ sprintf(icp->err,"icmLut_write: write_UInt8Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt8Number(p->clutPoints, bp+10)) != 0) {
+ sprintf(icp->err,"icmLut_write: write_UInt8Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ write_UInt8Number(0, bp+11); /* Set padding to 0 */
+
+ /* Write 3x3 transform matrix */
+ for (j = 0; j < 3; j++) { /* Rows */
+ for (i = 0; i < 3; i++) { /* Columns */
+ if ((rv = write_S15Fixed16Number(p->e[j][i],bp + 12 + ((j * 3 + i) * 4))) != 0) {
+ sprintf(icp->err,"icmLut_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ /* Write 16 bit specific stuff */
+ if (p->ttype == icSigLut8Type) {
+ if (p->inputEnt != 256 || p->outputEnt != 256) {
+ sprintf(icp->err,"icmLut_write: 8 bit Input and Output tables must be 256 entries");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp = buf+48;
+ } else {
+ if (p->inputEnt > 4096 || p->outputEnt > 4096) {
+ sprintf(icp->err,"icmLut_write: 16 bit Input and Output tables must each be less than 4096 entries");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if ((rv = write_UInt16Number(p->inputEnt, bp+48)) != 0) {
+ sprintf(icp->err,"icmLut_write: write_UInt16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt16Number(p->outputEnt, bp+50)) != 0) {
+ sprintf(icp->err,"icmLut_write: write_UInt16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp = buf+52;
+ }
+
+ /* Write the input tables */
+ size = (p->inputChan * p->inputEnt);
+ if (p->ttype == icSigLut8Type) {
+ for (i = 0; i < size; i++, bp += 1) {
+ if ((rv = write_DCS8Number(p->inputTable[i], bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: inputTable write_DCS8Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ } else {
+ for (i = 0; i < size; i++, bp += 2) {
+ if ((rv = write_DCS16Number(p->inputTable[i], bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: inputTable write_DCS16Number(%f) failed",p->inputTable[i]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ /* Write the clut table */
+ size = (p->outputChan * sat_pow(p->clutPoints,p->inputChan));
+ if (p->ttype == icSigLut8Type) {
+ for (i = 0; i < size; i++, bp += 1) {
+ if ((rv = write_DCS8Number(p->clutTable[i], bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: clutTable write_DCS8Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ } else {
+ for (i = 0; i < size; i++, bp += 2) {
+ if ((rv = write_DCS16Number(p->clutTable[i], bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: clutTable write_DCS16Number(%f) failed",p->clutTable[i]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ /* Write the output tables */
+ size = (p->outputChan * p->outputEnt);
+ if (p->ttype == icSigLut8Type) {
+ for (i = 0; i < size; i++, bp += 1) {
+ if ((rv = write_DCS8Number(p->outputTable[i], bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: outputTable write_DCS8Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ } else {
+ for (i = 0; i < size; i++, bp += 2) {
+ if ((rv = write_DCS16Number(p->outputTable[i], bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: outputTable write_DCS16Number(%f) failed",p->outputTable[i]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ /* Write buffer to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmLut_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmLut_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmLut *p = (icmLut *)pp;
+ if (verb <= 0)
+ return;
+
+ if (p->ttype == icSigLut8Type) {
+ op->gprintf(op,"Lut8:\n");
+ } else {
+ op->gprintf(op,"Lut16:\n");
+ }
+ op->gprintf(op," Input Channels = %u\n",p->inputChan);
+ op->gprintf(op," Output Channels = %u\n",p->outputChan);
+ op->gprintf(op," CLUT resolution = %u\n",p->clutPoints);
+ op->gprintf(op," Input Table entries = %u\n",p->inputEnt);
+ op->gprintf(op," Output Table entries = %u\n",p->outputEnt);
+ op->gprintf(op," XYZ matrix = %f, %f, %f\n",p->e[0][0],p->e[0][1],p->e[0][2]);
+ op->gprintf(op," %f, %f, %f\n",p->e[1][0],p->e[1][1],p->e[1][2]);
+ op->gprintf(op," %f, %f, %f\n",p->e[2][0],p->e[2][1],p->e[2][2]);
+
+ if (verb >= 2) {
+ unsigned int i, j, size;
+ unsigned int ii[MAX_CHAN]; /* maximum no of input channels */
+
+ op->gprintf(op," Input table:\n");
+ for (i = 0; i < p->inputEnt; i++) {
+ op->gprintf(op," %3u: ",i);
+ for (j = 0; j < p->inputChan; j++)
+ op->gprintf(op," %1.10f",p->inputTable[j * p->inputEnt + i]);
+ op->gprintf(op,"\n");
+ }
+
+ op->gprintf(op,"\n CLUT table:\n");
+ if (p->inputChan > MAX_CHAN) {
+ op->gprintf(op," !!Can't dump > %d input channel CLUT table!!\n",MAX_CHAN);
+ } else {
+ size = (p->outputChan * sat_pow(p->clutPoints,p->inputChan));
+ for (j = 0; j < p->inputChan; j++)
+ ii[j] = 0;
+ for (i = 0; i < size;) {
+ unsigned int k;
+ /* Print table entry index */
+ op->gprintf(op," ");
+ for (j = p->inputChan-1; j < p->inputChan; j--)
+ op->gprintf(op," %2u",ii[j]);
+ op->gprintf(op,":");
+ /* Print table entry contents */
+ for (k = 0; k < p->outputChan; k++, i++)
+ op->gprintf(op," %1.10f",p->clutTable[i]);
+ op->gprintf(op,"\n");
+
+ for (j = 0; j < p->inputChan; j++) { /* Increment index */
+ ii[j]++;
+ if (ii[j] < p->clutPoints)
+ break; /* No carry */
+ ii[j] = 0;
+ }
+ }
+ }
+
+ op->gprintf(op,"\n Output table:\n");
+ for (i = 0; i < p->outputEnt; i++) {
+ op->gprintf(op," %3u: ",i);
+ for (j = 0; j < p->outputChan; j++)
+ op->gprintf(op," %1.10f",p->outputTable[j * p->outputEnt + i]);
+ op->gprintf(op,"\n");
+ }
+
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmLut_allocate(
+ icmBase *pp
+) {
+ unsigned int i, j, g, size;
+ icmLut *p = (icmLut *)pp;
+ icc *icp = p->icp;
+
+ /* Sanity check */
+ if (p->inputChan < 1) {
+ sprintf(icp->err,"icmLut_alloc: Can't handle %d input channels\n",p->inputChan);
+ return icp->errc = 1;
+ }
+
+ if (p->inputChan > MAX_CHAN) {
+ sprintf(icp->err,"icmLut_alloc: Can't handle > %d input channels\n",MAX_CHAN);
+ return icp->errc = 1;
+ }
+
+ if (p->outputChan > MAX_CHAN) {
+ sprintf(icp->err,"icmLut_alloc: Can't handle > %d output channels\n",MAX_CHAN);
+ return icp->errc = 1;
+ }
+
+ if ((size = sat_mul(p->inputChan, p->inputEnt)) == UINT_MAX) {
+ sprintf(icp->err,"icmLut_alloc size overflow");
+ return icp->errc = 1;
+ }
+ if (size != p->inputTable_size) {
+ if (ovr_mul(size, sizeof(double))) {
+ sprintf(icp->err,"icmLut_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->inputTable != NULL)
+ icp->al->free(icp->al, p->inputTable);
+ if ((p->inputTable = (double *) icp->al->calloc(icp->al,size, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmLut_alloc: calloc() of Lut inputTable data failed");
+ return icp->errc = 2;
+ }
+ p->inputTable_size = size;
+ }
+ if ((size = sat_mul(p->outputChan, sat_pow(p->clutPoints,p->inputChan))) == UINT_MAX) {
+ sprintf(icp->err,"icmLut_alloc size overflow");
+ return icp->errc = 1;
+ }
+ if (size != p->clutTable_size) {
+ if (ovr_mul(size, sizeof(double))) {
+ sprintf(icp->err,"icmLut_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->clutTable != NULL)
+ icp->al->free(icp->al, p->clutTable);
+ if ((p->clutTable = (double *) icp->al->calloc(icp->al,size, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmLut_alloc: calloc() of Lut clutTable data failed");
+ return icp->errc = 2;
+ }
+ p->clutTable_size = size;
+ }
+ if ((size = sat_mul(p->outputChan, p->outputEnt)) == UINT_MAX) {
+ sprintf(icp->err,"icmLut_alloc size overflow");
+ return icp->errc = 1;
+ }
+ if (size != p->outputTable_size) {
+ if (ovr_mul(size, sizeof(double))) {
+ sprintf(icp->err,"icmLut_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->outputTable != NULL)
+ icp->al->free(icp->al, p->outputTable);
+ if ((p->outputTable = (double *) icp->al->calloc(icp->al,size, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmLut_alloc: calloc() of Lut outputTable data failed");
+ return icp->errc = 2;
+ }
+ p->outputTable_size = size;
+ }
+
+ /* Private: compute dimensional increment though clut */
+ /* Note that first channel varies least rapidly. */
+ i = p->inputChan-1;
+ p->dinc[i--] = p->outputChan;
+ for (; i < p->inputChan; i--)
+ p->dinc[i] = p->dinc[i+1] * p->clutPoints;
+
+ /* Private: compute offsets from base of cube to other corners */
+ for (p->dcube[0] = 0, g = 1, j = 0; j < p->inputChan; j++) {
+ for (i = 0; i < g; i++)
+ p->dcube[g+i] = p->dcube[i] + p->dinc[j];
+ g *= 2;
+ }
+
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmLut_delete(
+ icmBase *pp
+) {
+ icmLut *p = (icmLut *)pp;
+ icc *icp = p->icp;
+ int i;
+
+ if (p->inputTable != NULL)
+ icp->al->free(icp->al, p->inputTable);
+ if (p->clutTable != NULL)
+ icp->al->free(icp->al, p->clutTable);
+ if (p->outputTable != NULL)
+ icp->al->free(icp->al, p->outputTable);
+ for (i = 0; i < p->inputChan; i++)
+ icmTable_delete_bwd(icp, &p->rit[i]);
+ for (i = 0; i < p->outputChan; i++)
+ icmTable_delete_bwd(icp, &p->rot[i]);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmLut(
+ icc *icp
+) {
+ int i,j;
+ icmLut *p;
+ if ((p = (icmLut *) icp->al->calloc(icp->al,1,sizeof(icmLut))) == NULL)
+ return NULL;
+ p->ttype = icSigLut16Type;
+ p->refcount = 1;
+ p->get_size = icmLut_get_size;
+ p->read = icmLut_read;
+ p->write = icmLut_write;
+ p->dump = icmLut_dump;
+ p->allocate = icmLut_allocate;
+ p->del = icmLut_delete;
+
+ /* Lookup methods */
+ p->nu_matrix = icmLut_nu_matrix;
+ p->min_max = icmLut_min_max;
+ p->lookup_matrix = icmLut_lookup_matrix;
+ p->lookup_input = icmLut_lookup_input;
+ p->lookup_clut_nl = icmLut_lookup_clut_nl;
+ p->lookup_clut_sx = icmLut_lookup_clut_sx;
+ p->lookup_output = icmLut_lookup_output;
+
+ /* Set method */
+ p->set_tables = icmLut_set_tables;
+
+ p->icp = icp;
+
+ /* Set matrix to reasonable default */
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++) {
+ if (i == j)
+ p->e[i][j] = 1.0;
+ else
+ p->e[i][j] = 0.0;
+ }
+
+ /* Init lookups to non-dangerous values */
+ for (i = 0; i < MAX_CHAN; i++)
+ p->dinc[i] = 0;
+
+ for (i = 0; i < (1 << MAX_CHAN); i++)
+ p->dcube[i] = 0;
+
+ for (i = 0; i < MAX_CHAN; i++) {
+ p->rit[i].inited = 0;
+ p->rot[i].inited = 0;
+ }
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* Measurement */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmMeasurement_get_size(
+ icmBase *pp
+) {
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 4); /* 4 for standard observer */
+ len = sat_add(len, 12); /* 12 for XYZ of measurement backing */
+ len = sat_add(len, 4); /* 4 for measurement geometry */
+ len = sat_add(len, 4); /* 4 for measurement flare */
+ len = sat_add(len, 4); /* 4 for standard illuminant */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmMeasurement_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmMeasurement *p = (icmMeasurement *)pp;
+ icc *icp = p->icp;
+ int rv;
+ char *bp, *buf;
+
+ if (len < 36) {
+ sprintf(icp->err,"icmMeasurement_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmMeasurement_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmMeasurement_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmMeasurement_read: Wrong tag type for icmMeasurement");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read the encoded standard observer */
+ p->observer = (icStandardObserver)read_SInt32Number(bp + 8);
+
+ /* Read the XYZ values for measurement backing */
+ if ((rv = read_XYZNumber(&p->backing, bp+12)) != 0) {
+ sprintf(icp->err,"icmMeasurement: read_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Read the encoded measurement geometry */
+ p->geometry = (icMeasurementGeometry)read_SInt32Number(bp + 24);
+
+ /* Read the proportion of flare */
+ p->flare = read_U16Fixed16Number(bp + 28);
+
+ /* Read the encoded standard illuminant */
+ p->illuminant = (icIlluminant)read_SInt32Number(bp + 32);
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmMeasurement_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmMeasurement *p = (icmMeasurement *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmMeasurement_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmMeasurement_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmMeasurement_write, type: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write the encoded standard observer */
+ if ((rv = write_SInt32Number((int)p->observer, bp + 8)) != 0) {
+ sprintf(icp->err,"icmMeasurementa_write, observer: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write the XYZ values for measurement backing */
+ if ((rv = write_XYZNumber(&p->backing, bp+12)) != 0) {
+ sprintf(icp->err,"icmMeasurement, backing: write_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write the encoded measurement geometry */
+ if ((rv = write_SInt32Number((int)p->geometry, bp + 24)) != 0) {
+ sprintf(icp->err,"icmMeasurementa_write, geometry: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write the proportion of flare */
+ if ((rv = write_U16Fixed16Number(p->flare, bp + 28)) != 0) {
+ sprintf(icp->err,"icmMeasurementa_write, flare: write_U16Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write the encoded standard illuminant */
+ if ((rv = write_SInt32Number((int)p->illuminant, bp + 32)) != 0) {
+ sprintf(icp->err,"icmMeasurementa_write, illuminant: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmMeasurement_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmMeasurement_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmMeasurement *p = (icmMeasurement *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Measurement:\n");
+ op->gprintf(op," Standard Observer = %s\n", string_StandardObserver(p->observer));
+ op->gprintf(op," XYZ for Measurement Backing = %s\n", string_XYZNumber_and_Lab(&p->backing));
+ op->gprintf(op," Measurement Geometry = %s\n", string_MeasurementGeometry(p->geometry));
+ op->gprintf(op," Measurement Flare = %5.1f%%\n", p->flare * 100.0);
+ op->gprintf(op," Standard Illuminant = %s\n", string_Illuminant(p->illuminant));
+}
+
+/* Allocate variable sized data elements */
+static int icmMeasurement_allocate(
+ icmBase *pp
+) {
+ /* Nothing to do */
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmMeasurement_delete(
+ icmBase *pp
+) {
+ icmMeasurement *p = (icmMeasurement *)pp;
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmMeasurement(
+ icc *icp
+) {
+ icmMeasurement *p;
+ if ((p = (icmMeasurement *) icp->al->calloc(icp->al,1,sizeof(icmMeasurement))) == NULL)
+ return NULL;
+ p->ttype = icSigMeasurementType;
+ p->refcount = 1;
+ p->get_size = icmMeasurement_get_size;
+ p->read = icmMeasurement_read;
+ p->write = icmMeasurement_write;
+ p->dump = icmMeasurement_dump;
+ p->allocate = icmMeasurement_allocate;
+ p->del = icmMeasurement_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+
+/* Named color structure read/write support */
+static int read_NamedColorVal(
+ icmNamedColorVal *p,
+ char *bp,
+ char *end,
+ icColorSpaceSignature pcs, /* Header Profile Connection Space */
+ unsigned int ndc /* Number of device corrds */
+) {
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int mxl; /* Max possible string length */
+ int rv;
+
+ if (bp > end) {
+ sprintf(icp->err,"icmNamedColorVal_read: Data too short to read");
+ return icp->errc = 1;
+ }
+ mxl = (end - bp) < 32 ? (end - bp) : 32;
+ if ((rv = check_null_string(bp,mxl)) == 1) {
+ sprintf(icp->err,"icmNamedColorVal_read: Root name string not terminated");
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ strcpy(p->root, bp);
+ bp += strlen(p->root) + 1;
+ if (bp > end || ndc > (end - bp)) {
+ sprintf(icp->err,"icmNamedColorVal_read: Data too short to read device coords");
+ return icp->errc = 1;
+ }
+ for (i = 0; i < ndc; i++) {
+ p->deviceCoords[i] = read_DCS8Number(bp);
+ bp += 1;
+ }
+ return 0;
+}
+
+static int read_NamedColorVal2(
+ icmNamedColorVal *p,
+ char *bp,
+ char *end,
+ icColorSpaceSignature pcs, /* Header Profile Connection Space */
+ unsigned int ndc /* Number of device coords */
+) {
+ int rv;
+ icc *icp = p->icp;
+ unsigned int i;
+
+ if (bp > end
+ || (32 + 6) > (end - bp)
+ || ndc > (end - bp - 32 - 6)/2) {
+ sprintf(icp->err,"icmNamedColorVal2_read: Data too short to read");
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string(bp,32)) == 1) {
+ sprintf(icp->err,"icmNamedColorVal2_read: Root name string not terminated");
+ return icp->errc = 1;
+ }
+ memmove((void *)p->root,(void *)(bp + 0),32);
+ switch(pcs) {
+ case icSigXYZData:
+ read_PCSNumber(icp, icSigXYZData, p->pcsCoords, bp+32);
+ break;
+ case icSigLabData:
+ /* namedColor2Type retains legacy Lab encoding */
+ read_PCSNumber(icp, icmSigLabV2Data, p->pcsCoords, bp+32);
+ break;
+ default:
+ return 1; /* Unknown PCS */
+ }
+ for (i = 0; i < ndc; i++)
+ p->deviceCoords[i] = read_DCS16Number(bp + 32 + 6 + 2 * i);
+ return 0;
+}
+
+static int write_NamedColorVal(
+ icmNamedColorVal *p,
+ char *d,
+ icColorSpaceSignature pcs, /* Header Profile Connection Space */
+ unsigned int ndc /* Number of device corrds */
+) {
+ icc *icp = p->icp;
+ unsigned int i;
+ int rv;
+
+ if ((rv = check_null_string(p->root,32)) == 1) {
+ sprintf(icp->err,"icmNamedColorVal_write: Root string names is unterminated");
+ return icp->errc = 1;
+ }
+ strcpy(d, p->root);
+ d += strlen(p->root) + 1;
+ for (i = 0; i < ndc; i++) {
+ if ((rv = write_DCS8Number(p->deviceCoords[i], d)) != 0) {
+ sprintf(icp->err,"icmNamedColorVal_write: write of device coord failed");
+ return icp->errc = 1;
+ }
+ d += 1;
+ }
+ return 0;
+}
+
+static int write_NamedColorVal2(
+ icmNamedColorVal *p,
+ char *bp,
+ icColorSpaceSignature pcs, /* Header Profile Connection Space */
+ unsigned int ndc /* Number of device coords */
+) {
+ icc *icp = p->icp;
+ unsigned int i;
+ int rv;
+
+ if ((rv = check_null_string(p->root,32)) == 1) {
+ sprintf(icp->err,"icmNamedColorVal2_write: Root string names is unterminated");
+ return icp->errc = 1;
+ }
+ rv = 0;
+ memmove((void *)(bp + 0),(void *)p->root,32);
+ switch(pcs) {
+ case icSigXYZData:
+ rv |= write_PCSNumber(icp, icSigXYZData, p->pcsCoords, bp+32);
+ break;
+ case icSigLabData:
+ /* namedColor2Type retains legacy Lab encoding */
+ rv |= write_PCSNumber(icp, icmSigLabV2Data, p->pcsCoords, bp+32);
+ break;
+ default:
+ sprintf(icp->err,"icmNamedColorVal2_write: Unknown PCS");
+ return icp->errc = 1;
+ }
+ if (rv) {
+ sprintf(icp->err,"icmNamedColorVal2_write: write of PCS coord failed");
+ return icp->errc = 1;
+ }
+ for (i = 0; i < ndc; i++) {
+ if ((rv = write_DCS16Number(p->deviceCoords[i], bp + 32 + 6 + 2 * i)) != 0) {
+ sprintf(icp->err,"icmNamedColorVal2_write: write of device coord failed");
+ return icp->errc = 1;
+ }
+ }
+ return 0;
+}
+
+/* - - - - - - - - - - - */
+/* icmNamedColor object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmNamedColor_get_size(
+ icmBase *pp
+) {
+ icmNamedColor *p = (icmNamedColor *)pp;
+ unsigned int len = 0;
+ if (p->ttype == icSigNamedColorType) {
+ unsigned int i;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 4); /* 4 for vendor specific flags */
+ len = sat_add(len, 4); /* 4 for count of named colors */
+ len = sat_add(len, strlen(p->prefix) + 1); /* prefix of color names */
+ len = sat_add(len, strlen(p->suffix) + 1); /* suffix of color names */
+ for (i = 0; i < p->count; i++) {
+ len = sat_add(len, strlen(p->data[i].root) + 1); /* color names */
+ len = sat_add(len, p->nDeviceCoords * 1); /* bytes for each named color */
+ }
+ } else { /* Named Color 2 */
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 4); /* 4 for vendor specific flags */
+ len = sat_add(len, 4); /* 4 for count of named colors */
+ len = sat_add(len, 4); /* 4 for number of device coords */
+ len = sat_add(len, 32); /* 32 for prefix of color names */
+ len = sat_add(len, 32); /* 32 for suffix of color names */
+ len = sat_add(len, sat_mul(p->count, (32 + 6 + p->nDeviceCoords * 2)));
+ /* bytes for each named color */
+ }
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmNamedColor_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmNamedColor *p = (icmNamedColor *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ char *bp, *buf, *end;
+ int rv;
+
+ if (len < 4) {
+ sprintf(icp->err,"icmNamedColor_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmNamedColor_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmNamedColor_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ p->ttype = (icTagTypeSignature)read_SInt32Number(bp);
+ if (p->ttype != icSigNamedColorType && p->ttype != icSigNamedColor2Type) {
+ sprintf(icp->err,"icmNamedColor_read: Wrong tag type for icmNamedColor");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ if (p->ttype == icSigNamedColorType) {
+ if (len < 16) {
+ sprintf(icp->err,"icmNamedColor_read: Tag too small to be legal");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Make sure that the number of device coords in known */
+ p->nDeviceCoords = number_ColorSpaceSignature(icp->header->colorSpace);
+ if (p->nDeviceCoords > MAX_CHAN) {
+ sprintf(icp->err,"icmNamedColor_read: Can't handle more than %d device channels",MAX_CHAN);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ } else { /* icmNC2 */
+ if (len < 84) {
+ sprintf(icp->err,"icmNamedColor_read: Tag too small to be legal");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ }
+
+ /* Read vendor specific flag */
+ p->vendorFlag = read_UInt32Number(bp+8);
+
+ /* Read count of named colors */
+ p->count = read_UInt32Number(bp+12);
+
+ if (p->ttype == icSigNamedColorType) {
+ unsigned int mxl; /* Max possible string length */
+ bp = bp + 16;
+
+ /* Prefix for each color name */
+ if (bp > end) {
+ sprintf(icp->err,"icmNamedColor_read: Data too short to read");
+ return icp->errc = 1;
+ }
+ mxl = (end - bp) < 32 ? (end - bp) : 32;
+ if ((rv = check_null_string(bp,mxl)) == 1) {
+ sprintf(icp->err,"icmNamedColor_read: Color prefix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ strcpy(p->prefix, bp);
+ bp += strlen(p->prefix) + 1;
+
+ /* Suffix for each color name */
+ if (bp > end) {
+ sprintf(icp->err,"icmNamedColor_read: Data too short to read");
+ return icp->errc = 1;
+ }
+ mxl = (end - bp) < 32 ? (end - bp) : 32;
+ if ((rv = check_null_string(bp,mxl)) == 1) {
+ sprintf(icp->err,"icmNamedColor_read: Color suffix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ strcpy(p->suffix, bp);
+ bp += strlen(p->suffix) + 1;
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < p->count; i++) {
+ if ((rv = read_NamedColorVal(p->data+i, bp, end, icp->header->pcs, p->nDeviceCoords)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ bp += strlen(p->data[i].root) + 1;
+ bp += p->nDeviceCoords * 1;
+ }
+ } else { /* icmNC2 */
+ /* Number of device coords per color */
+ p->nDeviceCoords = read_UInt32Number(bp+16);
+ if (p->nDeviceCoords > MAX_CHAN) {
+ sprintf(icp->err,"icmNamedColor_read: Can't handle more than %d device channels",MAX_CHAN);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Prefix for each color name */
+ memmove((void *)p->prefix, (void *)(bp + 20), 32);
+ if ((rv = check_null_string(p->prefix,32)) == 1) {
+ sprintf(icp->err,"icmNamedColor_read: Color prefix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Suffix for each color name */
+ memmove((void *)p->suffix, (void *)(bp + 52), 32);
+ if ((rv = check_null_string(p->suffix,32)) == 1) {
+ sprintf(icp->err,"icmNamedColor_read: Color suffix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read all the data from the buffer */
+ bp = bp + 84;
+ for (i = 0; i < p->count; i++) {
+ if ((rv = read_NamedColorVal2(p->data+i, bp, end, icp->header->pcs, p->nDeviceCoords)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ bp += 32 + 6 + p->nDeviceCoords * 2;
+ }
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmNamedColor_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmNamedColor *p = (icmNamedColor *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmNamedColor_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmNamedColor_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmNamedColor_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write vendor specific flag */
+ if ((rv = write_UInt32Number(p->vendorFlag, bp+8)) != 0) {
+ sprintf(icp->err,"icmNamedColor_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write count of named colors */
+ if ((rv = write_UInt32Number(p->count, bp+12)) != 0) {
+ sprintf(icp->err,"icmNamedColor_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ if (p->ttype == icSigNamedColorType) {
+ bp = bp + 16;
+
+ /* Prefix for each color name */
+ if ((rv = check_null_string(p->prefix,32)) == 1) {
+ sprintf(icp->err,"icmNamedColor_write: Color prefix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ strcpy(bp, p->prefix);
+ bp += strlen(p->prefix) + 1;
+
+ /* Suffix for each color name */
+ if ((rv = check_null_string(p->suffix,32)) == 1) {
+ sprintf(icp->err,"icmNamedColor_write: Color sufix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ strcpy(bp, p->suffix);
+ bp += strlen(p->suffix) + 1;
+
+ /* Write all the data to the buffer */
+
+ for (i = 0; i < p->count; i++) {
+ if ((rv = write_NamedColorVal(p->data+i, bp, icp->header->pcs, p->nDeviceCoords)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ bp += strlen(p->data[i].root) + 1;
+ bp += p->nDeviceCoords * 1;
+ }
+ } else { /* icmNC2 */
+ /* Number of device coords per color */
+ if ((rv = write_UInt32Number(p->nDeviceCoords, bp+16)) != 0) {
+ sprintf(icp->err,"icmNamedColor_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Prefix for each color name */
+ if ((rv = check_null_string(p->prefix,32)) == 1) {
+ sprintf(icp->err,"icmNamedColor_write: Color prefix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ memmove((void *)(bp + 20), (void *)p->prefix, 32);
+
+ /* Suffix for each color name */
+ if ((rv = check_null_string(p->suffix,32)) == 1) {
+ sprintf(icp->err,"icmNamedColor_write: Color sufix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ memmove((void *)(bp + 52), (void *)p->suffix, 32);
+
+ /* Write all the data to the buffer */
+ bp = bp + 84;
+ for (i = 0; i < p->count; i++, bp += (32 + 6 + p->nDeviceCoords * 2)) {
+ if ((rv = write_NamedColorVal2(p->data+i, bp, icp->header->pcs, p->nDeviceCoords)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmNamedColor_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmNamedColor_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmNamedColor *p = (icmNamedColor *)pp;
+ icc *icp = p->icp;
+ if (verb <= 0)
+ return;
+
+ if (p->ttype == icSigNamedColorType)
+ op->gprintf(op,"NamedColor:\n");
+ else
+ op->gprintf(op,"NamedColor2:\n");
+ op->gprintf(op," Vendor Flag = 0x%x\n",p->vendorFlag);
+ op->gprintf(op," No. colors = %u\n",p->count);
+ op->gprintf(op," No. dev. coords = %u\n",p->nDeviceCoords);
+ op->gprintf(op," Name prefix = '%s'\n",p->prefix);
+ op->gprintf(op," Name suffix = '%s'\n",p->suffix);
+ if (verb >= 2) {
+ unsigned int i, n;
+ icmNamedColorVal *vp;
+ for (i = 0; i < p->count; i++) {
+ vp = p->data + i;
+ op->gprintf(op," Color %lu:\n",i);
+ op->gprintf(op," Name root = '%s'\n",vp->root);
+
+ if (p->ttype == icSigNamedColor2Type) {
+ switch(icp->header->pcs) {
+ case icSigXYZData:
+ op->gprintf(op," XYZ = %f, %f, %f\n",
+ vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]);
+ break;
+ case icSigLabData:
+ op->gprintf(op," Lab = %f, %f, %f\n",
+ vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]);
+ break;
+ default:
+ op->gprintf(op," Unexpected PCS\n");
+ break;
+ }
+ }
+ if (p->nDeviceCoords > 0) {
+ op->gprintf(op," Device Coords = ");
+ for (n = 0; n < p->nDeviceCoords; n++) {
+ if (n > 0)
+ op->gprintf(op,", ");
+ op->gprintf(op,"%f",vp->deviceCoords[n]);
+ }
+ op->gprintf(op,"\n");
+ }
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmNamedColor_allocate(
+ icmBase *pp
+) {
+ icmNamedColor *p = (icmNamedColor *)pp;
+ icc *icp = p->icp;
+
+ if (p->count != p->_count) {
+ unsigned int i;
+ if (ovr_mul(p->count, sizeof(icmNamedColorVal))) {
+ sprintf(icp->err,"icmNamedColor_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (icmNamedColorVal *) icp->al->calloc(icp->al,p->count, sizeof(icmNamedColorVal))) == NULL) {
+ sprintf(icp->err,"icmNamedColor_alloc: malloc() of icmNamedColor data failed");
+ return icp->errc = 2;
+ }
+ for (i = 0; i < p->count; i++) {
+ p->data[i].icp = icp; /* Do init */
+ }
+ p->_count = p->count;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmNamedColor_delete(
+ icmBase *pp
+) {
+ icmNamedColor *p = (icmNamedColor *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmNamedColor(
+ icc *icp
+) {
+ icmNamedColor *p;
+ if ((p = (icmNamedColor *) icp->al->calloc(icp->al,1,sizeof(icmNamedColor))) == NULL)
+ return NULL;
+ p->ttype = icSigNamedColor2Type;
+ p->refcount = 1;
+ p->get_size = icmNamedColor_get_size;
+ p->read = icmNamedColor_read;
+ p->write = icmNamedColor_write;
+ p->dump = icmNamedColor_dump;
+ p->allocate = icmNamedColor_allocate;
+ p->del = icmNamedColor_delete;
+ p->icp = icp;
+
+ /* Default the the number of device coords appropriately for NamedColorType */
+ p->nDeviceCoords = number_ColorSpaceSignature(icp->header->colorSpace);
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* Colorant table structure read/write support */
+/* (Contribution from Piet Vandenborre) */
+
+static int read_ColorantTableVal(
+ icmColorantTableVal *p,
+ char *bp,
+ char *end,
+ icColorSpaceSignature pcs /* Header Profile Connection Space */
+) {
+ int rv;
+ icc *icp = p->icp;
+ if (bp > end || (32 + 6) > (end - bp)) {
+ sprintf(icp->err,"icmColorantTableVal_read: Data too short to read");
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string(bp,32)) == 1) {
+ sprintf(icp->err,"icmColorantTableVal_read: Name string not terminated");
+ return icp->errc = 1;
+ }
+ memmove((void *)p->name,(void *)(bp + 0),32);
+ switch(pcs) {
+ case icSigXYZData:
+ case icSigLabData:
+ read_PCSNumber(icp, pcs, p->pcsCoords, bp+32);
+ break;
+ default:
+ return 1; /* Unknown PCS */
+ }
+ return 0;
+}
+
+static int write_ColorantTableVal(
+ icmColorantTableVal *p,
+ char *bp,
+ icColorSpaceSignature pcs /* Header Profile Connection Space */
+) {
+ int rv;
+ icc *icp = p->icp;
+
+ if ((rv = check_null_string(p->name,32)) == 1) {
+ sprintf(icp->err,"icmColorantTableVal_write: Name string is unterminated");
+ return icp->errc = 1;
+ }
+ memmove((void *)(bp + 0),(void *)p->name,32);
+ rv = 0;
+ switch(pcs) {
+ case icSigXYZData:
+ case icSigLabData:
+ rv |= write_PCSNumber(icp, pcs, p->pcsCoords, bp+32);
+ break;
+ default:
+ sprintf(icp->err,"icmColorantTableVal_write: Unknown PCS");
+ return icp->errc = 1;
+ }
+ if (rv) {
+ sprintf(icp->err,"icmColorantTableVal_write: write of PCS coord failed");
+ return icp->errc = 1;
+ }
+ return 0;
+}
+
+/* - - - - - - - - - - - */
+/* icmColorantTable object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmColorantTable_get_size(
+ icmBase *pp
+) {
+ icmColorantTable *p = (icmColorantTable *)pp;
+ unsigned int len = 0;
+ if (p->ttype == icSigColorantTableType
+ || p->ttype == icmSigAltColorantTableType) {
+ unsigned int i;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 4); /* 4 for count of colorants */
+ for (i = 0; i < p->count; i++) {
+ len = sat_add(len, 32); /* colorant names - 32 bytes*/
+ len = sat_add(len, 6); /* colorant pcs value - 3 x 16bit number*/
+ }
+ }
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmColorantTable_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmColorantTable *p = (icmColorantTable *)pp;
+ icc *icp = p->icp;
+ icColorSpaceSignature pcs;
+ unsigned int i;
+ char *bp, *buf, *end;
+ int rv = 0;
+
+ if (icp->header->deviceClass != icSigLinkClass)
+ pcs = icp->header->pcs;
+ else
+ pcs = icSigLabData;
+
+ if (len < 4) {
+ sprintf(icp->err,"icmColorantTable_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmColorantTable_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmColorantTable_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ p->ttype = (icTagTypeSignature)read_SInt32Number(bp);
+ if (p->ttype != icSigColorantTableType
+ && p->ttype != icmSigAltColorantTableType) {
+ sprintf(icp->err,"icmColorantTable_read: Wrong tag type for icmColorantTable");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ if (len < 12) {
+ sprintf(icp->err,"icmColorantTable_read: Tag too small to be legal");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read count of colorants */
+ if (p->ttype == icmSigAltColorantTableType)
+ p->count = read_UInt8Number(bp+8); /* Hmm. This is Little Endian */
+ else
+ p->count = read_UInt32Number(bp+8);
+
+ if (p->count > ((len - 12) / (32 + 6))) {
+ sprintf(icp->err,"icmColorantTable_read count overflow, count %x, len %d",p->count,len);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ bp = bp + 12;
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < p->count; i++, bp += (32 + 6)) {
+ if (p->ttype == icmSigAltColorantTableType /* Hack to reverse little endian */
+ && (end - bp) >= 38) {
+ int tt;
+ tt = *(bp + 32);
+ *(bp+32) = *(bp+33);
+ *(bp+33) = tt;
+ tt = *(bp + 34);
+ *(bp+34) = *(bp+35);
+ *(bp+35) = tt;
+ tt = *(bp + 36);
+ *(bp+36) = *(bp+37);
+ *(bp+37) = tt;
+ }
+ if ((rv = read_ColorantTableVal(p->data+i, bp, end, pcs)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ }
+
+ icp->al->free(icp->al, buf);
+ return rv;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmColorantTable_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmColorantTable *p = (icmColorantTable *)pp;
+ icc *icp = p->icp;
+ icColorSpaceSignature pcs;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ if (icp->header->deviceClass != icSigLinkClass)
+ pcs = icp->header->pcs;
+ else
+ pcs = icSigLabData;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmColorantTable_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmColorantTable_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmColorantTable_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write count of colorants */
+ if ((rv = write_UInt32Number(p->count, bp+8)) != 0) {
+ sprintf(icp->err,"icmColorantTable_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ bp = bp + 12;
+
+ /* Write all the data to the buffer */
+ for (i = 0; i < p->count; i++, bp += (32 + 6)) {
+ if ((rv = write_ColorantTableVal(p->data+i, bp, pcs)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmColorantTable_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmColorantTable_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmColorantTable *p = (icmColorantTable *)pp;
+ icc *icp = p->icp;
+ icColorSpaceSignature pcs;
+
+ if (icp->header->deviceClass != icSigLinkClass)
+ pcs = icp->header->pcs;
+ else
+ pcs = icSigLabData;
+
+ if (verb <= 0)
+ return;
+
+ if (p->ttype == icSigColorantTableType
+ || p->ttype == icmSigAltColorantTableType)
+ op->gprintf(op,"ColorantTable:\n");
+ op->gprintf(op," No. colorants = %u\n",p->count);
+ if (verb >= 2) {
+ unsigned int i;
+ icmColorantTableVal *vp;
+ for (i = 0; i < p->count; i++) {
+ vp = p->data + i;
+ op->gprintf(op," Colorant %lu:\n",i);
+ op->gprintf(op," Name = '%s'\n",vp->name);
+
+ if (p->ttype == icSigColorantTableType
+ || p->ttype == icmSigAltColorantTableType) {
+
+ switch(pcs) {
+ case icSigXYZData:
+ op->gprintf(op," XYZ = %f, %f, %f\n",
+ vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]);
+ break;
+ case icSigLabData:
+ op->gprintf(op," Lab = %f, %f, %f\n",
+ vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]);
+ break;
+ default:
+ op->gprintf(op," Unexpected PCS\n");
+ break;
+ }
+ }
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmColorantTable_allocate(
+ icmBase *pp
+) {
+ icmColorantTable *p = (icmColorantTable *)pp;
+ icc *icp = p->icp;
+
+ if (p->count != p->_count) {
+ unsigned int i;
+ if (ovr_mul(p->count, sizeof(icmColorantTableVal))) {
+ sprintf(icp->err,"icmColorantTable_alloc: count overflow (%d of %ld bytes)",p->count,sizeof(icmColorantTableVal));
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (icmColorantTableVal *) icp->al->calloc(icp->al,p->count, sizeof(icmColorantTableVal))) == NULL) {
+ sprintf(icp->err,"icmColorantTable_alloc: malloc() of icmColorantTable data failed");
+ return icp->errc = 2;
+ }
+ for (i = 0; i < p->count; i++) {
+ p->data[i].icp = icp; /* Do init */
+ }
+ p->_count = p->count;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmColorantTable_delete(
+ icmBase *pp
+) {
+ icmColorantTable *p = (icmColorantTable *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmColorantTable(
+ icc *icp
+) {
+ icmColorantTable *p;
+ if ((p = (icmColorantTable *) icp->al->calloc(icp->al,1,sizeof(icmColorantTable))) == NULL)
+ return NULL;
+ p->ttype = icSigColorantTableType;
+ p->refcount = 1;
+ p->get_size = icmColorantTable_get_size;
+ p->read = icmColorantTable_read;
+ p->write = icmColorantTable_write;
+ p->dump = icmColorantTable_dump;
+ p->allocate = icmColorantTable_allocate;
+ p->del = icmColorantTable_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* textDescription */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmTextDescription_get_size(
+ icmBase *pp
+) {
+ icmTextDescription *p = (icmTextDescription *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addadd(len, 4, p->size); /* Ascii string length + ascii string */
+ len = sat_addaddmul(len, 8, 2, p->ucSize); /* Unicode language code + length + string */
+ len = sat_addadd(len, 3, 67); /* ScriptCode code, length string */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmTextDescription_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmTextDescription *p = (icmTextDescription *)pp;
+ icc *icp = p->icp;
+ int rv;
+ char *bp, *buf, *end;
+
+#ifdef ICM_STRICT
+ if (len < (8 + 4 + 8 + 3 /* + 67 */)) {
+#else
+ if (len < (8 + 4 + 8 + 3)) {
+#endif
+ sprintf(icp->err,"icmTextDescription_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmTextDescription_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmTextDescription_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read from the buffer into the structure */
+ if ((rv = p->core_read(p, &bp, end)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* core read the object, return 0 on success, error code on fail */
+static int icmTextDescription_core_read(
+ icmTextDescription *p,
+ char **bpp, /* Pointer to buffer pointer, returns next after read */
+ char *end /* Pointer to past end of read buffer */
+) {
+ icc *icp = p->icp;
+ int rv;
+ char *bp = *bpp;
+
+ if (bp > end || 8 > (end - bp)) {
+ sprintf(icp->err,"icmTextDescription_read: Data too short to type descriptor");
+ *bpp = bp;
+ return icp->errc = 1;
+ }
+
+ p->size = read_UInt32Number(bp);
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Wrong tag type ('%s') for icmTextDescription",
+ tag2str((icTagTypeSignature)read_SInt32Number(bp)));
+ return icp->errc = 1;
+ }
+ bp = bp + 8;
+
+ /* Read the Ascii string */
+ if (bp > end || 4 > (end - bp)) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Data too short to read Ascii header");
+ return icp->errc = 1;
+ }
+ p->size = read_UInt32Number(bp);
+ bp += 4;
+ if (p->size > 0) {
+ if (bp > end || p->size > (end - bp)) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Data too short to read Ascii string");
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string(bp,p->size)) == 1) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: ascii string is not terminated");
+ return icp->errc = 1;
+ }
+#ifdef ICM_STRICT
+ if (rv == 2) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: ascii string is shorter than count");
+ return icp->errc = 1;
+ }
+#endif
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ return rv;
+ }
+ strcpy(p->desc, bp);
+ bp += p->size;
+ }
+
+ /* Read the Unicode string */
+ if (bp > end || 8 > (end - bp)) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Data too short to read Unicode string");
+ return icp->errc = 1;
+ }
+ p->ucLangCode = read_UInt32Number(bp);
+ bp += 4;
+ p->ucSize = read_UInt32Number(bp);
+ bp += 4;
+ if (p->ucSize > 0) {
+ ORD16 *up;
+ char *tbp;
+ if (bp > end || p->ucSize > (end - bp)/2) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Data too short to read Unicode string");
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string16(bp,p->ucSize)) == 1) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Unicode string is not terminated");
+ return icp->errc = 1;
+ }
+#ifdef ICM_STRICT
+ if (rv == 2) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Unicode string is shorter than count");
+ return icp->errc = 1;
+ }
+#endif
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ return rv;
+ }
+ for (up = p->ucDesc, tbp = bp; tbp[0] != 0 || tbp[1] != 0; up++, tbp += 2)
+ *up = read_UInt16Number(tbp);
+ *up = 0; /* Unicode null */
+ bp += p->ucSize * 2;
+ }
+
+ /* Read the ScriptCode string */
+ if (bp > end || 3 > (end - bp)) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Data too short to read ScriptCode header");
+ return icp->errc = 1;
+ }
+ p->scCode = read_UInt16Number(bp);
+ bp += 2;
+ p->scSize = read_UInt8Number(bp);
+ bp += 1;
+ if (p->scSize > 0) {
+ if (p->scSize > 67) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: ScriptCode string too long");
+ return icp->errc = 1;
+ }
+ if (bp > end || p->scSize > (end - bp)) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Data too short to read ScriptCode string");
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string(bp,p->scSize)) == 1) {
+#ifdef ICM_STRICT
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: ScriptCode string is not terminated");
+ return icp->errc = 1;
+#else
+ /* Patch it up */
+ bp[p->scSize-1] = '\000';
+#endif
+ }
+ memmove((void *)p->scDesc, (void *)bp, p->scSize);
+ } else {
+ memset((void *)p->scDesc, 0, 67);
+ }
+ bp += 67;
+
+ *bpp = bp;
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmTextDescription_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmTextDescription *p = (icmTextDescription *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmTextDescription_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmTextDescription_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write to the buffer from the structure */
+ if ((rv = p->core_write(p, &bp)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmTextDescription_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Core write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmTextDescription_core_write(
+ icmTextDescription *p,
+ char **bpp /* Pointer to buffer pointer, returns next after read */
+) {
+ icc *icp = p->icp;
+ char *bp = *bpp;
+ int rv;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_SInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ bp = bp + 8;
+
+ /* Write the Ascii string */
+ if ((rv = write_UInt32Number(p->size,bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_UInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ bp += 4;
+ if (p->size > 0) {
+ if ((rv = check_null_string(p->desc,p->size)) == 1) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_write: ascii string is not terminated");
+ return icp->errc = 1;
+ }
+ if (rv == 2) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_write: ascii string is shorter than length");
+ return icp->errc = 1;
+ }
+ strcpy(bp, p->desc);
+ bp += strlen(p->desc) + 1;
+ }
+
+ /* Write the Unicode string */
+ if ((rv = write_UInt32Number(p->ucLangCode, bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_UInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ bp += 4;
+ if ((rv = write_UInt32Number(p->ucSize, bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_UInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ bp += 4;
+ if (p->ucSize > 0) {
+ ORD16 *up;
+ if ((rv = check_null_string16((char *)p->ucDesc,p->ucSize)) == 1) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_write: Unicode string is not terminated");
+ return icp->errc = 1;
+ }
+ if (rv == 2) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_write: Unicode string is shorter than length");
+ return icp->errc = 1;
+ }
+ for(up = p->ucDesc; *up != 0; up++, bp += 2) {
+ if ((rv = write_UInt16Number(((unsigned int)*up), bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_UInt16Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ }
+ bp[0] = 0; /* null */
+ bp[1] = 0;
+ bp += 2;
+ }
+
+ /* Write the ScriptCode string */
+ if ((rv = write_UInt16Number(p->scCode, bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_UInt16Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ bp += 2;
+ if ((rv = write_UInt8Number(p->scSize, bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_UInt8Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ bp += 1;
+ if (p->scSize > 0) {
+ if (p->scSize > 67) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_write: ScriptCode string too long");
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string((char *)p->scDesc,p->scSize)) == 1) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_write: ScriptCode string is not terminated");
+ return icp->errc = 1;
+ }
+ memmove((void *)bp, (void *)p->scDesc, 67);
+ } else {
+ memset((void *)bp, 0, 67);
+ }
+ bp += 67;
+
+ *bpp = bp;
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmTextDescription_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmTextDescription *p = (icmTextDescription *)pp;
+ unsigned int i, r, c;
+
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"TextDescription:\n");
+
+ if (p->size > 0) {
+ unsigned int size = p->size > 0 ? p->size-1 : 0;
+ op->gprintf(op," ASCII data, length %lu chars:\n",p->size);
+
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 75) {
+ if (isprint(p->desc[i])) {
+ op->gprintf(op,"%c",p->desc[i]);
+ c++;
+ } else {
+ op->gprintf(op,"\\%03o",p->desc[i]);
+ c += 4;
+ }
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+ } else {
+ op->gprintf(op," No ASCII data\n");
+ }
+
+ /* Can't dump Unicode or ScriptCode as text with portable code */
+ if (p->ucSize > 0) {
+ unsigned int size = p->ucSize;
+ op->gprintf(op," Unicode Data, Language code 0x%x, length %lu chars\n",
+ p->ucLangCode, p->ucSize);
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 75) {
+ op->gprintf(op,"%04x ",p->ucDesc[i]);
+ c += 5;
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+ } else {
+ op->gprintf(op," No Unicode data\n");
+ }
+ if (p->scSize > 0) {
+ unsigned int size = p->scSize;
+ op->gprintf(op," ScriptCode Data, Code 0x%x, length %lu chars\n",
+ p->scCode, p->scSize);
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 75) {
+ op->gprintf(op,"%02x ",p->scDesc[i]);
+ c += 3;
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+ } else {
+ op->gprintf(op," No ScriptCode data\n");
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmTextDescription_allocate(
+ icmBase *pp
+) {
+ icmTextDescription *p = (icmTextDescription *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(char))) {
+ sprintf(icp->err,"icmTextDescription_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->desc != NULL)
+ icp->al->free(icp->al, p->desc);
+ if ((p->desc = (char *) icp->al->calloc(icp->al, p->size, sizeof(char))) == NULL) {
+ sprintf(icp->err,"icmTextDescription_alloc: malloc() of Ascii description failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ if (p->ucSize != p->uc_size) {
+ if (ovr_mul(p->ucSize, sizeof(ORD16))) {
+ sprintf(icp->err,"icmTextDescription_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->ucDesc != NULL)
+ icp->al->free(icp->al, p->ucDesc);
+ if ((p->ucDesc = (ORD16 *) icp->al->calloc(icp->al, p->ucSize, sizeof(ORD16))) == NULL) {
+ sprintf(icp->err,"icmTextDescription_alloc: malloc() of Unicode description failed");
+ return icp->errc = 2;
+ }
+ p->uc_size = p->ucSize;
+ }
+ return 0;
+}
+
+/* Free all variable sized elements */
+static void icmTextDescription_unallocate(
+ icmTextDescription *p
+) {
+ icc *icp = p->icp;
+
+ if (p->desc != NULL)
+ icp->al->free(icp->al, p->desc);
+ if (p->ucDesc != NULL)
+ icp->al->free(icp->al, p->ucDesc);
+}
+
+/* Free all storage in the object */
+static void icmTextDescription_delete(
+ icmBase *pp
+) {
+ icmTextDescription *p = (icmTextDescription *)pp;
+ icc *icp = p->icp;
+
+ icmTextDescription_unallocate(p);
+ icp->al->free(icp->al, p);
+}
+
+/* Initialze a named object */
+static void icmTextDescription_init(
+ icmTextDescription *p,
+ icc *icp
+) {
+ memset((void *)p, 0, sizeof(icmTextDescription)); /* Imitate calloc */
+
+ p->ttype = icSigTextDescriptionType;
+ p->refcount = 1;
+ p->get_size = icmTextDescription_get_size;
+ p->read = icmTextDescription_read;
+ p->write = icmTextDescription_write;
+ p->dump = icmTextDescription_dump;
+ p->allocate = icmTextDescription_allocate;
+ p->del = icmTextDescription_delete;
+ p->icp = icp;
+
+ p->core_read = icmTextDescription_core_read;
+ p->core_write = icmTextDescription_core_write;
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmTextDescription(
+ icc *icp
+) {
+ icmTextDescription *p;
+ if ((p = (icmTextDescription *) icp->al->calloc(icp->al,1,sizeof(icmTextDescription))) == NULL)
+ return NULL;
+
+ icmTextDescription_init(p,icp);
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+
+/* Support for icmDescStruct */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmDescStruct_get_size(
+ icmDescStruct *p
+) {
+ unsigned int len = 0;
+ len = sat_add(len, 20); /* 20 bytes for header info */
+ len = sat_add(len, p->device.get_size((icmBase *)&p->device));
+ if (p->device.size == 0)
+ len = sat_add(len, 1); /* Extra 1 because of zero length desciption */
+ len = sat_add(len, p->model.get_size((icmBase *)&p->model));
+ if (p->model.size == 0)
+ len = sat_add(len, 1); /* Extra 1 because of zero length desciption */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmDescStruct_read(
+ icmDescStruct *p,
+ char **bpp, /* Pointer to buffer pointer, returns next after read */
+ char *end /* Pointer to past end of read buffer */
+) {
+ icc *icp = p->icp;
+ char *bp = *bpp;
+ int rv = 0;
+
+ if (bp > end || 20 > (end - bp)) {
+ sprintf(icp->err,"icmDescStruct_read: Data too short read header");
+ *bpp = bp;
+ return icp->errc = 1;
+ }
+
+ p->deviceMfg = read_SInt32Number(bp + 0);
+ p->deviceModel = read_UInt32Number(bp + 4);
+ read_UInt64Number(&p->attributes, bp + 8);
+ p->technology = (icTechnologySignature) read_UInt32Number(bp + 16);
+ *bpp = bp += 20;
+
+ /* Read the device text description */
+ if ((rv = p->device.core_read(&p->device, bpp, end)) != 0) {
+ return rv;
+ }
+
+ /* Read the model text description */
+ if ((rv = p->model.core_read(&p->model, bpp, end)) != 0) {
+ return rv;
+ }
+
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmDescStruct_write(
+ icmDescStruct *p,
+ char **bpp /* Pointer to buffer pointer, returns next after read */
+) {
+ icc *icp = p->icp;
+ char *bp = *bpp;
+ int rv = 0;
+ char *ttd = NULL;
+ unsigned int tts = 0;
+
+ if ((rv = write_SInt32Number(p->deviceMfg, bp + 0)) != 0) {
+ sprintf(icp->err,"icmDescStruct_write: write_SInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt32Number(p->deviceModel, bp + 4)) != 0) {
+ sprintf(icp->err,"icmDescStruct_write: write_UInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt64Number(&p->attributes, bp + 8)) != 0) {
+ sprintf(icp->err,"icmDescStruct_write: write_UInt64Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt32Number(p->technology, bp + 16)) != 0) {
+ sprintf(icp->err,"icmDescStruct_write: write_UInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ *bpp = bp += 20;
+
+ /* Make sure the ASCII device text is a minimum size of 1, as per the spec. */
+ ttd = p->device.desc;
+ tts = p->device.size;
+
+ if (p->device.size == 0) {
+ p->device.desc = "";
+ p->device.size = 1;
+ }
+
+ /* Write the device text description */
+ if ((rv = p->device.core_write(&p->device, bpp)) != 0) {
+ return rv;
+ }
+
+ p->device.desc = ttd;
+ p->device.size = tts;
+
+ /* Make sure the ASCII model text is a minimum size of 1, as per the spec. */
+ ttd = p->model.desc;
+ tts = p->model.size;
+
+ if (p->model.size == 0) {
+ p->model.desc = "";
+ p->model.size = 1;
+ }
+
+ /* Write the model text description */
+ if ((rv = p->model.core_write(&p->model, bpp)) != 0) {
+ return rv;
+ }
+
+ p->model.desc = ttd;
+ p->model.size = tts;
+
+ /* Make sure the ASCII model text is a minimum size of 1, as per the spec. */
+ ttd = p->device.desc;
+ tts = p->device.size;
+
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmDescStruct_dump(
+ icmDescStruct *p,
+ icmFile *op, /* Output to dump to */
+ int verb, /* Verbosity level */
+ int index /* Description index */
+) {
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"DescStruct %u:\n",index);
+ if (verb >= 1) {
+ op->gprintf(op," Dev. Mnfctr. = %s\n",tag2str(p->deviceMfg)); /* ~~~ */
+ op->gprintf(op," Dev. Model = %s\n",tag2str(p->deviceModel)); /* ~~~ */
+ op->gprintf(op," Dev. Attrbts = %s\n", string_DeviceAttributes(p->attributes.l));
+ op->gprintf(op," Dev. Technology = %s\n", string_TechnologySignature(p->technology));
+ p->device.dump((icmBase *)&p->device, op,verb);
+ p->model.dump((icmBase *)&p->model, op,verb);
+ op->gprintf(op,"\n");
+ }
+}
+
+/* Allocate variable sized data elements (ie. descriptions) */
+static int icmDescStruct_allocate(
+ icmDescStruct *p
+) {
+ int rv;
+
+ if ((rv = p->device.allocate((icmBase *)&p->device)) != 0) {
+ return rv;
+ }
+ if ((rv = p->model.allocate((icmBase *)&p->model)) != 0) {
+ return rv;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmDescStruct_delete(
+ icmDescStruct *p
+) {
+ icmTextDescription_unallocate(&p->device);
+ icmTextDescription_unallocate(&p->model);
+}
+
+/* Init a DescStruct object */
+static void icmDescStruct_init(
+ icmDescStruct *p,
+ icc *icp
+) {
+
+ p->allocate = icmDescStruct_allocate;
+ p->icp = icp;
+
+ icmTextDescription_init(&p->device, icp);
+ icmTextDescription_init(&p->model, icp);
+}
+
+/* - - - - - - - - - - - - - - - */
+/* icmProfileSequenceDesc object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmProfileSequenceDesc_get_size(
+ icmBase *pp
+) {
+ icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
+ unsigned int len = 0;
+ unsigned int i;
+ len = sat_add(len, 12); /* 12 bytes for tag, padding and count */
+ for (i = 0; i < p->count; i++) { /* All the description structures */
+ len = sat_add(len, icmDescStruct_get_size(&p->data[i]));
+ }
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmProfileSequenceDesc_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ char *bp, *buf, *end;
+ int rv = 0;
+
+ if (len < 12) {
+ sprintf(icp->err,"icmProfileSequenceDesc_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmProfileSequenceDesc_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmProfileSequenceDesc_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmProfileSequenceDesc_read: Wrong tag type for icmProfileSequenceDesc");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ p->count = read_UInt32Number(bp); /* Number of sequence descriptions */
+ bp += 4;
+
+ /* Read all the sequence descriptions */
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ for (i = 0; i < p->count; i++) {
+ if ((rv = icmDescStruct_read(&p->data[i], &bp, end)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmProfileSequenceDesc_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmProfileSequenceDesc_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmProfileSequenceDesc_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmProfileSequenceDesc_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ if ((rv = write_UInt32Number(p->count,bp+8)) != 0) {
+ sprintf(icp->err,"icmProfileSequenceDesc_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp = bp + 12;
+
+ /* Write all the description structures */
+ for (i = 0; i < p->count; i++) {
+ if ((rv = icmDescStruct_write(&p->data[i], &bp)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmProfileSequenceDesc_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmProfileSequenceDesc_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"ProfileSequenceDesc:\n");
+ op->gprintf(op," No. elements = %u\n",p->count);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->count; i++)
+ icmDescStruct_dump(&p->data[i], op, verb-1, i);
+ }
+}
+
+/* Allocate variable sized data elements (ie. count of profile descriptions) */
+static int icmProfileSequenceDesc_allocate(
+ icmBase *pp
+) {
+ icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+
+ if (p->count != p->_count) {
+ if (ovr_mul(p->count, sizeof(icmDescStruct))) {
+ sprintf(icp->err,"icmProfileSequenceDesc_allocate: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (icmDescStruct *) icp->al->calloc(icp->al, p->count, sizeof(icmDescStruct))) == NULL) {
+ sprintf(icp->err,"icmProfileSequenceDesc_allocate Allocation of DescStruct array failed");
+ return icp->errc = 2;
+ }
+ /* Now init the DescStructs */
+ for (i = 0; i < p->count; i++) {
+ icmDescStruct_init(&p->data[i], icp);
+ }
+ p->_count = p->count;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmProfileSequenceDesc_delete(
+ icmBase *pp
+) {
+ icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++) {
+ icmDescStruct_delete(&p->data[i]); /* Free allocated contents */
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmProfileSequenceDesc(
+ icc *icp
+) {
+ icmProfileSequenceDesc *p;
+ if ((p = (icmProfileSequenceDesc *) icp->al->calloc(icp->al,1,sizeof(icmProfileSequenceDesc))) == NULL)
+ return NULL;
+ p->ttype = icSigProfileSequenceDescType;
+ p->refcount = 1;
+ p->get_size = icmProfileSequenceDesc_get_size;
+ p->read = icmProfileSequenceDesc_read;
+ p->write = icmProfileSequenceDesc_write;
+ p->dump = icmProfileSequenceDesc_dump;
+ p->allocate = icmProfileSequenceDesc_allocate;
+ p->del = icmProfileSequenceDesc_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* Signature */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmSignature_get_size(
+ icmBase *pp
+) {
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 4); /* 4 for signature */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmSignature_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmSignature *p = (icmSignature *)pp;
+ icc *icp = p->icp;
+ char *bp, *buf;
+
+ if (len < 12) {
+ sprintf(icp->err,"icmSignature_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmSignature_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmSignature_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmSignaturSignatureng tag type for icmSignature");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read the encoded measurement geometry */
+ p->sig = (icTechnologySignature)read_SInt32Number(bp + 8);
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmSignature_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmSignature *p = (icmSignature *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmSignature_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmSignature_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmSignature_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write the signature */
+ if ((rv = write_SInt32Number((int)p->sig, bp + 8)) != 0) {
+ sprintf(icp->err,"icmSignaturea_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmSignature_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmSignature_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmSignature *p = (icmSignature *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Signature\n");
+ op->gprintf(op," Technology = %s\n", string_TechnologySignature(p->sig));
+}
+
+/* Allocate variable sized data elements */
+static int icmSignature_allocate(
+ icmBase *pp
+) {
+ /* Nothing to do */
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmSignature_delete(
+ icmBase *pp
+) {
+ icmSignature *p = (icmSignature *)pp;
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmSignature(
+ icc *icp
+) {
+ icmSignature *p;
+ if ((p = (icmSignature *) icp->al->calloc(icp->al,1,sizeof(icmSignature))) == NULL)
+ return NULL;
+ p->ttype = icSigSignatureType;
+ p->refcount = 1;
+ p->get_size = icmSignature_get_size;
+ p->read = icmSignature_read;
+ p->write = icmSignature_write;
+ p->dump = icmSignature_dump;
+ p->allocate = icmSignature_allocate;
+ p->del = icmSignature_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+
+/* Data conversion support functions */
+static int read_ScreeningData(icmScreeningData *p, char *d) {
+ p->frequency = read_S15Fixed16Number(d + 0);
+ p->angle = read_S15Fixed16Number(d + 4);
+ p->spotShape = (icSpotShape)read_SInt32Number(d + 8);
+ return 0;
+}
+
+static int write_ScreeningData(icmScreeningData *p, char *d) {
+ int rv;
+ if ((rv = write_S15Fixed16Number(p->frequency, d + 0)) != 0)
+ return rv;
+ if ((rv = write_S15Fixed16Number(p->angle, d + 4)) != 0)
+ return rv;
+ if ((rv = write_SInt32Number((int)p->spotShape, d + 8)) != 0)
+ return rv;
+ return 0;
+}
+
+
+/* icmScreening object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmScreening_get_size(
+ icmBase *pp
+) {
+ icmScreening *p = (icmScreening *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 16); /* 16 bytes for tag, padding, flag & channeles */
+ len = sat_addmul(len, p->channels, 12); /* 12 bytes for each channel */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmScreening_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmScreening *p = (icmScreening *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i;
+ char *bp, *buf, *end;
+
+ if (len < 12) {
+ sprintf(icp->err,"icmScreening_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmScreening_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmScreening_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmScreening_read: Wrong tag type for icmScreening");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->screeningFlag = read_UInt32Number(bp+8); /* Flags */
+ p->channels = read_UInt32Number(bp+12); /* Number of channels */
+ bp = bp + 16;
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < p->channels; i++, bp += 12) {
+ if (bp > end || 12 > (end - bp)) {
+ sprintf(icp->err,"icmScreening_read: Data too short to read Screening Data");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ read_ScreeningData(&p->data[i], bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmScreening_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmScreening *p = (icmScreening *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmScreening_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmScreening_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmScreening_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ if ((rv = write_UInt32Number(p->screeningFlag,bp+8)) != 0) {
+ sprintf(icp->err,"icmScreening_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt32Number(p->channels,bp+12)) != 0) {
+ sprintf(icp->err,"icmScreening_write: write_UInt32NumberXYZumber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp = bp + 16;
+
+ /* Write all the data to the buffer */
+ for (i = 0; i < p->channels; i++, bp += 12) {
+ if ((rv = write_ScreeningData(&p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmScreening_write: write_ScreeningData() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmScreening_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmScreening_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmScreening *p = (icmScreening *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Screening:\n");
+ op->gprintf(op," Flags = %s\n", string_ScreenEncodings(p->screeningFlag));
+ op->gprintf(op," No. channels = %u\n",p->channels);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->channels; i++) {
+ op->gprintf(op," %lu:\n",i);
+ op->gprintf(op," Frequency: %f\n",p->data[i].frequency);
+ op->gprintf(op," Angle: %f\n",p->data[i].angle);
+ op->gprintf(op," Spot shape: %s\n", string_SpotShape(p->data[i].spotShape));
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmScreening_allocate(
+ icmBase *pp
+) {
+ icmScreening *p = (icmScreening *)pp;
+ icc *icp = p->icp;
+
+ if (p->channels != p->_channels) {
+ if (ovr_mul(p->channels, sizeof(icmScreeningData))) {
+ sprintf(icp->err,"icmScreening_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (icmScreeningData *) icp->al->malloc(icp->al, p->channels * sizeof(icmScreeningData))) == NULL) {
+ sprintf(icp->err,"icmScreening_alloc: malloc() of icmScreening data failed");
+ return icp->errc = 2;
+ }
+ p->_channels = p->channels;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmScreening_delete(
+ icmBase *pp
+) {
+ icmScreening *p = (icmScreening *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmScreening(
+ icc *icp
+) {
+ icmScreening *p;
+ if ((p = (icmScreening *) icp->al->calloc(icp->al,1,sizeof(icmScreening))) == NULL)
+ return NULL;
+ p->ttype = icSigScreeningType;
+ p->refcount = 1;
+ p->get_size = icmScreening_get_size;
+ p->read = icmScreening_read;
+ p->write = icmScreening_write;
+ p->dump = icmScreening_dump;
+ p->allocate = icmScreening_allocate;
+ p->del = icmScreening_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmUcrBg object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmUcrBg_get_size(
+ icmBase *pp
+) {
+ icmUcrBg *p = (icmUcrBg *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addaddmul(len, 4, p->UCRcount, 2); /* Undercolor Removal */
+ len = sat_addaddmul(len, 4, p->BGcount, 2); /* Black Generation */
+ len = sat_add(len, p->size); /* Description string */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmUcrBg_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmUcrBg *p = (icmUcrBg *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ int rv;
+ char *bp, *buf, *end;
+
+ if (len < 16) {
+ sprintf(icp->err,"icmUcrBg_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUcrBg_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmUcrBg_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmUcrBg_read: Wrong tag type for icmUcrBg");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->UCRcount = read_UInt32Number(bp+8); /* First curve count */
+ bp = bp + 12;
+
+ if (p->UCRcount > 0) {
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ for (i = 0; i < p->UCRcount; i++, bp += 2) {
+ if (bp > end || 2 > (end - bp)) {
+ sprintf(icp->err,"icmUcrBg_read: Data too short to read UCR Data");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if (p->UCRcount == 1) /* % */
+ p->UCRcurve[i] = (double)read_UInt16Number(bp);
+ else /* 0.0 - 1.0 */
+ p->UCRcurve[i] = read_DCS16Number(bp);
+ }
+ } else {
+ p->UCRcurve = NULL;
+ }
+
+ if (bp > end || 4 > (end - bp)) {
+ sprintf(icp->err,"icmData_read: Data too short to read Black Gen count");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->BGcount = read_UInt32Number(bp); /* First curve count */
+ bp += 4;
+
+ if (p->BGcount > 0) {
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ for (i = 0; i < p->BGcount; i++, bp += 2) {
+ if (bp > end || 2 > (end - bp)) {
+ sprintf(icp->err,"icmUcrBg_read: Data too short to read BG Data");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if (p->BGcount == 1) /* % */
+ p->BGcurve[i] = (double)read_UInt16Number(bp);
+ else /* 0.0 - 1.0 */
+ p->BGcurve[i] = read_DCS16Number(bp);
+ }
+ } else {
+ p->BGcurve = NULL;
+ }
+
+ p->size = end - bp; /* Nominal string length */
+ if (p->size > 0) {
+ if ((rv = check_null_string(bp, p->size)) == 1) {
+ sprintf(icp->err,"icmUcrBg_read: string is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = strlen(bp) + 1;
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ memmove((void *)p->string, (void *)bp, p->size);
+ bp += p->size;
+ } else {
+ p->string = NULL;
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmUcrBg_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmUcrBg *p = (icmUcrBg *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmUcrBg_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUcrBg_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ bp = bp + 8;
+
+ /* Write UCR curve */
+ if ((rv = write_UInt32Number(p->UCRcount,bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp += 4;
+
+ for (i = 0; i < p->UCRcount; i++, bp += 2) {
+ if (p->UCRcount == 1) { /* % */
+ if ((rv = write_UInt16Number((unsigned int)(p->UCRcurve[i]+0.5),bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_UInt16umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ } else {
+ if ((rv = write_DCS16Number(p->UCRcurve[i],bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_DCS16umber(%f) failed",p->UCRcurve[i]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ /* Write BG curve */
+ if ((rv = write_UInt32Number(p->BGcount,bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp += 4;
+
+ for (i = 0; i < p->BGcount; i++, bp += 2) {
+ if (p->BGcount == 1) { /* % */
+ if ((rv = write_UInt16Number((unsigned int)(p->BGcurve[i]+0.5),bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_UInt16umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ } else {
+ if ((rv = write_DCS16Number(p->BGcurve[i],bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_DCS16umber(%f) failed",p->BGcurve[i]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ if (p->string != NULL) {
+ if ((rv = check_null_string(p->string,p->size)) == 1) {
+ sprintf(icp->err,"icmUcrBg_write: text is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if (rv == 2) {
+ sprintf(icp->err,"icmUcrBg_write: text is shorter than length");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ memmove((void *)bp, (void *)p->string, p->size);
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmUcrBg_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmUcrBg_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmUcrBg *p = (icmUcrBg *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Undercolor Removal Curve & Black Generation:\n");
+
+ if (p->UCRcount == 0) {
+ op->gprintf(op," UCR: Not specified\n");
+ } else if (p->UCRcount == 1) {
+ op->gprintf(op," UCR: %f%%\n",p->UCRcurve[0]);
+ } else {
+ op->gprintf(op," UCR curve no. elements = %u\n",p->UCRcount);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->UCRcount; i++)
+ op->gprintf(op," %3lu: %f\n",i,p->UCRcurve[i]);
+ }
+ }
+ if (p->BGcount == 0) {
+ op->gprintf(op," BG: Not specified\n");
+ } else if (p->BGcount == 1) {
+ op->gprintf(op," BG: %f%%\n",p->BGcurve[0]);
+ } else {
+ op->gprintf(op," BG curve no. elements = %u\n",p->BGcount);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->BGcount; i++)
+ op->gprintf(op," %3lu: %f\n",i,p->BGcurve[i]);
+ }
+ }
+
+ {
+ unsigned int i, r, c, size;
+ op->gprintf(op," Description:\n");
+ op->gprintf(op," No. chars = %lu\n",p->size);
+
+ size = p->size > 0 ? p->size-1 : 0;
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 73) {
+ if (isprint(p->string[i])) {
+ op->gprintf(op,"%c",p->string[i]);
+ c++;
+ } else {
+ op->gprintf(op,"\\%03o",p->string[i]);
+ c += 4;
+ }
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmUcrBg_allocate(
+ icmBase *pp
+) {
+ icmUcrBg *p = (icmUcrBg *)pp;
+ icc *icp = p->icp;
+
+ if (p->UCRcount != p->UCR_count) {
+ if (ovr_mul(p->UCRcount, sizeof(double))) {
+ sprintf(icp->err,"icmUcrBg_allocate: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->UCRcurve != NULL)
+ icp->al->free(icp->al, p->UCRcurve);
+ if ((p->UCRcurve = (double *) icp->al->calloc(icp->al, p->UCRcount, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmUcrBg_allocate: malloc() of UCR curve data failed");
+ return icp->errc = 2;
+ }
+ p->UCR_count = p->UCRcount;
+ }
+ if (p->BGcount != p->BG_count) {
+ if (ovr_mul(p->BGcount, sizeof(double))) {
+ sprintf(icp->err,"icmUcrBg_allocate: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->BGcurve != NULL)
+ icp->al->free(icp->al, p->BGcurve);
+ if ((p->BGcurve = (double *) icp->al->calloc(icp->al, p->BGcount, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmUcrBg_allocate: malloc() of BG curve data failed");
+ return icp->errc = 2;
+ }
+ p->BG_count = p->BGcount;
+ }
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(char))) {
+ sprintf(icp->err,"icmUcrBg_allocate: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->string != NULL)
+ icp->al->free(icp->al, p->string);
+ if ((p->string = (char *) icp->al->calloc(icp->al, p->size, sizeof(char))) == NULL) {
+ sprintf(icp->err,"icmUcrBg_allocate: malloc() of string data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmUcrBg_delete(
+ icmBase *pp
+) {
+ icmUcrBg *p = (icmUcrBg *)pp;
+ icc *icp = p->icp;
+
+ if (p->UCRcurve != NULL)
+ icp->al->free(icp->al, p->UCRcurve);
+ if (p->BGcurve != NULL)
+ icp->al->free(icp->al, p->BGcurve);
+ if (p->string != NULL)
+ icp->al->free(icp->al, p->string);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmUcrBg(
+ icc *icp
+) {
+ icmUcrBg *p;
+ if ((p = (icmUcrBg *) icp->al->calloc(icp->al,1,sizeof(icmUcrBg))) == NULL)
+ return NULL;
+ p->ttype = icSigUcrBgType;
+ p->refcount = 1;
+ p->get_size = icmUcrBg_get_size;
+ p->read = icmUcrBg_read;
+ p->write = icmUcrBg_write;
+ p->dump = icmUcrBg_dump;
+ p->allocate = icmUcrBg_allocate;
+ p->del = icmUcrBg_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* VideoCardGamma (ColorSync 2.5 specific - c/o Neil Okamoto) */
+/* 'vcgt' */
+
+static unsigned int icmVideoCardGamma_get_size(
+ icmBase *pp
+) {
+ icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
+ unsigned int len = 0;
+
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 4); /* 4 for gamma type */
+
+ /* compute size of remainder */
+ if (p->tagType == icmVideoCardGammaTableType) {
+ len = sat_add(len, 2); /* 2 bytes for channels */
+ len = sat_add(len, 2); /* 2 for entry count */
+ len = sat_add(len, 2); /* 2 for entry size */
+ len = sat_add(len, sat_mul3(p->u.table.channels, /* compute table size */
+ p->u.table.entryCount, p->u.table.entrySize));
+ }
+ else if (p->tagType == icmVideoCardGammaFormulaType) {
+ len = sat_add(len, 12); /* 4 bytes each for red gamma, min, & max */
+ len = sat_add(len, 12); /* 4 bytes each for green gamma, min & max */
+ len = sat_add(len, 12); /* 4 bytes each for blue gamma, min & max */
+ }
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmVideoCardGamma_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
+ icc *icp = p->icp;
+ int rv, c;
+ char *bp, *buf;
+ ORD8 *pchar;
+ ORD16 *pshort;
+
+ if (len < 18) {
+ sprintf(icp->err,"icmVideoCardGamma_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmVideoCardGamma_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmVideoCardGamma_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmVideoCardGamma_read: Wrong tag type for icmVideoCardGamma");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read gamma format (eg. table or formula) from the buffer */
+ p->tagType = (icmVideoCardGammaTagType)read_UInt32Number(bp+8);
+
+ /* Read remaining gamma data based on format */
+ if (p->tagType == icmVideoCardGammaTableType) {
+ p->u.table.channels = read_UInt16Number(bp+12);
+ p->u.table.entryCount = read_UInt16Number(bp+14);
+ p->u.table.entrySize = read_UInt16Number(bp+16);
+ if ((len-18) < sat_mul3(p->u.table.channels, p->u.table.entryCount,
+ p->u.table.entrySize)) {
+ sprintf(icp->err,"icmVideoCardGamma_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+ if ((rv = pp->allocate(pp)) != 0) { /* make space for table */
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ /* ~~~~ This should be a table of doubles like the rest of icclib ! ~~~~ */
+ pchar = (ORD8 *)p->u.table.data;
+ pshort = (ORD16 *)p->u.table.data;
+ for (c=0, bp=bp+18; c<p->u.table.channels*p->u.table.entryCount; c++) {
+ switch (p->u.table.entrySize) {
+ case 1:
+ *pchar++ = read_UInt8Number(bp);
+ bp++;
+ break;
+ case 2:
+ *pshort++ = read_UInt16Number(bp);
+ bp+=2;
+ break;
+ default:
+ sprintf(icp->err,"icmVideoCardGamma_read: unsupported table entry size");
+ pp->del(pp);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ }
+ } else if (p->tagType == icmVideoCardGammaFormulaType) {
+ if (len < 48) {
+ sprintf(icp->err,"icmVideoCardGamma_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+ p->u.table.channels = 3; /* Always 3 for formula */
+ p->u.formula.redGamma = read_S15Fixed16Number(bp+12);
+ p->u.formula.redMin = read_S15Fixed16Number(bp+16);
+ p->u.formula.redMax = read_S15Fixed16Number(bp+20);
+ p->u.formula.greenGamma = read_S15Fixed16Number(bp+24);
+ p->u.formula.greenMin = read_S15Fixed16Number(bp+28);
+ p->u.formula.greenMax = read_S15Fixed16Number(bp+32);
+ p->u.formula.blueGamma = read_S15Fixed16Number(bp+36);
+ p->u.formula.blueMin = read_S15Fixed16Number(bp+40);
+ p->u.formula.blueMax = read_S15Fixed16Number(bp+44);
+ } else {
+ sprintf(icp->err,"icmVideoCardGammaTable_read: Unknown gamma format for icmVideoCardGamma");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmVideoCardGamma_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0, c;
+ ORD8 *pchar;
+ ORD16 *pshort;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmViewingConditions_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmViewingConditions_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write gamma format (eg. table of formula) */
+ if ((rv = write_UInt32Number(p->tagType,bp+8)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write remaining gamma data based on format */
+ if (p->tagType == icmVideoCardGammaTableType) {
+ if ((rv = write_UInt16Number(p->u.table.channels,bp+12)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_UInt16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt16Number(p->u.table.entryCount,bp+14)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_UInt16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt16Number(p->u.table.entrySize,bp+16)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_UInt16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ pchar = (ORD8 *)p->u.table.data;
+ pshort = (ORD16 *)p->u.table.data;
+ for (c=0, bp=bp+18; c<p->u.table.channels*p->u.table.entryCount; c++) {
+ switch (p->u.table.entrySize) {
+ case 1:
+ write_UInt8Number(*pchar++,bp);
+ bp++;
+ break;
+ case 2:
+ write_UInt16Number(*pshort++,bp);
+ bp+=2;
+ break;
+ default:
+ sprintf(icp->err,"icmVideoCardGamma_write: unsupported table entry size");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ }
+ } else if (p->tagType == icmVideoCardGammaFormulaType) {
+ if ((rv = write_S15Fixed16Number(p->u.formula.redGamma,bp+12)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.redMin,bp+16)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.redMax,bp+20)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.greenGamma,bp+24)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.greenMin,bp+28)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.greenMax,bp+32)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.blueGamma,bp+36)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.blueMin,bp+40)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.blueMax,bp+44)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ } else {
+ sprintf(icp->err,"icmVideoCardGammaTable_write: Unknown gamma format for icmVideoCardGamma");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmViewingConditions_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmVideoCardGamma_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
+ int c,i;
+
+ if (verb <= 0)
+ return;
+
+ if (p->tagType == icmVideoCardGammaTableType) {
+ op->gprintf(op,"VideoCardGammaTable:\n");
+ op->gprintf(op," channels = %d\n", p->u.table.channels);
+ op->gprintf(op," entries = %d\n", p->u.table.entryCount);
+ op->gprintf(op," entrysize = %d\n", p->u.table.entrySize);
+ if (verb >= 2) {
+ /* dump array contents also */
+ for (c=0; c<p->u.table.channels; c++) {
+ op->gprintf(op," channel #%d\n",c);
+ for (i=0; i<p->u.table.entryCount; i++) {
+ if (p->u.table.entrySize == 1) {
+ op->gprintf(op," %d: %d\n",i,((ORD8 *)p->u.table.data)[c*p->u.table.entryCount+i]);
+ }
+ else if (p->u.table.entrySize == 2) {
+ op->gprintf(op," %d: %d\n",i,((ORD16 *)p->u.table.data)[c*p->u.table.entryCount+i]);
+ }
+ }
+ }
+ }
+ } else if (p->tagType == icmVideoCardGammaFormulaType) {
+ op->gprintf(op,"VideoCardGammaFormula:\n");
+ op->gprintf(op," red gamma = %f\n", p->u.formula.redGamma);
+ op->gprintf(op," red min = %f\n", p->u.formula.redMin);
+ op->gprintf(op," red max = %f\n", p->u.formula.redMax);
+ op->gprintf(op," green gamma = %f\n", p->u.formula.greenGamma);
+ op->gprintf(op," green min = %f\n", p->u.formula.greenMin);
+ op->gprintf(op," green max = %f\n", p->u.formula.greenMax);
+ op->gprintf(op," blue gamma = %f\n", p->u.formula.blueGamma);
+ op->gprintf(op," blue min = %f\n", p->u.formula.blueMin);
+ op->gprintf(op," blue max = %f\n", p->u.formula.blueMax);
+ } else {
+ op->gprintf(op," Unknown tag format\n");
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmVideoCardGamma_allocate(
+ icmBase *pp
+) {
+ icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
+ icc *icp = p->icp;
+ unsigned int size;
+
+ /* note: allocation is only relevant for table type
+ * and in that case the channels, entryCount, and entrySize
+ * fields must all be set prior to getting here
+ */
+
+ if (p->tagType == icmVideoCardGammaTableType) {
+ size = sat_mul(p->u.table.channels, p->u.table.entryCount);
+ switch (p->u.table.entrySize) {
+ case 1:
+ size = sat_mul(size, sizeof(ORD8));
+ break;
+ case 2:
+ size = sat_mul(size, sizeof(unsigned short));
+ break;
+ default:
+ sprintf(icp->err,"icmVideoCardGamma_alloc: unsupported table entry size");
+ return icp->errc = 1;
+ }
+ if (size == UINT_MAX) {
+ sprintf(icp->err,"icmVideoCardGamma_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->u.table.data != NULL)
+ icp->al->free(icp->al, p->u.table.data);
+ if ((p->u.table.data = (void*) icp->al->malloc(icp->al, size)) == NULL) {
+ sprintf(icp->err,"icmVideoCardGamma_alloc: malloc() of table data failed");
+ return icp->errc = 2;
+ }
+ }
+
+ return 0;
+}
+
+/* Read a value */
+static double icmVideoCardGamma_lookup(
+ icmVideoCardGamma *p,
+ int chan, /* Channel, 0, 1 or 2 */
+ double iv /* Input value 0.0 - 1.0 */
+) {
+ double ov = 0.0;
+
+ if (chan < 0 || chan > (p->u.table.channels-1)
+ || iv < 0.0 || iv > 1.0)
+ return iv;
+
+ if (p->tagType == icmVideoCardGammaTableType && p->u.table.entryCount == 0) {
+ /* Deal with siliness */
+ ov = iv;
+ } else if (p->tagType == icmVideoCardGammaTableType) {
+ /* Use linear interpolation */
+ unsigned int ix;
+ double val0, val1, w;
+ double inputEnt_1 = (double)(p->u.table.entryCount-1);
+
+ val0 = iv * inputEnt_1;
+ if (val0 < 0.0)
+ val0 = 0.0;
+ else if (val0 > inputEnt_1)
+ val0 = inputEnt_1;
+ ix = (unsigned int)floor(val0); /* Coordinate */
+ if (ix > (p->u.table.entryCount-2))
+ ix = (p->u.table.entryCount-2);
+ w = val0 - (double)ix; /* weight */
+ if (p->u.table.entrySize == 1) {
+ val0 = ((ORD8 *)p->u.table.data)[chan * p->u.table.entryCount + ix]/255.0;
+ val1 = ((ORD8 *)p->u.table.data)[chan * p->u.table.entryCount + ix + 1]/255.0;
+ } else if (p->u.table.entrySize == 2) {
+ val0 = ((ORD16 *)p->u.table.data)[chan * p->u.table.entryCount + ix]/65535.0;
+ val1 = ((ORD16 *)p->u.table.data)[chan * p->u.table.entryCount + ix + 1]/65535.0;
+ } else {
+ val0 = val1 = iv;
+ }
+ ov = val0 + w * (val1 - val0);
+
+ } else if (p->tagType == icmVideoCardGammaFormulaType) {
+ double min, max, gam;
+
+ if (iv == 0) {
+ min = p->u.formula.redMin;
+ max = p->u.formula.redMax;
+ gam = p->u.formula.redGamma;
+ } else if (iv == 1) {
+ min = p->u.formula.greenMin;
+ max = p->u.formula.greenMax;
+ gam = p->u.formula.greenGamma;
+ } else {
+ min = p->u.formula.blueMin;
+ max = p->u.formula.blueMax;
+ gam = p->u.formula.blueGamma;
+ }
+
+ /* The Apple OSX doco confirms this is the formula */
+ ov = min + (max - min) * pow(iv, gam);
+ }
+ return ov;
+}
+
+/* Free all storage in the object */
+static void icmVideoCardGamma_delete(
+ icmBase *pp
+) {
+ icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
+ icc *icp = p->icp;
+
+ if (p->tagType == icmVideoCardGammaTableType && p->u.table.data != NULL)
+ icp->al->free(icp->al, p->u.table.data);
+
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmVideoCardGamma(
+ icc *icp
+) {
+ icmVideoCardGamma *p;
+ if ((p = (icmVideoCardGamma *) icp->al->calloc(icp->al,1,sizeof(icmVideoCardGamma))) == NULL)
+ return NULL;
+ p->ttype = icSigVideoCardGammaType;
+ p->refcount = 1;
+ p->get_size = icmVideoCardGamma_get_size;
+ p->read = icmVideoCardGamma_read;
+ p->write = icmVideoCardGamma_write;
+ p->lookup = icmVideoCardGamma_lookup;
+ p->dump = icmVideoCardGamma_dump;
+ p->allocate = icmVideoCardGamma_allocate;
+ p->del = icmVideoCardGamma_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* ViewingConditions */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmViewingConditions_get_size(
+ icmBase *pp
+) {
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 12); /* 12 for XYZ of illuminant */
+ len = sat_add(len, 12); /* 12 for XYZ of surround */
+ len = sat_add(len, 4); /* 4 for illuminant type */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmViewingConditions_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmViewingConditions *p = (icmViewingConditions *)pp;
+ icc *icp = p->icp;
+ int rv;
+ char *bp, *buf;
+
+ if (len < 36) {
+ sprintf(icp->err,"icmViewingConditions_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmViewingConditions_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmViewingConditions_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmViewingConditions_read: Wrong tag type for icmViewingConditions");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read the XYZ values for the illuminant */
+ if ((rv = read_XYZNumber(&p->illuminant, bp+8)) != 0) {
+ sprintf(icp->err,"icmViewingConditions: read_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Read the XYZ values for the surround */
+ if ((rv = read_XYZNumber(&p->surround, bp+20)) != 0) {
+ sprintf(icp->err,"icmViewingConditions: read_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Read the encoded standard illuminant */
+ p->stdIlluminant = (icIlluminant)read_SInt32Number(bp + 32);
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmViewingConditions_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmViewingConditions *p = (icmViewingConditions *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmViewingConditions_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmViewingConditions_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmViewingConditions_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write the XYZ values for the illuminant */
+ if ((rv = write_XYZNumber(&p->illuminant, bp+8)) != 0) {
+ sprintf(icp->err,"icmViewingConditions: write_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write the XYZ values for the surround */
+ if ((rv = write_XYZNumber(&p->surround, bp+20)) != 0) {
+ sprintf(icp->err,"icmViewingConditions: write_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write the encoded standard illuminant */
+ if ((rv = write_SInt32Number((int)p->stdIlluminant, bp + 32)) != 0) {
+ sprintf(icp->err,"icmViewingConditionsa_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmViewingConditions_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmViewingConditions_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmViewingConditions *p = (icmViewingConditions *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Viewing Conditions:\n");
+ op->gprintf(op," XYZ value of illuminant in cd/m^2 = %s\n", string_XYZNumber(&p->illuminant));
+ op->gprintf(op," XYZ value of surround in cd/m^2 = %s\n", string_XYZNumber(&p->surround));
+ op->gprintf(op," Illuminant type = %s\n", string_Illuminant(p->stdIlluminant));
+}
+
+/* Allocate variable sized data elements */
+static int icmViewingConditions_allocate(
+ icmBase *pp
+) {
+ /* Nothing to do */
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmViewingConditions_delete(
+ icmBase *pp
+) {
+ icmViewingConditions *p = (icmViewingConditions *)pp;
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmViewingConditions(
+ icc *icp
+) {
+ icmViewingConditions *p;
+ if ((p = (icmViewingConditions *) icp->al->calloc(icp->al,1,sizeof(icmViewingConditions))) == NULL)
+ return NULL;
+ p->ttype = icSigViewingConditionsType;
+ p->refcount = 1;
+ p->get_size = icmViewingConditions_get_size;
+ p->read = icmViewingConditions_read;
+ p->write = icmViewingConditions_write;
+ p->dump = icmViewingConditions_dump;
+ p->allocate = icmViewingConditions_allocate;
+ p->del = icmViewingConditions_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmCrdInfo object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmCrdInfo_get_size(
+ icmBase *pp
+) {
+ icmCrdInfo *p = (icmCrdInfo *)pp;
+ unsigned int len = 0, t;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addadd(len, 4, p->ppsize); /* Postscript product name */
+ for (t = 0; t < 4; t++) { /* For all 4 intents */
+ len = sat_addadd(len, 4, p->crdsize[t]); /* crd names */
+ }
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmCrdInfo_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmCrdInfo *p = (icmCrdInfo *)pp;
+ icc *icp = p->icp;
+ unsigned int t;
+ int rv;
+ char *bp, *buf, *end;
+
+ if (len < 28) {
+ sprintf(icp->err,"icmCrdInfo_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmCrdInfo_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmCrdInfo_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmCrdInfo_read: Wrong tag type for icmCrdInfo");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp = bp + 8;
+
+ /* Postscript product name */
+ if (bp > end || 4 > (end - bp)) {
+ sprintf(icp->err,"icmCrdInfo_read: Data too short to read Postscript product name");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->ppsize = read_UInt32Number(bp);
+ bp += 4;
+ if (p->ppsize > 0) {
+ if (p->ppsize > (end - bp)) {
+ sprintf(icp->err,"icmCrdInfo_read: Data to short to read Postscript product string");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string(bp,p->ppsize)) == 1) {
+ sprintf(icp->err,"icmCrdInfo_read: Postscript product name is not terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ memmove((void *)p->ppname, (void *)bp, p->ppsize);
+ bp += p->ppsize;
+ }
+
+ /* CRD names for the four rendering intents */
+ for (t = 0; t < 4; t++) { /* For all 4 intents */
+ if (bp > end || 4 > (end - bp)) {
+ sprintf(icp->err,"icmCrdInfo_read: Data too short to read CRD%d name",t);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->crdsize[t] = read_UInt32Number(bp);
+ bp += 4;
+ if (p->crdsize[t] > 0) {
+ if (p->crdsize[t] > (end - bp)) {
+ sprintf(icp->err,"icmCrdInfo_read: Data to short to read CRD%d string",t);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string(bp,p->crdsize[t])) == 1) {
+ sprintf(icp->err,"icmCrdInfo_read: CRD%d name is not terminated",t);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ memmove((void *)p->crdname[t], (void *)bp, p->crdsize[t]);
+ bp += p->crdsize[t];
+ }
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmCrdInfo_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmCrdInfo *p = (icmCrdInfo *)pp;
+ icc *icp = p->icp;
+ unsigned int t;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmCrdInfo_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmCrdInfo_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmCrdInfo_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ bp = bp + 8;
+
+ /* Postscript product name */
+ if ((rv = write_UInt32Number(p->ppsize,bp)) != 0) {
+ sprintf(icp->err,"icmCrdInfo_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp += 4;
+ if (p->ppsize > 0) {
+ if ((rv = check_null_string(p->ppname,p->ppsize)) == 1) {
+ sprintf(icp->err,"icmCrdInfo_write: Postscript product name is not terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ memmove((void *)bp, (void *)p->ppname, p->ppsize);
+ bp += p->ppsize;
+ }
+
+ /* CRD names for the four rendering intents */
+ for (t = 0; t < 4; t++) { /* For all 4 intents */
+ if ((rv = write_UInt32Number(p->crdsize[t],bp)) != 0) {
+ sprintf(icp->err,"icmCrdInfo_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp += 4;
+ if (p->ppsize > 0) {
+ if ((rv = check_null_string(p->crdname[t],p->crdsize[t])) == 1) {
+ sprintf(icp->err,"icmCrdInfo_write: CRD%d name is not terminated",t);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ memmove((void *)bp, (void *)p->crdname[t], p->crdsize[t]);
+ bp += p->crdsize[t];
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmCrdInfo_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmCrdInfo_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmCrdInfo *p = (icmCrdInfo *)pp;
+ unsigned int i, r, c, size, t;
+
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"PostScript Product name and CRD names:\n");
+
+ op->gprintf(op," Product name:\n");
+ op->gprintf(op," No. chars = %lu\n",p->ppsize);
+
+ size = p->ppsize > 0 ? p->ppsize-1 : 0;
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 73) {
+ if (isprint(p->ppname[i])) {
+ op->gprintf(op,"%c",p->ppname[i]);
+ c++;
+ } else {
+ op->gprintf(op,"\\%03o",p->ppname[i]);
+ c += 4;
+ }
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+
+ for (t = 0; t < 4; t++) { /* For all 4 intents */
+ op->gprintf(op," CRD%ld name:\n",t);
+ op->gprintf(op," No. chars = %lu\n",p->crdsize[t]);
+
+ size = p->crdsize[t] > 0 ? p->crdsize[t]-1 : 0;
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 73) {
+ if (isprint(p->crdname[t][i])) {
+ op->gprintf(op,"%c",p->crdname[t][i]);
+ c++;
+ } else {
+ op->gprintf(op,"\\%03o",p->crdname[t][i]);
+ c += 4;
+ }
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmCrdInfo_allocate(
+ icmBase *pp
+) {
+ icmCrdInfo *p = (icmCrdInfo *)pp;
+ icc *icp = p->icp;
+ unsigned int t;
+
+ if (p->ppsize != p->_ppsize) {
+ if (ovr_mul(p->ppsize, sizeof(char))) {
+ sprintf(icp->err,"icmCrdInfo_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->ppname != NULL)
+ icp->al->free(icp->al, p->ppname);
+ if ((p->ppname = (char *) icp->al->calloc(icp->al, p->ppsize, sizeof(char))) == NULL) {
+ sprintf(icp->err,"icmCrdInfo_alloc: malloc() of string data failed");
+ return icp->errc = 2;
+ }
+ p->_ppsize = p->ppsize;
+ }
+ for (t = 0; t < 4; t++) { /* For all 4 intents */
+ if (p->crdsize[t] != p->_crdsize[t]) {
+ if (ovr_mul(p->crdsize[t], sizeof(char))) {
+ sprintf(icp->err,"icmCrdInfo_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->crdname[t] != NULL)
+ icp->al->free(icp->al, p->crdname[t]);
+ if ((p->crdname[t] = (char *) icp->al->calloc(icp->al, p->crdsize[t], sizeof(char))) == NULL) {
+ sprintf(icp->err,"icmCrdInfo_alloc: malloc() of CRD%d name string failed",t);
+ return icp->errc = 2;
+ }
+ p->_crdsize[t] = p->crdsize[t];
+ }
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmCrdInfo_delete(
+ icmBase *pp
+) {
+ icmCrdInfo *p = (icmCrdInfo *)pp;
+ icc *icp = p->icp;
+ unsigned int t;
+
+ if (p->ppname != NULL)
+ icp->al->free(icp->al, p->ppname);
+ for (t = 0; t < 4; t++) { /* For all 4 intents */
+ if (p->crdname[t] != NULL)
+ icp->al->free(icp->al, p->crdname[t]);
+ }
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmCrdInfo(
+ icc *icp
+) {
+ icmCrdInfo *p;
+ if ((p = (icmCrdInfo *) icp->al->calloc(icp->al,1,sizeof(icmCrdInfo))) == NULL)
+ return NULL;
+ p->ttype = icSigCrdInfoType;
+ p->refcount = 1;
+ p->get_size = icmCrdInfo_get_size;
+ p->read = icmCrdInfo_read;
+ p->write = icmCrdInfo_write;
+ p->dump = icmCrdInfo_dump;
+ p->allocate = icmCrdInfo_allocate;
+ p->del = icmCrdInfo_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ========================================================== */
+/* icmHeader object */
+/* ========================================================== */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmHeader_get_size(
+ icmHeader *p
+) {
+ return 128; /* By definition */
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmHeader_read(
+ icmHeader *p,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icc *icp = p->icp;
+ char *buf;
+ unsigned int tt;
+ int rv = 0;
+
+ if (len != 128) {
+ sprintf(icp->err,"icmHeader_read: Length expected to be 128");
+ return icp->errc = 1;
+ }
+
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmHeader_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmHeader_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Check that the magic number is right */
+ tt = read_SInt32Number(buf+36);
+ if (tt != icMagicNumber) { /* Check magic number */
+ sprintf(icp->err,"icmHeader_read: wrong magic number 0x%x",tt);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Fill in the in-memory structure */
+ p->size = read_UInt32Number(buf + 0); /* Profile size in bytes */
+ if (p->size < (128 + 4)) {
+ sprintf(icp->err,"icmHeader_read: file size %d too small to be legal",p->size);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->cmmId = read_SInt32Number(buf + 4); /* CMM for profile */
+ tt = read_UInt8Number(buf + 8); /* Raw major version number */
+ p->majv = (tt >> 4) * 10 + (tt & 0xf); /* Integer major version number */
+ icp->ver = p->majv > 3 ? 1 : 0; /* Set major version flag in icc */
+ tt = read_UInt8Number(buf + 9); /* Raw minor/bug fix version numbers */
+ p->minv = (tt >> 4); /* Integer minor version number */
+ p->bfv = (tt & 0xf); /* Integer bug fix version number */
+ p->deviceClass = (icProfileClassSignature)
+ read_SInt32Number(buf + 12); /* Type of profile */
+ p->colorSpace = (icColorSpaceSignature)
+ read_SInt32Number(buf + 16); /* Color space of data */
+ p->pcs = (icColorSpaceSignature)
+ read_SInt32Number(buf + 20); /* PCS: XYZ or Lab */
+ if ((rv = read_DateTimeNumber(&p->date, buf + 24)) != 0) { /* Creation Date */
+ sprintf(icp->err,"icmHeader_read: read_DateTimeNumber corrupted");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ p->platform = (icPlatformSignature)
+ read_SInt32Number(buf + 40); /* Primary platform */
+ p->flags = read_UInt32Number(buf + 44); /* Various bits */
+ p->manufacturer = read_SInt32Number(buf + 48); /* Dev manufacturer */
+ p->model = read_SInt32Number(buf + 52); /* Dev model */
+ read_UInt64Number(&p->attributes, buf + 56); /* Device attributes */
+ p->renderingIntent = (icRenderingIntent)
+ read_SInt32Number(buf + 64); /* Rendering intent */
+ if ((rv = read_XYZNumber(&p->illuminant, buf + 68)) != 0) { /* Profile illuminant */
+ sprintf(icp->err,"icmHeader_read: read_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ p->creator = read_SInt32Number(buf + 80); /* Profile creator */
+
+ for (tt = 0; tt < 16; tt++)
+ p->id[tt] = icp->ver ? read_UInt8Number(buf + 84 + tt) : 0; /* Profile ID */
+
+ icp->al->free(icp->al, buf);
+
+#ifndef ENABLE_V4
+ if (icp->ver) {
+ sprintf(icp->err,"icmHeader_read: ICC V4 not supported!");
+ return icp->errc = 1;
+ }
+#endif
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmHeader_write(
+ icmHeader *p,
+ unsigned int of, /* File offset to write from */
+ int doid /* Flag, nz = writing to compute ID */
+) {
+ icc *icp = p->icp;
+ char *buf; /* Buffer to write from */
+ unsigned int len;
+ unsigned int tt;
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size(p)) == UINT_MAX) {
+ sprintf(icp->err,"icmHeader_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->calloc(icp->al,1,len)) == NULL) { /* Zero it - some CMS are fussy */
+ sprintf(icp->err,"icmHeader_write calloc() failed");
+ return icp->errc = 2;
+ }
+
+ /* Fill in the write buffer */
+ if ((rv = write_UInt32Number(p->size, buf + 0)) != 0) { /* Profile size in bytes */
+ sprintf(icp->err,"icmHeader_write: profile size");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ if ((rv = write_SInt32Number(p->cmmId, buf + 4)) != 0) { /* CMM for profile */
+ sprintf(icp->err,"icmHeader_write: cmmId");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if (p->majv < 0 || p->majv > 99 /* Sanity check version numbers */
+ || p->minv < 0 || p->minv > 9
+ || p->bfv < 0 || p->bfv > 9) {
+ sprintf(icp->err,"icmHeader_write: version number");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ tt = ((p->majv/10) << 4) + (p->majv % 10);
+ if ((rv = write_UInt8Number(tt, buf + 8)) != 0) { /* Raw major version number */
+ sprintf(icp->err,"icmHeader_write: Uint8Number major version");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ tt = (p->minv << 4) + p->bfv;
+ if ((rv = write_UInt8Number(tt, buf + 9)) != 0) { /* Raw minor/bug fix version numbers */
+ sprintf(icp->err,"icmHeader_write: Uint8Number minor/bug fix");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number((int)p->deviceClass, buf + 12)) != 0) { /* Type of profile */
+ sprintf(icp->err,"icmHeader_write: SInt32Number deviceClass");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number((int)p->colorSpace, buf + 16)) != 0) { /* Color space of data */
+ sprintf(icp->err,"icmHeader_write: SInt32Number data color space");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number((int)p->pcs, buf + 20)) != 0) { /* PCS: XYZ or Lab */
+ sprintf(icp->err,"icmHeader_write: SInt32Number PCS");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_DateTimeNumber(&p->date, buf + 24)) != 0) { /* Creation Date */
+ sprintf(icp->err,"icmHeader_write: DateTimeNumber creation");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number(icMagicNumber, buf+36)) != 0) { /* Magic number */
+ sprintf(icp->err,"icmHeader_write: SInt32Number magic");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number((int)p->platform, buf + 40)) != 0) { /* Primary platform */
+ sprintf(icp->err,"icmHeader_write: SInt32Number platform");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt32Number(doid ? 0 : p->flags, buf + 44)) != 0) { /* Various flag bits */
+ sprintf(icp->err,"icmHeader_write: UInt32Number flags");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number(p->manufacturer, buf + 48)) != 0) { /* Dev manufacturer */
+ sprintf(icp->err,"icmHeader_write: SInt32Number manufaturer");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((write_SInt32Number(p->model, buf + 52)) != 0) { /* Dev model */
+ sprintf(icp->err,"icmHeader_write: SInt32Number model");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt64Number(&p->attributes, buf + 56)) != 0) { /* Device attributes */
+ sprintf(icp->err,"icmHeader_write: UInt64Number attributes");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number(doid ? 0 : (int)p->renderingIntent, buf + 64)) != 0) { /* Rendering intent */
+ sprintf(icp->err,"icmHeader_write: SInt32Number rendering intent");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_XYZNumber(&p->illuminant, buf + 68)) != 0) { /* Profile illuminant */
+ sprintf(icp->err,"icmHeader_write: XYZNumber illuminant");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number(p->creator, buf + 80)) != 0) { /* Profile creator */
+ sprintf(icp->err,"icmHeader_write: SInt32Number creator");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if (doid == 0 && icp->ver) { /* ID is V4.0+ feature */
+ for (tt = 0; tt < 16; tt++) {
+ if ((rv = write_UInt8Number(p->id[tt], buf + 84 + tt)) != 0) { /* Profile ID */
+ sprintf(icp->err,"icmHeader_write: UInt8Number creator");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmHeader_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+
+ icp->al->free(icp->al, buf);
+ return rv;
+}
+
+static void icmHeader_dump(
+ icmHeader *p,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ int i;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Header:\n");
+ op->gprintf(op," size = %d bytes\n",p->size);
+ op->gprintf(op," CMM = %s\n",tag2str(p->cmmId));
+ op->gprintf(op," Version = %d.%d.%d\n",p->majv, p->minv, p->bfv);
+ op->gprintf(op," Device Class = %s\n", string_ProfileClassSignature(p->deviceClass));
+ op->gprintf(op," Color Space = %s\n", string_ColorSpaceSignature(p->colorSpace));
+ op->gprintf(op," Conn. Space = %s\n", string_ColorSpaceSignature(p->pcs));
+ op->gprintf(op," Date, Time = %s\n", string_DateTimeNumber(&p->date));
+ op->gprintf(op," Platform = %s\n", string_PlatformSignature(p->platform));
+ op->gprintf(op," Flags = %s\n", string_ProfileHeaderFlags(p->flags));
+ op->gprintf(op," Dev. Mnfctr. = %s\n", tag2str(p->manufacturer)); /* ~~~ */
+ op->gprintf(op," Dev. Model = %s\n", tag2str(p->model)); /* ~~~ */
+ op->gprintf(op," Dev. Attrbts = %s\n", string_DeviceAttributes(p->attributes.l));
+ op->gprintf(op," Rndrng Intnt = %s\n", string_RenderingIntent(p->renderingIntent));
+ op->gprintf(op," Illuminant = %s\n", string_XYZNumber_and_Lab(&p->illuminant));
+ op->gprintf(op," Creator = %s\n", tag2str(p->creator)); /* ~~~ */
+ if (p->icp->ver) { /* V4.0+ feature */
+ for (i = 0; i < 16; i++) { /* Check if ID has been set */
+ if (p->id[i] != 0)
+ break;
+ }
+ if (i < 16)
+ op->gprintf(op," ID = %02X%02X%02X%02X%02X%02X%02X%02X"
+ "%02X%02X%02X%02X%02X%02X%02X%02X\n",
+ p->id[0], p->id[1], p->id[2], p->id[3], p->id[4], p->id[5], p->id[6], p->id[7],
+ p->id[8], p->id[9], p->id[10], p->id[11], p->id[12], p->id[13], p->id[14], p->id[15]);
+ else
+ op->gprintf(op," ID = <Not set>\n");
+ }
+ op->gprintf(op,"\n");
+}
+
+static void icmHeader_delete(
+ icmHeader *p
+) {
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmHeader *new_icmHeader(
+ icc *icp
+) {
+ icmHeader *p;
+ if ((p = (icmHeader *) icp->al->calloc(icp->al,1,sizeof(icmHeader))) == NULL)
+ return NULL;
+ p->icp = icp;
+ p->get_size = icmHeader_get_size;
+ p->read = icmHeader_read;
+ p->write = icmHeader_write;
+ p->dump = icmHeader_dump;
+ p->del = icmHeader_delete;
+
+ return p;
+}
+
+/* ---------------------------------------------------------- */
+/* Type vector table. Match the Tag type against the object creator */
+static struct {
+ icTagTypeSignature ttype; /* The tag type signature */
+ icmBase * (*new_obj)(icc *icp);
+} typetable[] = {
+ {icSigColorantTableType, new_icmColorantTable},
+ {icmSigAltColorantTableType, new_icmColorantTable},
+ {icSigCrdInfoType, new_icmCrdInfo},
+ {icSigCurveType, new_icmCurve},
+ {icSigDataType, new_icmData},
+ {icSigDateTimeType, new_icmDateTimeNumber},
+ {icSigLut16Type, new_icmLut},
+ {icSigLut8Type, new_icmLut},
+ {icSigMeasurementType, new_icmMeasurement},
+ {icSigNamedColorType, new_icmNamedColor},
+ {icSigNamedColor2Type, new_icmNamedColor},
+ {icSigProfileSequenceDescType, new_icmProfileSequenceDesc},
+ {icSigS15Fixed16ArrayType, new_icmS15Fixed16Array},
+ {icSigScreeningType, new_icmScreening},
+ {icSigSignatureType, new_icmSignature},
+ {icSigTextDescriptionType, new_icmTextDescription},
+ {icSigTextType, new_icmText},
+ {icSigU16Fixed16ArrayType, new_icmU16Fixed16Array},
+ {icSigUcrBgType, new_icmUcrBg},
+ {icSigVideoCardGammaType, new_icmVideoCardGamma},
+ {icSigUInt16ArrayType, new_icmUInt16Array},
+ {icSigUInt32ArrayType, new_icmUInt32Array},
+ {icSigUInt64ArrayType, new_icmUInt64Array},
+ {icSigUInt8ArrayType, new_icmUInt8Array},
+ {icSigViewingConditionsType, new_icmViewingConditions},
+ {icSigXYZArrayType, new_icmXYZArray},
+ {icMaxEnumType, NULL}
+};
+
+/* Table that lists the legal Types for each Tag Signature */
+static struct {
+ icTagSignature sig;
+ icTagTypeSignature ttypes[4]; /* Arbitrary max of 4 */
+} sigtypetable[] = {
+ {icSigAToB0Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigAToB1Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigAToB2Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigBlueColorantTag, {icSigXYZType,icMaxEnumType}},
+ {icSigBlueTRCTag, {icSigCurveType,icMaxEnumType}},
+ {icSigBToA0Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigBToA1Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigBToA2Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigCalibrationDateTimeTag, {icSigDateTimeType,icMaxEnumType}},
+ {icSigCharTargetTag, {icSigTextType,icMaxEnumType}},
+ {icSigColorantTableTag, {icSigColorantTableType,icmSigAltColorantTableType,
+ icMaxEnumType}},
+ {icSigColorantTableOutTag, {icSigColorantTableType,icmSigAltColorantTableType,
+ icMaxEnumType}},
+ {icSigCopyrightTag, {icSigTextType,icMaxEnumType}},
+ {icSigCrdInfoTag, {icSigCrdInfoType,icMaxEnumType}},
+ {icSigDeviceMfgDescTag, {icSigTextDescriptionType,icMaxEnumType}},
+ {icSigDeviceModelDescTag, {icSigTextDescriptionType,icMaxEnumType}},
+ {icSigGamutTag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigGrayTRCTag, {icSigCurveType,icMaxEnumType}},
+ {icSigGreenColorantTag, {icSigXYZType,icMaxEnumType}},
+ {icSigGreenTRCTag, {icSigCurveType,icMaxEnumType}},
+ {icSigLuminanceTag, {icSigXYZType,icMaxEnumType}},
+ {icSigMeasurementTag, {icSigMeasurementType,icMaxEnumType}},
+ {icSigMediaBlackPointTag, {icSigXYZType,icMaxEnumType}},
+ {icSigMediaWhitePointTag, {icSigXYZType,icMaxEnumType}},
+ {icSigNamedColorTag, {icSigNamedColorType,icMaxEnumType}},
+ {icSigNamedColor2Tag, {icSigNamedColor2Type,icMaxEnumType}},
+ {icSigPreview0Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigPreview1Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigPreview2Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigProfileDescriptionTag, {icSigTextDescriptionType,icMaxEnumType}},
+ {icSigProfileSequenceDescTag, {icSigProfileSequenceDescType,icMaxEnumType}},
+ {icSigPs2CRD0Tag, {icSigDataType,icMaxEnumType}},
+ {icSigPs2CRD1Tag, {icSigDataType,icMaxEnumType}},
+ {icSigPs2CRD2Tag, {icSigDataType,icMaxEnumType}},
+ {icSigPs2CRD3Tag, {icSigDataType,icMaxEnumType}},
+ {icSigPs2CSATag, {icSigDataType,icMaxEnumType}},
+ {icSigPs2RenderingIntentTag, {icSigDataType,icMaxEnumType}},
+ {icSigRedColorantTag, {icSigXYZType,icMaxEnumType}},
+ {icSigRedTRCTag, {icSigCurveType,icMaxEnumType}},
+ {icSigScreeningDescTag, {icSigTextDescriptionType,icMaxEnumType}},
+ {icSigScreeningTag, {icSigScreeningType,icMaxEnumType}},
+ {icSigTechnologyTag, {icSigSignatureType,icMaxEnumType}},
+ {icSigUcrBgTag, {icSigUcrBgType,icMaxEnumType}},
+ {icSigVideoCardGammaTag, {icSigVideoCardGammaType,icMaxEnumType}},
+ {icSigViewingCondDescTag, {icSigTextDescriptionType,icMaxEnumType}},
+ {icSigViewingConditionsTag, {icSigViewingConditionsType,icMaxEnumType}},
+ {icMaxEnumTag, {icMaxEnumType}}
+};
+
+/* Fake color tag for specifying PCS */
+#define icSigPCSData ((icColorSpaceSignature) 0x50435320L)
+
+/* Table that lists the required tags for various profiles */
+static struct {
+ icProfileClassSignature sig; /* Profile signature */
+ int chans; /* Data Color channels, -ve for match but try next, */
+ /* -100 for ignore, -200 for ignore and try next */
+ icColorSpaceSignature colsig; /* Data Color space signature, icMaxEnumData for ignore, */
+ /* icSigPCSData for XYZ of Lab */
+ icColorSpaceSignature pcssig; /* PCS Color space signature, icMaxEnumData for ignore, */
+ /* icSigPCSData for XYZ or Lab */
+ icTagSignature tags[12]; /* Arbitrary max of 12 */
+} tagchecktable[] = {
+ {icSigInputClass, -1, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigGrayTRCTag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigInputClass, -3, icMaxEnumData, icSigXYZData,
+ {icSigProfileDescriptionTag,
+ icSigRedColorantTag,
+ icSigGreenColorantTag,
+ icSigBlueColorantTag,
+ icSigRedTRCTag,
+ icSigGreenTRCTag,
+ icSigBlueTRCTag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigInputClass, -100, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigDisplayClass, -1, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigGrayTRCTag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigDisplayClass, -3, icSigRgbData, icSigXYZData, /* Rgb or any 3 component space ?? */
+ {icSigProfileDescriptionTag,
+ icSigRedColorantTag,
+ icSigGreenColorantTag,
+ icSigBlueColorantTag,
+ icSigRedTRCTag,
+ icSigGreenTRCTag,
+ icSigBlueTRCTag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ /* Non-3 component Display device */
+ {icSigDisplayClass, -100, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag, /* BToA doesn't seem to be required, which is strange... */
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigOutputClass, -1, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigGrayTRCTag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigOutputClass, -1, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigBToA0Tag,
+ icSigGamutTag,
+ icSigAToB1Tag,
+ icSigBToA1Tag,
+ icSigAToB2Tag,
+ icSigBToA2Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigOutputClass, -2, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigBToA0Tag,
+ icSigGamutTag,
+ icSigAToB1Tag,
+ icSigBToA1Tag,
+ icSigAToB2Tag,
+ icSigBToA2Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigOutputClass, -3, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigBToA0Tag,
+ icSigGamutTag,
+ icSigAToB1Tag,
+ icSigBToA1Tag,
+ icSigAToB2Tag,
+ icSigBToA2Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigOutputClass, -4, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigBToA0Tag,
+ icSigGamutTag,
+ icSigAToB1Tag,
+ icSigBToA1Tag,
+ icSigAToB2Tag,
+ icSigBToA2Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigOutputClass, -100, icMaxEnumData, icSigPCSData, /* Assumed from Hexachrome examples */
+ {icSigProfileDescriptionTag,
+ icSigBToA0Tag,
+ icSigBToA1Tag,
+ icSigBToA2Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigLinkClass, -100, icMaxEnumData, icMaxEnumData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigProfileSequenceDescTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigColorSpaceClass, -100, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigBToA0Tag,
+ icSigAToB0Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigAbstractClass, -100, icSigPCSData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigNamedColorClass, -200, icMaxEnumData, icMaxEnumData,
+ {icSigProfileDescriptionTag,
+ icSigNamedColor2Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigNamedColorClass, -100, icMaxEnumData, icMaxEnumData,
+ {icSigProfileDescriptionTag,
+ icSigNamedColorTag, /* Not strictly V3.4 */
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icMaxEnumClass,-1,icMaxEnumData, icMaxEnumData, {icMaxEnumTag}}
+};
+
+/* ------------------------------------------------------------- */
+
+/* Return the current read fp (if any) */
+static icmFile *icc_get_rfp(icc *p) {
+ return p->fp;
+}
+
+/* Change the version to be non-default (ie. not 2.2.0), */
+/* e.g. ICC V4 (used for creation) */
+/* Return 0 if OK */
+/* Return 1 on error */
+static int icc_set_version(icc *p, icmICCVersion ver) {
+
+ if (p->header == NULL) {
+ sprintf(p->err,"icc_set_version: Header is missing");
+ return p->errc = 1;
+ }
+
+ switch (ver) {
+ case icmVersionDefault:
+ p->header->majv = 2;
+ p->header->minv = 2;
+ p->header->bfv = 0;
+ break;
+ case icmVersion2_3:
+ p->header->majv = 2;
+ p->header->minv = 3;
+ p->header->bfv = 0;
+ break;
+ case icmVersion2_4:
+ p->header->majv = 2;
+ p->header->minv = 4;
+ p->header->bfv = 0;
+ break;
+#ifdef ENABLE_V4
+ case icmVersion4_1:
+ p->header->majv = 4;
+ p->header->minv = 1;
+ p->header->bfv = 0;
+ break;
+#endif
+ default:
+ sprintf(p->err,"icc_set_version: Unsupported version 0x%x",ver);
+ return p->errc = 1;
+ }
+ return 0;
+}
+
+
+/* Check that the ICC profile looks like it will be legal. */
+/* Return non-zero and set error string if not */
+static int check_icc_legal(
+ icc *p
+) {
+ int i, j;
+ icProfileClassSignature sig;
+ icColorSpaceSignature colsig;
+ icColorSpaceSignature pcssig;
+ int dchans;
+
+ if (p->header == NULL) {
+ sprintf(p->err,"icc_check_legal: Header is missing");
+ return p->errc = 1;
+ }
+
+ sig = p->header->deviceClass;
+ colsig = p->header->colorSpace;
+ dchans = number_ColorSpaceSignature(colsig);
+ pcssig = p->header->pcs;
+
+ /* Find a matching table entry */
+ for (i = 0; tagchecktable[i].sig != icMaxEnumType; i++) {
+ if ( tagchecktable[i].sig == sig
+ && ( tagchecktable[i].chans == dchans /* Exactly matches */
+ || tagchecktable[i].chans == -dchans /* Exactly matches, but can try next table */
+ || tagchecktable[i].chans < -99) /* Doesn't have to match or try next table */
+ && ( tagchecktable[i].colsig == colsig
+ || (tagchecktable[i].colsig == icSigPCSData
+ && (colsig == icSigXYZData || colsig == icSigLabData))
+ || tagchecktable[i].colsig == icMaxEnumData)
+ && ( tagchecktable[i].pcssig == pcssig
+ || (tagchecktable[i].pcssig == icSigPCSData
+ && (pcssig == icSigXYZData || pcssig == icSigLabData))
+ || tagchecktable[i].pcssig == icMaxEnumData)) {
+
+ /* Found entry, so now check that all the required tags are present. */
+ for (j = 0; tagchecktable[i].tags[j] != icMaxEnumType; j++) {
+ if (p->find_tag(p, tagchecktable[i].tags[j]) != 0) { /* Not present! */
+ if (tagchecktable[i].chans == -200
+ || tagchecktable[i].chans == -dchans) { /* But can try next table */
+ break;
+ }
+ sprintf(p->err,"icc_check_legal: deviceClass %s is missing required tag %s",
+ tag2str(sig), tag2str(tagchecktable[i].tags[j]));
+ return p->errc = 1;
+ }
+ }
+ if (tagchecktable[i].tags[j] == icMaxEnumType) {
+ break; /* Fount all required tags */
+ }
+ }
+ }
+
+ /* According to the spec. if the deviceClass is:
+ an Abstract Class: both in and out color spaces should be PCS
+ an Link Class: both in and out color spaces can be any, and should
+ be the input space of the first profile in the link, and the
+ input space of the last profile in the link respectively.
+ a Named Class: in and out color spaces are not defined in the spec.
+ Input, Display, Output and ColorSpace Classes, input color
+ space can be any, and the output space must be PCS.
+ ~~ should check this here ???
+ */
+
+ return 0; /* Assume anything is ok */
+}
+
+
+/* read the object, return 0 on success, error code on fail */
+/* NOTE: this doesn't read the tag types, they should be read on demand. */
+/* NOTE: fp ownership is taken even if the function fails. */
+static int icc_read_x(
+ icc *p,
+ icmFile *fp, /* File to read from */
+ unsigned int of, /* File offset to read from */
+ int take_fp /* NZ if icc is to take ownership of fp */
+) {
+ char tcbuf[4]; /* Tag count read buffer */
+ unsigned int i, len;
+ unsigned int minoff, maxoff; /* Minimum and maximum offsets of tag data */
+ int er = 0; /* Error code */
+
+ p->fp = fp;
+ if (take_fp)
+ p->del_fp = 1;
+ p->of = of;
+ if (p->header == NULL) {
+ sprintf(p->err,"icc_read: No header defined");
+ return p->errc = 1;
+ }
+
+ /* Read the header */
+ if (p->header->read(p->header, 128, of)) {
+ return 1;
+ }
+
+ /* Read the tag count */
+ if ( p->fp->seek(p->fp, of + 128) != 0
+ || p->fp->read(p->fp, tcbuf, 1, 4) != 4) {
+ sprintf(p->err,"icc_read: fseek() or fread() failed on tag count");
+ return p->errc = 1;
+ }
+ p->count = read_UInt32Number(tcbuf);
+
+ /* Sanity check it */
+ if (p->count > 357913940 /* (2^32-5)/12 */
+ || (p->count > ((p->header->size - 128 - 4) / 12))) {
+ sprintf(p->err,"icc_read: tag count %d is too large to be legal",p->count);
+ return p->errc = 1;
+ }
+ minoff = 128 + 4 + p->count * 12;
+ maxoff = p->header->size;
+
+ if (p->count > 0) {
+ char *bp, *buf;
+
+ if (ovr_mul(p->count, sizeof(icmTag))) {
+ sprintf(p->err,"icc_read: size overflow");
+ return p->errc = 1;
+ }
+
+ /* Read the table into memory */
+ if ((p->data = (icmTag *) p->al->calloc(p->al, p->count, sizeof(icmTag))) == NULL) {
+ sprintf(p->err,"icc_read: Tag table malloc() failed");
+ return p->errc = 2;
+ }
+
+ len = sat_mul(p->count, 12);
+ if ((buf = (char *) p->al->malloc(p->al, len)) == NULL) {
+ sprintf(p->err,"icc_read: Tag table read buffer malloc() failed");
+ p->al->free(p->al, p->data);
+ p->data = NULL;
+ return p->errc = 2;
+ }
+ if ( p->fp->seek(p->fp, of + 128 + 4) != 0
+ || p->fp->read(p->fp, buf, 1, len) != len) {
+ sprintf(p->err,"icc_read: fseek() or fread() failed on tag table");
+ p->al->free(p->al, p->data);
+ p->data = NULL;
+ p->al->free(p->al, buf);
+ return p->errc = 1;
+ }
+
+ /* Fill in the tag table structure for each tag */
+ for (bp = buf, i = 0; i < p->count; i++, bp += 12) {
+ p->data[i].sig = (icTagSignature)read_SInt32Number(bp + 0);
+ p->data[i].offset = read_UInt32Number(bp + 4);
+ p->data[i].size = read_UInt32Number(bp + 8);
+ }
+ p->al->free(p->al, buf);
+
+ /* Check that each tag lies within the nominated space available, */
+ /* and has a reasonable size. */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].offset < minoff
+ || p->data[i].offset > maxoff
+ || p->data[i].size < 4
+ || p->data[i].size > (maxoff - minoff)
+ || (p->data[i].offset + p->data[i].size) < p->data[i].offset /* Overflow */
+ || (p->data[i].offset + p->data[i].size) > p->header->size) {
+ sprintf(p->err,"icc_read: tag %d is out of range of the nominated file size",i);
+ p->al->free(p->al, p->data);
+ p->data = NULL;
+ return p->errc = 1;
+ }
+ }
+
+ /* Read each tag type */
+ for (i = 0; i < p->count; i++) {
+ if ( p->fp->seek(p->fp, of + p->data[i].offset) != 0
+ || p->fp->read(p->fp, tcbuf, 1, 4) != 4) {
+ sprintf(p->err,"icc_read: fseek() or fread() failed on tag headers");
+ p->al->free(p->al, p->data);
+ p->data = NULL;
+ return p->errc = 1;
+ }
+ p->data[i].ttype = (icTagTypeSignature) read_SInt32Number(tcbuf); /* Tag type */
+ p->data[i].objp = NULL; /* Read on demand */
+ }
+ } /* p->count > 0 */
+
+ return er;
+}
+
+/* read the object, return 0 on success, error code on fail */
+/* NOTE: this doesn't read the tag types, they should be read on demand. */
+/* (backward compatible version) */
+static int icc_read(
+ icc *p,
+ icmFile *fp, /* File to read from */
+ unsigned int of /* File offset to read from */
+) {
+ return icc_read_x(p, fp, of, 0);
+}
+
+/* Check the profiles ID. We assume the file has already been read. */
+/* Return 0 if OK, 1 if no ID to check, 2 if doesn't match, 3 if some other error. */
+/* NOTE: this reads the whole file again, to compute the checksum. */
+static int icc_check_id(
+ icc *p,
+ ORD8 *rid /* Optionaly return computed ID */
+) {
+ unsigned char buf[128];
+ ORD8 id[16];
+ icmMD5 *md5 = NULL;
+ unsigned int len;
+
+ if (p->header == NULL) {
+ sprintf(p->err,"icc_check_id: No header defined");
+ return p->errc = 3;
+ }
+ len = p->header->size; /* Claimed size of profile */
+
+ /* See if there is an ID to compare against */
+ for (len = 0; len < 16; len++) {
+ if (p->header->id[len] != 0)
+ break;
+ }
+ if (len >= 16) {
+ return 1;
+ }
+
+ if ((md5 = new_icmMD5(p->al)) == NULL) {
+ sprintf(p->err,"icc_check_id: new_icmMD5 failed");
+ return p->errc = 3;
+ }
+
+ /* Check the header */
+ if ( p->fp->seek(p->fp, p->of) != 0
+ || p->fp->read(p->fp, buf, 1, 128) != 128) {
+ sprintf(p->err,"icc_check_id: fseek() or fread() failed");
+ return p->errc = 3;
+ }
+
+ /* Zero the appropriate bytes in the header */
+ buf[44] = buf[45] = buf[46] = buf[47] = 0;
+ buf[64] = buf[65] = buf[66] = buf[67] = 0;
+ buf[84] = buf[85] = buf[86] = buf[87] =
+ buf[88] = buf[89] = buf[90] = buf[91] =
+ buf[92] = buf[93] = buf[94] = buf[95] =
+ buf[96] = buf[97] = buf[98] = buf[99] = 0;
+
+ md5->add(md5, buf, 128);
+
+ /* Suck in the rest of the profile */
+ for (;len > 0;) {
+ unsigned int rsize = 128;
+ if (rsize > len)
+ rsize = len;
+ if (p->fp->read(p->fp, buf, 1, rsize) != rsize) {
+ sprintf(p->err,"icc_check_id: fread() failed");
+ return p->errc = 3;
+ }
+ md5->add(md5, buf, rsize);
+ len -= rsize;
+ }
+
+ md5->get(md5, id);
+ md5->del(md5);
+
+ if (rid != NULL) {
+ for (len = 0; len < 16; len++)
+ rid[len] = id[len];
+ }
+
+ /* Check the ID */
+ for (len = 0; len < 16; len++) {
+ if (p->header->id[len] != id[len])
+ break;
+ }
+ if (len >= 16) {
+ return 0; /* Matched */
+ }
+ return 2; /* Didn't match */
+}
+
+/* Return the total size needed for the profile */
+/* Return 0 on error. */
+static unsigned int icc_get_size(
+ icc *p
+) {
+ unsigned int i, size = 0;
+
+#ifdef ICM_STRICT
+ /* Check that the right tags etc. are present for a legal ICC profile */
+ if (check_icc_legal(p) != 0) {
+ return 0;
+ }
+#endif /* ICM_STRICT */
+
+ /* Compute the total size and tag element data offsets */
+ if (p->header == NULL) {
+ sprintf(p->err,"icc_get_size: No header defined");
+ p->errc = 1;
+ return 0;
+ }
+
+ size = sat_add(size, p->header->get_size(p->header));
+ /* Assume header is aligned */
+ size = sat_addaddmul(size, 4, p->count, 12); /* Tag table length */
+ size = sat_align(ALIGN_SIZE, size);
+
+ if (size == UINT_MAX) {
+ sprintf(p->err,"icc_get_size: size overflow");
+ return p->errc = 1;
+ }
+
+ /* Reset touched flag for each tag type */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].objp == NULL) {
+ sprintf(p->err,"icc_get_size: Internal error - NULL tag element");
+ p->errc = 1;
+ return 0;
+ }
+ p->data[i].objp->touched = 0;
+ }
+ /* Get size for each tag type, skipping links */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].objp->touched == 0) { /* Not alllowed for previously */
+ size = sat_add(size, p->data[i].objp->get_size(p->data[i].objp));
+ size = sat_align(ALIGN_SIZE, size);
+ p->data[i].objp->touched = 1; /* Don't account for this again */
+ }
+ }
+
+ return size; /* Total size needed, or UINT_MAX if overflow */
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+/* NOTE: fp ownership is taken even if the function fails. */
+static int icc_write_x(
+ icc *p,
+ icmFile *fp, /* File to write to */
+ unsigned int of, /* File offset to write to */
+ int take_fp /* NZ if icc is to take ownership of fp */
+) {
+ char *bp, *buf; /* tag table buffer */
+ unsigned int len;
+ int rv = 0;
+ unsigned int i, size = 0;
+ unsigned char pbuf[ALIGN_SIZE];
+
+ p->fp = fp; /* Open file pointer */
+ if (take_fp)
+ p->del_fp = 1;
+ p->of = of; /* Offset of ICC profile */
+
+ for (i = 0; i < ALIGN_SIZE; i++)
+ pbuf[i] = 0;
+
+ /* Check that the right tags etc. are present for a legal ICC profile */
+ if ((rv = check_icc_legal(p)) != 0) {
+ return rv;
+ }
+
+ /* Compute the total size and tag element data offsets */
+ if (p->header == NULL) {
+ sprintf(p->err,"icc_write: No header defined");
+ return p->errc = 1;
+ }
+
+ size = sat_add(size, p->header->get_size(p->header));
+ /* Assume header is aligned */
+ len = sat_addmul(4, p->count, 12); /* Tag table length */
+ len = sat_sub(sat_align(ALIGN_SIZE, sat_add(size, len)), size); /* Aligned size */
+ size = sat_align(ALIGN_SIZE, sat_add(size, len));
+
+ if (len == UINT_MAX) {
+ sprintf(p->err,"icc_write get_size overflow");
+ return p->errc = 1;
+ }
+
+ /* Allocate memory buffer for tag table */
+ if ((buf = (char *) p->al->calloc(p->al, 1, len)) == NULL) {
+ sprintf(p->err,"icc_write calloc() failed");
+ return p->errc = 2;
+ }
+ bp = buf;
+
+ if ((rv = write_UInt32Number(p->count, bp)) != 0) { /* Tag count */
+ sprintf(p->err,"icc_write: write_UInt32Number() failed on tag count");
+ p->al->free(p->al, buf);
+ return p->errc = rv;
+ }
+ bp += 4;
+ /* Reset touched flag for each tag type */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].objp == NULL) {
+ sprintf(p->err,"icc_write: Internal error - NULL tag element");
+ p->al->free(p->al, buf);
+ return p->errc = 1;
+ }
+ p->data[i].objp->touched = 0;
+ }
+ /* Set the offset and size for each tag type, create the tag table write data */
+ /* and compute the total profile size. */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].objp->touched == 0) { /* Allocate space for tag type */
+ p->data[i].offset = size; /* Profile relative target */
+ p->data[i].size = p->data[i].objp->get_size(p->data[i].objp);
+ size = sat_add(size, p->data[i].size);
+ p->data[i].pad = sat_sub(sat_align(ALIGN_SIZE, size), size);
+ size = sat_align(ALIGN_SIZE, size);
+ p->data[i].objp->touched = 1; /* Allocated space for it */
+ if (size == UINT_MAX) {
+ sprintf(p->err,"icc_write: size overflow");
+ return p->errc = 1;
+ }
+ } else { /* must be linked - copy allocation */
+ unsigned int k;
+ for (k = 0; k < p->count; k++) { /* Find linked tag */
+ if (p->data[k].objp == p->data[i].objp)
+ break;
+ }
+ if (k == p->count) {
+ sprintf(p->err,"icc_write: corrupted link");
+ return p->errc = 2;
+ }
+ p->data[i].offset = p->data[k].offset;
+ p->data[i].size = p->data[k].size;
+ p->data[i].pad = p->data[k].pad;
+ }
+ /* Write tag table entry for this tag */
+ if ((rv = write_SInt32Number((int)p->data[i].sig,bp + 0)) != 0) {
+ sprintf(p->err,"icc_write: write_SInt32Number() failed on tag signature");
+ p->al->free(p->al, buf);
+ return p->errc = rv;
+ }
+ if ((rv = write_UInt32Number(p->data[i].offset, bp + 4)) != 0) {
+ sprintf(p->err,"icc_write: write_UInt32Number() failed on tag offset");
+ p->al->free(p->al, buf);
+ return p->errc = rv;
+ }
+ if ((rv = write_UInt32Number(p->data[i].size, bp + 8)) != 0) {
+ sprintf(p->err,"icc_write: write_UInt32Number() failed on tag size");
+ p->al->free(p->al, buf);
+ return p->errc = rv;
+ }
+ bp += 12;
+ }
+ p->header->size = size; /* Record total icc padded size */
+
+
+ /* If V4.0+, Compute the MD5 id for the profile. */
+ /* We do this by writing to a fake icmFile */
+ if (p->ver) {
+ icmMD5 *md5 = NULL;
+ icmFile *ofp, *dfp = NULL;
+
+ if ((md5 = new_icmMD5(p->al)) == NULL) {
+ sprintf(p->err,"icc_write: new_icmMD5 failed");
+ p->al->free(p->al, buf);
+ return p->errc = 2;
+ }
+
+ if ((dfp = new_icmFileMD5_a(md5, p->al)) == NULL) {
+ sprintf(p->err,"icc_write: new_icmFileMD5 failed");
+ md5->del(md5);
+ p->al->free(p->al, buf);
+ return p->errc = 2;
+ }
+
+ ofp = p->fp;
+ p->fp = dfp;
+
+ /* Dumy write the header */
+ if ((rv = p->header->write(p->header, 0, 1)) != 0) {
+ p->al->free(p->al, buf);
+ return rv;
+ }
+
+ /* Dumy write the tag table */
+ if ( p->fp->seek(p->fp, 128) != 0
+ || p->fp->write(p->fp, buf, 1, len) != len) {
+ sprintf(p->err,"icc_write: seek() or write() failed");
+ p->al->free(p->al, buf);
+ return p->errc = 1;
+ }
+
+ /* Dumy write all the tag element data */
+ /* (We invert meaning of touched here) */
+ for (i = 0; i < p->count; i++) { /* For all the tag element data */
+ if (p->data[i].objp->touched == 0)
+ continue; /* Must be linked, and we've already written it */
+ if ((rv = p->data[i].objp->write(p->data[i].objp, p->data[i].offset)) != 0) {
+ p->al->free(p->al, buf);
+ return rv;
+ }
+ /* Pad with 0 to next boundary */
+ if (p->data[i].pad > 0) {
+ if (p->fp->write(p->fp, pbuf, 1, p->data[i].pad) != p->data[i].pad) {
+ sprintf(p->err,"icc_write: write() failed");
+ p->al->free(p->al, buf);
+ return p->errc = 1;
+ }
+ }
+ p->data[i].objp->touched = 0; /* Written it, so don't write it again. */
+ }
+
+ if (p->fp->flush(p->fp) != 0) {
+ sprintf(p->err,"icc_write flush() failed");
+ p->al->free(p->al, buf);
+ return p->errc = 1;
+ }
+
+ if ((p->errc = ((icmFileMD5 *)dfp)->get_errc(dfp)) != 0) {
+ sprintf(p->err,"icc_write compute ID failed with code %d", p->errc);
+ p->al->free(p->al, buf);
+ return p->errc;
+ }
+
+ /* Get the MD5 checksum ID */
+ md5->get(md5, p->header->id);
+
+ dfp->del(dfp);
+ md5->del(md5);
+ p->fp = ofp;
+
+ /* Reset the touched flags */
+ for (i = 0; i < p->count; i++)
+ p->data[i].objp->touched = 1;
+ }
+
+ /* Now write out the profile for real. */
+ /* Although it may appear like we're seeking for each element, */
+ /* in fact elements will be written in file order. */
+
+ /* Write the header */
+ if ((rv = p->header->write(p->header, of, 0)) != 0) {
+ p->al->free(p->al, buf);
+ return rv;
+ }
+
+ /* Write the tag table */
+ if ( p->fp->seek(p->fp, of + 128) != 0
+ || p->fp->write(p->fp, buf, 1, len) != len) {
+ sprintf(p->err,"icc_write: seek() or write() failed");
+ p->al->free(p->al, buf);
+ return p->errc = 1;
+ }
+ p->al->free(p->al, buf);
+
+ /* Write all the tag element data */
+ /* (We invert the meaning of touched here) */
+ for (i = 0; i < p->count; i++) { /* For all the tag element data */
+ if (p->data[i].objp->touched == 0)
+ continue; /* Must be linked, and we've already written it */
+ if ((rv = p->data[i].objp->write(p->data[i].objp, of + p->data[i].offset)) != 0) {
+ return rv;
+ }
+ /* Pad with 0 to next boundary */
+ if (p->data[i].pad > 0) {
+ if (p->fp->write(p->fp, pbuf, 1, p->data[i].pad) != p->data[i].pad) {
+ sprintf(p->err,"icc_write: write() failed");
+ return p->errc = 1;
+ }
+ }
+ p->data[i].objp->touched = 0; /* Written it, so don't write it again. */
+ }
+
+ if (p->fp->flush(p->fp) != 0) {
+ sprintf(p->err,"icc_write flush() failed");
+ return p->errc = 1;
+ }
+
+ return rv;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+/* (backwards compatible version) */
+static int icc_write(
+ icc *p,
+ icmFile *fp, /* File to write to */
+ unsigned int of /* File offset to write to */
+) {
+ return icc_write_x(p, fp, of, 0);
+}
+
+/* Create and add a tag with the given signature. */
+/* Returns a pointer to the element object */
+/* Returns NULL if error - icc->errc will contain */
+/* 2 on system error, */
+/* 3 if unknown tag */
+/* NOTE: that we prevent tag duplication */
+/* NOTE: to create a tag type icmSigUnknownType, set ttype to icmSigUnknownType, */
+/* and set the actual tag type in icmSigUnknownType->uttype */
+static icmBase *icc_add_tag(
+ icc *p,
+ icTagSignature sig, /* Tag signature - may be unknown */
+ icTagTypeSignature ttype /* Tag type */
+) {
+ icmBase *tp;
+ icmBase *nob;
+ int i = 0, ok = 1;
+ unsigned int j;
+
+ if (ttype != icmSigUnknownType) { /* Check only for possibly known types */
+
+ /* Check that a known signature has an acceptable type */
+ for (i = 0; sigtypetable[i].sig != icMaxEnumType; i++) {
+ if (sigtypetable[i].sig == sig) { /* recognized signature */
+ ok = 0;
+ for (j = 0; sigtypetable[i].ttypes[j] != icMaxEnumType; j++) {
+ if (sigtypetable[i].ttypes[j] == ttype) /* recognized type */
+ ok = 1;
+ }
+ break;
+ }
+ }
+ if (!ok) {
+ sprintf(p->err,"icc_add_tag: wrong tag type for signature");
+ p->errc = 1;
+ return NULL;
+ }
+
+ /* Check that we know how to handle this type */
+ for (i = 0; typetable[i].ttype != icMaxEnumType; i++) {
+ if (typetable[i].ttype == ttype)
+ break;
+ }
+ if (typetable[i].ttype == icMaxEnumType) {
+ sprintf(p->err,"icc_add_tag: unsupported tag type");
+ p->errc = 1;
+ return NULL;
+ }
+ }
+
+ /* Check that this tag doesn't already exist */
+ /* (Perhaps we should simply replace it, rather than erroring ?) */
+ for (j = 0; j < p->count; j++) {
+ if (p->data[j].sig == sig) {
+ sprintf(p->err,"icc_add_tag: Already have tag '%s' in profile",tag2str(p->data[j].sig));
+ p->errc = 1;
+ return NULL;
+ }
+ }
+
+ /* Make space in tag table for new tag item */
+ if (ovr_mul(sat_add(p->count,1), sizeof(icmTag))) {
+ sprintf(p->err,"icc_add_tag: size overflow");
+ p->errc = 1;
+ return NULL;
+ }
+ if (p->data == NULL)
+ tp = (icmBase *)p->al->malloc(p->al, (p->count+1) * sizeof(icmTag));
+ else
+ tp = (icmBase *)p->al->realloc(p->al, (void *)p->data, (p->count+1) * sizeof(icmTag));
+ if (tp == NULL) {
+ sprintf(p->err,"icc_add_tag: Tag table realloc() failed");
+ p->errc = 2;
+ return NULL;
+ }
+ p->data = (icmTag *)tp;
+
+ if (ttype == icmSigUnknownType) {
+ if ((nob = new_icmUnknown(p)) == NULL)
+ return NULL;
+ } else {
+ /* Allocate the empty object */
+ if ((nob = typetable[i].new_obj(p)) == NULL)
+ return NULL;
+ }
+
+ /* Fill out our tag table entry */
+ p->data[p->count].sig = sig; /* The tag signature */
+ p->data[p->count].ttype = nob->ttype = ttype; /* The tag type signature */
+ p->data[p->count].offset = 0; /* Unknown offset yet */
+ p->data[p->count].size = 0; /* Unknown size yet */
+ p->data[p->count].objp = nob; /* Empty object */
+ p->count++;
+
+ return nob;
+}
+
+/* Create and add a tag which is a link to an existing tag. */
+/* Returns a pointer to the element object */
+/* Returns NULL if error - icc->errc will contain */
+/* 3 if incompatible tag */
+/* NOTE: that we prevent tag duplication */
+static icmBase *icc_link_tag(
+ icc *p,
+ icTagSignature sig, /* Tag signature - may be unknown */
+ icTagSignature ex_sig /* Tag signature of tag to link to */
+) {
+ icmBase *tp;
+ unsigned int j, exi;
+ int i, ok = 1;
+
+ /* Search for existing signature */
+ for (exi = 0; exi < p->count; exi++) {
+ if (p->data[exi].sig == ex_sig) /* Found it */
+ break;
+ }
+ if (exi == p->count) {
+ sprintf(p->err,"icc_link_tag: Can't find existing tag '%s'",tag2str(ex_sig));
+ p->errc = 1;
+ return NULL;
+ }
+
+ if (p->data[exi].objp == NULL) {
+ sprintf(p->err,"icc_link_tag: Existing tag '%s' isn't loaded",tag2str(ex_sig));
+ p->errc = 1;
+ return NULL;
+ }
+
+ /* Check that a known signature has an acceptable type */
+ for (i = 0; sigtypetable[i].sig != icMaxEnumType; i++) {
+ if (sigtypetable[i].sig == sig) { /* recognized signature */
+ ok = 0;
+ for (j = 0; sigtypetable[i].ttypes[j] != icMaxEnumType; j++) {
+ if (sigtypetable[i].ttypes[j] == p->data[exi].ttype) /* recognized type */
+ ok = 1;
+ }
+ break;
+ }
+ }
+ if (!ok) {
+ sprintf(p->err,"icc_link_tag: wrong tag type for signature");
+ p->errc = 1;
+ return NULL;
+ }
+
+ /* Check that this tag doesn't already exits */
+ for (j = 0; j < p->count; j++) {
+ if (p->data[j].sig == sig) {
+ sprintf(p->err,"icc_link_tag: Already have tag '%s' in profile",tag2str(p->data[j].sig));
+ p->errc = 1;
+ return NULL;
+ }
+ }
+
+ /* Make space in tag table for new tag item */
+ if (p->data == NULL)
+ tp = (icmBase *)p->al->malloc(p->al, (p->count+1) * sizeof(icmTag));
+ else
+ tp = (icmBase *)p->al->realloc(p->al, (void *)p->data, (p->count+1) * sizeof(icmTag));
+ if (tp == NULL) {
+ sprintf(p->err,"icc_link_tag: Tag table realloc() failed");
+ p->errc = 2;
+ return NULL;
+ }
+ p->data = (icmTag *)tp;
+
+ /* Fill out our tag table entry */
+ p->data[p->count].sig = sig; /* The tag signature */
+ p->data[p->count].ttype = p->data[exi].ttype; /* The tag type signature */
+ p->data[p->count].offset = p->data[exi].offset; /* Same offset (may not be allocated yet) */
+ p->data[p->count].size = p->data[exi].size; /* Same size (may not be allocated yet) */
+ p->data[p->count].objp = p->data[exi].objp; /* Shared object */
+ p->data[exi].objp->refcount++; /* Bump reference count on tag type */
+ p->count++;
+
+ return p->data[exi].objp;
+}
+
+/* Search for tag signature */
+/* return: */
+/* 0 if found */
+/* 1 if found but not handled type */
+/* 2 if not found */
+/* NOTE: doesn't set icc->errc or icc->err[] */
+/* NOTE: we don't handle tag duplication - you'll always get the first in the file. */
+static int icc_find_tag(
+ icc *p,
+ icTagSignature sig /* Tag signature - may be unknown */
+) {
+ unsigned int i;
+ int j;
+
+ /* Search for signature */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].sig == sig) /* Found it */
+ break;
+ }
+ if (i == p->count)
+ return 2;
+
+ /* See if we can handle this type */
+ for (j = 0; typetable[j].ttype != icMaxEnumType; j++) {
+ if (typetable[j].ttype == p->data[i].ttype)
+ break;
+ }
+ if (typetable[j].ttype == icMaxEnumType)
+ return 1;
+
+ return 0;
+}
+
+/* Read the specific tag element data, and return a pointer to the object */
+/* (This is an internal function) */
+/* Returns NULL if error - icc->errc will contain: */
+/* 2 if not found */
+/* Returns an icmSigUnknownType object if the tag type isn't handled by a */
+/* specific object and alow_unk is NZ */
+/* NOTE: we don't handle tag duplication - you'll always get the first in the file */
+static icmBase *icc_read_tag_ix(
+ icc *p,
+ unsigned int i, /* Index from 0.. p->count-1 */
+ int alow_unk /* NZ to allow unknown tag to load */
+) {
+ icTagTypeSignature ttype; /* Tag type we will create */
+ icmBase *nob;
+ unsigned int k;
+ int j;
+
+ if (i >= p->count) {
+ sprintf(p->err,"icc_read_tag_ix: index %d is out of range",i);
+ p->errc = 2;
+ return NULL;
+ }
+
+ /* See if it's already been read */
+ if (p->data[i].objp != NULL) {
+ return p->data[i].objp; /* Just return it */
+ }
+
+ /* See if this should be a link */
+ for (k = 0; k < p->count; k++) {
+ if (i == k)
+ continue;
+ if (p->data[i].ttype == p->data[k].ttype /* Exact match and already read */
+ && p->data[i].offset == p->data[k].offset
+ && p->data[i].size == p->data[k].size
+ && p->data[k].objp != NULL)
+ break;
+ }
+ if (k < p->count) { /* Make this a link */
+ p->data[i].objp = p->data[k].objp;
+ p->data[k].objp->refcount++; /* Bump reference count */
+ return p->data[k].objp; /* Done */
+ }
+
+ /* See if we can handle this type */
+ for (j = 0; typetable[j].ttype != icMaxEnumType; j++) {
+ if (typetable[j].ttype == p->data[i].ttype)
+ break;
+ }
+
+ if (typetable[j].ttype == icMaxEnumType) {
+ if (!alow_unk) {
+ p->errc = 2;
+ return NULL;
+ }
+ ttype = icmSigUnknownType; /* Use the Unknown type to handle an unknown tag type */
+ } else {
+ ttype = p->data[i].ttype; /* We known this type */
+ }
+
+ /* Create and read in the object */
+ if (ttype == icmSigUnknownType)
+ nob = new_icmUnknown(p);
+ else
+ nob = typetable[j].new_obj(p);
+
+ if (nob == NULL)
+ return NULL;
+
+ if ((nob->read(nob, p->data[i].size, p->of + p->data[i].offset)) != 0) {
+ nob->del(nob); /* Failed, so destroy it */
+ return NULL;
+ }
+ p->data[i].objp = nob;
+ return nob;
+}
+
+/* Read the tag element data of the first matching, and return a pointer to the object */
+/* Returns NULL if error - icc->errc will contain: */
+/* 2 if not found */
+/* Doesn't read uknown type tags */
+static icmBase *icc_read_tag(
+ icc *p,
+ icTagSignature sig /* Tag signature - may be unknown */
+) {
+ unsigned int i;
+
+ /* Search for signature */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].sig == sig) /* Found it */
+ break;
+ }
+ if (i >= p->count) {
+ sprintf(p->err,"icc_read_tag: Tag '%s' not found",string_TagSignature(sig));
+ p->errc = 2;
+ return NULL;
+ }
+
+ /* Let read_tag_ix do all the work */
+ return icc_read_tag_ix(p, i, 0);
+}
+
+/* Read the tag element data of the first matching, and return a pointer to the object */
+/* Returns NULL if error.
+/* Returns an icmSigUnknownType object if the tag type isn't handled by a specific object. */
+/* NOTE: we don't handle tag duplication - you'll always get the first in the file. */
+static icmBase *icc_read_tag_any(
+ icc *p,
+ icTagSignature sig /* Tag signature - may be unknown */
+) {
+ unsigned int i;
+
+ /* Search for signature */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].sig == sig) /* Found it */
+ break;
+ }
+ if (i >= p->count) {
+ sprintf(p->err,"icc_read_tag: Tag '%s' not found",string_TagSignature(sig));
+ p->errc = 2;
+ return NULL;
+ }
+
+ /* Let read_tag_ix do all the work */
+ return icc_read_tag_ix(p, i, 1);
+}
+
+/* Rename a tag signature */
+static int icc_rename_tag(
+ icc *p,
+ icTagSignature sig, /* Existing Tag signature - may be unknown */
+ icTagSignature sigNew /* New Tag signature - may be unknown */
+) {
+ unsigned int k;
+ int i, j, ok = 1;
+
+ /* Search for signature */
+ for (k = 0; k < p->count; k++) {
+ if (p->data[k].sig == sig) /* Found it */
+ break;
+ }
+ if (k >= p->count) {
+ sprintf(p->err,"icc_rename_tag: Tag '%s' not found",string_TagSignature(sig));
+ return p->errc = 2;
+ }
+
+ /* Check that a known new signature has an acceptable type */
+ for (i = 0; sigtypetable[i].sig != icMaxEnumType; i++) {
+ if (sigtypetable[i].sig == sigNew) { /* recognized signature */
+ ok = 0;
+ for (j = 0; sigtypetable[i].ttypes[j] != icMaxEnumType; j++) {
+ if (sigtypetable[i].ttypes[j] == p->data[k].ttype) /* recognized type */
+ ok = 1;
+ }
+ break;
+ }
+ }
+
+ if (!ok) {
+ sprintf(p->err,"icc_rename_tag: wrong signature for tag type");
+ p->errc = 1;
+ return p->errc;
+ }
+
+ /* change its signature */
+ p->data[k].sig = sigNew;
+
+ return 0;
+}
+
+/* Unread a specific tag, and free the underlying tag type data */
+/* if this was the last reference to it. */
+/* (This is an internal function) */
+/* Returns non-zero on error: */
+/* tag not found - icc->errc will contain 2 */
+/* tag not read - icc->errc will contain 2 */
+static int icc_unread_tag_ix(
+ icc *p,
+ unsigned int i /* Index from 0.. p->count-1 */
+) {
+ if (i >= p->count) {
+ sprintf(p->err,"icc_unread_tag_ix: index %d is out of range",i);
+ return p->errc = 2;
+ }
+
+ /* See if it's been read */
+ if (p->data[i].objp == NULL) {
+ sprintf(p->err,"icc_unread_tag: Tag '%s' not currently loaded",string_TagSignature(p->data[i].sig));
+ return p->errc = 2;
+ }
+
+ if (--(p->data[i].objp->refcount) == 0) /* decrement reference count */
+ (p->data[i].objp->del)(p->data[i].objp); /* Last reference */
+ p->data[i].objp = NULL;
+
+ return 0;
+}
+
+/* Unread the tag, and free the underlying tag type */
+/* if this was the last reference to it. */
+/* Returns non-zero on error: */
+/* tag not found - icc->errc will contain 2 */
+/* tag not read - icc->errc will contain 2 */
+/* NOTE: we don't handle tag duplication - you'll always get the first in the file */
+static int icc_unread_tag(
+ icc *p,
+ icTagSignature sig /* Tag signature - may be unknown */
+) {
+ unsigned int i;
+
+ /* Search for signature */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].sig == sig) /* Found it */
+ break;
+ }
+ if (i >= p->count) {
+ sprintf(p->err,"icc_unread_tag: Tag '%s' not found",string_TagSignature(sig));
+ return p->errc = 2;
+ }
+
+ return icc_unread_tag(p, i);
+}
+
+/* Delete the tag, and free the underlying tag type, */
+/* if this was the last reference to it. */
+/* Note this finds the first tag with a matching signature */
+/* Returns non-zero on error: */
+/* tag not found - icc->errc will contain 2 */
+static int icc_delete_tag_ix(
+ icc *p,
+ unsigned int i /* Index from 0.. p->count-1 */
+) {
+ if (i >= p->count) {
+ sprintf(p->err,"icc_delete_tag_ix: index %d of range",i);
+ return p->errc = 2;
+ }
+
+ /* If it's been read into memory, decrement the reference count */
+ if (p->data[i].objp != NULL) {
+ if (--(p->data[i].objp->refcount) == 0) /* decrement reference count */
+ (p->data[i].objp->del)(p->data[i].objp); /* Last reference */
+ p->data[i].objp = NULL;
+ }
+
+ /* Now remove it from the tag list */
+ for (; i < (p->count-1); i++)
+ p->data[i] = p->data[i+1]; /* Copy the structure down */
+
+ p->count--; /* One less tag in list */
+
+ return 0;
+}
+
+/* Delete the tag, and free the underlying tag type, */
+/* if this was the last reference to it. */
+/* Note this finds the first tag with a matching signature. */
+/* Returns non-zero on error: */
+/* tag not found - icc->errc will contain 2 */
+static int icc_delete_tag(
+ icc *p,
+ icTagSignature sig /* Tag signature - may be unknown */
+) {
+ unsigned int i;
+
+ /* Search for signature */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].sig == sig) /* Found it */
+ break;
+ }
+ if (i >= p->count) {
+ sprintf(p->err,"icc_delete_tag: Tag '%s' not found",string_TagSignature(sig));
+ return p->errc = 2;
+ }
+
+ return icc_delete_tag_ix(p, i);
+}
+
+/* Read all the tags into memory, including unknown types. */
+/* Returns non-zero on error. */
+static int icc_read_all_tags(
+ icc *p
+) {
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++) { /* For all the tag element data */
+ if (icc_read_tag_ix(p, i, 1) == NULL)
+ return p->errc;
+ }
+ return 0;
+}
+
+
+static void icc_dump(
+ icc *p,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ unsigned int i;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"icc:\n");
+
+ /* Dump the header */
+ if (p->header != NULL)
+ p->header->dump(p->header,op,verb);
+
+ /* Dump all the tag elements */
+ for (i = 0; i < p->count; i++) { /* For all the tag element data */
+ icmBase *ob;
+ int tr;
+ op->gprintf(op,"tag %d:\n",i);
+ op->gprintf(op," sig %s\n",tag2str(p->data[i].sig));
+ op->gprintf(op," type %s\n",tag2str(p->data[i].ttype));
+ op->gprintf(op," offset %d\n", p->data[i].offset);
+ op->gprintf(op," size %d\n", p->data[i].size);
+ tr = 0;
+ if (p->data[i].objp == NULL) {
+ /* The object is not loaded, so load it then free it */
+ if (icc_read_tag_ix(p, i, 1) == NULL)
+ op->gprintf(op,"Unable to read: %d, %s\n",p->errc,p->err);
+ tr = 1;
+ }
+ if ((ob = p->data[i].objp) != NULL) {
+ /* op->gprintf(op," refcount %d\n", ob->refcount); */
+ ob->dump(ob,op,verb-1);
+
+ if (tr != 0) { /* Cleanup if temporary */
+ icc_unread_tag_ix(p, i);
+ }
+ }
+ op->gprintf(op,"\n");
+ }
+}
+
+static void icc_delete(
+ icc *p
+) {
+ unsigned int i;
+ icmAlloc *al = p->al;
+ int del_al = p->del_al;
+
+ /* Free up the header */
+ if (p->header != NULL)
+ (p->header->del)(p->header);
+
+ /* Free up the tag data objects */
+ if (p->data != NULL) {
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].objp != NULL) {
+ if (--(p->data[i].objp->refcount) == 0) /* decrement reference count */
+ (p->data[i].objp->del)(p->data[i].objp); /* Last reference */
+ p->data[i].objp = NULL;
+ }
+ }
+ /* Free tag table */
+ al->free(al, p->data);
+ }
+
+ /* We are responsible for deleting the file object */
+ if (p->del_fp && p->fp != NULL)
+ p->fp->del(p->fp);
+
+ /* This object */
+ al->free(al, p);
+
+ if (del_al) /* We are responsible for deleting allocator */
+ al->del(al);
+}
+
+/* ================================================== */
+/* Lut Color normalizing and de-normalizing functions */
+
+/* As a rule, I am representing Lut in memory as values in machine form as real */
+/* numbers in the range 0.0 - 1.0. For many color spaces (ie. RGB, Gray, */
+/* hsv, hls, cmyk and other device coords), this is entirely appropriate. */
+/* For CIE based spaces though, this is not correct, since (I assume!) the binary */
+/* representation will be consistent with the encoding in Annex A, page 74 */
+/* of the standard. Note that the standard doesn't specify the encoding of */
+/* many color spaces (ie. Yuv, Yxy etc.), and is unclear about PCS. */
+
+/* The following functions convert to and from the CIE base spaces */
+/* and the real Lut input/output values. These are used to convert real color */
+/* space values into/out of the raw lut 0.0-1.0 representation (which subsequently */
+/* get converted to ICC integer values in the obvious way as a mapping to 0 .. 2^n-1). */
+
+/* This is used internally to support the Lut->lookup() function, */
+/* and can also be used by someone writing a Lut based profile to determine */
+/* the colorspace range that the input lut indexes cover, as well */
+/* as processing the output luts values into normalized form ready */
+/* for writing. */
+
+/* These functions should be accessed by calling icc.getNormFuncs() */
+
+/* - - - - - - - - - - - - - - - - */
+/* According to 6.5.5 and 6.5.6 of the spec., */
+/* XYZ index values are represented the same as their table */
+/* values, ie. as a u1.15 representation, with a value */
+/* range from 0.0 -> 1.999969482422 */
+
+/* Convert Lut index/value to XYZ coord. */
+static void Lut_Lut2XYZ(double *out, double *in) {
+ out[0] = in[0] * (1.0 + 32767.0/32768); /* X */
+ out[1] = in[1] * (1.0 + 32767.0/32768); /* Y */
+ out[2] = in[2] * (1.0 + 32767.0/32768); /* Z */
+}
+
+/* Convert XYZ coord to Lut index/value. */
+static void Lut_XYZ2Lut(double *out, double *in) {
+ out[0] = in[0] * (1.0/(1.0 + 32767.0/32768));
+ out[1] = in[1] * (1.0/(1.0 + 32767.0/32768));
+ out[2] = in[2] * (1.0/(1.0 + 32767.0/32768));
+}
+
+/* Convert Lut index/value to Y coord. */
+static void Lut_Lut2Y(double *out, double *in) {
+ out[0] = in[0] * (1.0 + 32767.0/32768); /* Y */
+}
+
+/* Convert Y coord to Lut index/value. */
+static void Lut_Y2Lut(double *out, double *in) {
+ out[0] = in[0] * (1.0/(1.0 + 32767.0/32768));
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Convert 8 bit Lab to Lut numbers */
+/* Annex A specifies 8 and 16 bit encoding, but is */
+/* silent on the Lut index normalization. */
+/* Following Michael Bourgoin's 1998 SIGGRAPH course comment on this, */
+/* we assume here that the index encoding is the same as the */
+/* value encoding. */
+
+/* Convert Lut8 table index/value to Lab */
+static void Lut_Lut2Lab_8(double *out, double *in) {
+ out[0] = in[0] * 100.0; /* L */
+ out[1] = (in[1] * 255.0) - 128.0; /* a */
+ out[2] = (in[2] * 255.0) - 128.0; /* b */
+}
+
+/* Convert Lab to Lut8 table index/value */
+static void Lut_Lab2Lut_8(double *out, double *in) {
+ out[0] = in[0] * 1.0/100.0; /* L */
+ out[1] = (in[1] + 128.0) * 1.0/255.0; /* a */
+ out[2] = (in[2] + 128.0) * 1.0/255.0; /* b */
+}
+
+/* Convert Lut8 table index/value to L */
+static void Lut_Lut2L_8(double *out, double *in) {
+ out[0] = in[0] * 100.0; /* L */
+}
+
+/* Convert L to Lut8 table index/value */
+static void Lut_L2Lut_8(double *out, double *in) {
+ out[0] = in[0] * 1.0/100.0; /* L */
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Convert 16 bit Lab to Lut numbers, V2 */
+
+/* Convert Lut16 table index/value to Lab */
+static void Lut_Lut2LabV2_16(double *out, double *in) {
+ out[0] = in[0] * (100.0 * 65535.0)/65280.0; /* L */
+ out[1] = (in[1] * (255.0 * 65535.0)/65280) - 128.0; /* a */
+ out[2] = (in[2] * (255.0 * 65535.0)/65280) - 128.0; /* b */
+}
+
+/* Convert Lab to Lut16 table index/value */
+static void Lut_Lab2LutV2_16(double *out, double *in) {
+ out[0] = in[0] * 65280.0/(100.0 * 65535.0); /* L */
+ out[1] = (in[1] + 128.0) * 65280.0/(255.0 * 65535.0); /* a */
+ out[2] = (in[2] + 128.0) * 65280.0/(255.0 * 65535.0); /* b */
+}
+
+/* Convert Lut16 table index/value to L */
+static void Lut_Lut2LV2_16(double *out, double *in) {
+ out[0] = in[0] * (100.0 * 65535.0)/65280.0; /* L */
+}
+
+/* Convert Lab to Lut16 table index/value */
+static void Lut_L2LutV2_16(double *out, double *in) {
+ out[0] = in[0] * 65280.0/(100.0 * 65535.0); /* L */
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Convert 16 bit Lab to Lut numbers, V4 */
+
+/* Convert Lut16 table index/value to Lab */
+static void Lut_Lut2LabV4_16(double *out, double *in) {
+ out[0] = in[0] * 100.0; /* L */
+ out[1] = (in[1] * 255.0) - 128.0; /* a */
+ out[2] = (in[2] * 255.0) - 128.0; /* b */
+}
+
+/* Convert Lab to Lut16 table index/value */
+static void Lut_Lab2LutV4_16(double *out, double *in) {
+ out[0] = in[0] * 1.0/100.0; /* L */
+ out[1] = (in[1] + 128.0) * 1.0/255.0; /* a */
+ out[2] = (in[2] + 128.0) * 1.0/255.0; /* b */
+}
+
+/* Convert Lut16 table index/value to L */
+static void Lut_Lut2LV4_16(double *out, double *in) {
+ out[0] = in[0] * 100.0; /* L */
+}
+
+/* Convert L to Lut16 table index/value */
+static void Lut_L2LutV4_16(double *out, double *in) {
+ out[0] = in[0] * 1.0/100.0; /* L */
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Convert Luv to Lut number */
+/* This data normalization is taken from Apples */
+/* Colorsync specification. */
+/* As per other color spaces, we assume that the index */
+/* normalization is the same as the data normalization. */
+
+/* Convert Lut table index/value to Luv */
+static void Lut_Lut2Luv(double *out, double *in) {
+ out[0] = in[0] * 100.0; /* L */
+ out[1] = (in[1] * 65535.0/256.0) - 128.0; /* u */
+ out[2] = (in[2] * 65535.0/256.0) - 128.0; /* v */
+}
+
+/* Convert Luv to Lut table index/value */
+static void Lut_Luv2Lut(double *out, double *in) {
+ out[0] = in[0] * 1.0/100.0; /* L */
+ out[1] = (in[1] + 128.0) * 256.0/65535.0; /* u */
+ out[2] = (in[2] + 128.0) * 256.0/65535.0; /* v */
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Default N component conversions */
+static void Lut_N(double *out, double *in, int nc) {
+ for (--nc; nc >= 0; nc--)
+ out[nc] = in[nc];
+}
+
+/* 1 */
+static void Lut_1(double *out, double *in) {
+ out[0] = in[0];
+}
+
+/* 2 */
+static void Lut_2(double *out, double *in) {
+ out[0] = in[0];
+ out[1] = in[1];
+}
+
+/* 3 */
+static void Lut_3(double *out, double *in) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+}
+
+/* 4 */
+static void Lut_4(double *out, double *in) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3];
+}
+
+/* 5 */
+static void Lut_5(double *out, double *in) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3];
+ out[4] = in[4];
+}
+
+/* 6 */
+static void Lut_6(double *out, double *in) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3];
+ out[4] = in[4];
+ out[5] = in[5];
+}
+
+/* 7 */
+static void Lut_7(double *out, double *in) {
+ Lut_N(out, in, 7);
+}
+
+/* 8 */
+static void Lut_8(double *out, double *in) {
+ Lut_N(out, in, 8);
+}
+
+/* 9 */
+static void Lut_9(double *out, double *in) {
+ Lut_N(out, in, 9);
+}
+
+/* 10 */
+static void Lut_10(double *out, double *in) {
+ Lut_N(out, in, 10);
+}
+
+/* 11 */
+static void Lut_11(double *out, double *in) {
+ Lut_N(out, in, 11);
+}
+
+/* 12 */
+static void Lut_12(double *out, double *in) {
+ Lut_N(out, in, 12);
+}
+
+/* 13 */
+static void Lut_13(double *out, double *in) {
+ Lut_N(out, in, 13);
+}
+
+/* 14 */
+static void Lut_14(double *out, double *in) {
+ Lut_N(out, in, 14);
+}
+
+/* 15 */
+static void Lut_15(double *out, double *in) {
+ Lut_N(out, in, 15);
+}
+
+/* Function table - match conversions to color spaces. */
+/* Anything not here, we don't know how to convert. */
+/* (ie. YCbCr) */
+static struct {
+ icColorSpaceSignature csig;
+ void (*fromLut)(double *out, double *in); /* from Lut index/entry */
+ void (*toLut)(double *out, double *in); /* to Lut index/entry */
+} colnormtable[] = {
+ {icSigXYZData, Lut_Lut2XYZ, Lut_XYZ2Lut },
+ {icmSigYData, Lut_Lut2Y, Lut_Y2Lut },
+ {icmSigLab8Data, Lut_Lut2Lab_8, Lut_Lab2Lut_8 },
+ {icmSigLabV2Data, Lut_Lut2LabV2_16, Lut_Lab2LutV2_16 },
+ {icmSigLabV4Data, Lut_Lut2LabV4_16, Lut_Lab2LutV4_16 },
+ {icmSigL8Data, Lut_Lut2L_8, Lut_L2Lut_8 },
+ {icmSigLV2Data, Lut_Lut2LV2_16, Lut_L2LutV2_16 },
+ {icmSigLV4Data, Lut_Lut2LV4_16, Lut_L2LutV4_16 },
+ {icSigLuvData, Lut_Lut2Luv, Lut_Luv2Lut },
+ {icSigYxyData, Lut_3, Lut_3 },
+ {icSigRgbData, Lut_3, Lut_3 },
+ {icSigGrayData, Lut_1, Lut_1 },
+ {icSigHsvData, Lut_3, Lut_3 },
+ {icSigHlsData, Lut_3, Lut_3 },
+ {icSigCmykData, Lut_4, Lut_4 },
+ {icSigCmyData, Lut_3, Lut_3 },
+ {icSigMch6Data, Lut_6, Lut_6 },
+ {icSig2colorData, Lut_2, Lut_2 },
+ {icSig3colorData, Lut_3, Lut_3 },
+ {icSig4colorData, Lut_4, Lut_4 },
+ {icSig5colorData, Lut_5, Lut_5 },
+ {icSig6colorData, Lut_6, Lut_6 },
+ {icSig7colorData, Lut_7, Lut_7 },
+ {icSigMch5Data, Lut_5, Lut_5 },
+ {icSigMch6Data, Lut_6, Lut_6 },
+ {icSigMch7Data, Lut_7, Lut_7 },
+ {icSigMch8Data, Lut_8, Lut_8 },
+ {icSig8colorData, Lut_8, Lut_8 },
+ {icSig9colorData, Lut_9, Lut_9 },
+ {icSig10colorData, Lut_10, Lut_10 },
+ {icSig11colorData, Lut_11, Lut_11 },
+ {icSig12colorData, Lut_12, Lut_12 },
+ {icSig13colorData, Lut_13, Lut_13 },
+ {icSig14colorData, Lut_14, Lut_14 },
+ {icSig15colorData, Lut_15, Lut_15 },
+ {icMaxEnumData, NULL, NULL }
+};
+
+/*
+ Legacy Lab encoding:
+
+ The V4 specificatins are misleading on this, since they assume in this
+ instance that all devices spaces, however labeled, have no defined
+ ICC encoding. The end result is simple enough though:
+
+ ICC V2 Lab encoding should be used in all PCS encodings in
+ a icSigLut16Type or icSigNamedColor2Type tag, and can be used
+ for Lab encoding in device spaces for these tags.
+
+ ICC V4 Lab encoding should be used in all PCS encodings in
+ all other situations, and can be used for Lab encoding in
+ device spaces for all other situtaions.
+
+ [ Since the ICC spec. doesn't cover device spaces labeled as Lab,
+ these are ripe for mis-matches between different implementations.]
+
+ This logic has yet to be fully implemented here.
+*/
+
+/* Find appropriate conversion functions from the normalised */
+/* Lut data range 0.0 - 1.0 to/from a given colorspace value, */
+/* given the color space and Lut type. */
+/* Return 0 on success, 1 on match failure. */
+/* NOTE: doesn't set error value, message etc.! */
+static int getNormFunc(
+ icc *icp,
+// icProfileClassSignature psig, /* Profile signature to use */
+ icColorSpaceSignature csig, /* Colorspace to use. */
+// int lutin, /* 0 if this is for a icSigLut16Type input, nz for output */
+// icTagSignature tagSig, /* Tag signature involved (AtoB or B2A etc.) */
+ icTagTypeSignature tagType, /* icSigLut8Type or icSigLut16Type or V4 lut */
+ icmNormFlag flag, /* icmFromLuti, icmFromLutv or icmToLuti, icmToLutv */
+ void (**nfunc)(double *out, double *in)
+) {
+ int i;
+ if (tagType == icSigLut8Type && csig == icSigLabData) {
+ csig = icmSigLab8Data;
+ }
+ if (csig == icSigLabData) {
+ if (tagType == icSigLut16Type) /* Lut16 retains legacy encoding */
+ csig = icmSigLabV2Data;
+ else { /* Other tag types use version specific encoding */
+ if (icp->ver)
+ csig = icmSigLabV4Data;
+ else
+ csig = icmSigLabV2Data;
+ }
+ }
+
+ for (i = 0; colnormtable[i].csig != icMaxEnumData; i++) {
+ if (colnormtable[i].csig == csig)
+ break; /* Found it */
+ }
+ if (colnormtable[i].csig == icMaxEnumData) { /* Oops */
+ *nfunc = NULL;
+ return 1;
+ }
+
+ if (flag == icmFromLuti || flag == icmFromLutv) { /* Table index/value decoding functions */
+ *nfunc = colnormtable[i].fromLut;
+ return 0;
+ } else if (flag == icmToLuti || flag == icmToLutv) { /* Table index/value encoding functions */
+ *nfunc = colnormtable[i].toLut;
+ return 0;
+ }
+ *nfunc = NULL;
+ return 1;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Colorspace ranges - used instead of norm/denorm by Mono, Matrix and */
+/* override PCS */
+
+/* Function table - match ranges to color spaces. */
+/* Anything not here, we don't know how to convert. */
+/* (ie. YCbCr) */
+/* Hmm. we're not handling Lab8 properly ?? ~~~8888 */
+static struct {
+ icColorSpaceSignature csig;
+ int same; /* Non zero if first entry applies to all channels */
+ double min[3]; /* Minimum value for this colorspace */
+ double max[3]; /* Maximum value for this colorspace */
+} colorrangetable[] = {
+ {icSigXYZData, 1, { 0.0 } , { 1.0 + 32767.0/32768.0 } },
+ {icmSigLab8Data, 0, { 0.0, -128.0, -128.0 }, { 100.0, 127.0, 127.0 } },
+ {icmSigLabV2Data, 0, { 0.0, -128.0, -128.0 },
+ { 100.0 + 25500.0/65280.0, 127.0 + 255.0/256.0, 127.0 + 255.0/256.0 } },
+ {icmSigLabV4Data, 0, { 0.0, -128.0, -128.0 }, { 100.0, 127.0, 127.0 } },
+ {icmSigYData, 1, { 0.0 }, { 1.0 + 32767.0/32768.0 } },
+ {icmSigL8Data, 1, { 0.0 }, { 100.0 } },
+ {icmSigLV2Data, 1, { 0.0 }, { 100.0 + 25500.0/65280.0 } },
+ {icmSigLV4Data, 1, { 0.0 }, { 100.0 } },
+ {icSigLuvData, 0, { 0.0, -128.0, -128.0 },
+ { 100.0, 127.0 + 255.0/256.0, 127.0 + 255.0/256.0 } },
+ {icSigYCbCrData, 1, { 0.0 }, { 1.0 } }, /* ??? */
+ {icSigYxyData, 1, { 0.0 }, { 1.0 } }, /* ??? */
+ {icSigRgbData, 1, { 0.0 }, { 1.0 } },
+ {icSigGrayData, 1, { 0.0 }, { 1.0 } },
+ {icSigHsvData, 1, { 0.0 }, { 1.0 } },
+ {icSigHlsData, 1, { 0.0 }, { 1.0 } },
+ {icSigCmykData, 1, { 0.0 }, { 1.0 } },
+ {icSigCmyData, 1, { 0.0 }, { 1.0 } },
+ {icSigMch6Data, 1, { 0.0 }, { 1.0 } },
+ {icSig2colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig3colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig4colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig5colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig6colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig7colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig8colorData, 1, { 0.0 }, { 1.0 } },
+ {icSigMch5Data, 1, { 0.0 }, { 1.0 } },
+ {icSigMch6Data, 1, { 0.0 }, { 1.0 } },
+ {icSigMch7Data, 1, { 0.0 }, { 1.0 } },
+ {icSigMch8Data, 1, { 0.0 }, { 1.0 } },
+ {icSig9colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig10colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig11colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig12colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig13colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig14colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig15colorData, 1, { 0.0 }, { 1.0 } },
+ {icMaxEnumData, 1, { 0.0 }, { 1.0 } }
+};
+
+/* Find appropriate typical encoding ranges for a */
+/* colorspace given the color space. */
+/* Return 0 on success, 1 on match failure */
+static int getRange(
+ icc *icp,
+// icProfileClassSignature psig, /* Profile signature to use */
+ icColorSpaceSignature csig, /* Colorspace to use. */
+// int lutin, /* 0 if this is for a icSigLut16Type input, nz for output */
+// icTagSignature tagSig, /* Tag signature involved (AtoB or B2A etc.) */
+ icTagTypeSignature tagType, /* icSigLut8Type or icSigLut16Type or V4 lut */
+ double *min, double *max /* Return range values */
+) {
+ int i, e, ee;
+
+ if (tagType == icSigLut8Type && csig == icSigLabData) {
+ csig = icmSigLab8Data;
+ }
+ if (csig == icSigLabData) {
+ if (tagType == icSigLut16Type) /* Lut16 retains legacy encoding */
+ csig = icmSigLabV2Data;
+ else { /* Other tag types use version specific encoding */
+ if (icp->ver)
+ csig = icmSigLabV4Data;
+ else
+ csig = icmSigLabV2Data;
+ }
+ }
+
+ for (i = 0; colorrangetable[i].csig != icMaxEnumData; i++) {
+ if (colorrangetable[i].csig == csig)
+ break; /* Found it */
+ }
+ if (colorrangetable[i].csig == icMaxEnumData) { /* Oops */
+ return 1;
+ }
+ ee = number_ColorSpaceSignature(csig); /* Get number of components */
+
+ if (colorrangetable[i].same) { /* All channels are the same */
+ for (e = 0; e < ee; e++) {
+ if (min != NULL)
+ min[e] = colorrangetable[i].min[0];
+ if (max != NULL)
+ max[e] = colorrangetable[i].max[0];
+ }
+ } else {
+ for (e = 0; e < ee; e++) {
+ if (min != NULL)
+ min[e] = colorrangetable[i].min[e];
+ if (max != NULL)
+ max[e] = colorrangetable[i].max[e];
+ }
+ }
+ return 0;
+}
+
+/* ============================================= */
+/* Misc. support functions. */
+
+/* Clamp a 3 vector to be +ve */
+void icmClamp3(double out[3], double in[3]) {
+ int i;
+ for (i = 0; i < 3; i++)
+ out[i] = in[i] < 0.0 ? 0.0 : in[i];
+}
+
+/* Add two 3 vectors */
+void icmAdd3(double out[3], double in1[3], double in2[3]) {
+ out[0] = in1[0] + in2[0];
+ out[1] = in1[1] + in2[1];
+ out[2] = in1[2] + in2[2];
+}
+
+/* Subtract two 3 vectors, out = in1 - in2 */
+void icmSub3(double out[3], double in1[3], double in2[3]) {
+ out[0] = in1[0] - in2[0];
+ out[1] = in1[1] - in2[1];
+ out[2] = in1[2] - in2[2];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Set a 3x3 matrix to unity */
+void icmSetUnity3x3(double mat[3][3]) {
+ int i, j;
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++) {
+ if (i == j)
+ mat[j][i] = 1.0;
+ else
+ mat[j][i] = 0.0;
+ }
+ }
+
+}
+
+/* Copy a 3x3 transform matrix */
+void icmCpy3x3(double dst[3][3], double src[3][3]) {
+ int i, j;
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++)
+ dst[j][i] = src[j][i];
+ }
+}
+
+/* Scale each element of a 3x3 transform matrix */
+void icmScale3x3(double dst[3][3], double src[3][3], double scale) {
+ int i, j;
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++)
+ dst[j][i] = src[j][i] * scale;
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/*
+
+ mat in out
+
+[ ] [] []
+[ ] * [] => []
+[ ] [] []
+
+ */
+
+/* Multiply 3 array by 3x3 transform matrix */
+void icmMulBy3x3(double out[3], double mat[3][3], double in[3]) {
+ double tt[3];
+
+ tt[0] = mat[0][0] * in[0] + mat[0][1] * in[1] + mat[0][2] * in[2];
+ tt[1] = mat[1][0] * in[0] + mat[1][1] * in[1] + mat[1][2] * in[2];
+ tt[2] = mat[2][0] * in[0] + mat[2][1] * in[1] + mat[2][2] * in[2];
+
+ out[0] = tt[0];
+ out[1] = tt[1];
+ out[2] = tt[2];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Add one 3x3 to another */
+/* dst = src1 + src2 */
+void icmAdd3x3(double dst[3][3], double src1[3][3], double src2[3][3]) {
+ int i, j;
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++)
+ dst[j][i] = src1[j][i] + src2[j][i];
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Tensor product. Multiply two 3 vectors to form a 3x3 matrix */
+/* src1[] forms the colums, and src2[] forms the rows in the result */
+void icmTensMul3(double dst[3][3], double src1[3], double src2[3]) {
+ int i, j;
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++)
+ dst[j][i] = src1[j] * src2[i];
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Multiply one 3x3 with another */
+/* dst = src * dst */
+void icmMul3x3(double dst[3][3], double src[3][3]) {
+ int i, j, k;
+ double td[3][3]; /* Temporary dest */
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++) {
+ double tt = 0.0;
+ for (k = 0; k < 3; k++)
+ tt += src[j][k] * dst[k][i];
+ td[j][i] = tt;
+ }
+ }
+
+ /* Copy result out */
+ for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ dst[j][i] = td[j][i];
+}
+
+/* Multiply one 3x3 with another #2 */
+/* dst = src1 * src2 */
+void icmMul3x3_2(double dst[3][3], double src1[3][3], double src2[3][3]) {
+ int i, j, k;
+ double td[3][3]; /* Temporary dest */
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++) {
+ double tt = 0.0;
+ for (k = 0; k < 3; k++)
+ tt += src1[j][k] * src2[k][i];
+ td[j][i] = tt;
+ }
+ }
+
+ /* Copy result out */
+ for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ dst[j][i] = td[j][i];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/*
+ Matrix Inversion
+ by Richard Carling
+ from "Graphics Gems", Academic Press, 1990
+*/
+
+/*
+ * adjoint( original_matrix, inverse_matrix )
+ *
+ * calculate the adjoint of a 3x3 matrix
+ *
+ * Let a denote the minor determinant of matrix A obtained by
+ * ij
+ *
+ * deleting the ith row and jth column from A.
+ *
+ * i+j
+ * Let b = (-1) a
+ * ij ji
+ *
+ * The matrix B = (b ) is the adjoint of A
+ * ij
+ */
+
+#define det2x2(a, b, c, d) (a * d - b * c)
+
+static void adjoint(
+double out[3][3],
+double in[3][3]
+) {
+ double a1, a2, a3, b1, b2, b3, c1, c2, c3;
+
+ /* assign to individual variable names to aid */
+ /* selecting correct values */
+
+ a1 = in[0][0]; b1 = in[0][1]; c1 = in[0][2];
+ a2 = in[1][0]; b2 = in[1][1]; c2 = in[1][2];
+ a3 = in[2][0]; b3 = in[2][1]; c3 = in[2][2];
+
+ /* row column labeling reversed since we transpose rows & columns */
+
+ out[0][0] = det2x2(b2, b3, c2, c3);
+ out[1][0] = - det2x2(a2, a3, c2, c3);
+ out[2][0] = det2x2(a2, a3, b2, b3);
+
+ out[0][1] = - det2x2(b1, b3, c1, c3);
+ out[1][1] = det2x2(a1, a3, c1, c3);
+ out[2][1] = - det2x2(a1, a3, b1, b3);
+
+ out[0][2] = det2x2(b1, b2, c1, c2);
+ out[1][2] = - det2x2(a1, a2, c1, c2);
+ out[2][2] = det2x2(a1, a2, b1, b2);
+}
+
+/*
+ * double = icmDet3x3( a1, a2, a3, b1, b2, b3, c1, c2, c3 )
+ *
+ * calculate the determinant of a 3x3 matrix
+ * in the form
+ *
+ * | a1, b1, c1 |
+ * | a2, b2, c2 |
+ * | a3, b3, c3 |
+ */
+
+double icmDet3x3(double in[3][3]) {
+ double a1, a2, a3, b1, b2, b3, c1, c2, c3;
+ double ans;
+
+ a1 = in[0][0]; b1 = in[0][1]; c1 = in[0][2];
+ a2 = in[1][0]; b2 = in[1][1]; c2 = in[1][2];
+ a3 = in[2][0]; b3 = in[2][1]; c3 = in[2][2];
+
+ ans = a1 * det2x2(b2, b3, c2, c3)
+ - b1 * det2x2(a2, a3, c2, c3)
+ + c1 * det2x2(a2, a3, b2, b3);
+ return ans;
+}
+
+#define ICM_SMALL_NUMBER 1.e-8
+/*
+ * inverse( original_matrix, inverse_matrix )
+ *
+ * calculate the inverse of a 4x4 matrix
+ *
+ * -1
+ * A = ___1__ adjoint A
+ * det A
+ */
+
+/* Return non-zero if not invertable */
+int icmInverse3x3(
+double out[3][3],
+double in[3][3]
+) {
+ int i, j;
+ double det;
+
+ /* calculate the 3x3 determinant
+ * if the determinant is zero,
+ * then the inverse matrix is not unique.
+ */
+ det = icmDet3x3(in);
+
+ if ( fabs(det) < ICM_SMALL_NUMBER)
+ return 1;
+
+ /* calculate the adjoint matrix */
+ adjoint(out, in);
+
+ /* scale the adjoint matrix to get the inverse */
+ for (i = 0; i < 3; i++)
+ for(j = 0; j < 3; j++)
+ out[i][j] /= det;
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Transpose a 3x3 matrix */
+void icmTranspose3x3(double out[3][3], double in[3][3]) {
+ int i, j;
+ if (out != in) {
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ out[i][j] = in[j][i];
+ } else {
+ double tt[3][3];
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ tt[i][j] = in[j][i];
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ out[i][j] = tt[i][j];
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Compute the dot product of two 3 vectors */
+double icmDot3(double in1[3], double in2[3]) {
+ return in1[0] * in2[0] + in1[1] * in2[1] + in1[2] * in2[2];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Compute the cross product of two 3D vectors, out = in1 x in2 */
+void icmCross3(double out[3], double in1[3], double in2[3]) {
+ double tt[3];
+ tt[0] = (in1[1] * in2[2]) - (in1[2] * in2[1]);
+ tt[1] = (in1[2] * in2[0]) - (in1[0] * in2[2]);
+ tt[2] = (in1[0] * in2[1]) - (in1[1] * in2[0]);
+ out[0] = tt[0];
+ out[1] = tt[1];
+ out[2] = tt[2];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Compute the norm (length) squared of a 3 vector */
+double icmNorm3sq(double in[3]) {
+ return in[0] * in[0] + in[1] * in[1] + in[2] * in[2];
+}
+
+/* Compute the norm (length) of a 3 vector */
+double icmNorm3(double in[3]) {
+ return sqrt(in[0] * in[0] + in[1] * in[1] + in[2] * in[2]);
+}
+
+/* Scale a 3 vector by the given ratio */
+void icmScale3(double out[3], double in[3], double rat) {
+ out[0] = in[0] * rat;
+ out[1] = in[1] * rat;
+ out[2] = in[2] * rat;
+}
+
+/* Compute a blend between in0 and in1 */
+void icmBlend3(double out[3], double in0[3], double in1[3], double bf) {
+ out[0] = (1.0 - bf) * in0[0] + bf * in1[0];
+ out[1] = (1.0 - bf) * in0[1] + bf * in1[1];
+ out[2] = (1.0 - bf) * in0[2] + bf * in1[2];
+}
+
+/* Normalise a 3 vector to the given length. Return nz if not normalisable */
+int icmNormalize3(double out[3], double in[3], double len) {
+ double tt = sqrt(in[0] * in[0] + in[1] * in[1] + in[2] * in[2]);
+
+ if (tt < ICM_SMALL_NUMBER)
+ return 1;
+ tt = len/tt;
+ out[0] = in[0] * tt;
+ out[1] = in[1] * tt;
+ out[2] = in[2] * tt;
+ return 0;
+}
+
+/* Compute the norm (length) squared of a vector define by two points */
+double icmNorm33sq(double in1[3], double in0[3]) {
+ int j;
+ double rv;
+ for (rv = 0.0, j = 0; j < 3; j++) {
+ double tt = in1[j] - in0[j];
+ rv += tt * tt;
+ }
+ return rv;
+}
+
+/* Compute the norm (length) of a vector define by two points */
+double icmNorm33(double in1[3], double in0[3]) {
+ int j;
+ double rv;
+ for (rv = 0.0, j = 0; j < 3; j++) {
+ double tt = in1[j] - in0[j];
+ rv += tt * tt;
+ }
+ return sqrt(rv);
+}
+
+/* Scale a two point vector by the given ratio. in0[] is the origin */
+void icmScale33(double out[3], double in1[3], double in0[3], double rat) {
+ out[0] = in0[0] + (in1[0] - in0[0]) * rat;
+ out[1] = in0[1] + (in1[1] - in0[1]) * rat;
+ out[2] = in0[2] + (in1[2] - in0[2]) * rat;
+}
+
+/* Normalise a two point vector to the given length. */
+/* The new location of in1[] is returned in out[]. */
+/* Return nz if not normalisable */
+int icmNormalize33(double out[3], double in1[3], double in0[3], double len) {
+ int j;
+ double vl;
+
+ for (vl = 0.0, j = 0; j < 3; j++) {
+ double tt = in1[j] - in0[j];
+ vl += tt * tt;
+ }
+ vl = sqrt(vl);
+ if (vl < ICM_SMALL_NUMBER)
+ return 1;
+
+ vl = len/vl;
+ for (j = 0; j < 3; j++) {
+ out[j] = in0[j] + (in1[j] - in0[j]) * vl;
+ }
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Given two 3D points, create a matrix that rotates */
+/* and scales one onto the other about the origin 0,0,0. */
+/* The maths is from page 52 of Tomas Moller and Eric Haines "Real-Time Rendering". */
+/* s is source vector, t is target vector. */
+/* Usage of icmRotMat: */
+/* t[0] == mat[0][0] * s[0] + mat[0][1] * s[1] + mat[0][2] * s[2]; */
+/* t[1] == mat[1][0] * s[0] + mat[1][1] * s[1] + mat[1][2] * s[2]; */
+/* t[2] == mat[2][0] * s[0] + mat[2][1] * s[1] + mat[2][2] * s[2]; */
+/* i.e. use icmMulBy3x3 */
+void icmRotMat(double m[3][3], double s[3], double t[3]) {
+ double sl, tl, sn[3], tn[3];
+ double v[3]; /* Cross product */
+ double e; /* Dot product */
+ double h; /* 1-e/Cross product dot squared */
+
+ /* Normalise input vectors */
+ /* The rotation will be about 0,0,0 */
+ sl = sqrt(s[0] * s[0] + s[1] * s[1] + s[2] * s[2]);
+ tl = sqrt(t[0] * t[0] + t[1] * t[1] + t[2] * t[2]);
+
+ if (sl < 1e-12 || tl < 1e-12) { /* Hmm. Do nothing */
+ m[0][0] = 1.0;
+ m[0][1] = 0.0;
+ m[0][2] = 0.0;
+ m[1][0] = 0.0;
+ m[1][1] = 1.0;
+ m[1][2] = 0.0;
+ m[2][0] = 0.0;
+ m[2][1] = 0.0;
+ m[2][2] = 1.0;
+ return;
+ }
+
+ sn[0] = s[0]/sl; sn[1] = s[1]/sl; sn[2] = s[2]/sl;
+ tn[0] = t[0]/tl; tn[1] = t[1]/tl; tn[2] = t[2]/tl;
+
+ /* Compute the cross product */
+ v[0] = (sn[1] * tn[2]) - (sn[2] * tn[1]);
+ v[1] = (sn[2] * tn[0]) - (sn[0] * tn[2]);
+ v[2] = (sn[0] * tn[1]) - (sn[1] * tn[0]);
+
+ /* Compute the dot product */
+ e = sn[0] * tn[0] + sn[1] * tn[1] + sn[2] * tn[2];
+
+ /* Cross product dot squared */
+ h = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
+
+ /* If the two input vectors are close to being parallel, */
+ /* then h will be close to zero. */
+ if (fabs(h) < 1e-12) {
+ m[0][0] = tl/sl;
+ m[0][1] = 0.0;
+ m[0][2] = 0.0;
+ m[1][0] = 0.0;
+ m[1][1] = tl/sl;
+ m[1][2] = 0.0;
+ m[2][0] = 0.0;
+ m[2][1] = 0.0;
+ m[2][2] = tl/sl;
+ } else {
+ /* 1-e/Cross product dot squared */
+ h = (1.0 - e) / h;
+
+ m[0][0] = tl/sl * (e + h * v[0] * v[0]);
+ m[0][1] = tl/sl * (h * v[0] * v[1] - v[2]);
+ m[0][2] = tl/sl * (h * v[0] * v[2] + v[1]);
+ m[1][0] = tl/sl * (h * v[0] * v[1] + v[2]);
+ m[1][1] = tl/sl * (e + h * v[1] * v[1]);
+ m[1][2] = tl/sl * (h * v[1] * v[2] - v[0]);
+ m[2][0] = tl/sl * (h * v[0] * v[2] - v[1]);
+ m[2][1] = tl/sl * (h * v[1] * v[2] + v[0]);
+ m[2][2] = tl/sl * (e + h * v[2] * v[2]);
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Multiply 2 array by 2x2 transform matrix */
+void icmMulBy2x2(double out[2], double mat[2][2], double in[2]) {
+ double tt[2];
+
+ tt[0] = mat[0][0] * in[0] + mat[0][1] * in[1];
+ tt[1] = mat[1][0] * in[0] + mat[1][1] * in[1];
+
+ out[0] = tt[0];
+ out[1] = tt[1];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Copy a 3x4 transform matrix */
+void icmCpy3x4(double dst[3][4], double src[3][4]) {
+ int i, j;
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 4; i++)
+ dst[j][i] = src[j][i];
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Multiply 3 array by 3x4 transform matrix */
+void icmMul3By3x4(double out[3], double mat[3][4], double in[3]) {
+ double tt[3];
+
+ tt[0] = mat[0][0] * in[0] + mat[0][1] * in[1] + mat[0][2] * in[2] + mat[0][3];
+ tt[1] = mat[1][0] * in[0] + mat[1][1] * in[1] + mat[1][2] * in[2] + mat[1][3];
+ tt[2] = mat[2][0] * in[0] + mat[2][1] * in[1] + mat[2][2] * in[2] + mat[2][3];
+
+ out[0] = tt[0];
+ out[1] = tt[1];
+ out[2] = tt[2];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Given two 3D vectors, create a matrix that translates, */
+/* rotates and scales one onto the other. */
+/* The maths is from page 52 of Tomas Moller and Eric Haines */
+/* "Real-Time Rendering". */
+/* s0 -> s1 is source vector, t0 -> t1 is target vector. */
+/* Usage of icmRotMat: */
+/* t[0] == mat[0][0] * s[0] + mat[0][1] * s[1] + mat[0][2] * s[2] + mat[0][3]; */
+/* t[1] == mat[1][0] * s[0] + mat[1][1] * s[1] + mat[1][2] * s[2] + mat[1][3]; */
+/* t[2] == mat[2][0] * s[0] + mat[2][1] * s[1] + mat[2][2] * s[2] + mat[2][3]; */
+/* i.e. use icmMul3By3x4 */
+void icmVecRotMat(double m[3][4], double s1[3], double s0[3], double t1[3], double t0[3]) {
+ int i, j;
+ double ss[3], tt[3], rr[3][3];
+
+ /* Create the rotation matrix: */
+ for (i = 0; i < 3; i++) {
+ ss[i] = s1[i] - s0[i];
+ tt[i] = t1[i] - t0[i];
+ }
+ icmRotMat(rr, ss, tt);
+
+ /* Create rotated version of s0: */
+ icmMulBy3x3(ss, rr, s0);
+
+ /* Create 4x4 matrix */
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 4; i++) {
+ if (i < 3 && j < 3)
+ m[j][i] = rr[j][i];
+ else if (i == 3 && j < 3)
+ m[j][i] = t0[j] - ss[j];
+ else if (i == j)
+ m[j][i] = 1.0;
+ else
+ m[j][i] = 0.0;
+ }
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Compute the 3D intersection of a vector and a plane */
+/* return nz if there is no intersection */
+int icmVecPlaneIsect(
+double isect[3], /* return intersection point */
+double pl_const, /* Plane equation constant */
+double pl_norm[3], /* Plane normal vector */
+double ve_1[3], /* Point on line */
+double ve_0[3] /* Second point on line */
+) {
+ double den; /* denominator */
+ double rv; /* Vector parameter value */
+ double vvec[3]; /* Vector vector */
+ double ival[3]; /* Intersection value */
+
+ /* Compute vector between the two points */
+ vvec[0] = ve_1[0] - ve_0[0];
+ vvec[1] = ve_1[1] - ve_0[1];
+ vvec[2] = ve_1[2] - ve_0[2];
+
+ /* Compute the denominator */
+ den = pl_norm[0] * vvec[0] + pl_norm[1] * vvec[1] + pl_norm[2] * vvec[2];
+
+ /* Too small to intersect ? */
+ if (fabs(den) < 1e-12) {
+ return 1;
+ }
+
+ /* Compute the parameterized intersction point */
+ rv = -(pl_norm[0] * ve_0[0] + pl_norm[1] * ve_0[1] + pl_norm[2] * ve_0[2] + pl_const)/den;
+
+ /* Compute the actual intersection point */
+ isect[0] = ve_0[0] + rv * vvec[0];
+ isect[1] = ve_0[1] + rv * vvec[1];
+ isect[2] = ve_0[2] + rv * vvec[2];
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Compute the closest points between two lines a and b. */
+/* Return closest points and parameter values if not NULL. */
+/* Return nz if they are paralel. */
+/* The maths is from page 338 of Tomas Moller and Eric Haines "Real-Time Rendering". */
+int icmLineLineClosest(double ca[3], double cb[3], double *pa, double * pb,
+ double la0[3], double la1[3], double lb0[3], double lb1[3]) {
+ double va[3], vb[3];
+ double vvab[3], vvabns; /* Cross product of va and vb and its norm squared */
+ double vba0[3]; /* lb0 - la0 */
+ double tt[3];
+ double a, b; /* Parameter values */
+
+ icmSub3(va, la1, la0);
+ icmSub3(vb, lb1, lb0);
+ icmCross3(vvab, va, vb);
+ vvabns = icmNorm3sq(vvab);
+
+ if (vvabns < 1e-12)
+ return 1;
+
+ icmSub3(vba0, lb0, la0);
+ icmCross3(tt, vba0, vb);
+ a = icmDot3(tt, vvab) / vvabns;
+
+ icmCross3(tt, vba0, va);
+ b = icmDot3(tt, vvab) / vvabns;
+
+ if (pa != NULL)
+ *pa = a;
+
+ if (pb != NULL)
+ *pb = b;
+
+ if (ca != NULL) {
+ ca[0] = la0[0] + a * va[0];
+ ca[1] = la0[1] + a * va[1];
+ ca[2] = la0[2] + a * va[2];
+ }
+
+ if (cb != NULL) {
+ cb[0] = lb0[0] + b * vb[0];
+ cb[1] = lb0[1] + b * vb[1];
+ cb[2] = lb0[2] + b * vb[2];
+ }
+
+#ifdef NEVER /* Verify */
+ {
+ double vab[3]; /* Vector from ca to cb */
+
+ vab[0] = lb0[0] + b * vb[0] - la0[0] - a * va[0];
+ vab[1] = lb0[1] + b * vb[1] - la0[1] - a * va[1];
+ vab[2] = lb0[2] + b * vb[2] - la0[2] - a * va[2];
+
+ if (icmDot3(va, vab) > 1e-6
+ || icmDot3(vb, vab) > 1e-6)
+ warning("icmLineLineClosest verify failed\n");
+ }
+#endif
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Given 3 3D points, compute a plane equation. */
+/* The normal will be right handed given the order of the points */
+/* The plane equation will be the 3 normal components and the constant. */
+/* Return nz if any points are cooincident or co-linear */
+int icmPlaneEqn3(double eq[4], double p0[3], double p1[3], double p2[3]) {
+ double ll, v1[3], v2[3];
+
+ /* Compute vectors along edges */
+ v2[0] = p1[0] - p0[0];
+ v2[1] = p1[1] - p0[1];
+ v2[2] = p1[2] - p0[2];
+
+ v1[0] = p2[0] - p0[0];
+ v1[1] = p2[1] - p0[1];
+ v1[2] = p2[2] - p0[2];
+
+ /* Compute cross products v1 x v2, which will be the normal */
+ eq[0] = v1[1] * v2[2] - v1[2] * v2[1];
+ eq[1] = v1[2] * v2[0] - v1[0] * v2[2];
+ eq[2] = v1[0] * v2[1] - v1[1] * v2[0];
+
+ /* Normalise the equation */
+ ll = sqrt(eq[0] * eq[0] + eq[1] * eq[1] + eq[2] * eq[2]);
+ if (ll < 1e-10) {
+ return 1;
+ }
+ eq[0] /= ll;
+ eq[1] /= ll;
+ eq[2] /= ll;
+
+ /* Compute the plane equation constant */
+ eq[3] = - (eq[0] * p0[0])
+ - (eq[1] * p0[1])
+ - (eq[2] * p0[2]);
+
+ return 0;
+}
+
+/* Given a 3D point and a plane equation, return the signed */
+/* distance from the plane */
+double icmPlaneDist3(double eq[4], double p[3]) {
+ double rv;
+
+ rv = eq[0] * p[0]
+ + eq[1] * p[1]
+ + eq[2] * p[2]
+ + eq[3];
+
+ return rv;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* CIE Y (range 0 .. 1) to perceptual CIE 1976 L* (range 0 .. 100) */
+double
+icmY2L(double val) {
+ if (val > 0.008856451586)
+ val = pow(val,1.0/3.0);
+ else
+ val = 7.787036979 * val + 16.0/116.0;
+
+ val = (116.0 * val - 16.0);
+ return val;
+}
+
+/* Perceptual CIE 1976 L* (range 0 .. 100) to CIE Y (range 0 .. 1) */
+double
+icmL2Y(double val) {
+ val = (val + 16.0)/116.0;
+
+ if (val > 24.0/116.0)
+ val = pow(val,3.0);
+ else
+ val = (val - 16.0/116.0)/7.787036979;
+ return val;
+}
+
+/* CIE XYZ to perceptual CIE 1976 L*a*b* */
+void
+icmXYZ2Lab(icmXYZNumber *w, double *out, double *in) {
+ double X = in[0], Y = in[1], Z = in[2];
+ double x,y,z,fx,fy,fz;
+
+ x = X/w->X;
+ y = Y/w->Y;
+ z = Z/w->Z;
+
+ if (x > 0.008856451586)
+ fx = pow(x,1.0/3.0);
+ else
+ fx = 7.787036979 * x + 16.0/116.0;
+
+ if (y > 0.008856451586)
+ fy = pow(y,1.0/3.0);
+ else
+ fy = 7.787036979 * y + 16.0/116.0;
+
+ if (z > 0.008856451586)
+ fz = pow(z,1.0/3.0);
+ else
+ fz = 7.787036979 * z + 16.0/116.0;
+
+ out[0] = 116.0 * fy - 16.0;
+ out[1] = 500.0 * (fx - fy);
+ out[2] = 200.0 * (fy - fz);
+}
+
+/* Perceptual CIE 1976 L*a*b* to CIE XYZ */
+void
+icmLab2XYZ(icmXYZNumber *w, double *out, double *in) {
+ double L = in[0], a = in[1], b = in[2];
+ double x,y,z,fx,fy,fz;
+
+ fy = (L + 16.0)/116.0;
+ fx = a/500.0 + fy;
+ fz = fy - b/200.0;
+
+ if (fy > 24.0/116.0)
+ y = pow(fy,3.0);
+ else
+ y = (fy - 16.0/116.0)/7.787036979;
+
+ if (fx > 24.0/116.0)
+ x = pow(fx,3.0);
+ else
+ x = (fx - 16.0/116.0)/7.787036979;
+
+ if (fz > 24.0/116.0)
+ z = pow(fz,3.0);
+ else
+ z = (fz - 16.0/116.0)/7.787036979;
+
+ out[0] = x * w->X;
+ out[1] = y * w->Y;
+ out[2] = z * w->Z;
+}
+
+
+/* LCh to Lab */
+void icmLCh2Lab(double *out, double *in) {
+ double C, h;
+
+ C = in[1];
+ h = 3.14159265359/180.0 * in[2];
+
+ out[0] = in[0];
+ out[1] = C * cos(h);
+ out[2] = C * sin(h);
+}
+
+/* Lab to LCh */
+void icmLab2LCh(double *out, double *in) {
+ double C, h;
+
+ C = sqrt(in[1] * in[1] + in[2] * in[2]);
+
+ h = (180.0/3.14159265359) * atan2(in[2], in[1]);
+ h = (h < 0.0) ? h + 360.0 : h;
+
+ out[0] = in[0];
+ out[1] = C;
+ out[2] = h;
+}
+
+/* XYZ to Yxy */
+extern ICCLIB_API void icmXYZ2Yxy(double *out, double *in) {
+ double sum = in[0] + in[1] + in[2];
+ double Y, x, y;
+
+ if (sum < 1e-9) {
+ Y = 0.0;
+ y = 0.333333333;
+ x = 0.333333333;
+ } else {
+ Y = in[1];
+ x = in[0]/sum;
+ y = in[1]/sum;
+ }
+ out[0] = Y;
+ out[1] = x;
+ out[2] = y;
+}
+
+/* Yxy to XYZ */
+extern ICCLIB_API void icmYxy2XYZ(double *out, double *in) {
+ double Y = in[0];
+ double x = in[1];
+ double y = in[2];
+ double z = 1.0 - x - y;
+ double sum;
+ if (y < 1e-9) {
+ out[0] = out[1] = out[2] = 0.0;
+ } else {
+ sum = Y/y;
+ out[0] = x * sum;
+ out[1] = Y;
+ out[2] = z * sum;
+ }
+}
+
+
+/* CIE XYZ to perceptual CIE 1976 L*u*v* */
+extern ICCLIB_API void icmXYZ2Luv(icmXYZNumber *w, double *out, double *in) {
+ double X = in[0], Y = in[1], Z = in[2];
+ double un, vn, u, v, fl, fu, fv;
+
+ un = (4.0 * w->X) / (w->X + 15.0 * w->Y + 3.0 * w->Z);
+ vn = (9.0 * w->Y) / (w->X + 15.0 * w->Y + 3.0 * w->Z);
+ u = (4.0 * X) / (X + 15.0 * Y + 3.0 * Z);
+ v = (9.0 * Y) / (X + 15.0 * Y + 3.0 * Z);
+
+ Y /= w->Y;
+
+ if (Y > 0.008856451586)
+ fl = pow(Y,1.0/3.0);
+ else
+ fl = 7.787036979 * Y + 16.0/116.0;
+
+ fu = u - un;
+ fv = v - vn;
+
+ out[0] = 116.0 * fl - 16.0;
+ out[1] = 13.0 * out[0] * fu;
+ out[2] = 13.0 * out[0] * fv;
+}
+
+/* Perceptual CIE 1976 L*u*v* to CIE XYZ */
+extern ICCLIB_API void icmLuv2XYZ(icmXYZNumber *w, double *out, double *in) {
+ double un, vn, u, v, fl, fu, fv, sum, X, Y, Z;
+
+ fl = (in[0] + 16.0)/116.0;
+ fu = in[1] / (13.0 * in[0]);
+ fv = in[2] / (13.0 * in[0]);
+
+ un = (4.0 * w->X) / (w->X + 15.0 * w->Y + 3.0 * w->Z);
+ vn = (9.0 * w->Y) / (w->X + 15.0 * w->Y + 3.0 * w->Z);
+
+ u = fu + un;
+ v = fv + vn;
+
+ if (fl > 24.0/116.0)
+ Y = pow(fl,3.0);
+ else
+ Y = (fl - 16.0/116.0)/7.787036979;
+ Y *= w->Y;
+
+ sum = (9.0 * Y)/v;
+ X = (u * sum)/4.0;
+ Z = (sum - X - 15.0 * Y)/3.0;
+
+ out[0] = X;
+ out[1] = Y;
+ out[2] = Z;
+}
+
+/* NOTE :- none of the following seven have been protected */
+/* against arithmmetic issues (ie. for black) */
+
+/* CIE XYZ to perceptual CIE 1976 UCS diagram Yu'v'*/
+/* (Yu'v' is a better chromaticity space than Yxy) */
+extern ICCLIB_API void icmXYZ21976UCS(double *out, double *in) {
+ double X = in[0], Y = in[1], Z = in[2];
+ double den, u, v;
+
+ den = (X + 15.0 * Y + 3.0 * Z);
+ u = (4.0 * X) / den;
+ v = (9.0 * Y) / den;
+
+ out[0] = Y;
+ out[1] = u;
+ out[2] = v;
+}
+
+/* Perceptual CIE 1976 UCS diagram Yu'v' to CIE XYZ */
+extern ICCLIB_API void icm1976UCS2XYZ(double *out, double *in) {
+ double u, v, fl, fu, fv, sum, X, Y, Z;
+
+ Y = in[0];
+ u = in[1];
+ v = in[2];
+
+ X = ((9.0 * u * Y)/(4.0 * v));
+ Z = -(((20.0 * v + 3.0 * u - 12.0) * Y)/(4.0 * v));
+
+ out[0] = X;
+ out[1] = Y;
+ out[2] = Z;
+}
+
+/* CIE XYZ to perceptual CIE 1960 UCS */
+/* (This was obsoleted by the 1976UCS, but is still used */
+/* in computing color temperatures.) */
+extern ICCLIB_API void icmXYZ21960UCS(double *out, double *in) {
+ double X = in[0], Y = in[1], Z = in[2];
+ double u, v;
+
+ u = (4.0 * X) / (X + 15.0 * Y + 3.0 * Z);
+ v = (6.0 * Y) / (X + 15.0 * Y + 3.0 * Z);
+
+ out[0] = Y;
+ out[1] = u;
+ out[2] = v;
+}
+
+/* Perceptual CIE 1960 UCS to CIE XYZ */
+extern ICCLIB_API void icm1960UCS2XYZ(double *out, double *in) {
+ double u, v, fl, fu, fv, sum, X, Y, Z;
+
+ Y = in[0];
+ u = in[1];
+ v = in[2];
+
+ X = ((3.0 * u * Y)/(2.0 * v));
+ Z = -(((10.0 * v + u - 4.0) * Y)/(2.0 * v));
+
+ out[0] = X;
+ out[1] = Y;
+ out[2] = Z;
+}
+
+/* CIE XYZ to perceptual CIE 1964 WUV (U*V*W*) */
+/* (This is obsolete but still used in computing CRI) */
+extern ICCLIB_API void icmXYZ21964WUV(icmXYZNumber *w, double *out, double *in) {
+ double W, U, V;
+ double wucs[3];
+ double iucs[3];
+
+ icmXYZ2Ary(wucs, *w);
+ icmXYZ21960UCS(wucs, wucs);
+ icmXYZ21960UCS(iucs, in);
+
+ W = 25.0 * pow(iucs[0] * 100.0/wucs[0], 1.0/3.0) - 17.0;
+ U = 13.0 * W * (iucs[1] - wucs[1]);
+ V = 13.0 * W * (iucs[2] - wucs[2]);
+
+ out[0] = W;
+ out[1] = U;
+ out[2] = V;
+}
+
+/* Perceptual CIE 1964 WUV (U*V*W*) to CIE XYZ */
+extern ICCLIB_API void icm1964WUV2XYZ(icmXYZNumber *w, double *out, double *in) {
+ double W, U, V;
+ double wucs[3];
+ double iucs[3];
+
+ icmXYZ2Ary(wucs, *w);
+ icmXYZ21960UCS(wucs, wucs);
+
+ W = in[0];
+ U = in[1];
+ V = in[2];
+
+ iucs[0] = pow((W + 17.0)/25.0, 3.0) * wucs[0]/100.0;
+ iucs[1] = U / (13.0 * W) + wucs[1];
+ iucs[2] = V / (13.0 * W) + wucs[2];
+
+ icm1960UCS2XYZ(out, iucs);
+}
+
+/* CIE CIE1960 UCS to perceptual CIE 1964 WUV (U*V*W*) */
+extern ICCLIB_API void icm1960UCS21964WUV(icmXYZNumber *w, double *out, double *in) {
+ double W, U, V;
+ double wucs[3];
+
+ icmXYZ2Ary(wucs, *w);
+ icmXYZ21960UCS(wucs, wucs);
+
+ W = 25.0 * pow(in[0] * 100.0/wucs[0], 1.0/3.0) - 17.0;
+ U = 13.0 * W * (in[1] - wucs[1]);
+ V = 13.0 * W * (in[2] - wucs[2]);
+
+ out[0] = W;
+ out[1] = U;
+ out[2] = V;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* available D50 Illuminant */
+icmXYZNumber icmD50 = { /* Profile illuminant - D50 */
+ 0.9642, 1.0000, 0.8249
+};
+
+icmXYZNumber icmD50_100 = { /* Profile illuminant - D50, scaled to 100 */
+ 96.42, 100.00, 82.49
+};
+
+double icmD50_ary3[3] = { /* Profile illuminant - D50 */
+ 0.9642, 1.0000, 0.8249
+};
+
+double icmD50_100_ary3[3] = { /* Profile illuminant - D50, scaled to 100 */
+ 96.42, 100.00, 82.49
+};
+
+/* available D65 Illuminant */
+icmXYZNumber icmD65 = { /* Profile illuminant - D65 */
+ 0.9505, 1.0000, 1.0890
+};
+
+icmXYZNumber icmD65_100 = { /* Profile illuminant - D65, scaled to 100 */
+ 95.05, 100.00, 108.90
+};
+
+double icmD65_ary3[3] = { /* Profile illuminant - D65 */
+ 0.9505, 1.0000, 1.0890
+};
+
+double icmD65_100_ary3[3] = { /* Profile illuminant - D65, scaled to 100 */
+ 95.05, 100.00, 108.90
+};
+
+/* Default black point */
+icmXYZNumber icmBlack = {
+ 0.0000, 0.0000, 0.0000
+};
+
+/* Return the normal Delta E given two Lab values */
+double icmLabDE(double *Lab0, double *Lab1) {
+ double rv = 0.0, tt;
+
+ tt = Lab0[0] - Lab1[0];
+ rv += tt * tt;
+ tt = Lab0[1] - Lab1[1];
+ rv += tt * tt;
+ tt = Lab0[2] - Lab1[2];
+ rv += tt * tt;
+
+ return sqrt(rv);
+}
+
+/* Return the normal Delta E squared, given two Lab values */
+double icmLabDEsq(double *Lab0, double *Lab1) {
+ double rv = 0.0, tt;
+
+ tt = Lab0[0] - Lab1[0];
+ rv += tt * tt;
+ tt = Lab0[1] - Lab1[1];
+ rv += tt * tt;
+ tt = Lab0[2] - Lab1[2];
+ rv += tt * tt;
+
+ return rv;
+}
+
+/* Return the normal Delta E given two XYZ values */
+extern ICCLIB_API double icmXYZLabDE(icmXYZNumber *w, double *in0, double *in1) {
+ double lab0[3], lab1[3], rv;
+
+ icmXYZ2Lab(w, lab0, in0);
+ icmXYZ2Lab(w, lab1, in1);
+ rv = icmLabDE(lab0, lab1);
+ return rv;
+}
+
+/* (Note that CIE94 can give odd results for very large delta E's, */
+/* when one of the two points is near the neutral axis: */
+/* ie DE(A,B + del) != DE(A,B) + DE(del) */
+#ifdef NEVER
+{
+ double w1[3] = { 99.996101, -0.058417, -0.010557 };
+ double c1[3] = { 60.267956, 98.845863, -61.163277 };
+ double w2[3] = { 100.014977, -0.138339, 0.089744 };
+ double c2[3] = { 60.294464, 98.117104, -60.843159 };
+
+ printf("DE 1 = %f, 2 = %f\n", icmLabDE(c1, w1), icmLabDE(c2, w2));
+ printf("DE94 1 = %f, 2 = %f\n", icmCIE94(c1, w1), icmCIE94(c2, w2));
+}
+#endif
+
+
+/* Return the CIE94 Delta E color difference measure, squared */
+double icmCIE94sq(double Lab0[3], double Lab1[3]) {
+ double desq, dhsq;
+ double dlsq, dcsq;
+ double c12;
+
+ {
+ double dl, da, db;
+ dl = Lab0[0] - Lab1[0];
+ dlsq = dl * dl; /* dl squared */
+ da = Lab0[1] - Lab1[1];
+ db = Lab0[2] - Lab1[2];
+
+ /* Compute normal Lab delta E squared */
+ desq = dlsq + da * da + db * db;
+ }
+
+ {
+ double c1, c2, dc;
+
+ /* Compute chromanance for the two colors */
+ c1 = sqrt(Lab0[1] * Lab0[1] + Lab0[2] * Lab0[2]);
+ c2 = sqrt(Lab1[1] * Lab1[1] + Lab1[2] * Lab1[2]);
+ c12 = sqrt(c1 * c2); /* Symetric chromanance */
+
+ /* delta chromanance squared */
+ dc = c2 - c1;
+ dcsq = dc * dc;
+ }
+
+ /* Compute delta hue squared */
+ if ((dhsq = desq - dlsq - dcsq) < 0.0)
+ dhsq = 0.0;
+ {
+ double sc, sh;
+
+ /* Weighting factors for delta chromanance & delta hue */
+ sc = 1.0 + 0.048 * c12;
+ sh = 1.0 + 0.014 * c12;
+ return dlsq + dcsq/(sc * sc) + dhsq/(sh * sh);
+ }
+}
+
+/* Return the CIE94 Delta E color difference measure */
+double icmCIE94(double Lab0[3], double Lab1[3]) {
+ return sqrt(icmCIE94sq(Lab0, Lab1));
+}
+
+/* Return the CIE94 Delta E color difference measure for two XYZ values */
+extern ICCLIB_API double icmXYZCIE94(icmXYZNumber *w, double *in0, double *in1) {
+ double lab0[3], lab1[3];
+
+ icmXYZ2Lab(w, lab0, in0);
+ icmXYZ2Lab(w, lab1, in1);
+ return sqrt(icmCIE94sq(lab0, lab1));
+}
+
+
+/* From the paper "The CIEDE2000 Color-Difference Formula: Implementation Notes, */
+/* Supplementary Test Data, and Mathematical Observations", by */
+/* Gaurav Sharma, Wencheng Wu and Edul N. Dalal, */
+/* Color Res. Appl., vol. 30, no. 1, pp. 21-30, Feb. 2005. */
+
+/* Return the CIEDE2000 Delta E color difference measure squared, for two Lab values */
+double icmCIE2Ksq(double *Lab0, double *Lab1) {
+ double C1, C2;
+ double h1, h2;
+ double dL, dC, dH;
+ double dsq;
+
+ /* The trucated value of PI is needed to ensure that the */
+ /* test cases pass, as one of them lies on the edge of */
+ /* a mathematical discontinuity. The precision is still */
+ /* enough for any practical use. */
+#define RAD2DEG(xx) (180.0/3.14159265358979 * (xx))
+#define DEG2RAD(xx) (3.14159265358979/180.0 * (xx))
+
+ /* Compute Cromanance and Hue angles */
+ {
+ double C1ab, C2ab;
+ double Cab, Cab7, G;
+ double a1, a2;
+
+ C1ab = sqrt(Lab0[1] * Lab0[1] + Lab0[2] * Lab0[2]);
+ C2ab = sqrt(Lab1[1] * Lab1[1] + Lab1[2] * Lab1[2]);
+ Cab = 0.5 * (C1ab + C2ab);
+ Cab7 = pow(Cab,7.0);
+ G = 0.5 * (1.0 - sqrt(Cab7/(Cab7 + 6103515625.0)));
+ a1 = (1.0 + G) * Lab0[1];
+ a2 = (1.0 + G) * Lab1[1];
+ C1 = sqrt(a1 * a1 + Lab0[2] * Lab0[2]);
+ C2 = sqrt(a2 * a2 + Lab1[2] * Lab1[2]);
+
+ if (C1 < 1e-9)
+ h1 = 0.0;
+ else {
+ h1 = RAD2DEG(atan2(Lab0[2], a1));
+ if (h1 < 0.0)
+ h1 += 360.0;
+ }
+
+ if (C2 < 1e-9)
+ h2 = 0.0;
+ else {
+ h2 = RAD2DEG(atan2(Lab1[2], a2));
+ if (h2 < 0.0)
+ h2 += 360.0;
+ }
+ }
+
+ /* Compute delta L, C and H */
+ {
+ double dh;
+
+ dL = Lab1[0] - Lab0[0];
+ dC = C2 - C1;
+ if (C1 < 1e-9 || C2 < 1e-9) {
+ dh = 0.0;
+ } else {
+ dh = h2 - h1;
+ if (dh > 180.0)
+ dh -= 360.0;
+ else if (dh < -180.0)
+ dh += 360.0;
+ }
+
+ dH = 2.0 * sqrt(C1 * C2) * sin(DEG2RAD(0.5 * dh));
+ }
+
+ {
+ double L, C, h, T;
+ double hh, ddeg;
+ double C7, RC, L50sq, SL, SC, SH, RT;
+ double dLsq, dCsq, dHsq, RCH;
+
+ L = 0.5 * (Lab0[0] + Lab1[0]);
+ C = 0.5 * (C1 + C2);
+ if (C1 < 1e-9 || C2 < 1e-9) {
+ h = h1 + h2;
+ } else {
+ h = h1 + h2;
+ if (fabs(h1 - h2) > 180.0) {
+ if (h < 360.0)
+ h += 360.0;
+ else if (h >= 360.0)
+ h -= 360.0;
+ }
+ h *= 0.5;
+ }
+ T = 1.0 - 0.17 * cos(DEG2RAD(h-30.0)) + 0.24 * cos(DEG2RAD(2.0 * h))
+ + 0.32 * cos(DEG2RAD(3.0 * h + 6.0)) - 0.2 * cos(DEG2RAD(4.0 * h - 63.0));
+ hh = (h - 275.0)/25.0;
+ ddeg = 30.0 * exp(-hh * hh);
+ C7 = pow(C,7.0);
+ RC = 2.0 * sqrt(C7/(C7 + 6103515625.0));
+ L50sq = (L - 50.0) * (L - 50.0);
+ SL = 1.0 + (0.015 * L50sq)/sqrt(20.0 + L50sq);
+ SC = 1.0 + 0.045 * C;
+ SH = 1.0 + 0.015 * C * T;
+ RT = -sin(DEG2RAD(2 * ddeg)) * RC;
+
+ dLsq = dL/SL;
+ dCsq = dC/SC;
+ dHsq = dH/SH;
+
+ RCH = RT * dCsq * dHsq;
+
+ dLsq *= dLsq;
+ dCsq *= dCsq;
+ dHsq *= dHsq;
+
+ dsq = dLsq + dCsq + dHsq + RCH;
+ }
+
+ return dsq;
+
+#undef RAD2DEG
+#undef DEG2RAD
+}
+
+/* Return the CIE2DE000 Delta E color difference measure for two Lab values */
+double icmCIE2K(double *Lab0, double *Lab1) {
+ return sqrt(icmCIE2Ksq(Lab0, Lab1));
+}
+
+/* Return the CIEDE2000 Delta E color difference measure for two XYZ values */
+extern ICCLIB_API double icmXYZCIE2K(icmXYZNumber *w, double *in0, double *in1) {
+ double lab0[3], lab1[3];
+
+ icmXYZ2Lab(w, lab0, in0);
+ icmXYZ2Lab(w, lab1, in1);
+ return sqrt(icmCIE2Ksq(lab0, lab1));
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Chromatic adaptation transform utility */
+/* Return a 3x3 chromatic adaptation matrix */
+/* Use icmMulBy3x3(dst, mat, src) */
+void icmChromAdaptMatrix(
+ int flags, /* Use bradford, Transform given matrix flags */
+ icmXYZNumber d_wp, /* Destination white point */
+ icmXYZNumber s_wp, /* Source white point */
+ double mat[3][3] /* Destination matrix */
+) {
+ double dst[3], src[3]; /* Source & destination white points */
+ double vkmat[3][3]; /* Von Kries matrix */
+#ifdef NEVER
+ static double bradford[3][3] = { /* Linear cone space matrix */
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 }
+ };
+#endif /* NEVER */
+ static double bradford[3][3] = { /* Bradford cone space matrix */
+ { 0.8951, 0.2664, -0.1614 },
+ { -0.7502, 1.7135, 0.0367 },
+ { 0.0389, -0.0685, 1.0296 }
+ };
+ static int inited = 0; /* Compute inverse bradford once */
+ static double ibradford[3][3]; /* Inverse Bradford */
+
+ /* Set initial matrix to unity */
+ if (!(flags & ICM_CAM_MULMATRIX)) {
+ icmSetUnity3x3(mat);
+ }
+
+ icmXYZ2Ary(src, s_wp);
+ icmXYZ2Ary(dst, d_wp);
+
+ if (flags & ICM_CAM_BRADFORD) {
+ icmMulBy3x3(src, bradford, src);
+ icmMulBy3x3(dst, bradford, dst);
+ }
+
+ /* Setup the Von Kries white point adaptation matrix */
+ vkmat[0][0] = dst[0]/src[0];
+ vkmat[1][1] = dst[1]/src[1];
+ vkmat[2][2] = dst[2]/src[2];
+ vkmat[0][1] = vkmat[0][2] = 0.0;
+ vkmat[1][0] = vkmat[1][2] = 0.0;
+ vkmat[2][0] = vkmat[2][1] = 0.0;
+
+ /* Transform to Bradford space if requested */
+ if (flags & ICM_CAM_BRADFORD) {
+ icmMul3x3(mat, bradford);
+ }
+
+ /* Apply chromatic adaptation */
+ icmMul3x3(mat, vkmat);
+
+ /* Transform from Bradford space */
+ if (flags & ICM_CAM_BRADFORD) {
+ if (inited == 0) {
+ icmInverse3x3(ibradford, bradford);
+ inited = 1;
+ }
+ icmMul3x3(mat, ibradford);
+ }
+
+ /* We're done */
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* RGB primaries device to RGB->XYZ transform matrix. */
+/* We assume that the device is perfectly additive, but that */
+/* there may be a scale factor applied to the channels to */
+/* match the white point at RGB = 1. */
+
+/* Return non-zero if matrix would be singular */
+int icmRGBprim2matrix(
+ icmXYZNumber white, /* White point */
+ icmXYZNumber red, /* Red colorant */
+ icmXYZNumber green, /* Green colorant */
+ icmXYZNumber blue, /* Blue colorant */
+ double mat[3][3] /* Destination matrix */
+) {
+ double tmat[3][3];
+ double t[3];
+
+ /* Assemble the colorants into a matrix */
+ tmat[0][0] = red.X;
+ tmat[0][1] = green.X;
+ tmat[0][2] = blue.X;
+ tmat[1][0] = red.Y;
+ tmat[1][1] = green.Y;
+ tmat[1][2] = blue.Y;
+ tmat[2][0] = red.Z;
+ tmat[2][1] = green.Z;
+ tmat[2][2] = blue.Z;
+
+ /* Compute the inverse */
+ if (icmInverse3x3(mat, tmat))
+ return 1;
+
+ /* Compute scale vector that maps colorants to white point */
+ t[0] = mat[0][0] * white.X
+ + mat[0][1] * white.Y
+ + mat[0][2] * white.Z;
+ t[1] = mat[1][0] * white.X
+ + mat[1][1] * white.Y
+ + mat[1][2] * white.Z;
+ t[2] = mat[2][0] * white.X
+ + mat[2][1] * white.Y
+ + mat[2][2] * white.Z;
+
+ /* Now formulate the transform matrix */
+ mat[0][0] = red.X * t[0];
+ mat[0][1] = green.X * t[1];
+ mat[0][2] = blue.X * t[2];
+ mat[1][0] = red.Y * t[0];
+ mat[1][1] = green.Y * t[1];
+ mat[1][2] = blue.Y * t[2];
+ mat[2][0] = red.Z * t[0];
+ mat[2][1] = green.Z * t[1];
+ mat[2][2] = blue.Z * t[2];
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Some PCS utility functions */
+
+/* Clip Lab, while maintaining hue angle. */
+/* Return nz if clipping occured */
+int icmClipLab(double out[3], double in[3]) {
+ double C;
+
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+
+ if (out[0] >= 0.0 && out[0] <= 100.0
+ && out[1] >= -128.0 && out[1] <= 127.0
+ && out[2] >= -128.0 && out[2] <= 127.0)
+ return 0;
+
+ /* Clip L */
+ if (out[0] < 0.0)
+ out[0] = 0.0;
+ else if (out[0] > 100.0)
+ out[0] = 100.0;
+
+ C = out[1];
+ if (fabs(out[2]) > fabs(C))
+ C = out[2];
+ if (C < -128.0 || C > 127.0) {
+ if (C < 0.0)
+ C = -128.0/C;
+ else
+ C = 127.0/C;
+ out[1] *= C;
+ out[2] *= C;
+ }
+
+ return 1;
+}
+
+/* Clip XYZ, while maintaining hue angle */
+/* Return nz if clipping occured */
+int icmClipXYZ(double out[3], double in[3]) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+
+ if (out[0] >= 0.0 && out[0] <= 1.9999
+ && out[1] >= 0.0 && out[1] <= 1.9999
+ && out[2] >= 0.0 && out[2] <= 1.9999)
+ return 0;
+
+ /* Clip Y and scale X and Z similarly */
+
+ if (out[1] > 1.9999) {
+ out[0] *= 1.9999/out[1];
+ out[2] *= 1.9999/out[1];
+ out[1] = 1.9999;
+ } else if (out[1] < 0.0) {
+ out[0] = 0.0;
+ out[1] = 0.0;
+ out[2] = 0.0;
+ }
+
+ if (out[0] < 0.0 || out[0] > 1.9999
+ || out[2] < 0.0 || out[2] > 1.9999) {
+ double D50[3] = { 0.9642, 1.0000, 0.8249 };
+ double bb = 0.0;
+
+ /* Scale the D50 so that it has the same Y value as our color */
+ D50[0] *= out[1];
+ D50[1] *= out[1];
+ D50[2] *= out[1];
+
+ /* Figure out what blend factor with Y scaled D50, brings our */
+ /* color X and Z values into range */
+
+ if (out[0] < 0.0) {
+ double b;
+ b = (0.0 - out[0])/(D50[0] - out[0]);
+ if (b > bb)
+ bb = b;
+ } else if (out[0] > 1.9999) {
+ double b;
+ b = (1.9999 - out[0])/(D50[0] - out[0]);
+ if (b > bb)
+ bb = b;
+ }
+ if (out[2] < 0.0) {
+ double b;
+ b = (0.0 - out[2])/(D50[2] - out[2]);
+ if (b > bb)
+ bb = b;
+ } else if (out[2] > 1.9999) {
+ double b;
+ b = (1.9999 - out[2])/(D50[2] - out[2]);
+ if (b > bb)
+ bb = b;
+ }
+ /* Do the desaturation */
+ out[0] = D50[0] * bb + (1.0 - bb) * out[0];
+ out[2] = D50[2] * bb + (1.0 - bb) * out[2];
+ }
+ return 1;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Object for computing RFC 1321 MD5 checksums. */
+/* Derived from Colin Plumb's 1993 public domain code. */
+
+/* Reset the checksum */
+static void icmMD5_reset(icmMD5 *p) {
+ p->tlen = 0;
+
+ p->sum[0] = 0x67452301;
+ p->sum[1] = 0xefcdab89;
+ p->sum[2] = 0x98badcfe;
+ p->sum[3] = 0x10325476;
+
+ p->fin = 0;
+}
+
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+#define MD5STEP(f, w, x, y, z, pp, xtra, s) \
+ data = (pp)[0] + ((pp)[3] << 24) + ((pp)[2] << 16) + ((pp)[1] << 8); \
+ w += f(x, y, z) + data + xtra; \
+ w = (w << s) | (w >> (32-s)); \
+ w += x;
+
+/* Add another 64 bytes to the checksum */
+static void icmMD5_accume(icmMD5 *p, ORD8 *in) {
+ ORD32 data, a, b, c, d;
+
+ a = p->sum[0];
+ b = p->sum[1];
+ c = p->sum[2];
+ d = p->sum[3];
+
+ MD5STEP(F1, a, b, c, d, in + (4 * 0), 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in + (4 * 1), 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in + (4 * 2), 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in + (4 * 3), 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in + (4 * 4), 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in + (4 * 5), 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in + (4 * 6), 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in + (4 * 7), 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in + (4 * 8), 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in + (4 * 9), 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in + (4 * 10), 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in + (4 * 11), 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in + (4 * 12), 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in + (4 * 13), 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in + (4 * 14), 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in + (4 * 15), 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in + (4 * 1), 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in + (4 * 6), 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in + (4 * 11), 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in + (4 * 0), 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in + (4 * 5), 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in + (4 * 10), 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in + (4 * 15), 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in + (4 * 4), 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in + (4 * 9), 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in + (4 * 14), 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in + (4 * 3), 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in + (4 * 8), 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in + (4 * 13), 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in + (4 * 2), 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in + (4 * 7), 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in + (4 * 12), 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in + (4 * 5), 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in + (4 * 8), 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in + (4 * 11), 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in + (4 * 14), 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in + (4 * 1), 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in + (4 * 4), 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in + (4 * 7), 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in + (4 * 10), 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in + (4 * 13), 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in + (4 * 0), 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in + (4 * 3), 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in + (4 * 6), 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in + (4 * 9), 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in + (4 * 12), 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in + (4 * 15), 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in + (4 * 2), 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in + (4 * 0), 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in + (4 * 7), 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in + (4 * 14), 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in + (4 * 5), 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in + (4 * 12), 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in + (4 * 3), 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in + (4 * 10), 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in + (4 * 1), 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in + (4 * 8), 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in + (4 * 15), 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in + (4 * 6), 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in + (4 * 13), 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in + (4 * 4), 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in + (4 * 11), 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in + (4 * 2), 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in + (4 * 9), 0xeb86d391, 21);
+
+ p->sum[0] += a;
+ p->sum[1] += b;
+ p->sum[2] += c;
+ p->sum[3] += d;
+}
+
+#undef F1
+#undef F2
+#undef F3
+#undef F4
+#undef MD5STEP
+
+/* Add some bytes */
+static void icmMD5_add(icmMD5 *p, ORD8 *ibuf, unsigned int len) {
+ unsigned int bs;
+
+ if (p->fin)
+ return; /* This is actually an error */
+
+ bs = p->tlen; /* Current bytes added */
+ p->tlen = bs + len; /* Update length after adding this buffer */
+ bs &= 0x3f; /* Bytes already in buffer */
+
+ /* Deal with any existing partial bytes in p->buf */
+ if (bs) {
+ ORD8 *np = (ORD8 *)p->buf + bs; /* Next free location in partial buffer */
+
+ bs = 64 - bs; /* Free space in partial buffer */
+
+ if (len < bs) { /* Not enought new to make a full buffer */
+ memmove(np, ibuf, len);
+ return;
+ }
+
+ memmove(np, ibuf, bs); /* Now got one full buffer */
+ icmMD5_accume(p, np);
+ ibuf += bs;
+ len -= bs;
+ }
+
+ /* Deal with input data 64 bytes at a time */
+ while (len >= 64) {
+ icmMD5_accume(p, ibuf);
+ ibuf += 64;
+ len -= 64;
+ }
+
+ /* Deal with any remaining bytes */
+ memmove(p->buf, ibuf, len);
+}
+
+/* Finalise the checksum and return the result. */
+static void icmMD5_get(icmMD5 *p, ORD8 chsum[16]) {
+ int i;
+ unsigned count;
+ ORD32 bits1, bits0;
+ ORD8 *pp;
+
+ if (p->fin == 0) {
+
+ /* Compute number of bytes processed mod 64 */
+ count = p->tlen & 0x3f;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ pp = p->buf + count;
+ *pp++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64, allowing 8 bytes for length in bits. */
+ if (count < 8) { /* Not enough space for padding and length */
+
+ memset(pp, 0, count);
+ icmMD5_accume(p, p->buf);
+
+ /* Now fill the next block with 56 bytes */
+ memset(p->buf, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(pp, 0, count - 8);
+ }
+
+ /* Compute number of bits */
+ bits1 = 0x7 & (p->tlen >> (32 - 3));
+ bits0 = p->tlen << 3;
+
+ /* Append number of bits */
+ p->buf[64 - 8] = bits0 & 0xff;
+ p->buf[64 - 7] = (bits0 >> 8) & 0xff;
+ p->buf[64 - 6] = (bits0 >> 16) & 0xff;
+ p->buf[64 - 5] = (bits0 >> 24) & 0xff;
+ p->buf[64 - 4] = bits1 & 0xff;
+ p->buf[64 - 3] = (bits1 >> 8) & 0xff;
+ p->buf[64 - 2] = (bits1 >> 16) & 0xff;
+ p->buf[64 - 1] = (bits1 >> 24) & 0xff;
+
+ icmMD5_accume(p, p->buf);
+
+ p->fin = 1;
+ }
+
+ /* Return the result, lsb to msb */
+ pp = chsum;
+ for (i = 0; i < 4; i++) {
+ *pp++ = p->sum[i] & 0xff;
+ *pp++ = (p->sum[i] >> 8) & 0xff;
+ *pp++ = (p->sum[i] >> 16) & 0xff;
+ *pp++ = (p->sum[i] >> 24) & 0xff;
+ }
+}
+
+
+/* Delete the instance */
+static void icmMD5_del(icmMD5 *p) {
+ p->al->free(p->al, p);
+}
+
+/* Create a new MD5 checksumming object, with a reset checksum value */
+/* Return it or NULL if there is an error */
+icmMD5 *new_icmMD5(icmAlloc *al) {
+ icmMD5 *p;
+
+ if ((p = (icmMD5 *)al->calloc(al,1,sizeof(icmMD5))) == NULL)
+ return NULL;
+
+ p->al = al;
+ p->reset = icmMD5_reset;
+ p->add = icmMD5_add;
+ p->get = icmMD5_get;
+ p->del = icmMD5_del;
+
+ p->reset(p);
+
+ return p;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Dumy icmFile used to compute MD5 checksum on write */
+
+/* Get the size of the file (Only valid for reading file. */
+static size_t icmFileMD5_get_size(icmFile *pp) {
+ icmFileMD5 *p = (icmFileMD5 *)pp;
+
+ return p->size;
+}
+
+/* Set current position to offset. Return 0 on success, nz on failure. */
+/* Seek can't be supported for MD5, so and seek must be to current location. */
+static int icmFileMD5_seek(
+icmFile *pp,
+unsigned int offset
+) {
+ icmFileMD5 *p = (icmFileMD5 *)pp;
+
+ if (p->of != offset) {
+ p->errc = 1;
+ }
+ if (p->of > p->size)
+ p->size = p->of;
+ return 0;
+}
+
+/* Read count items of size length. Return number of items successfully read. */
+/* Read is not implemented */
+static size_t icmFileMD5_read(
+icmFile *pp,
+void *buffer,
+size_t size,
+size_t count
+) {
+ return 0;
+}
+
+/* write count items of size length. Return number of items successfully written. */
+/* Simply pass to MD5 to compute checksum */
+static size_t icmFileMD5_write(
+icmFile *pp,
+void *buffer,
+size_t size,
+size_t count
+) {
+ icmFileMD5 *p = (icmFileMD5 *)pp;
+ size_t len = size * count;
+
+ p->md5->add(p->md5, (ORD8 *)buffer, len);
+ p->of += len;
+ if (p->of > p->size)
+ p->size = p->of;
+ return count;
+}
+
+/* do a printf */
+/* Not implemented */
+static int icmFileMD5_printf(
+icmFile *pp,
+const char *format,
+...
+) {
+ icmFileMD5 *p = (icmFileMD5 *)pp;
+ p->errc = 2;
+ return 0;
+}
+
+/* flush all write data out to secondary storage. Return nz on failure. */
+static int icmFileMD5_flush(
+icmFile *pp
+) {
+ return 0;
+}
+
+/* we're done with the file object, return nz on failure */
+static int icmFileMD5_delete(
+icmFile *pp
+) {
+ icmFileMD5 *p = (icmFileMD5 *)pp;
+
+ p->al->free(p->al, p); /* Free object */
+ return 0;
+}
+
+/* Return the error code. Error code will usually be set */
+/* if we did a seek to other than the current location, */
+/* or did a printf. */
+static int icmFileMD5_geterrc(
+icmFile *pp
+) {
+ icmFileMD5 *p = (icmFileMD5 *)pp;
+
+ return p->errc;
+}
+
+/* Create a checksum dump file access class with allocator */
+icmFile *new_icmFileMD5_a(
+icmMD5 *md5, /* MD5 object to use */
+icmAlloc *al /* heap allocator */
+) {
+ icmFileMD5 *p;
+
+ if ((p = (icmFileMD5 *) al->calloc(al, 1, sizeof(icmFileMD5))) == NULL) {
+ return NULL;
+ }
+ p->md5 = md5; /* MD5 compute object */
+ p->al = al; /* Heap allocator */
+ p->get_size = icmFileMD5_get_size;
+ p->seek = icmFileMD5_seek;
+ p->read = icmFileMD5_read;
+ p->write = icmFileMD5_write;
+ p->gprintf = icmFileMD5_printf;
+ p->flush = icmFileMD5_flush;
+ p->del = icmFileMD5_delete;
+ p->get_errc = icmFileMD5_geterrc;
+
+ p->of = 0;
+ p->errc = 0;
+
+ return (icmFile *)p;
+}
+
+
+/* ============================================= */
+/* Implementation of color transform lookups. */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+/* Methods common to all transforms (icmLuBase) : */
+
+/* Return information about the native lut in/out/pcs colorspaces. */
+/* Any pointer may be NULL if value is not to be returned */
+static void
+icmLutSpaces(
+ struct _icmLuBase *p, /* This */
+ icColorSpaceSignature *ins, /* Return Native input color space */
+ int *inn, /* Return number of input components */
+ icColorSpaceSignature *outs, /* Return Native output color space */
+ int *outn, /* Return number of output components */
+ icColorSpaceSignature *pcs /* Return Native PCS color space */
+ /* (this will be the same as ins or outs */
+ /* depending on the lookup direction) */
+) {
+ if (ins != NULL)
+ *ins = p->inSpace;
+ if (inn != NULL)
+ *inn = (int)number_ColorSpaceSignature(p->inSpace);
+
+ if (outs != NULL)
+ *outs = p->outSpace;
+ if (outn != NULL)
+ *outn = (int)number_ColorSpaceSignature(p->outSpace);
+ if (pcs != NULL)
+ *pcs = p->pcs;
+}
+
+/* Return information about the effective lookup in/out colorspaces, */
+/* including allowance for PCS override. */
+/* Any pointer may be NULL if value is not to be returned */
+static void
+icmLuSpaces(
+ struct _icmLuBase *p, /* This */
+ icColorSpaceSignature *ins, /* Return effective input color space */
+ int *inn, /* Return number of input components */
+ icColorSpaceSignature *outs, /* Return effective output color space */
+ int *outn, /* Return number of output components */
+ icmLuAlgType *alg, /* Return type of lookup algorithm used */
+ icRenderingIntent *intt, /* Return the intent being implented */
+ icmLookupFunc *fnc, /* Return the profile function being implemented */
+ icColorSpaceSignature *pcs, /* Return the profile effective PCS */
+ icmLookupOrder *ord /* return the search Order */
+) {
+ if (ins != NULL)
+ *ins = p->e_inSpace;
+ if (inn != NULL)
+ *inn = (int)number_ColorSpaceSignature(p->e_inSpace);
+
+ if (outs != NULL)
+ *outs = p->e_outSpace;
+ if (outn != NULL)
+ *outn = (int)number_ColorSpaceSignature(p->e_outSpace);
+
+ if (alg != NULL)
+ *alg = p->ttype;
+
+ if (intt != NULL)
+ *intt = p->intent;
+
+ if (fnc != NULL)
+ *fnc = p->function;
+
+ if (pcs != NULL)
+ *pcs = p->e_pcs;
+
+ if (ord != NULL)
+ *ord = p->order;
+}
+
+/* Relative to Absolute for this WP in XYZ */
+static void icmLuXYZ_Rel2Abs(icmLuBase *p, double *out, double *in) {
+ icmMulBy3x3(out, p->toAbs, in);
+}
+
+/* Absolute to Relative for this WP in XYZ */
+static void icmLuXYZ_Abs2Rel(icmLuBase *p, double *out, double *in) {
+ icmMulBy3x3(out, p->fromAbs, in);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Methods common to all non-named transforms (icmLuBase) : */
+
+/* Initialise the LU white and black points from the ICC tags, */
+/* and the corresponding absolute<->relative conversion matrices */
+/* return nz on error */
+static int icmLuInit_Wh_bk(
+struct _icmLuBase *lup
+) {
+ icmXYZArray *whitePointTag, *blackPointTag;
+ icc *p = lup->icp;
+
+ if ((whitePointTag = (icmXYZArray *)p->read_tag(p, icSigMediaWhitePointTag)) == NULL
+ || whitePointTag->ttype != icSigXYZType || whitePointTag->size < 1) {
+ if (p->header->deviceClass != icSigLinkClass
+ && (lup->intent == icAbsoluteColorimetric
+ || lup->intent == icmAbsolutePerceptual
+ || lup->intent == icmAbsoluteSaturation)) {
+ sprintf(p->err,"icc_lookup: Profile is missing Media White Point Tag");
+ p->errc = 1;
+ return 1;
+ }
+ p->err[0] = '\000';
+ p->errc = 0;
+ lup->whitePoint = icmD50; /* safe value */
+ } else
+ lup->whitePoint = whitePointTag->data[0]; /* Copy structure */
+
+ if ((blackPointTag = (icmXYZArray *)p->read_tag(p, icSigMediaBlackPointTag)) == NULL
+ || blackPointTag->ttype != icSigXYZType || blackPointTag->size < 1) {
+ p->err[0] = '\000';
+ p->errc = 0;
+ lup->blackPoint = icmBlack; /* default */
+ lup->blackisassumed = 1; /* We assumed the default */
+ } else {
+ lup->blackPoint = blackPointTag->data[0]; /* Copy structure */
+ lup->blackisassumed = 0; /* The black is from the tag */
+ }
+
+ /* Create absolute <-> relative conversion matricies */
+ icmChromAdaptMatrix(ICM_CAM_BRADFORD, lup->whitePoint, icmD50, lup->toAbs);
+ icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, lup->whitePoint, lup->fromAbs);
+ DBLLL(("toAbs and fromAbs created from wp %f %f %f and D50 %f %f %f\n", lup->whitePoint.X, lup->whitePoint.Y, lup->whitePoint.Z, icmD50.X, icmD50.Y, icmD50.Z));
+ DBLLL(("toAbs = %f %f %f\n %f %f %f\n %f %f %f\n", lup->toAbs[0][0], lup->toAbs[0][1], lup->toAbs[0][2], lup->toAbs[1][0], lup->toAbs[1][1], lup->toAbs[1][2], lup->toAbs[2][0], lup->toAbs[2][1], lup->toAbs[2][2]));
+ DBLLL(("fromAbs = %f %f %f\n %f %f %f\n %f %f %f\n", lup->fromAbs[0][0], lup->fromAbs[0][1], lup->fromAbs[0][2], lup->fromAbs[1][0], lup->fromAbs[1][1], lup->fromAbs[1][2], lup->fromAbs[2][0], lup->fromAbs[2][1], lup->fromAbs[2][2]));
+
+ return 0;
+}
+
+/* Return the media white and black points in absolute XYZ space. */
+/* Note that if not in the icc, the black point will be returned as 0, 0, 0, */
+/* and the function will return nz. */
+/* Any pointer may be NULL if value is not to be returned */
+static int icmLuWh_bk_points(
+struct _icmLuBase *p,
+double *wht,
+double *blk
+) {
+ if (wht != NULL) {
+ icmXYZ2Ary(wht,p->whitePoint);
+ }
+
+ if (blk != NULL) {
+ icmXYZ2Ary(blk,p->blackPoint);
+ }
+ if (p->blackisassumed)
+ return 1;
+ return 0;
+}
+
+/* Get the LU white and black points in LU PCS space, converted to XYZ. */
+/* (ie. white and black will be relative if LU is relative intent etc.) */
+/* Return nz if the black point is being assumed to be 0,0,0 rather */
+/* than being from the tag. */ \
+static int icmLuLu_wh_bk_points(
+struct _icmLuBase *p,
+double *wht,
+double *blk
+) {
+ if (wht != NULL) {
+ icmXYZ2Ary(wht,p->whitePoint);
+ }
+
+ if (blk != NULL) {
+ icmXYZ2Ary(blk,p->blackPoint);
+ }
+ if (p->intent != icAbsoluteColorimetric
+ && p->intent != icmAbsolutePerceptual
+ && p->intent != icmAbsoluteSaturation) {
+ if (wht != NULL)
+ icmMulBy3x3(wht, p->fromAbs, wht);
+ if (blk != NULL)
+ icmMulBy3x3(blk, p->fromAbs, blk);
+ }
+ if (p->blackisassumed)
+ return 1;
+ return 0;
+}
+
+/* Get the native (internal) ranges for the Monochrome or Matrix profile */
+/* Arguments may be NULL */
+static void
+icmLu_get_lutranges (
+ struct _icmLuBase *p,
+ double *inmin, double *inmax, /* Return maximum range of inspace values */
+ double *outmin, double *outmax /* Return maximum range of outspace values */
+) {
+ icTagTypeSignature tagType;
+
+ if (p->ttype == icmLutType) {
+ icmLuLut *pp = (icmLuLut *)p;
+ tagType = pp->lut->ttype;
+ } else {
+ tagType = icMaxEnumType;
+ }
+
+ /* Hmm. we have no way of handling an error from getRange. */
+ /* It shouldn't ever return one unless there is a mismatch between */
+ /* getRange and Lu creation... */
+ getRange(p->icp, p->inSpace, tagType, inmin, inmax);
+ getRange(p->icp, p->outSpace, tagType, outmin, outmax);
+}
+
+/* Get the effective (externally visible) ranges for the all profile types */
+/* Arguments may be NULL */
+static void
+icmLu_get_ranges (
+ struct _icmLuBase *p,
+ double *inmin, double *inmax, /* Return maximum range of inspace values */
+ double *outmin, double *outmax /* Return maximum range of outspace values */
+) {
+ icTagTypeSignature tagType;
+
+ if (p->ttype == icmLutType) {
+ icmLuLut *pp = (icmLuLut *)p;
+ tagType = pp->lut->ttype;
+ } else {
+ tagType = icMaxEnumType;
+ }
+ /* Hmm. we have no way of handling an error from getRange. */
+ /* It shouldn't ever return one unless there is a mismatch between */
+ /* getRange and Lu creation... */
+ getRange(p->icp, p->e_inSpace, tagType, inmin, inmax);
+ getRange(p->icp, p->e_outSpace, tagType, outmin, outmax);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Forward and Backward Monochrome type methods: */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+
+/* Individual components of Fwd conversion: */
+
+/* Actual device to linearised device */
+static int
+icmLuMonoFwd_curve (
+icmLuMono *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ icc *icp = p->icp;
+ int rv = 0;
+
+ /* Translate from device to PCS scale */
+ if ((rv |= p->grayCurve->lookup_fwd(p->grayCurve,&out[0],&in[0])) > 1) {
+ sprintf(icp->err,"icc_lookup: Curve->lookup_fwd() failed");
+ icp->errc = rv;
+ return 2;
+ }
+
+ return rv;
+}
+
+/* Linearised device to relative PCS */
+static int
+icmLuMonoFwd_map (
+icmLuMono *p, /* This */
+double *out, /* Vector of output values (native space) */
+double *in /* Vector of input values (native space) */
+) {
+ int rv = 0;
+ double Y = in[0]; /* In case out == in */
+
+ out[0] = p->pcswht.X;
+ out[1] = p->pcswht.Y;
+ out[2] = p->pcswht.Z;
+ if (p->pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, out, out); /* in Lab */
+
+ /* Scale linearized device level to PCS white */
+ out[0] *= Y;
+ out[1] *= Y;
+ out[2] *= Y;
+
+ return rv;
+}
+
+/* relative PCS to absolute PCS (if required) */
+static int
+icmLuMonoFwd_abs ( /* Abs comes last in Fwd conversion */
+icmLuMono *p, /* This */
+double *out, /* Vector of output values in Effective PCS */
+double *in /* Vector of input values in Native PCS */
+) {
+ int rv = 0;
+
+ if (out != in) { /* Don't alter input values */
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ }
+
+ /* Do absolute conversion */
+ if (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation) {
+
+ if (p->pcs == icSigLabData) /* Convert L to Y */
+ icmLab2XYZ(&p->pcswht, out, out);
+
+ /* Convert from Relative to Absolute colorimetric */
+ icmMulBy3x3(out, p->toAbs, out);
+
+ if (p->e_pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, out, out);
+
+ } else {
+
+ /* Convert from Native to Effective output space */
+ if (p->pcs == icSigLabData && p->e_pcs == icSigXYZData)
+ icmLab2XYZ(&p->pcswht, out, out);
+ else if (p->pcs == icSigXYZData && p->e_pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, out, out);
+ }
+
+ return rv;
+}
+
+
+/* Overall Fwd conversion routine (Dev->PCS) */
+static int
+icmLuMonoFwd_lookup (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Input value */
+) {
+ int rv = 0;
+ icmLuMono *p = (icmLuMono *)pp;
+ rv |= icmLuMonoFwd_curve(p, out, in);
+ rv |= icmLuMonoFwd_map(p, out, out);
+ rv |= icmLuMonoFwd_abs(p, out, out);
+ return rv;
+}
+
+/* Three stage conversion routines */
+static int
+icmLuMonoFwd_lookup_in(
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMono *p = (icmLuMono *)pp;
+ rv |= icmLuMonoFwd_curve(p, out, in);
+ return rv;
+}
+
+static int
+icmLuMonoFwd_lookup_core(
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMono *p = (icmLuMono *)pp;
+ rv |= icmLuMonoFwd_map(p, out, in);
+ rv |= icmLuMonoFwd_abs(p, out, out);
+ return rv;
+}
+
+static int
+icmLuMonoFwd_lookup_out(
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ return rv;
+}
+
+
+/* - - - - - - - - - - - - - - */
+/* Individual components of Bwd conversion: */
+
+/* Convert from relative PCS to absolute PCS (if required) */
+static int
+icmLuMonoBwd_abs ( /* Abs comes first in Bwd conversion */
+icmLuMono *p, /* This */
+double *out, /* Vector of output values in Native PCS */
+double *in /* Vector of input values in Effective PCS */
+) {
+ int rv = 0;
+
+ if (out != in) { /* Don't alter input values */
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ }
+
+ /* Force to monochrome locus in correct space */
+ if (p->e_pcs == icSigLabData) {
+ double wp[3];
+
+ if (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation) {
+ wp[0] = p->whitePoint.X;
+ wp[1] = p->whitePoint.Y;
+ wp[2] = p->whitePoint.Z;
+ } else {
+ wp[0] = p->pcswht.X;
+ wp[1] = p->pcswht.Y;
+ wp[2] = p->pcswht.Z;
+ }
+ icmXYZ2Lab(&p->pcswht, wp, wp); /* Convert to Lab white point */
+ out[1] = out[0]/wp[0] * wp[1];
+ out[2] = out[0]/wp[0] * wp[2];
+
+ } else {
+ if (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation) {
+ out[0] = out[1]/p->whitePoint.Y * p->whitePoint.X;
+ out[2] = out[1]/p->whitePoint.Y * p->whitePoint.Z;
+ } else {
+ out[0] = out[1]/p->pcswht.Y * p->pcswht.X;
+ out[2] = out[1]/p->pcswht.Y * p->pcswht.Z;
+ }
+ }
+
+ /* Do absolute conversion, and conversion to effective PCS */
+ if (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation) {
+
+ if (p->e_pcs == icSigLabData)
+ icmLab2XYZ(&p->pcswht, out, out);
+
+ icmMulBy3x3(out, p->fromAbs, out);
+
+ /* Convert from Effective to Native input space */
+ if (p->pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, out, out);
+
+ } else {
+
+ /* Convert from Effective to Native input space */
+ if (p->e_pcs == icSigLabData && p->pcs == icSigXYZData)
+ icmLab2XYZ(&p->pcswht, out, out);
+ else if (p->e_pcs == icSigXYZData && p->pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, out, out);
+ }
+
+ return rv;
+}
+
+/* Map from relative PCS to linearised device */
+static int
+icmLuMonoBwd_map (
+icmLuMono *p, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values (native space) */
+) {
+ int rv = 0;
+ double pcsw[3];
+
+ pcsw[0] = p->pcswht.X;
+ pcsw[1] = p->pcswht.Y;
+ pcsw[2] = p->pcswht.Z;
+ if (p->pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, pcsw, pcsw); /* in Lab (should be 100.0!) */
+
+ /* Divide linearized device level into PCS white luminence */
+ if (p->pcs == icSigLabData)
+ out[0] = in[0]/pcsw[0];
+ else
+ out[0] = in[1]/pcsw[1];
+
+ return rv;
+}
+
+/* Map from linearised device to actual device */
+static int
+icmLuMonoBwd_curve (
+icmLuMono *p, /* This */
+double *out, /* Output value */
+double *in /* Input value */
+) {
+ icc *icp = p->icp;
+ int rv = 0;
+
+ /* Convert to device value through curve */
+ if ((rv = p->grayCurve->lookup_bwd(p->grayCurve,&out[0],&in[0])) > 1) {
+ sprintf(icp->err,"icc_lookup: Curve->lookup_bwd() failed");
+ icp->errc = rv;
+ return 2;
+ }
+
+ return rv;
+}
+
+/* Overall Bwd conversion routine (PCS->Dev) */
+static int
+icmLuMonoBwd_lookup (
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ double temp[3];
+ int rv = 0;
+ icmLuMono *p = (icmLuMono *)pp;
+ rv |= icmLuMonoBwd_abs(p, temp, in);
+ rv |= icmLuMonoBwd_map(p, out, temp);
+ rv |= icmLuMonoBwd_curve(p, out, out);
+ return rv;
+}
+
+/* Three stage conversion routines */
+static int
+icmLuMonoBwd_lookup_in(
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ return rv;
+}
+
+static int
+icmLuMonoBwd_lookup_core(
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ double temp[3];
+ int rv = 0;
+ icmLuMono *p = (icmLuMono *)pp;
+ rv |= icmLuMonoBwd_abs(p, temp, in);
+ rv |= icmLuMonoBwd_map(p, out, temp);
+ return rv;
+}
+
+static int
+icmLuMonoBwd_lookup_out(
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMono *p = (icmLuMono *)pp;
+ rv |= icmLuMonoBwd_curve(p, out, in);
+ return rv;
+}
+
+/* - - - - - - - - - - - - - - */
+
+static void
+icmLuMono_delete(
+icmLuBase *p
+) {
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+static icmLuBase *
+new_icmLuMono(
+ struct _icc *icp,
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent */
+ icmLookupFunc func, /* Functionality requested */
+ int dir /* 0 = fwd, 1 = bwd */
+) {
+ icmLuMono *p;
+
+ if ((p = (icmLuMono *) icp->al->calloc(icp->al,1,sizeof(icmLuMono))) == NULL)
+ return NULL;
+ p->icp = icp;
+ p->del = icmLuMono_delete;
+ p->lutspaces= icmLutSpaces;
+ p->spaces = icmLuSpaces;
+ p->XYZ_Rel2Abs = icmLuXYZ_Rel2Abs;
+ p->XYZ_Abs2Rel = icmLuXYZ_Abs2Rel;
+ p->get_lutranges = icmLu_get_lutranges;
+ p->get_ranges = icmLu_get_ranges;
+ p->init_wh_bk = icmLuInit_Wh_bk;
+ p->wh_bk_points = icmLuWh_bk_points;
+ p->lu_wh_bk_points = icmLuLu_wh_bk_points;
+ p->fwd_lookup = icmLuMonoFwd_lookup;
+ p->fwd_curve = icmLuMonoFwd_curve;
+ p->fwd_map = icmLuMonoFwd_map;
+ p->fwd_abs = icmLuMonoFwd_abs;
+ p->bwd_lookup = icmLuMonoBwd_lookup;
+ p->bwd_abs = icmLuMonoFwd_abs;
+ p->bwd_map = icmLuMonoFwd_map;
+ p->bwd_curve = icmLuMonoFwd_curve;
+ if (dir) {
+ p->ttype = icmMonoBwdType;
+ p->lookup = icmLuMonoBwd_lookup;
+ p->lookup_in = icmLuMonoBwd_lookup_in;
+ p->lookup_core = icmLuMonoBwd_lookup_core;
+ p->lookup_out = icmLuMonoBwd_lookup_out;
+ p->lookup_inv_in = icmLuMonoFwd_lookup_out; /* Opposite of Bwd_lookup_in */
+ } else {
+ p->ttype = icmMonoFwdType;
+ p->lookup = icmLuMonoFwd_lookup;
+ p->lookup_in = icmLuMonoFwd_lookup_in;
+ p->lookup_core = icmLuMonoFwd_lookup_core;
+ p->lookup_out = icmLuMonoFwd_lookup_out;
+ p->lookup_inv_in = icmLuMonoBwd_lookup_out; /* Opposite of Fwd_lookup_in */
+ }
+
+ /* Lookup the white and black points */
+ if (p->init_wh_bk((icmLuBase *)p)) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* See if the color spaces are appropriate for the mono type */
+ if (number_ColorSpaceSignature(icp->header->colorSpace) != 1
+ || ( icp->header->pcs != icSigXYZData && icp->header->pcs != icSigLabData)) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Find the appropriate tags */
+ if ((p->grayCurve = (icmCurve *)icp->read_tag(icp, icSigGrayTRCTag)) == NULL
+ || p->grayCurve->ttype != icSigCurveType) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ p->pcswht = icp->header->illuminant;
+ p->intent = intent;
+ p->function = func;
+ p->inSpace = inSpace;
+ p->outSpace = outSpace;
+ p->pcs = pcs;
+ p->e_inSpace = e_inSpace;
+ p->e_outSpace = e_outSpace;
+ p->e_pcs = e_pcs;
+
+ return (icmLuBase *)p;
+}
+
+static icmLuBase *
+new_icmLuMonoFwd(
+ struct _icc *icp,
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent */
+ icmLookupFunc func /* Functionality requested */
+) {
+ return new_icmLuMono(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
+ intent, func, 0);
+}
+
+
+static icmLuBase *
+new_icmLuMonoBwd(
+ struct _icc *icp,
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent */
+ icmLookupFunc func /* Functionality requested */
+) {
+ return new_icmLuMono(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
+ intent, func, 1);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+/* Forward and Backward Matrix type conversion */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+
+/* Individual components of Fwd conversion: */
+static int
+icmLuMatrixFwd_curve (
+icmLuMatrix *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ icc *icp = p->icp;
+ int rv = 0;
+
+ /* Curve lookups */
+ if ((rv |= p->redCurve->lookup_fwd( p->redCurve, &out[0],&in[0])) > 1
+ || (rv |= p->greenCurve->lookup_fwd(p->greenCurve,&out[1],&in[1])) > 1
+ || (rv |= p->blueCurve->lookup_fwd( p->blueCurve, &out[2],&in[2])) > 1) {
+ sprintf(icp->err,"icc_lookup: Curve->lookup_fwd() failed");
+ icp->errc = rv;
+ return 2;
+ }
+
+ return rv;
+}
+
+static int
+icmLuMatrixFwd_matrix (
+icmLuMatrix *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ double tt[3];
+
+ /* Matrix */
+ tt[0] = p->mx[0][0] * in[0] + p->mx[0][1] * in[1] + p->mx[0][2] * in[2];
+ tt[1] = p->mx[1][0] * in[0] + p->mx[1][1] * in[1] + p->mx[1][2] * in[2];
+ tt[2] = p->mx[2][0] * in[0] + p->mx[2][1] * in[1] + p->mx[2][2] * in[2];
+
+ out[0] = tt[0];
+ out[1] = tt[1];
+ out[2] = tt[2];
+
+ return rv;
+}
+
+static int
+icmLuMatrixFwd_abs (/* Abs comes last in Fwd conversion */
+icmLuMatrix *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+
+ if (out != in) { /* Don't alter input values */
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ }
+
+ /* If required, convert from Relative to Absolute colorimetric */
+ if (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation) {
+ icmMulBy3x3(out, p->toAbs, out);
+ }
+
+ /* If e_pcs is Lab, then convert XYZ to Lab */
+ if (p->e_pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, out, out);
+
+ return rv;
+}
+
+
+/* Overall Fwd conversion (Dev->PCS)*/
+static int
+icmLuMatrixFwd_lookup (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMatrix *p = (icmLuMatrix *)pp;
+ rv |= icmLuMatrixFwd_curve(p, out, in);
+ rv |= icmLuMatrixFwd_matrix(p, out, out);
+ rv |= icmLuMatrixFwd_abs(p, out, out);
+ return rv;
+}
+
+/* Three stage conversion routines */
+static int
+icmLuMatrixFwd_lookup_in (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMatrix *p = (icmLuMatrix *)pp;
+ rv |= icmLuMatrixFwd_curve(p, out, in);
+ return rv;
+}
+
+static int
+icmLuMatrixFwd_lookup_core (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMatrix *p = (icmLuMatrix *)pp;
+ rv |= icmLuMatrixFwd_matrix(p, out, in);
+ rv |= icmLuMatrixFwd_abs(p, out, out);
+ return rv;
+}
+
+static int
+icmLuMatrixFwd_lookup_out (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ return rv;
+}
+
+/* - - - - - - - - - - - - - - */
+/* Individual components of Bwd conversion: */
+
+static int
+icmLuMatrixBwd_abs (/* Abs comes first in Bwd conversion */
+icmLuMatrix *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+
+ if (out != in) { /* Don't alter input values */
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ }
+
+ /* If e_pcs is Lab, then convert Lab to XYZ */
+ if (p->e_pcs == icSigLabData)
+ icmLab2XYZ(&p->pcswht, out, out);
+
+ /* If required, convert from Absolute to Relative colorimetric */
+ if (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation) {
+ icmMulBy3x3(out, p->fromAbs, out);
+ }
+
+ return rv;
+}
+
+static int
+icmLuMatrixBwd_matrix (
+icmLuMatrix *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ double tt[3];
+
+ tt[0] = in[0];
+ tt[1] = in[1];
+ tt[2] = in[2];
+
+ /* Matrix */
+ out[0] = p->bmx[0][0] * tt[0] + p->bmx[0][1] * tt[1] + p->bmx[0][2] * tt[2];
+ out[1] = p->bmx[1][0] * tt[0] + p->bmx[1][1] * tt[1] + p->bmx[1][2] * tt[2];
+ out[2] = p->bmx[2][0] * tt[0] + p->bmx[2][1] * tt[1] + p->bmx[2][2] * tt[2];
+
+ return rv;
+}
+
+static int
+icmLuMatrixBwd_curve (
+icmLuMatrix *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ icc *icp = p->icp;
+ int rv = 0;
+
+ /* Curves */
+ if ((rv |= p->redCurve->lookup_bwd(p->redCurve,&out[0],&in[0])) > 1
+ || (rv |= p->greenCurve->lookup_bwd(p->greenCurve,&out[1],&in[1])) > 1
+ || (rv |= p->blueCurve->lookup_bwd(p->blueCurve,&out[2],&in[2])) > 1) {
+ sprintf(icp->err,"icc_lookup: Curve->lookup_bwd() failed");
+ icp->errc = rv;
+ return 2;
+ }
+ return rv;
+}
+
+/* Overall Bwd conversion (PCS->Dev) */
+static int
+icmLuMatrixBwd_lookup (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMatrix *p = (icmLuMatrix *)pp;
+ rv |= icmLuMatrixBwd_abs(p, out, in);
+ rv |= icmLuMatrixBwd_matrix(p, out, out);
+ rv |= icmLuMatrixBwd_curve(p, out, out);
+ return rv;
+}
+
+/* Three stage conversion routines */
+static int
+icmLuMatrixBwd_lookup_in (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ return rv;
+}
+
+static int
+icmLuMatrixBwd_lookup_core (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMatrix *p = (icmLuMatrix *)pp;
+ rv |= icmLuMatrixBwd_abs(p, out, in);
+ rv |= icmLuMatrixBwd_matrix(p, out, out);
+ return rv;
+}
+
+static int
+icmLuMatrixBwd_lookup_out (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMatrix *p = (icmLuMatrix *)pp;
+ rv |= icmLuMatrixBwd_curve(p, out, in);
+ return rv;
+}
+
+/* - - - - - - - - - - - - - - */
+
+static void
+icmLuMatrix_delete(
+icmLuBase *p
+) {
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+/* We setup valid fwd and bwd component conversions, */
+/* but setup only the asked for overal conversion. */
+static icmLuBase *
+new_icmLuMatrix(
+ struct _icc *icp,
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent */
+ icmLookupFunc func, /* Functionality requested */
+ int dir /* 0 = fwd, 1 = bwd */
+) {
+ icmLuMatrix *p;
+
+ if ((p = (icmLuMatrix *) icp->al->calloc(icp->al,1,sizeof(icmLuMatrix))) == NULL)
+ return NULL;
+ p->icp = icp;
+ p->del = icmLuMatrix_delete;
+ p->lutspaces= icmLutSpaces;
+ p->spaces = icmLuSpaces;
+ p->XYZ_Rel2Abs = icmLuXYZ_Rel2Abs;
+ p->XYZ_Abs2Rel = icmLuXYZ_Abs2Rel;
+ p->get_lutranges = icmLu_get_lutranges;
+ p->get_ranges = icmLu_get_ranges;
+ p->init_wh_bk = icmLuInit_Wh_bk;
+ p->wh_bk_points = icmLuWh_bk_points;
+ p->lu_wh_bk_points = icmLuLu_wh_bk_points;
+ p->fwd_lookup = icmLuMatrixFwd_lookup;
+ p->fwd_curve = icmLuMatrixFwd_curve;
+ p->fwd_matrix = icmLuMatrixFwd_matrix;
+ p->fwd_abs = icmLuMatrixFwd_abs;
+ p->bwd_lookup = icmLuMatrixBwd_lookup;
+ p->bwd_abs = icmLuMatrixBwd_abs;
+ p->bwd_matrix = icmLuMatrixBwd_matrix;
+ p->bwd_curve = icmLuMatrixBwd_curve;
+ if (dir) {
+ p->ttype = icmMatrixBwdType;
+ p->lookup = icmLuMatrixBwd_lookup;
+ p->lookup_in = icmLuMatrixBwd_lookup_in;
+ p->lookup_core = icmLuMatrixBwd_lookup_core;
+ p->lookup_out = icmLuMatrixBwd_lookup_out;
+ p->lookup_inv_in = icmLuMatrixFwd_lookup_out; /* Opposite of Bwd_lookup_in */
+ } else {
+ p->ttype = icmMatrixFwdType;
+ p->lookup = icmLuMatrixFwd_lookup;
+ p->lookup_in = icmLuMatrixFwd_lookup_in;
+ p->lookup_core = icmLuMatrixFwd_lookup_core;
+ p->lookup_out = icmLuMatrixFwd_lookup_out;
+ p->lookup_inv_in = icmLuMatrixBwd_lookup_out; /* Opposite of Fwd_lookup_in */
+ }
+
+ /* Lookup the white and black points */
+ if (p->init_wh_bk((icmLuBase *)p)) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Note that we can use matrix type even if PCS is Lab, */
+ /* by simply converting it. */
+
+ /* Find the appropriate tags */
+ if ((p->redCurve = (icmCurve *)icp->read_tag(icp, icSigRedTRCTag)) == NULL
+ || p->redCurve->ttype != icSigCurveType
+ || (p->greenCurve = (icmCurve *)icp->read_tag(icp, icSigGreenTRCTag)) == NULL
+ || p->greenCurve->ttype != icSigCurveType
+ || (p->blueCurve = (icmCurve *)icp->read_tag(icp, icSigBlueTRCTag)) == NULL
+ || p->blueCurve->ttype != icSigCurveType
+ || (p->redColrnt = (icmXYZArray *)icp->read_tag(icp, icSigRedColorantTag)) == NULL
+ || p->redColrnt->ttype != icSigXYZType || p->redColrnt->size < 1
+ || (p->greenColrnt = (icmXYZArray *)icp->read_tag(icp, icSigGreenColorantTag)) == NULL
+ || p->greenColrnt->ttype != icSigXYZType || p->greenColrnt->size < 1
+ || (p->blueColrnt = (icmXYZArray *)icp->read_tag(icp, icSigBlueColorantTag)) == NULL
+ || p->blueColrnt->ttype != icSigXYZType || p->blueColrnt->size < 1) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Setup the matrix */
+ p->mx[0][0] = p->redColrnt->data[0].X;
+ p->mx[0][1] = p->greenColrnt->data[0].X;
+ p->mx[0][2] = p->blueColrnt->data[0].X;
+ p->mx[1][1] = p->greenColrnt->data[0].Y;
+ p->mx[1][0] = p->redColrnt->data[0].Y;
+ p->mx[1][2] = p->blueColrnt->data[0].Y;
+ p->mx[2][1] = p->greenColrnt->data[0].Z;
+ p->mx[2][0] = p->redColrnt->data[0].Z;
+ p->mx[2][2] = p->blueColrnt->data[0].Z;
+
+#ifndef ICM_STRICT
+ /* Workaround for buggy Kodak RGB profiles. Their matrix values */
+ /* may be scaled to 100 rather than 1.0, and the colorant curves */
+ /* may be scaled by 0.5 */
+ if (icp->header->cmmId == str2tag("KCMS")) {
+ int i, j, oc = 0;
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ if (p->mx[i][j] > 5.0)
+ oc++;
+ if (oc > 4) { /* Looks like it */
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ p->mx[i][j] /= 100.0;
+ }
+ }
+#endif /* ICM_STRICT */
+
+ if (icmInverse3x3(p->bmx, p->mx) != 0) { /* Compute inverse */
+ sprintf(icp->err,"icc_new_iccLuMatrix: Matrix wasn't invertable");
+ icp->errc = 2;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ p->pcswht = icp->header->illuminant;
+ p->intent = intent;
+ p->function = func;
+ p->inSpace = inSpace;
+ p->outSpace = outSpace;
+ p->pcs = pcs;
+ p->e_inSpace = e_inSpace;
+ p->e_outSpace = e_outSpace;
+ p->e_pcs = e_pcs;
+
+ /* Lookup the white and black points */
+ if (p->init_wh_bk((icmLuBase *)p)) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ return (icmLuBase *)p;
+}
+
+static icmLuBase *
+new_icmLuMatrixFwd(
+ struct _icc *icp,
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent */
+ icmLookupFunc func /* Functionality requested */
+) {
+ return new_icmLuMatrix(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
+ intent, func, 0);
+}
+
+
+static icmLuBase *
+new_icmLuMatrixBwd(
+ struct _icc *icp,
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent */
+ icmLookupFunc func /* Functionality requested */
+) {
+ return new_icmLuMatrix(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
+ intent, func, 1);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+/* Forward and Backward Multi-Dimensional Interpolation type conversion */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+
+/* Components of overall lookup, in order */
+static int icmLuLut_in_abs(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ DBLLL(("icm in_abs: input %s\n",icmPdv(lut->inputChan, in)));
+ if (out != in) {
+ unsigned int i;
+ for (i = 0; i < lut->inputChan; i++) /* Don't alter input values */
+ out[i] = in[i];
+ }
+
+ /* If Bwd Lut, take care of Absolute color space and effective input space */
+ if ((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation)) {
+
+ if (p->e_inSpace == icSigLabData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm in_abs: after Lab2XYZ %s\n",icmPdv(lut->inputChan, out)));
+ }
+
+ /* Convert from Absolute to Relative colorimetric */
+ icmMulBy3x3(out, p->fromAbs, out);
+ DBLLL(("icm in_abs: after fromAbs %s\n",icmPdv(lut->inputChan, out)));
+
+ if (p->inSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm in_abs: after XYZ2Lab %s\n",icmPdv(lut->inputChan, out)));
+ }
+
+ } else {
+
+ /* Convert from Effective to Native input space */
+ if (p->e_inSpace == icSigLabData && p->inSpace == icSigXYZData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm in_abs: after Lab2XYZ %s\n",icmPdv(lut->inputChan, out)));
+ } else if (p->e_inSpace == icSigXYZData && p->inSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm in_abs: after XYZ2Lab %s\n",icmPdv(lut->inputChan, out)));
+ }
+ }
+ DBLLL(("icm in_abs: returning %s\n",icmPdv(lut->inputChan, out)));
+
+ return rv;
+}
+
+/* Possible matrix lookup */
+static int icmLuLut_matrix(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ if (p->usematrix)
+ rv |= lut->lookup_matrix(lut,out,in);
+ else if (out != in) {
+ unsigned int i;
+ for (i = 0; i < lut->inputChan; i++)
+ out[i] = in[i];
+ }
+ return rv;
+}
+
+/* Do input -> input' lookup */
+static int icmLuLut_input(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ p->in_normf(out, in); /* Normalize from input color space */
+ rv |= lut->lookup_input(lut,out,out); /* Lookup though input tables */
+ p->in_denormf(out,out); /* De-normalize to input color space */
+ return rv;
+}
+
+/* Do input'->output' lookup */
+static int icmLuLut_clut(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ double temp[MAX_CHAN];
+ int rv = 0;
+
+ p->in_normf(temp, in); /* Normalize from input color space */
+ rv |= p->lookup_clut(lut,out,temp); /* Lookup though clut tables */
+ p->out_denormf(out,out); /* De-normalize to output color space */
+ return rv;
+}
+
+/* Do output'->output lookup */
+static int icmLuLut_output(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ p->out_normf(out,in); /* Normalize from output color space */
+ rv |= lut->lookup_output(lut,out,out); /* Lookup though output tables */
+ p->out_denormf(out, out); /* De-normalize to output color space */
+ return rv;
+}
+
+static int icmLuLut_out_abs(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ DBLLL(("icm out_abs: input %s\n",icmPdv(lut->outputChan, in)));
+ if (out != in) {
+ unsigned int i;
+ for (i = 0; i < lut->outputChan; i++) /* Don't alter input values */
+ out[i] = in[i];
+ }
+
+ /* If Fwd Lut, take care of Absolute color space */
+ /* and convert from native to effective out PCS */
+ if ((p->function == icmFwd || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation)) {
+
+ if (p->outSpace == icSigLabData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm out_abs: after Lab2XYZ %s\n",icmPdv(lut->outputChan, out)));
+ }
+
+ /* Convert from Relative to Absolute colorimetric XYZ */
+ icmMulBy3x3(out, p->toAbs, out);
+ DBLLL(("icm out_abs: after toAbs %s\n",icmPdv(lut->outputChan, out)));
+
+ if (p->e_outSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm out_abs: after XYZ2Lab %s\n",icmPdv(lut->outputChan, out)));
+ }
+ } else {
+
+ /* Convert from Native to Effective output space */
+ if (p->outSpace == icSigLabData && p->e_outSpace == icSigXYZData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm out_abs: after Lab2 %s\n",icmPdv(lut->outputChan, out)));
+ } else if (p->outSpace == icSigXYZData && p->e_outSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm out_abs: after XYZ2Lab %s\n",icmPdv(lut->outputChan, out)));
+ }
+ }
+ DBLLL(("icm out_abs: returning %s\n",icmPdv(lut->outputChan, out)));
+ return rv;
+}
+
+
+/* Overall lookup */
+static int
+icmLuLut_lookup (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuLut *p = (icmLuLut *)pp;
+ icmLut *lut = p->lut;
+ double temp[MAX_CHAN];
+
+ DBGLL(("icmLuLut_lookup: in = %s\n", icmPdv(p->inputChan, in)));
+ rv |= p->in_abs(p,temp,in); /* Possible absolute conversion */
+ DBGLL(("icmLuLut_lookup: in_abs = %s\n", icmPdv(p->inputChan, temp)));
+ if (p->usematrix) {
+ rv |= lut->lookup_matrix(lut,temp,temp);/* If XYZ, multiply by non-unity matrix */
+ DBGLL(("icmLuLut_lookup: matrix = %s\n", icmPdv(p->inputChan, temp)));
+ }
+ p->in_normf(temp, temp); /* Normalize for input color space */
+ DBGLL(("icmLuLut_lookup: norm = %s\n", icmPdv(p->inputChan, temp)));
+ rv |= lut->lookup_input(lut,temp,temp); /* Lookup though input tables */
+ DBGLL(("icmLuLut_lookup: input = %s\n", icmPdv(p->inputChan, temp)));
+ rv |= p->lookup_clut(lut,out,temp); /* Lookup though clut tables */
+ DBGLL(("icmLuLut_lookup: clut = %s\n", icmPdv(p->outputChan, out)));
+ rv |= lut->lookup_output(lut,out,out); /* Lookup though output tables */
+ DBGLL(("icmLuLut_lookup: output = %s\n", icmPdv(p->outputChan, out)));
+ p->out_denormf(out,out); /* Normalize for output color space */
+ DBGLL(("icmLuLut_lookup: denorm = %s\n", icmPdv(p->outputChan, out)));
+ rv |= p->out_abs(p,out,out); /* Possible absolute conversion */
+ DBGLL(("icmLuLut_lookup: out_abse = %s\n", icmPdv(p->outputChan, out)));
+
+ return rv;
+}
+
+#ifdef NEVER /* The following should be identical in effect to the above. */
+
+/* Overall lookup */
+static int
+icmLuLut_lookup (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int i, rv = 0;
+ icmLuLut *p = (icmLuLut *)pp;
+ icmLut *lut = p->lut;
+ double temp[MAX_CHAN];
+
+ rv |= p->in_abs(p,temp,in);
+ rv |= p->matrix(p,temp,temp);
+ rv |= p->input(p,temp,temp);
+ rv |= p->clut(p,out,temp);
+ rv |= p->output(p,out,out);
+ rv |= p->out_abs(p,out,out);
+
+ return rv;
+}
+
+#endif /* NEVER */
+
+/* Three stage conversion */
+static int
+icmLuLut_lookup_in (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuLut *p = (icmLuLut *)pp;
+ icmLut *lut = p->lut;
+
+ /* If in_abs() or matrix() are active, then we can't have a per component input curve */
+ if (((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation))
+ || (p->e_inSpace != p->inSpace)
+ || (p->usematrix)) {
+ unsigned int i;
+ for (i = 0; i < lut->inputChan; i++)
+ out[i] = in[i];
+ } else {
+ rv |= p->input(p,out,in);
+ }
+ return rv;
+}
+
+static int
+icmLuLut_lookup_core (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuLut *p = (icmLuLut *)pp;
+
+ /* If in_abs() or matrix() are active, then we have to do the per component input curve here */
+ if (((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation))
+ || (p->e_inSpace != p->inSpace)
+ || (p->usematrix)) {
+ double temp[MAX_CHAN];
+ rv |= p->in_abs(p,temp,in);
+ rv |= p->matrix(p,temp,temp);
+ rv |= p->input(p,temp,temp);
+ rv |= p->clut(p,out,temp);
+ } else {
+ rv |= p->clut(p,out,in);
+ }
+
+ /* If out_abs() is active, then we can't have do per component out curve here */
+ if (((p->function == icmFwd || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation))
+ || (p->outSpace != p->e_outSpace)) {
+ rv |= p->output(p,out,out);
+ rv |= p->out_abs(p,out,out);
+ }
+
+ return rv;
+}
+
+static int
+icmLuLut_lookup_out (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuLut *p = (icmLuLut *)pp;
+ icmLut *lut = p->lut;
+
+ /* If out_abs() is active, then we can't have a per component out curve */
+ if (((p->function == icmFwd || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation))
+ || (p->outSpace != p->e_outSpace)) {
+ unsigned int i;
+ for (i = 0; i < lut->outputChan; i++)
+ out[i] = in[i];
+ } else {
+ rv |= p->output(p,out,in);
+ }
+
+ return rv;
+}
+
+/* Inverse three stage conversion (partly implemented) */
+static int
+icmLuLut_lookup_inv_in (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuLut *p = (icmLuLut *)pp;
+ icmLut *lut = p->lut;
+
+ /* If in_abs() or matrix() are active, then we can't have a per component input curve */
+ if (((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation))
+ || (p->e_inSpace != p->inSpace)
+ || (p->usematrix)) {
+ unsigned int i;
+ for (i = 0; i < lut->inputChan; i++)
+ out[i] = in[i];
+ } else {
+ rv |= p->inv_input(p,out,in);
+ }
+ return rv;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Some components of inverse lookup, in order */
+/* ~~ should these be in icmLut (like all the fwd transforms)? */
+
+static int icmLuLut_inv_out_abs(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ DBLLL(("icm inv_out_abs: input %s\n",icmPdv(lut->outputChan, in)));
+ if (out != in) {
+ unsigned int i;
+ for (i = 0; i < lut->outputChan; i++) /* Don't alter input values */
+ out[i] = in[i];
+ }
+
+ /* If Fwd Lut, take care of Absolute color space */
+ /* and convert from effective to native inverse output PCS */
+ /* OutSpace must be PCS: XYZ or Lab */
+ if ((p->function == icmFwd || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation)) {
+
+ if (p->e_outSpace == icSigLabData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm inv_out_abs: after Lab2XYZ %s\n",icmPdv(lut->outputChan, out)));
+ }
+
+ /* Convert from Absolute to Relative colorimetric */
+ icmMulBy3x3(out, p->fromAbs, out);
+ DBLLL(("icm inv_out_abs: after fromAbs %s\n",icmPdv(lut->outputChan, out)));
+
+ if (p->outSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm inv_out_abs: after XYZ2Lab %s\n",icmPdv(lut->outputChan, out)));
+ }
+
+ } else {
+
+ /* Convert from Effective to Native output space */
+ if (p->e_outSpace == icSigLabData && p->outSpace == icSigXYZData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm inv_out_abs: after Lab2XYZ %s\n",icmPdv(lut->outputChan, out)));
+ } else if (p->e_outSpace == icSigXYZData && p->outSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm inv_out_abs: after XYZ2Lab %s\n",icmPdv(lut->outputChan, out)));
+ }
+ }
+ return rv;
+}
+
+/* Do output->output' inverse lookup */
+static int icmLuLut_inv_output(icmLuLut *p, double *out, double *in) {
+ icc *icp = p->icp;
+ icmLut *lut = p->lut;
+ int i;
+ int rv = 0;
+
+ if (lut->rot[0].inited == 0) {
+ for (i = 0; i < lut->outputChan; i++) {
+ rv = icmTable_setup_bwd(icp, &lut->rot[i], lut->outputEnt,
+ lut->outputTable + i * lut->outputEnt);
+ if (rv != 0) {
+ sprintf(icp->err,"icc_Lut_inv_input: Malloc failure in inverse lookup init.");
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ p->out_normf(out,in); /* Normalize from output color space */
+ for (i = 0; i < lut->outputChan; i++) {
+ /* Reverse lookup though output tables */
+ rv |= icmTable_lookup_bwd(&lut->rot[i], &out[i], &out[i]);
+ }
+ p->out_denormf(out, out); /* De-normalize to output color space */
+ return rv;
+}
+
+/* No output' -> input inverse lookup. */
+/* This is non-trivial ! */
+
+/* Do input' -> input inverse lookup */
+static int icmLuLut_inv_input(icmLuLut *p, double *out, double *in) {
+ icc *icp = p->icp;
+ icmLut *lut = p->lut;
+ int i;
+ int rv = 0;
+
+ if (lut->rit[0].inited == 0) {
+ for (i = 0; i < lut->inputChan; i++) {
+ rv = icmTable_setup_bwd(icp, &lut->rit[i], lut->inputEnt,
+ lut->inputTable + i * lut->inputEnt);
+ if (rv != 0) {
+ sprintf(icp->err,"icc_Lut_inv_input: Malloc failure in inverse lookup init.");
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ p->in_normf(out, in); /* Normalize from input color space */
+ for (i = 0; i < lut->inputChan; i++) {
+ /* Reverse lookup though input tables */
+ rv |= icmTable_lookup_bwd(&lut->rit[i], &out[i], &out[i]);
+ }
+ p->in_denormf(out,out); /* De-normalize to input color space */
+ return rv;
+}
+
+/* Possible inverse matrix lookup */
+static int icmLuLut_inv_matrix(icmLuLut *p, double *out, double *in) {
+ icc *icp = p->icp;
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ if (p->usematrix) {
+ double tt[3];
+ if (p->imx_valid == 0) {
+ if (icmInverse3x3(p->imx, lut->e) != 0) { /* Compute inverse */
+ sprintf(icp->err,"icc_new_iccLuMatrix: Matrix wasn't invertable");
+ icp->errc = 2;
+ return 2;
+ }
+ p->imx_valid = 1;
+ }
+ /* Matrix multiply */
+ tt[0] = p->imx[0][0] * in[0] + p->imx[0][1] * in[1] + p->imx[0][2] * in[2];
+ tt[1] = p->imx[1][0] * in[0] + p->imx[1][1] * in[1] + p->imx[1][2] * in[2];
+ tt[2] = p->imx[2][0] * in[0] + p->imx[2][1] * in[1] + p->imx[2][2] * in[2];
+ out[0] = tt[0], out[1] = tt[1], out[2] = tt[2];
+ } else if (out != in) {
+ unsigned int i;
+ for (i = 0; i < lut->inputChan; i++)
+ out[i] = in[i];
+ }
+ return rv;
+}
+
+static int icmLuLut_inv_in_abs(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ DBLLL(("icm inv_in_abs: input %s\n",icmPdv(lut->inputChan, in)));
+ if (out != in) {
+ unsigned int i;
+ for (i = 0; i < lut->inputChan; i++) /* Don't alter input values */
+ out[i] = in[i];
+ }
+
+ /* If Bwd Lut, take care of Absolute color space, and */
+ /* convert from native to effective input space */
+ if ((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation)) {
+
+ if (p->inSpace == icSigLabData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm inv_in_abs: after Lab2XYZ %s\n",icmPdv(lut->inputChan, out)));
+ }
+
+ /* Convert from Relative to Absolute colorimetric XYZ */
+ icmMulBy3x3(out, p->toAbs, out);
+ DBLLL(("icm inv_in_abs: after toAbs %s\n",icmPdv(lut->inputChan, out)));
+
+ if (p->e_inSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm inv_in_abs: after XYZ2Lab %s\n",icmPdv(lut->inputChan, out)));
+ }
+ } else {
+
+ /* Convert from Native to Effective input space */
+ if (p->inSpace == icSigLabData && p->e_inSpace == icSigXYZData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm inv_in_abs: after Lab2XYZ %s\n",icmPdv(lut->inputChan, out)));
+ } else if (p->inSpace == icSigXYZData && p->e_inSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm inv_in_abs: after XYZ2Lab %s\n",icmPdv(lut->inputChan, out)));
+ }
+ }
+ DBLLL(("icm inv_in_abs: returning %s\n",icmPdv(lut->inputChan, out)));
+ return rv;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Return LuLut information */
+static void icmLuLut_get_info(
+ icmLuLut *p, /* this */
+ icmLut **lutp, /* Pointer to icc lut type */
+ icmXYZNumber *pcswhtp, /* Pointer to profile PCS white point */
+ icmXYZNumber *whitep, /* Pointer to profile absolute white point */
+ icmXYZNumber *blackp /* Pointer to profile absolute black point */
+) {
+ if (lutp != NULL)
+ *lutp = p->lut;
+ if (pcswhtp != NULL)
+ *pcswhtp = p->pcswht;
+ if (whitep != NULL)
+ *whitep = p->whitePoint;
+ if (blackp != NULL)
+ *blackp = p->blackPoint;
+}
+
+/* Get the native ranges for the LuLut */
+/* This is computed differently to the mono & matrix types, to */
+/* accurately take into account the different range for 8 bit Lab */
+/* lut type. The range returned for the effective PCS is not so accurate. */
+static void
+icmLuLut_get_lutranges (
+ struct _icmLuBase *pp,
+ double *inmin, double *inmax, /* Return maximum range of inspace values */
+ double *outmin, double *outmax /* Return maximum range of outspace values */
+) {
+ icmLuLut *p = (icmLuLut *)pp;
+ unsigned int i;
+
+ for (i = 0; i < p->lut->inputChan; i++) {
+ inmin[i] = 0.0; /* Normalized range of input space values */
+ inmax[i] = 1.0;
+ }
+ p->in_denormf(inmin,inmin); /* Convert to real colorspace range */
+ p->in_denormf(inmax,inmax);
+
+ /* Make sure min and max are so. */
+ for (i = 0; i < p->lut->inputChan; i++) {
+ if (inmin[i] > inmax[i]) {
+ double tt;
+ tt = inmin[i];
+ inmin[i] = inmax[i];
+ inmax[i] = tt;
+ }
+ }
+
+ for (i = 0; i < p->lut->outputChan; i++) {
+ outmin[i] = 0.0; /* Normalized range of output space values */
+ outmax[i] = 1.0;
+ }
+ p->out_denormf(outmin,outmin); /* Convert to real colorspace range */
+ p->out_denormf(outmax,outmax);
+
+ /* Make sure min and max are so. */
+ for (i = 0; i < p->lut->outputChan; i++) {
+ if (outmin[i] > outmax[i]) {
+ double tt;
+ tt = outmin[i];
+ outmin[i] = outmax[i];
+ outmax[i] = tt;
+ }
+ }
+}
+
+/* Get the effective (externaly visible) ranges for the LuLut */
+/* This will be accurate if there is no override, but only */
+/* aproximate if a PCS override is in place. */
+static void
+icmLuLut_get_ranges (
+ struct _icmLuBase *pp,
+ double *inmin, double *inmax, /* Return maximum range of inspace values */
+ double *outmin, double *outmax /* Return maximum range of outspace values */
+) {
+ icmLuLut *p = (icmLuLut *)pp;
+
+ /* Get the native ranges first */
+ icmLuLut_get_lutranges(pp, inmin, inmax, outmin, outmax);
+
+ /* And replace them if the effective space is different */
+ if (p->e_inSpace != p->inSpace)
+ getRange(p->icp, p->e_inSpace, p->lut->ttype, inmin, inmax);
+
+ if (p->e_outSpace != p->outSpace)
+ getRange(p->icp, p->e_outSpace, p->lut->ttype, outmin, outmax);
+}
+
+/* Return the underlying Lut matrix */
+static void
+icmLuLut_get_matrix (
+ struct _icmLuLut *p,
+ double m[3][3]
+) {
+ int i, j;
+ icmLut *lut = p->lut;
+
+ if (p->usematrix) {
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ m[i][j] = lut->e[i][j]; /* Copy from Lut */
+
+ } else { /* return unity matrix */
+ icmSetUnity3x3(m);
+ }
+}
+
+
+static void
+icmLuLut_delete(
+icmLuBase *p
+) {
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+icmLuBase *
+icc_new_icmLuLut(
+ icc *icp,
+ icTagSignature ttag, /* Target Lut tag */
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS (from header) */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent (For absolute) */
+ icmLookupFunc func /* Functionality requested (for icmLuSpaces()) */
+) {
+ icmLuLut *p;
+
+ if ((p = (icmLuLut *) icp->al->calloc(icp->al,1,sizeof(icmLuLut))) == NULL)
+ return NULL;
+ p->ttype = icmLutType;
+ p->icp = icp;
+ p->del = icmLuLut_delete;
+ p->lutspaces= icmLutSpaces;
+ p->spaces = icmLuSpaces;
+ p->XYZ_Rel2Abs = icmLuXYZ_Rel2Abs;
+ p->XYZ_Abs2Rel = icmLuXYZ_Abs2Rel;
+ p->init_wh_bk = icmLuInit_Wh_bk;
+ p->wh_bk_points = icmLuWh_bk_points;
+ p->lu_wh_bk_points = icmLuLu_wh_bk_points;
+
+ p->lookup = icmLuLut_lookup;
+ p->lookup_in = icmLuLut_lookup_in;
+ p->lookup_core = icmLuLut_lookup_core;
+ p->lookup_out = icmLuLut_lookup_out;
+ p->lookup_inv_in = icmLuLut_lookup_inv_in;
+
+ p->in_abs = icmLuLut_in_abs;
+ p->matrix = icmLuLut_matrix;
+ p->input = icmLuLut_input;
+ p->clut = icmLuLut_clut;
+ p->output = icmLuLut_output;
+ p->out_abs = icmLuLut_out_abs;
+
+ p->inv_in_abs = icmLuLut_inv_in_abs;
+ p->inv_matrix = icmLuLut_inv_matrix;
+ p->inv_input = icmLuLut_inv_input;
+ p->inv_output = icmLuLut_inv_output;
+ p->inv_out_abs = icmLuLut_inv_out_abs;
+
+ p->pcswht = icp->header->illuminant;
+ p->intent = intent; /* used to trigger absolute processing */
+ p->function = func;
+ p->inSpace = inSpace;
+ p->outSpace = outSpace;
+ p->pcs = pcs;
+ p->e_inSpace = e_inSpace;
+ p->e_outSpace = e_outSpace;
+ p->e_pcs = e_pcs;
+ p->get_info = icmLuLut_get_info;
+ p->get_lutranges = icmLuLut_get_lutranges;
+ p->get_ranges = icmLuLut_get_ranges;
+ p->get_matrix = icmLuLut_get_matrix;
+
+ /* Lookup the white and black points */
+ if (p->init_wh_bk((icmLuBase *)p)) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Get the Lut tag, & check that it is expected type */
+ if ((p->lut = (icmLut *)icp->read_tag(icp, ttag)) == NULL
+ || (p->lut->ttype != icSigLut8Type && p->lut->ttype != icSigLut16Type)) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Check if matrix should be used */
+ if (inSpace == icSigXYZData && p->lut->nu_matrix(p->lut))
+ p->usematrix = 1;
+ else
+ p->usematrix = 0;
+
+ /* Lookup input color space to normalized index function */
+ if (getNormFunc(icp, inSpace, p->lut->ttype, icmToLuti, &p->in_normf)) {
+ sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
+ icp->errc = 1;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Lookup normalized index to input color space function */
+ if (getNormFunc(icp, inSpace, p->lut->ttype, icmFromLuti, &p->in_denormf)) {
+ sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
+ icp->errc = 1;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Lookup output color space to normalized Lut entry value function */
+ if (getNormFunc(icp, outSpace, p->lut->ttype, icmToLutv, &p->out_normf)) {
+ sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
+ icp->errc = 1;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Lookup normalized Lut entry value to output color space function */
+ if (getNormFunc(icp, outSpace, p->lut->ttype, icmFromLutv, &p->out_denormf)) {
+ sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
+ icp->errc = 1;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Note that the following two are only used in computing the expected */
+ /* value ranges of the effective PCS. This might not be the best way of */
+ /* doing this. */
+ /* Lookup normalized index to effective input color space function */
+ if (getNormFunc(icp, e_inSpace, p->lut->ttype, icmFromLuti, &p->e_in_denormf)) {
+ sprintf(icp->err,"icc_get_luobj: Unknown effective colorspace");
+ icp->errc = 1;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Lookup normalized Lut entry value to effective output color space function */
+ if (getNormFunc(icp, e_outSpace, p->lut->ttype, icmFromLutv, &p->e_out_denormf)) {
+ sprintf(icp->err,"icc_get_luobj: Unknown effective colorspace");
+ icp->errc = 1;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+// ~~~999
+#ifndef NEVER
+ /* Standard code */
+ /* Determine appropriate clut lookup algorithm */
+ {
+ int use_sx; /* -1 = undecided, 0 = N-linear, 1 = Simplex lookup */
+ icColorSpaceSignature ins, outs; /* In and out Lut color spaces */
+ int inn, outn; /* in and out number of Lut components */
+
+ p->lutspaces((icmLuBase *)p, &ins, &inn, &outs, &outn, NULL);
+
+ /* Determine if the input space is "Device" like, */
+ /* ie. luminance will be expected to vary most strongly */
+ /* with the diagonal change in input coordinates. */
+ switch(ins) {
+
+ /* Luminence is carried by the sum of all the output channels, */
+ /* so output luminence will dominantly be in diagonal direction. */
+ case icSigXYZData: /* This seems to be appropriate ? */
+ case icSigRgbData:
+ case icSigGrayData:
+ case icSigCmykData:
+ case icSigCmyData:
+ case icSigMch6Data:
+ use_sx = 1; /* Simplex interpolation is appropriate */
+ break;
+
+ /* A single channel carries the luminence information */
+ case icSigLabData:
+ case icSigLuvData:
+ case icSigYCbCrData:
+ case icSigYxyData:
+ case icSigHlsData:
+ case icSigHsvData:
+ use_sx = 0; /* N-linear interpolation is appropriate */
+ break;
+ default:
+ use_sx = -1; /* undecided */
+ break;
+ }
+
+ /* If we couldn't figure it out from the input space, */
+ /* check output luminance variation with a diagonal input */
+ /* change. */
+ if (use_sx == -1) {
+ int lc; /* Luminance channel */
+
+ /* Determine where the luminence is carried in the output */
+ switch(outs) {
+
+ /* Luminence is carried by the sum of all the output channels */
+ case icSigRgbData:
+ case icSigGrayData:
+ case icSigCmykData:
+ case icSigCmyData:
+ case icSigMch6Data:
+ lc = -1; /* Average all channels */
+ break;
+
+ /* A single channel carries the luminence information */
+ case icSigLabData:
+ case icSigLuvData:
+ case icSigYCbCrData:
+ case icSigYxyData:
+ lc = 0;
+ break;
+
+ case icSigXYZData:
+ case icSigHlsData:
+ lc = 1;
+ break;
+
+ case icSigHsvData:
+ lc = 2;
+ break;
+
+ /* default means give up and use N-linear type lookup */
+ default:
+ lc = -2;
+ break;
+ }
+
+ /* If we know how luminance is represented in output space */
+ if (lc != -2) {
+ double tout1[MAX_CHAN]; /* Test output values */
+ double tout2[MAX_CHAN];
+ double tt, diag;
+ int n;
+
+ /* Determine input space location of min and max of */
+ /* given output channel (chan = -1 means average of all) */
+ p->lut->min_max(p->lut, tout1, tout2, lc);
+
+ /* Convert to vector and then calculate normalized */
+ /* dot product with diagonal vector (1,1,1...) */
+ for (tt = 0.0, n = 0; n < inn; n++) {
+ tout1[n] = tout2[n] - tout1[n];
+ tt += tout1[n] * tout1[n];
+ }
+ if (tt > 0.0)
+ tt = sqrt(tt); /* normalizing factor for maximum delta */
+ else
+ tt = 1.0; /* Hmm. */
+ tt *= sqrt((double)inn); /* Normalizing factor for diagonal vector */
+ for (diag = 0.0, n = 0; n < outn; n++)
+ diag += tout1[n] / tt;
+ diag = fabs(diag);
+
+ /* I'm not really convinced that this is a reliable */
+ /* indicator of whether simplex interpolation should be used ... */
+ /* It does seem to do the right thing with YCC space though. */
+ if (diag > 0.8) /* Diagonal is dominant ? */
+ use_sx = 1;
+
+ /* If we couldn't figure it out, use N-linear interpolation */
+ if (use_sx == -1)
+ use_sx = 0;
+ }
+ }
+
+ if (use_sx) {
+ p->lookup_clut = p->lut->lookup_clut_sx;
+ } else {
+ p->lookup_clut = p->lut->lookup_clut_nl;
+ }
+ }
+#else /* Development code */
+ /* Determine appropriate clut lookup algorithm, */
+ /* and set optimised simplex tables */
+ {
+ int lualg = 0; /* 0 = simplex, 1 = multi-linear, 2 = optimised simlpex */
+ icColorSpaceSignature ins, outs; /* In and out Lut color spaces */
+ int inn, outn; /* in and out number of Lut components */
+
+ p->lutspaces((icmLuBase *)p, &ins, &inn, &outs, &outn, NULL);
+
+ /* Determine which type of Lut lookup algorithm is likely to give */
+ /* minimal interpolation errors. */
+ /* To use the optimised simplex, we should ideally determine */
+ /* the simplex cell orientation that makes the simplex split diagonal */
+ /* always point towards the white or black points. */
+ switch(ins) {
+
+ case icSigXYZData: /* This seems to be appropriate ? */
+ case icSigRgbData:
+ case icSigGrayData:
+ case icSigCmykData:
+ case icSigCmyData:
+ case icSigMch6Data:
+ lualg = 0; /* Simplex interpolation is appropriate */
+ break;
+
+ case icSigLabData:
+// ~~~~9999 this isn't right! need to lookup Lab 50,0,0 through input curves then */
+/* quantize to clut nodes to figure threshold for axis flips */
+ p->lut->finfo[0].fth = 0.5; p->lut->finfo[0].bthff = 0; p->lut->finfo[0].athff = 1;
+ p->lut->finfo[1].fth = 0.5; p->lut->finfo[1].bthff = 1; p->lut->finfo[1].athff = 0;
+ p->lut->finfo[2].fth = 0.5; p->lut->finfo[2].bthff = 1; p->lut->finfo[2].athff = 0;
+ lualg = 2;
+ break;
+
+ /* !!! Should switch to optimised simplex for these too !!! */
+ case icSigLuvData:
+ case icSigYCbCrData:
+ case icSigYxyData:
+ case icSigHlsData:
+ case icSigHsvData:
+ lualg = 1; /* Multi-linear is best as a default for these ? */
+ break;
+
+ default:
+ lualg = 0; /* Simplex is best if we known nothing. */
+ break;
+ }
+
+ if (lualg == 2) {
+ p->lookup_clut = icmLut_lookup_clut_osx;
+ } else if (lualg == 1) {
+ p->lookup_clut = p->lut->lookup_clut_nl;
+ } else {
+ p->lookup_clut = p->lut->lookup_clut_sx;
+ }
+ }
+#endif
+ return (icmLuBase *)p;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Return an appropriate lookup object */
+/* Return NULL on error, and detailed error in icc */
+static icmLuBase* icc_get_luobj (
+ icc *p, /* ICC */
+ icmLookupFunc func, /* Conversion functionality */
+ icRenderingIntent intent, /* Rendering intent, including icmAbsoluteColorimetricXYZ */
+ icColorSpaceSignature pcsor,/* PCS override (0 = def) */
+ icmLookupOrder order /* Conversion representation search Order */
+) {
+ icmLuBase *luobj = NULL; /* Lookup object to return */
+ icColorSpaceSignature pcs, e_pcs; /* PCS and effective PCS */
+
+#ifdef ICM_STRICT
+ int rv;
+ /* Check that the profile is legal, since we depend on it ? */
+ if ((rv = check_icc_legal(p)) != 0)
+ return NULL;
+#endif /* ICM_STRICT */
+
+ /* Figure out the native and effective PCS */
+ e_pcs = pcs = p->header->pcs;
+ if (pcsor != icmSigDefaultData)
+ e_pcs = pcsor; /* Override */
+
+ /* How we expect to execute the request depends firstly on the type of profile */
+ switch (p->header->deviceClass) {
+ case icSigInputClass:
+ case icSigDisplayClass:
+ case icSigColorSpaceClass:
+
+ /* Look for Intent based AToBX profile + optional BToAX reverse */
+ /* or for AToB0 based profile + optional BToA0 reverse */
+ /* or three component matrix profile (reversable) */
+ /* or momochrome table profile (reversable) */
+ /* No Lut intent for ICC < V2.4, but possible for >= V2.4, */
+ /* so fall back if we can't find the chosen Lut intent.. */
+ /* Device <-> PCS */
+ /* Determine the algorithm and set its parameters */
+
+ switch (func) {
+ icRenderingIntent fbintent; /* Fallback intent */
+ icTagSignature ttag, fbtag;
+
+ case icmFwd: /* Device to PCS */
+ if (intent == icmDefaultIntent)
+ intent = icPerceptual; /* Make this the default */
+
+ switch (intent) {
+ case icAbsoluteColorimetric:
+ ttag = icSigAToB1Tag;
+ fbtag = icSigAToB0Tag;
+ fbintent = intent;
+ break;
+ case icRelativeColorimetric:
+ ttag = icSigAToB1Tag;
+ fbtag = icSigAToB0Tag;
+ fbintent = icmDefaultIntent;
+ break;
+ case icPerceptual:
+ ttag = icSigAToB0Tag;
+ fbtag = icSigAToB0Tag;
+ fbintent = icmDefaultIntent;
+ break;
+ case icSaturation:
+ ttag = icSigAToB2Tag;
+ fbtag = icSigAToB0Tag;
+ fbintent = icmDefaultIntent;
+ break;
+ case icmAbsolutePerceptual: /* Special icclib intent */
+ ttag = icSigAToB0Tag;
+ fbtag = icSigAToB0Tag;
+ fbintent = intent;
+ break;
+ case icmAbsoluteSaturation: /* Special icclib intent */
+ ttag = icSigAToB2Tag;
+ fbtag = icSigAToB0Tag;
+ fbintent = intent;
+ break;
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown intent");
+ p->errc = 1;
+ return NULL;
+ }
+
+ if (order != icmLuOrdRev) {
+ /* Try Lut type lookup with the chosen intent first */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try the fallback tag */
+ if ((luobj = icc_new_icmLuLut(p, fbtag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ fbintent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ } else {
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try Lut type lookup last */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try the fallback tag */
+ if ((luobj = icc_new_icmLuLut(p, fbtag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ fbintent, func)) != NULL)
+ break;
+ }
+ break;
+
+ case icmBwd: /* PCS to Device */
+ if (intent == icmDefaultIntent)
+ intent = icPerceptual; /* Make this the default */
+
+ switch (intent) {
+ case icAbsoluteColorimetric:
+ ttag = icSigBToA1Tag;
+ fbtag = icSigBToA0Tag;
+ fbintent = intent;
+ break;
+ case icRelativeColorimetric:
+ ttag = icSigBToA1Tag;
+ fbtag = icSigBToA0Tag;
+ fbintent = icmDefaultIntent;
+ break;
+ case icPerceptual:
+ ttag = icSigBToA0Tag;
+ fbtag = icSigBToA0Tag;
+ fbintent = icmDefaultIntent;
+ break;
+ case icSaturation:
+ ttag = icSigBToA2Tag;
+ fbtag = icSigBToA0Tag;
+ fbintent = icmDefaultIntent;
+ break;
+ case icmAbsolutePerceptual: /* Special icclib intent */
+ ttag = icSigBToA0Tag;
+ fbtag = icSigBToA0Tag;
+ fbintent = intent;
+ break;
+ case icmAbsoluteSaturation: /* Special icclib intent */
+ ttag = icSigBToA2Tag;
+ fbtag = icSigBToA0Tag;
+ fbintent = intent;
+ break;
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown intent");
+ p->errc = 1;
+ return NULL;
+ }
+
+
+ if (order != icmLuOrdRev) {
+ /* Try Lut type lookup first */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try the fallback Lut */
+ if ((luobj = icc_new_icmLuLut(p, fbtag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ fbintent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+ } else {
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try Lut type lookup last */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try the fallback Lut */
+ if ((luobj = icc_new_icmLuLut(p, fbtag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ fbintent, func)) != NULL)
+ break;
+ }
+ break;
+
+ default:
+ sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
+ p->errc = 1;
+ return NULL;
+ }
+ break;
+
+ case icSigOutputClass:
+ /* Expect BToA Lut and optional AToB Lut, All intents, expect gamut */
+ /* or momochrome table profile (reversable) */
+ /* Device <-> PCS */
+ /* Gamut Lut - no intent */
+ /* Optional preview links PCS <-> PCS */
+
+ /* Determine the algorithm and set its parameters */
+ switch (func) {
+ icTagSignature ttag;
+
+ case icmFwd: /* Device to PCS */
+
+ if (intent == icmDefaultIntent)
+ intent = icPerceptual; /* Make this the default */
+
+ switch (intent) {
+ case icRelativeColorimetric:
+ case icAbsoluteColorimetric:
+ ttag = icSigAToB1Tag;
+ break;
+ case icPerceptual:
+ case icmAbsolutePerceptual: /* Special icclib intent */
+ ttag = icSigAToB0Tag;
+ break;
+ case icSaturation:
+ case icmAbsoluteSaturation: /* Special icclib intent */
+ ttag = icSigAToB2Tag;
+ break;
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown intent");
+ p->errc = 1;
+ return NULL;
+ }
+
+ if (order != icmLuOrdRev) {
+ /* Try Lut type lookup first */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL) {
+ break;
+ }
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+ } else {
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try Lut type lookup last */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+ }
+ break;
+
+ case icmBwd: /* PCS to Device */
+
+ if (intent == icmDefaultIntent)
+ intent = icPerceptual; /* Make this the default */
+
+ switch (intent) {
+ case icRelativeColorimetric:
+ case icAbsoluteColorimetric:
+ ttag = icSigBToA1Tag;
+ break;
+ case icPerceptual:
+ case icmAbsolutePerceptual: /* Special icclib intent */
+ ttag = icSigBToA0Tag;
+ break;
+ case icSaturation:
+ case icmAbsoluteSaturation: /* Special icclib intent */
+ ttag = icSigBToA2Tag;
+ break;
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown intent");
+ p->errc = 1;
+ return NULL;
+ }
+
+ if (order != icmLuOrdRev) {
+ /* Try Lut type lookup first */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+ } else {
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try Lut type lookup last */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+ }
+ break;
+
+ case icmGamut: /* PCS to 1D */
+
+#ifdef ICM_STRICT /* Allow only default and absolute */
+ if (intent != icmDefaultIntent
+ && intent != icAbsoluteColorimetric) {
+ sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Gamut table");
+ p->errc = 1;
+ return NULL;
+ }
+#else /* Be more forgiving */
+ switch (intent) {
+ case icAbsoluteColorimetric:
+ case icmAbsolutePerceptual: /* Special icclib intent */
+ case icmAbsoluteSaturation: /* Special icclib intent */
+ break;
+ case icmDefaultIntent:
+ case icRelativeColorimetric:
+ case icPerceptual:
+ case icSaturation:
+ intent = icmDefaultIntent; /* Make all other look like default */
+ break;
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown intent (0x%x)",intent);
+ p->errc = 1;
+ return NULL;
+ }
+
+#endif
+ /* If the target tag exists, and it is a Lut */
+ luobj = icc_new_icmLuLut(p, icSigGamutTag,
+ pcs, icSigGrayData, pcs,
+ e_pcs, icSigGrayData, e_pcs,
+ intent, func);
+ break;
+
+ case icmPreview: /* PCS to PCS */
+
+ switch (intent) {
+ case icRelativeColorimetric:
+ ttag = icSigPreview1Tag;
+ break;
+ case icPerceptual:
+ ttag = icSigPreview0Tag;
+ break;
+ case icSaturation:
+ ttag = icSigPreview2Tag;
+ break;
+ case icAbsoluteColorimetric:
+ case icmAbsolutePerceptual: /* Special icclib intent */
+ case icmAbsoluteSaturation: /* Special icclib intent */
+ sprintf(p->err,"icc_get_luobj: Intent is inappropriate for preview table");
+ p->errc = 1;
+ return NULL;
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown intent");
+ p->errc = 1;
+ return NULL;
+ }
+
+ /* If the target tag exists, and it is a Lut */
+ luobj = icc_new_icmLuLut(p, ttag,
+ pcs, pcs, pcs,
+ e_pcs, e_pcs, e_pcs,
+ intent, func);
+ break;
+
+ default:
+ sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
+ p->errc = 1;
+ return NULL;
+ }
+ break;
+
+ case icSigLinkClass:
+ /* Expect AToB0 Lut and optional BToA0 Lut, One intent in header */
+ /* Device <-> Device */
+
+ if (intent != p->header->renderingIntent
+ && intent != icmDefaultIntent) {
+ sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Link profile");
+ p->errc = 1;
+ return NULL;
+ }
+ intent = p->header->renderingIntent;
+
+ /* Determine the algorithm and set its parameters */
+ switch (func) {
+ case icmFwd: /* Device to PCS (== Device) */
+
+ luobj = icc_new_icmLuLut(p, icSigAToB0Tag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, pcs, pcs,
+ intent, func);
+ break;
+
+ case icmBwd: /* PCS (== Device) to Device */
+
+ luobj = icc_new_icmLuLut(p, icSigBToA0Tag,
+ pcs, p->header->colorSpace, pcs,
+ pcs, p->header->colorSpace, pcs,
+ intent, func);
+ break;
+
+ default:
+ sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
+ p->errc = 1;
+ return NULL;
+ }
+ break;
+
+ case icSigAbstractClass:
+ /* Expect AToB0 Lut and option BToA0 Lut, with either relative or absolute intent. */
+ /* PCS <-> PCS */
+ /* Determine the algorithm and set its parameters */
+
+ if (intent != icmDefaultIntent
+ && intent != icRelativeColorimetric
+ && intent != icAbsoluteColorimetric) {
+ sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Abstract profile");
+ p->errc = 1;
+ return NULL;
+ }
+
+ switch (func) {
+ case icmFwd: /* PCS (== Device) to PCS */
+
+ luobj = icc_new_icmLuLut(p, icSigAToB0Tag,
+ p->header->colorSpace, pcs, pcs,
+ e_pcs, e_pcs, e_pcs,
+ intent, func);
+ break;
+
+ case icmBwd: /* PCS to PCS (== Device) */
+
+ luobj = icc_new_icmLuLut(p, icSigBToA0Tag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, e_pcs, e_pcs,
+ intent, func);
+ break;
+
+ default:
+ sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
+ p->errc = 1;
+ return NULL;
+ }
+ break;
+
+ case icSigNamedColorClass:
+ /* Expect Name -> Device, Optional PCS */
+ /* and a reverse lookup would be useful */
+ /* (ie. PCS or Device coords to closest named color) */
+ /* ~~ to be implemented ~~ */
+
+ /* ~~ Absolute intent is valid for processing of */
+ /* PCS from named Colors. Also allow for e_pcs */
+ if (intent != icmDefaultIntent
+ && intent != icRelativeColorimetric
+ && intent != icAbsoluteColorimetric) {
+ sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Named Color profile");
+ p->errc = 1;
+ return NULL;
+ }
+
+ sprintf(p->err,"icc_get_luobj: Named Colors not handled yet");
+ p->errc = 1;
+ return NULL;
+
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown profile class");
+ p->errc = 1;
+ return NULL;
+ }
+
+ if (luobj == NULL) {
+ sprintf(p->err,"icc_get_luobj: Unable to locate usable conversion");
+ p->errc = 1;
+ } else {
+ luobj->order = order;
+ }
+
+ return luobj;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Returns total ink limit and channel maximums. */
+/* Returns -1.0 if not applicable for this type of profile. */
+/* Returns -1.0 for grey, additive, or any profiles < 4 channels. */
+/* This is a place holder that uses a heuristic, */
+/* until there is a private or standard tag for this information */
+static double icm_get_tac( /* return TAC */
+icc *p,
+double *chmax, /* device return channel sums. May be NULL */
+void (*calfunc)(void *cntx, double *out, double *in), /* Optional calibration func. */
+void *cntx
+) {
+ icmHeader *rh = p->header;
+ icmLuBase *luo;
+ icmLuLut *ll;
+ icmLut *lut;
+ icColorSpaceSignature outs; /* Type of output space */
+ int inn, outn; /* Number of components */
+ icmLuAlgType alg; /* Type of lookup algorithm */
+ double tac = 0.0;
+ double max[MAX_CHAN]; /* Channel maximums */
+ int i, f;
+ unsigned int uf;
+ int size; /* Lut table size */
+ double *gp; /* Pointer to grid cube base */
+
+ /* If not something that can really have a TAC */
+ if (rh->deviceClass != icSigDisplayClass
+ && rh->deviceClass != icSigOutputClass
+ && rh->deviceClass != icSigLinkClass) {
+ return -1.0;
+ }
+
+ /* If not a suitable color space */
+ switch (rh->colorSpace) {
+ /* Not applicable */
+ case icSigXYZData:
+ case icSigLabData:
+ case icSigLuvData:
+ case icSigYCbCrData:
+ case icSigYxyData:
+ case icSigHsvData:
+ case icSigHlsData:
+ return -1.0;
+
+ /* Assume no limit */
+ case icSigGrayData:
+ case icSig2colorData:
+ case icSig3colorData:
+ case icSigRgbData:
+ return -1.0;
+
+ default:
+ break;
+ }
+
+ /* Get a PCS->device colorimetric lookup */
+ if ((luo = p->get_luobj(p, icmBwd, icRelativeColorimetric, icmSigDefaultData, icmLuOrdNorm)) == NULL) {
+ if ((luo = p->get_luobj(p, icmBwd, icmDefaultIntent, icmSigDefaultData, icmLuOrdNorm)) == NULL) {
+ return -1.0;
+ }
+ }
+
+ /* Get details of conversion (Arguments may be NULL if info not needed) */
+ luo->spaces(luo, NULL, &inn, &outs, &outn, &alg, NULL, NULL, NULL, NULL);
+
+ /* Assume any non-Lut type doesn't have a TAC */
+ if (alg != icmLutType) {
+ return -1.0;
+ }
+
+ ll = (icmLuLut *)luo;
+
+ /* We have a Lut type. Search the lut for the largest values */
+ for (f = 0; f < outn; f++)
+ max[f] = 0.0;
+
+ lut = ll->lut;
+ gp = lut->clutTable; /* Base of grid array */
+ size = sat_pow(lut->clutPoints,lut->inputChan);
+ for (i = 0; i < size; i++) {
+ double tot, vv[MAX_CHAN];
+
+ lut->lookup_output(lut,vv,gp); /* Lookup though output tables */
+ ll->out_denormf(vv,vv); /* Normalize for output color space */
+
+ if (calfunc != NULL)
+ calfunc(cntx, vv, vv); /* Apply device calibration */
+
+ for (tot = 0.0, uf = 0; uf < lut->outputChan; uf++) {
+ tot += vv[uf];
+ if (vv[uf] > max[uf])
+ max[uf] = vv[uf];
+ }
+ if (tot > tac)
+ tac = tot;
+ gp += lut->outputChan;
+ }
+
+ if (chmax != NULL) {
+ for (f = 0; f < outn; f++)
+ chmax[f] = max[f];
+ }
+
+ luo->del(luo);
+
+ return tac;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Create an empty object. Return NULL on error */
+icc *new_icc_a(
+icmAlloc *al /* Memory allocator */
+) {
+ unsigned int i;
+ icc *p;
+
+ if ((p = (icc *) al->calloc(al, 1,sizeof(icc))) == NULL) {
+ return NULL;
+ }
+ p->ver = 0; /* default is V2 profile */
+
+ p->al = al; /* Heap allocator */
+
+ p->get_rfp = icc_get_rfp;
+ p->set_version = icc_set_version;
+ p->get_size = icc_get_size;
+ p->read = icc_read;
+ p->read_x = icc_read_x;
+ p->write = icc_write;
+ p->write_x = icc_write_x;
+ p->dump = icc_dump;
+ p->del = icc_delete;
+ p->add_tag = icc_add_tag;
+ p->link_tag = icc_link_tag;
+ p->find_tag = icc_find_tag;
+ p->read_tag = icc_read_tag;
+ p->read_tag_any = icc_read_tag_any;
+ p->rename_tag = icc_rename_tag;
+ p->unread_tag = icc_unread_tag;
+ p->read_all_tags = icc_read_all_tags;
+ p->delete_tag = icc_delete_tag;
+ p->check_id = icc_check_id;
+ p->get_tac = icm_get_tac;
+ p->get_luobj = icc_get_luobj;
+ p->new_clutluobj = icc_new_icmLuLut;
+
+#if defined(__IBMC__) && defined(_M_IX86)
+ _control87(EM_UNDERFLOW, EM_UNDERFLOW);
+#endif
+
+ /* Allocate a header object */
+ if ((p->header = new_icmHeader(p)) == NULL) {
+ al->free(al, p);
+ return NULL;
+ }
+
+ /* Values that must be set before writing */
+ p->header->deviceClass = icMaxEnumClass;/* Type of profile - must be set! */
+ p->header->colorSpace = icMaxEnumData; /* Clr space of data - must be set! */
+ p->header->pcs = icMaxEnumData; /* PCS: XYZ or Lab - must be set! */
+ p->header->renderingIntent = icMaxEnumIntent; /* Rendering intent - must be set ! */
+
+ /* Values that should be set before writing */
+ p->header->manufacturer = -1; /* Dev manufacturer - should be set ! */
+ p->header->model = -1; /* Dev model number - should be set ! */
+ p->header->attributes.l = 0; /* ICC Device attributes - should set ! */
+ p->header->flags = 0; /* Embedding flags - should be set ! */
+
+ /* Values that may be set before writing */
+ p->header->attributes.h = 0; /* Dev Device attributes - may be set ! */
+ p->header->creator = str2tag("argl"); /* Profile creator - Argyll - may be set ! */
+
+ /* Init default values in header */
+ p->header->cmmId = str2tag("argl"); /* CMM for profile - Argyll CMM */
+ p->header->majv = 2; /* Current version 2.2.0 */
+ p->header->minv = 2;
+ p->header->bfv = 0;
+ setcur_DateTimeNumber(&p->header->date);/* Creation Date */
+ p->header->platform = icSigMicrosoft; /* Primary Platform */
+ p->header->illuminant = icmD50; /* Profile illuminant - D50 */
+
+ /* Values that will be created automatically */
+ for (i = 0; i < 16; i++)
+ p->header->id[i] = 0;
+
+ return p;
+}
+
+
+/* ---------------------------------------------------------- */
+/* Print an int vector to a string. */
+/* Returned static buffer is re-used every 5 calls. */
+char *icmPiv(int di, int *p) {
+ static char buf[5][MAX_CHAN * 16];
+ static int ix = 0;
+ int e;
+ char *bp;
+
+ if (++ix >= 5)
+ ix = 0;
+ bp = buf[ix];
+
+ if (di > MAX_CHAN)
+ di = MAX_CHAN; /* Make sure that buf isn't overrun */
+
+ for (e = 0; e < di; e++) {
+ if (e > 0)
+ *bp++ = ' ';
+ sprintf(bp, "%d", p[e]); bp += strlen(bp);
+ }
+ return buf[ix];
+}
+
+/* Print a double color vector to a string. */
+/* Returned static buffer is re-used every 5 calls. */
+char *icmPdv(int di, double *p) {
+ static char buf[5][MAX_CHAN * 16];
+ static int ix = 0;
+ int e;
+ char *bp;
+
+ if (++ix >= 5)
+ ix = 0;
+ bp = buf[ix];
+
+ if (di > MAX_CHAN)
+ di = MAX_CHAN; /* Make sure that buf isn't overrun */
+
+ for (e = 0; e < di; e++) {
+ if (e > 0)
+ *bp++ = ' ';
+ sprintf(bp, "%f", p[e]); bp += strlen(bp);
+ }
+ return buf[ix];
+}
+
+/* Print a float color vector to a string. */
+/* Returned static buffer is re-used every 5 calls. */
+char *icmPfv(int di, float *p) {
+ static char buf[5][MAX_CHAN * 16];
+ static int ix = 0;
+ int e;
+ char *bp;
+
+ if (++ix >= 5)
+ ix = 0;
+ bp = buf[ix];
+
+ if (di > MAX_CHAN)
+ di = MAX_CHAN; /* Make sure that buf isn't overrun */
+
+ for (e = 0; e < di; e++) {
+ if (e > 0)
+ *bp++ = ' ';
+ sprintf(bp, "%f", p[e]); bp += strlen(bp);
+ }
+ return buf[ix];
+}
+
+/* Print an 0..1 range XYZ as a D50 Lab string */
+/* Returned static buffer is re-used every 5 calls. */
+char *icmPLab(double *p) {
+ static char buf[5][MAX_CHAN * 16];
+ static int ix = 0;
+ int e;
+ char *bp;
+ double lab[3];
+
+ if (++ix >= 5)
+ ix = 0;
+ bp = buf[ix];
+
+ icmXYZ2Lab(&icmD50, lab, p);
+
+ for (e = 0; e < 3; e++) {
+ if (e > 0)
+ *bp++ = ' ';
+ sprintf(bp, "%f", lab[e]); bp += strlen(bp);
+ }
+ return buf[ix];
+}
+
+
+/* ---------------------------------------------------------- */
+