From 22f703cab05b7cd368f4de9e03991b7664dc5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 13:56:46 +0200 Subject: Initial import of argyll version 1.5.1-8 --- icc/icc.c | 17838 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 17838 insertions(+) create mode 100644 icc/icc.c (limited to 'icc/icc.c') diff --git a/icc/icc.c b/icc/icc.c new file mode 100644 index 0000000..95dc0e9 --- /dev/null +++ b/icc/icc.c @@ -0,0 +1,17838 @@ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __sun +#include +#endif +#if defined(__IBMC__) && defined(_M_IX86) +#include +#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 < 1) { + sprintf(icp->err,"icmLut_read: No input channels!"); + return icp->errc = 1; + } + + 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; cu.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; cu.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; cu.table.channels; c++) { + op->gprintf(op," channel #%d\n",c); + for (i=0; iu.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 = \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]; +} + + +/* ---------------------------------------------------------- */ + -- cgit v1.2.3