diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/gs/icclib/icc.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/gs/icclib/icc.c')
-rwxr-xr-x | sys/src/cmd/gs/icclib/icc.c | 12776 |
1 files changed, 12776 insertions, 0 deletions
diff --git a/sys/src/cmd/gs/icclib/icc.c b/sys/src/cmd/gs/icclib/icc.c new file mode 100755 index 000000000..a21d55395 --- /dev/null +++ b/sys/src/cmd/gs/icclib/icc.c @@ -0,0 +1,12776 @@ + +/* + * International Color Consortium Format Library (icclib) + * For ICC profile version 3.4 + * + * Author: Graeme W. Gill + * Date: 2002/04/22 + * Version: 2.02 + * + * Copyright 1997 - 2002 Graeme W. Gill + * See Licence.txt file for conditions of use. + */ + +/* + * 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 ? + * + * 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. + * + * Should probably reject reading or writing profiles with majv != 2 ? + * + * 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 */ + +/* Trial: 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. */ +/* This seems a good idea. */ + +#define SYMETRICAL_DEFAULT_LAB_RANGE + +/* + * Change History: + * + * 2.02 + * Merged rename of [u]int64 to icm[Ui][I]nt64 (to work around + * AIX 5.1L portability bug) from Raph Levien. + * + * Fixed stray , in icmLookupOrder structure definition (from Dan Coby) + * + * 2.01 + * Change TextDescription code to not barf if #undef ICM_STRICT and + * Apple scriptcode not padded to 67 bytes. + * + * Add get_ranges() method to all Lu types, not just LuLut. + * Fix bug in PCS overide logic that was causing + * reverse conversions to apply the wrong conversion. + * + * Added Delta E convenience functions icmLabDE() and + * icmCIE94() etc. + * + * Merged Raph Levien's cleanups, to quiet gcc warnings. + * + * Merged another couple of warning cleanups from Jouk Jansen. + * + * 2.00 + * Change absolute conversion to be white point only, and use + * Bradford transform by default. (ie. we are now ignoring the + * comment in section 6.4.22 of the 1998 spec. about the + * media black point being used for absolute colorimetry, + * ignoring the recommendation on page 118 in section E.5, + * and are taking up the recommendation on page 124 in section + * E.16 that a more sophisticated chromatic adaption model be used.) + * + * This is for better compatibility with other CMM's, and to + * improve the results when using simple links between + * profiles with non-D50 white points. Standard profiles + * like sRGB will also be more accurate when interpreted + * with absolute colorimetric intent. + * This will cause some slight incompatibilty with previous + * versions of icclib. + * + * Added ColorSync 2.5 specific VideoCardGamma tag support + * (from Neil Okamoto) + * + * 1.31 + * Added file I/O class to allow substitution of alternative ICC profile + * file access. Provide standard file class instance, and memory image + * instance of file I/O class as default and example. + * Added an optional new_icc_a() object creator, that takes a memory + * allocator class instance. This allows an alternate memory heap to + * be used with the icc class. + * Renamed object free() methods to del() for more consistency with new(). + * + * 1.30 + * Added workaround for reading some Adobe profiles with confused header DateTime. + * Enhanced tag allocate() methods so that they can resize allocations. + * Enhanced icmLut_set_tables() to access grid points in a cache friendly order. + * Fixed bug in check_icc_legal() that caused bogus errors, removed + * uneccessary static declarations in icc.h, and fixed a bug in + * icmTable_lookup_bwd() that effected both accuracy and speed. (Thanks to Andrei Frolov) + * Removed icmAbsoluteColorimetricXYZ intent, and replaced it with + * a PCS overide capability. This adds a new parameter to get_luobj() + * Added Lab translations of some XYZ "dump" strings. + * Fix memory leak after failed tag read + rename_tag function + * + shared library support changes. (Thanks to Carles Llopis). + * Changed all the public 2str utility routines to a single function + * that can be used to interpret an enumeration or tag in a human + * readable form. + * + * 1.23 + * Fixed important bug in Lut read/write. The matrix values had their + * rows and columns switched. Not many profiles exercise this code. + * Thanks to David Gillman for discovering this problem. + * Fixup compiler complains about illegal enum values for icmCurveStyle, + * and icmDataStyle. Malloc memory icmLut_lookup_clut_nl for gw[], so that + * it is more friendly to systems with a limited stack. (Thanks to Dave White) + * + * 1.22 99/11/11 Snapshot of current code. + * Added more hooks to support inherited implementation of + * color conversion, used in Argyll to support reversing + * multi-dimentional table lookups. + * Cleaned up color conversion code to make it easier to follow. + * Adding simplex interpolation for non-Lab style input space interpolation. + * Fix Sun misalignment and realloc problems (Thanks to Allan N. Hessenflow) + * Fixed endian problem with Unicode on read and write. + * Expanded icmTextDescription_dump() to do hex dump of Unicode and ScriptCode. + * Changed over to ICC.1:1998-09 .h file. + * Started implementing ICC.1:1998-09, but not complete yet! + * + * 1.21 99/2/14 + * After re-reading Michael Bourgoin's 1998 SIGGRAPH notes, + * I have consolidated the Lut input index, and table value encodings. + * The default set_tables() scaling has been adjusted appropriately + * for this correction of Lab encoding. + * Trying to create an 8 bit XYZ Lut will now fail if icclib helper + * functions are used to create it. + * + * 1.20 99/2/7 + * Added profile color lookup functon. + * Added set_tables() support. + * Various bug fixes and enhancements. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <sys/types.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include <time.h> +#ifdef __sun +#include <unistd.h> +#endif +#if defined(__IBMC__) && defined(_M_IX86) +#include <float.h> +#endif +#include "icc.h" + +/* ========================================================== */ +/* Default system interface object implementations */ + +/* Standard Stream file I/O icmFile compatible class */ +/* Note that this uses malloc, so replace class if */ +/* you need a different memory allocator. */ + +/* Set current position to offset. Return 0 on success, nz on failure. */ +static int icmFileStd_seek( +icmFile *pp, +long int offset +) { + icmFileStd *p = (icmFileStd *)pp; + + return fseek(p->fp, offset, SEEK_SET); +} + +/* Read count items of size length. Return number of items successfully read. */ +static size_t icmFileStd_read( +icmFile *pp, +void *buffer, +size_t size, +size_t count +) { + icmFileStd *p = (icmFileStd *)pp; + + return fread(buffer, size, count, p->fp); +} + +/* write count items of size length. Return number of items successfully written. */ +static size_t icmFileStd_write( +icmFile *pp, +void *buffer, +size_t size, +size_t count +) { + icmFileStd *p = (icmFileStd *)pp; + + return fwrite(buffer, size, count, p->fp); +} + + +/* flush all write data out to secondary storage. Return nz on failure. */ +static int icmFileStd_flush( +icmFile *pp +) { + icmFileStd *p = (icmFileStd *)pp; + + return fflush(p->fp); +} + +/* we're done with the file object, return nz on failure */ +static int icmFileStd_delete( +icmFile *pp +) { + icmFileStd *p = (icmFileStd *)pp; + + if (p->doclose != 0) { + if (fclose(p->fp) != 0) + return 2; + } + + free(p); + return 0; +} + +/* Create icmFile given a (binary) FILE* */ +icmFile *new_icmFileStd_fp( +FILE *fp +) { + icmFileStd *p; + if ((p = (icmFileStd *) calloc(1,sizeof(icmFileStd))) == NULL) + return NULL; + p->seek = icmFileStd_seek; + p->read = icmFileStd_read; + p->write = icmFileStd_write; + p->flush = icmFileStd_flush; + p->del = icmFileStd_delete; + + p->fp = fp; + p->doclose = 0; + + return (icmFile *)p; +} + +/* Create icmFile given a file name */ +icmFile *new_icmFileStd_name( +char *name, +char *mode +) { + FILE *fp; + icmFile *p; +#if defined(O_BINARY) + char nmode[50]; +#endif + + if ((fp = fopen(name,mode)) == NULL) + return NULL; + +#if defined(O_BINARY) + strcpy(nmode, mode); + strcat(nmode, "b"); + if ((fp = freopen(name, nmode, fp)) == NULL) + return NULL; +#endif + + p = new_icmFileStd_fp(fp); + + if (p != NULL) { + icmFileStd *pp = (icmFileStd *)p; + pp->doclose = 1; + } + return p; +} + +/* ------------------------------------------------- */ +/* Memory image icmFile compatible class */ +/* Note that this uses malloc, so replace class if */ +/* you need a different memory allocator. */ + +/* Set current position to offset. Return 0 on success, nz on failure. */ +static int icmFileMem_seek( +icmFile *pp, +long 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 = size * count; + if ((p->cur + len) >= p->end) { /* Too much */ + if (size > 0) + count = (p->end - p->cur)/size; + else + count = 0; + } + len = size * count; + if (len > 0) + memcpy (buffer, p->cur, len); + p->cur += len; + return count; +} + +/* 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 = size * count; + if ((p->cur + len) >= p->end) { /* Too much */ + if (size > 0) + count = (p->end - p->cur)/size; + else + count = 0; + } + len = size * count; + if (len > 0) + memcpy (p->cur, buffer, len); + p->cur += len; + return count; +} + + +/* flush all write data out to secondary storage. Return nz on failure. */ +static int icmFileMem_flush( +icmFile *pp +) { + return 0; +} + +/* we're done with the file object, return nz on failure */ +static int icmFileMem_delete( +icmFile *pp +) { + icmFileMem *p = (icmFileMem *)pp; + + free(p); + return 0; +} + +/* Create a memory image file access class */ +icmFile *new_icmFileMem( +void *base, /* Pointer to base of memory buffer */ +size_t length /* Number of bytes in buffer */ +) { + icmFileMem *p; + if ((p = (icmFileMem *) calloc(1,sizeof(icmFileMem))) == NULL) + return NULL; + p->seek = icmFileMem_seek; + p->read = icmFileMem_read; + p->write = icmFileMem_write; + p->flush = icmFileMem_flush; + p->del = icmFileMem_delete; + + p->cur = p->start = base; + p->end = p->start + length; + + return (icmFile *)p; +} + +/* ------------------------------------------------- */ +/* Standard Heap allocator icmAlloc compatible class */ +/* Just call the standard system function */ + +static void *icmAllocStd_malloc( +struct _icmAlloc *pp, +size_t size +) { + return malloc(size); +} + +static void *icmAllocStd_calloc( +struct _icmAlloc *pp, +size_t num, +size_t size +) { + return calloc(num, size); +} + +static void *icmAllocStd_realloc( +struct _icmAlloc *pp, +void *ptr, +size_t size +) { + return realloc(ptr, size); +} + + +static void icmAllocStd_free( +struct _icmAlloc *pp, +void *ptr +) { + free(ptr); +} + +/* we're done with the AllocStd object */ +static void icmAllocStd_delete( +icmAlloc *pp +) { + icmAllocStd *p = (icmAllocStd *)pp; + + free(p); +} + +/* Create icmAllocStd */ +icmAlloc *new_icmAllocStd() { + icmAllocStd *p; + if ((p = (icmAllocStd *) calloc(1,sizeof(icmAllocStd))) == NULL) + return NULL; + p->malloc = icmAllocStd_malloc; + p->calloc = icmAllocStd_calloc; + p->realloc = icmAllocStd_realloc; + p->free = icmAllocStd_free; + p->del = icmAllocStd_delete; + + return (icmAlloc *)p; +} + +/* ========================================================== */ +/* 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; +} + + +#ifdef NEVER /* Not currently used anywhere */ + +/* 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; +} + +#endif /* NEVER */ + +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; +} + +#ifdef NEVER /* Not currently used anywhere */ + +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; +} + +#endif /* NEVER */ + +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 = ceil(d * 65536.0); /* 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; +} + +/* PCS encoded numbers */ + +/* 16 bit XYZ - value range 0.0 - 1.9997 */ +static double read_PCSXYZ16Number(char *p) { + ORD32 o32; + o32 = 256 * (ORD32)((ORD8 *)p)[0] /* Read big endian 16 bit unsigned */ + + (ORD32)((ORD8 *)p)[1]; + return (double)o32/32768.0; +} + +static int write_PCSXYZ16Number(double d, char *p) { + ORD32 o32; + d = d * 32768.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; +} + +#ifdef NEVER /* Not currently used */ + +/* L part of 8 bit Lab - value range 0.0 - 100.0 */ +static double read_PCSL8Number(char *p) { + ORD32 o32; + o32 = (ORD32)((ORD8 *)p)[0]; /* Read big endian 8 bit unsigned */ + return (double)o32/2.550; +} + +static int write_PCSL8Number(double d, char *p) { + ORD32 o32; + d = d * 2.550 + 0.5; + if (d >= 256.0) + return 1; + if (d < 0.0) + return 1; + o32 = (ORD32)d; + ((ORD8 *)p)[0] = (ORD8)((o32)); + return 0; +} + +/* ab part of 8 bit Lab - value range -128.0 - 127.0 */ +static double read_PCSab8Number(char *p) { + ORD32 o32; + o32 = (ORD32)((ORD8 *)p)[0]; /* Read big endian 8 bit unsigned */ + return (double)o32-128.0; +} + +static int write_PCSab8Number(double d, char *p) { + ORD32 o32; + d = (d+128.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; +} + +#endif /* NEVER */ + +/* L part of 16 bit Lab - value range 0.0 - 100.0 */ +static double read_PCSL16Number(char *p) { + ORD32 o32; + o32 = 256 * (ORD32)((ORD8 *)p)[0] /* Read big endian 16 bit unsigned */ + + (ORD32)((ORD8 *)p)[1]; + return (double)o32/652.800; /* 0xff00/100.0 */ +} + +static int write_PCSL16Number(double d, char *p) { + ORD32 o32; + d = d * 652.800 + 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; +} + +/* ab part of 16 bit Lab - value range -128.0 - 127.9961 */ +static double read_PCSab16Number(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)-128.0; +} + +static int write_PCSab16Number(double d, char *p) { + ORD32 o32; + d = (d+128.0) * 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; +} + +/* 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; +} + +/* ---------------------------------------------------------- */ +/* 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 */ +int str2tag( + const char *str +) { + unsigned long tag; + tag = (((unsigned long)str[0]) << 24) + + (((unsigned long)str[1]) << 16) + + (((unsigned long)str[2]) << 8) + + (((unsigned long)str[3])); + return (int)tag; +} + +/* helper - return 1 if the string doesn't have a */ +/* null terminator, return 0 if it does. */ +/* Note: will return 1 if len == 0 */ +static int check_null_string(char *cp, int len) { + for (; len > 0; len--) { + if (*cp++ == '\000') + break; + } + if (len == 0) + return 1; + return 0; +} + +/* helper - return 1 if the string doesn't have a */ +/* null terminator, return 0 if it does. */ +/* 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; + 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; + default: + return 0; + } +} + +/* ------------------------------------------------------- */ +/* 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 long 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 long 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); + + return bp; +} + +/* Profile header flags */ +static char *string_ProfileHeaderFlags(unsigned long 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 long 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"; + 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"; + 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"; + 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 long) enumval); + case icmDeviceAttributes: + return string_DeviceAttributes((unsigned long) enumval); + case icmProfileHeaderFlags: + return string_ProfileHeaderFlags((unsigned long) enumval); + case icmAsciiOrBinaryData: + return string_AsciiOrBinaryData((unsigned long) 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 icmProfileClassSignaure: + 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 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 */ +/* ========================================================== */ +/* 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 += 8; /* 8 bytes for tag and padding */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmUInt8Array *p = (icmUInt8Array *)pp; + icc *icp = p->icp; + int rv = 0; + unsigned long 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 long of /* File offset to write from */ +) { + icmUInt8Array *p = (icmUInt8Array *)pp; + icc *icp = p->icp; + unsigned long i; + unsigned int len; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmUInt8Array *p = (icmUInt8Array *)pp; + if (verb <= 0) + return; + + fprintf(op,"UInt8Array:\n"); + fprintf(op," No. elements = %lu\n",p->size); + if (verb >= 2) { + unsigned long i; + for (i = 0; i < p->size; i++) + fprintf(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 (p->data != NULL) + icp->al->free(icp->al, p->data); + if ((p->data = (unsigned int *) icp->al->malloc(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 += 8; /* 8 bytes for tag and padding */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmUInt16Array *p = (icmUInt16Array *)pp; + icc *icp = p->icp; + int rv = 0; + unsigned long 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 long of /* File offset to write from */ +) { + icmUInt16Array *p = (icmUInt16Array *)pp; + icc *icp = p->icp; + unsigned long i; + unsigned int len; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmUInt16Array *p = (icmUInt16Array *)pp; + if (verb <= 0) + return; + + fprintf(op,"UInt16Array:\n"); + fprintf(op," No. elements = %lu\n",p->size); + if (verb >= 2) { + unsigned long i; + for (i = 0; i < p->size; i++) + fprintf(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 (p->data != NULL) + icp->al->free(icp->al, p->data); + if ((p->data = (unsigned int *) icp->al->malloc(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 += 8; /* 8 bytes for tag and padding */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmUInt32Array *p = (icmUInt32Array *)pp; + icc *icp = p->icp; + int rv = 0; + unsigned long 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 long of /* File offset to write from */ +) { + icmUInt32Array *p = (icmUInt32Array *)pp; + icc *icp = p->icp; + unsigned long i; + unsigned int len; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmUInt32Array *p = (icmUInt32Array *)pp; + if (verb <= 0) + return; + + fprintf(op,"UInt32Array:\n"); + fprintf(op," No. elements = %lu\n",p->size); + if (verb >= 2) { + unsigned long i; + for (i = 0; i < p->size; i++) + fprintf(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 (p->data != NULL) + icp->al->free(icp->al, p->data); + if ((p->data = (unsigned int *) icp->al->malloc(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 += 8; /* 8 bytes for tag and padding */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmUInt64Array *p = (icmUInt64Array *)pp; + icc *icp = p->icp; + int rv = 0; + unsigned long 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 long of /* File offset to write from */ +) { + icmUInt64Array *p = (icmUInt64Array *)pp; + icc *icp = p->icp; + unsigned long i; + unsigned int len; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmUInt64Array *p = (icmUInt64Array *)pp; + if (verb <= 0) + return; + + fprintf(op,"UInt64Array:\n"); + fprintf(op," No. elements = %lu\n",p->size); + if (verb >= 2) { + unsigned long i; + for (i = 0; i < p->size; i++) + fprintf(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 (p->data != NULL) + icp->al->free(icp->al, p->data); + if ((p->data = (icmUint64 *) icp->al->malloc(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 += 8; /* 8 bytes for tag and padding */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp; + icc *icp = p->icp; + int rv = 0; + unsigned long 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 long of /* File offset to write from */ +) { + icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp; + icc *icp = p->icp; + unsigned long i; + unsigned int len; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp; + if (verb <= 0) + return; + + fprintf(op,"U16Fixed16Array:\n"); + fprintf(op," No. elements = %lu\n",p->size); + if (verb >= 2) { + unsigned long i; + for (i = 0; i < p->size; i++) + fprintf(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 (p->data != NULL) + icp->al->free(icp->al, p->data); + if ((p->data = (double *) icp->al->malloc(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 += 8; /* 8 bytes for tag and padding */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp; + icc *icp = p->icp; + int rv = 0; + unsigned long 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 long of /* File offset to write from */ +) { + icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp; + icc *icp = p->icp; + unsigned long i; + unsigned int len; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp; + if (verb <= 0) + return; + + fprintf(op,"S15Fixed16Array:\n"); + fprintf(op," No. elements = %lu\n",p->size); + if (verb >= 2) { + unsigned long i; + for (i = 0; i < p->size; i++) + fprintf(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 (p->data != NULL) + icp->al->free(icp->al, p->data); + if ((p->data = (double *) icp->al->malloc(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. */ +static char *string_XYZNumber_and_Lab(icmXYZNumber *p) { + static char buf[50]; + double lab[3]; + lab[0] = p->X; + lab[1] = p->Y; + lab[2] = p->Z; + icmXYZ2Lab(&icmD50, lab, lab); + sprintf(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 += 8; /* 8 bytes for tag and padding */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmXYZArray *p = (icmXYZArray *)pp; + icc *icp = p->icp; + int rv = 0; + unsigned long 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 long of /* File offset to write from */ +) { + icmXYZArray *p = (icmXYZArray *)pp; + icc *icp = p->icp; + unsigned long i; + unsigned int len; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmXYZArray *p = (icmXYZArray *)pp; + if (verb <= 0) + return; + + fprintf(op,"XYZArray:\n"); + fprintf(op," No. elements = %lu\n",p->size); + if (verb >= 2) { + unsigned long i; + for (i = 0; i < p->size; i++) { + fprintf(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 (p->data != NULL) + icp->al->free(icp->al, p->data); + if ((p->data = (icmXYZNumber *) icp->al->malloc(icp->al, 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 { /* Use linear interpolation */ + 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 = (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 long size, /* Size of fwd table */ + double *data /* Table */ +) { + 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 = (rt->size+2)/2; + rt->qscale = (double)rt->rsize/(rt->rmax - rt->rmin); /* Scale factor to quantize to */ + + /* Initialize the reverse lookup structures, and get overall min/max */ + if ((rt->rlists = (int **) icp->al->calloc(icp->al, 1, rt->rsize * sizeof(int *))) == NULL) { + return 2; + } + + /* Assign each output value range bucket lists it intersects */ + for (i = 0; i < (rt->size-1); i++) { + int s, e, j; /* Start and end indexes (inclusive) */ + s = (int)((rt->data[i] - rt->rmin) * rt->qscale); + e = (int)((rt->data[i+1] - rt->rmin) * rt->qscale); + if (s > e) { /* swap */ + int t; + t = s; s = e; e = t; + } + if (e >= rt->rsize) + e = rt->rsize-1; + + /* For all buckets that may contain this output range, add index of this output */ + for (j = s; j <= e; j++) { + int as; /* Allocation size */ + int nf; /* Next free slot */ + if (rt->rlists[j] == NULL) { /* No allocation */ + as = 5; /* Start with space for 5 */ + if ((rt->rlists[j] = (int *) icp->al->malloc(icp->al, sizeof(int) * as)) == 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 */ + as *= 2; + rt->rlists[j] = (int *) icp->al->realloc(icp->al,rt->rlists[j], sizeof(int) * as); + 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; + int ix, i, k; + 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 = (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 { /* 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 += 12; /* 12 bytes for tag, padding and count */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmCurve *p = (icmCurve *)pp; + icc *icp = p->icp; + int rv = 0; + unsigned long 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 ((rv = p->allocate((icmBase *)p)) != 0) { + icp->al->free(icp->al, buf); + return rv; + } + + if (p->flag == icmCurveGamma) { /* Gamma curve */ + if ((bp + 1) > end) { + sprintf(icp->err,"icmCurve_read: Data too short to 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 + 2) > end) { + sprintf(icp->err,"icmData_read: Data too short to 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 long of /* File offset to write from */ +) { + icmCurve *p = (icmCurve *)pp; + icc *icp = p->icp; + unsigned long i; + unsigned int len; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmCurve *p = (icmCurve *)pp; + if (verb <= 0) + return; + + fprintf(op,"Curve:\n"); + + if (p->flag == icmCurveLin) { + fprintf(op," Curve is linear\n"); + } else if (p->flag == icmCurveGamma) { + fprintf(op," Curve is gamma of %f\n",p->data[0]); + } else { + fprintf(op," No. elements = %lu\n",p->size); + if (verb >= 2) { + unsigned long i; + for (i = 0; i < p->size; i++) + fprintf(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 (p->data != NULL) + icp->al->free(icp->al, p->data); + if ((p->data = (double *) icp->al->malloc(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 += 12; /* 12 bytes for tag and padding */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmData *p = (icmData *)pp; + icc *icp = p->icp; + int rv = 0; + 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; + } 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 (check_null_string(bp,p->size) != 0) { + sprintf(icp->err,"icmData_read: ACSII 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; + } + + memcpy((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 long 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 = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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)) != 0) { + sprintf(icp->err,"icmData_write: ASCII is not null terminated"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + } + memcpy((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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmData *p = (icmData *)pp; + unsigned long i, r, c, size = 0; + + if (verb <= 0) + return; + + fprintf(op,"Data:\n"); + switch(p->flag) { + case icmDataASCII: + fprintf(op," ASCII data\n"); + size = p->size > 0 ? p->size-1 : 0; + break; + case icmDataBin: + fprintf(op," Binary data\n"); + size = p->size; + break; + case icmDataUndef: + fprintf(op," Undefined data\n"); + size = p->size; + break; + } + fprintf(op," No. elements = %lu\n",p->size); + + i = 0; + for (r = 1;; r++) { /* count rows */ + if (i >= size) { + fprintf(op,"\n"); + break; + } + if (r > 1 && verb < 2) { + fprintf(op,"...\n"); + break; /* Print 1 row if not verbose */ + } + c = 1; + fprintf(op," 0x%04lx: ",i); + c += 10; + while (i < size && c < 75) { + if (p->flag == icmDataASCII) { + if (isprint(p->data[i])) { + fprintf(op,"%c",p->data[i]); + c++; + } else { + fprintf(op,"\\%03o",p->data[i]); + c += 4; + } + } else { + fprintf(op,"%02x ",p->data[i]); + c += 3; + } + i++; + } + if (i < size) + fprintf(op,"\n"); + } +} + +/* 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 (p->data != NULL) + icp->al->free(icp->al, p->data); + if ((p->data = (unsigned char *) icp->al->malloc(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 += 8; /* 8 bytes for tag and padding */ + len += p->size; /* 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmText *p = (icmText *)pp; + icc *icp = p->icp; + int rv = 0; + 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 (check_null_string(bp,p->size) != 0) { + sprintf(icp->err,"icmText_read: text 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; + } + memcpy((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 long 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 = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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)) != 0) { + sprintf(icp->err,"icmText_write: text is not null terminated"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + memcpy((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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmText *p = (icmText *)pp; + unsigned long i, r, c, size; + + if (verb <= 0) + return; + + fprintf(op,"Text:\n"); + fprintf(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) { + fprintf(op,"\n"); + break; + } + if (r > 1 && verb < 2) { + fprintf(op,"...\n"); + break; /* Print 1 row if not verbose */ + } + c = 1; + fprintf(op," 0x%04lx: ",i); + c += 10; + while (i < size && c < 75) { + if (isprint(p->data[i])) { + fprintf(op,"%c",p->data[i]); + c++; + } else { + fprintf(op,"\\%03o",p->data[i]); + c += 4; + } + i++; + } + if (i < size) + fprintf(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 (p->data != NULL) + icp->al->free(icp->al, p->data); + if ((p->data = (char *) icp->al->malloc(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); + + 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) { + unsigned int tt; + + /* 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) + return 1; /* Nope */ + + /* 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; + } + return 0; +} + +/* 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 += 8; /* 8 bytes for tag and padding */ + 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 long len, /* tag length */ + unsigned long 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 long 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 */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmDateTimeNumber *p = (icmDateTimeNumber *)pp; + if (verb <= 0) + return; + + fprintf(op,"DateTimeNumber:\n"); + fprintf(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 */ + +/* Utility function - raise one integer to an integer power */ +static unsigned int uipow(unsigned int a, unsigned int b) { + unsigned int rv = 1; + for (; b > 0; b--) + rv *= a; + return rv; +} + +/* - - - - - - - - - - - - - - - - */ +/* 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 */ + int e, ee, f; + double 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++) { + gc[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; + int ix,n; + double inputEnt_1 = (double)(p->inputEnt-1); + double *table = p->inputTable; + + /* 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 = (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 n-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, (1 << p->inputChan) * sizeof(double))) == NULL) { + sprintf(icp->err,"icmLut_lookup_clut: malloc() failed"); + return icp->errc = 2; + } + } + + /* We are using an n-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 */ + { + 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++) { + 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 = (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 */ + { + int e, 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, 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 */ + { + 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++) { + 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 = (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 */ + } + } + /* Do selection sort on coordinates */ + { + 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]; + } + } + } + } + /* Now compute the weightings, simplex vertices and output values */ + { + 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; +} + +/* 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; + int ix,n; + double outputEnt_1 = (double)(p->outputEnt-1); + double *table = p->outputTable; + + /* 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 = (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 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; (1 << 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; + int res = p->res; + int bits = p->bits; + int e; + + do { + 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 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); +} + +/* ------------------------------------------------------- */ +/* 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( + icColorSpaceSignature csig, + icTagTypeSignature tagSig, + icmNormFlag flag, + void (**nfunc)(double *out, double *in) +); + +/* Helper function to initialize the three tables contents */ +/* from supplied transfer functions. */ +/* Set errc and return error number */ +static int icmLut_set_tables ( +icmLut *p, /* Pointer to Lut object */ +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) */ +) { + icc *icp = p->icp; + int n, e; + int ii[MAX_CHAN]; /* Index value */ + psh counter; /* Pseudo-Hilbert counter */ + double _iv[2 * MAX_CHAN], *iv = &_iv[MAX_CHAN]; /* 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 */ + + if (getNormFunc(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(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(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(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(outsig, p->ttype, icmFromLutv, &ofromentry) != 0) { + sprintf(icp->err,"icmLut_set_tables table entry to output colorspace function lookup failed"); + return icp->errc = 1; + } + + /* Setup input table value min-max */ + if (inmin == NULL) { +#ifdef SYMETRICAL_DEFAULT_LAB_RANGE /* Try symetrical range */ + if (insig == icSigLabData) { /* Special case Lab */ + double mn[3], mx[3]; + /* 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) { + mn[0] = 0.0, mn[1] = -127.0, mn[2] = -127.0; + mx[0] = 100.0, mx[1] = 127.0, mx[2] = 127.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) { +#ifdef SYMETRICAL_DEFAULT_LAB_RANGE /* Try symetrical range */ + if (outsig == icSigLabData) { /* Special case Lab */ + double mn[3], mx[3]; + /* Because the symetric range will cause slight clipping, */ + /* only do it if the output table has sufficient resolution */ + /* to represent the clipping faithfuly. */ + if (p->outputEnt >= 64) { + mn[0] = 0.0, mn[1] = -127.0, mn[2] = -127.0; + mx[0] = 100.0, mx[1] = 127.0, mx[2] = 127.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 (e = 0; e < p->outputChan; e++) { + omin[e] = 0.0; /* We are assuming this is true for all other color spaces. */ + omax[e] = 1.0; + } + } + } else { + otoentry(omin, clutmin);/* Convert from output color space to table representation */ + otoentry(omax, clutmax); + } + + /* Create the input table entry values */ + for (n = 0; n < p->inputEnt; n++) { + double fv; + fv = n/(p->inputEnt-1.0); + for (e = 0; e < p->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 < p->inputChan; e++) { + double tt; + tt = (iv[e] - imin[e])/(imax[e] - imin[e]); + if (tt < 0.0) + tt = 0.0; + else if (tt > 1.0) + tt = 1.0; + iv[e] = tt; + } + + for (e = 0; e < p->inputChan; e++) /* Input tables */ + p->inputTable[e * p->inputEnt + n] = iv[e]; + } + + /* 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. */ + + psh_init(&counter, p->inputChan, p->clutPoints, ii); /* Initialise counter */ + + /* Itterate through all verticies in the grid */ + for (;;) { + int ti; /* Table index */ + + 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[-e-1]) = ii[e]; /* Trick to supply grid index in iv[] */ + } + + ifromentry(iv,iv); /* Convert from table value to input color space */ + + /* Apply incolor -> outcolor function we want to represent */ + clutfunc(cbctx, iv, iv); + + otoentry(iv,iv); /* Convert from output color space value to table value */ + + /* Expand used range to 0.0 - 1.0, and clip to legal values */ + for (e = 0; e < p->outputChan; e++) { + double tt; + tt = (iv[e] - omin[e])/(omax[e] - omin[e]); + if (tt < 0.0) + tt = 0.0; + else if (tt > 1.0) + tt = 1.0; + iv[e] = tt; + } + + for (e = 0; e < p->outputChan; e++) /* Output chans */ + p->clutTable[ti++] = iv[e]; + + /* Increment index within block (Reverse index significancd) */ + if (psh_inc(&counter, ii)) + break; + } + + /* Create the output table entry values */ + for (n = 0; n < p->outputEnt; n++) { + double fv; + fv = n/(p->outputEnt-1.0); + for (e = 0; e < p->outputChan; e++) + iv[e] = fv; + + /* Undo expansion to 0.0 - 1.0 */ + for (e = 0; e < p->outputChan; e++) /* Output tables */ + iv[e] = iv[e] * (omax[e] - omin[e]) + omin[e]; + + 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 (e = 0; e < p->outputChan; e++) { + double tt; + tt = iv[e]; + if (tt < 0.0) + tt = 0.0; + else if (tt > 1.0) + tt = 1.0; + iv[e] = tt; + } + + for (e = 0; e < p->outputChan; e++) /* Input tables */ + p->outputTable[e * p->outputEnt + n] = iv[e]; + } + return 0; +} + +/* - - - - - - - - - - - - - - - - */ +/* 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 += 48; /* tag and header */ + len += 1 * (p->inputChan * p->inputEnt); + len += 1 * (p->outputChan * uipow(p->clutPoints,p->inputChan)); + len += 1 * (p->outputChan * p->outputEnt); + } else { + len += 52; /* tag and header */ + len += 2 * (p->inputChan * p->inputEnt); + len += 2 * (p->outputChan * uipow(p->clutPoints,p->inputChan)); + len += 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmLut *p = (icmLut *)pp; + icc *icp = p->icp; + int rv = 0; + unsigned long i, j, g, size; + char *bp, *buf; + + if (len < 4) { + sprintf(icp->err,"icmLut_read: Tag too small to be legal"); + return icp->errc = 1; + } + + /* Allocate a file read buffer */ + if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) { + sprintf(icp->err,"icmLut_read: malloc() failed"); + return icp->errc = 2; + } + bp = buf; + + /* Read portion of file into buffer */ + if ( icp->fp->seek(icp->fp, of) != 0 + || icp->fp->read(icp->fp, bp, 1, len) != len) { + sprintf(icp->err,"icmLut_read: fseek() or fread() failed"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + + /* Read type descriptor from the buffer */ + p->ttype = (icTagTypeSignature)read_SInt32Number(bp); + if (p->ttype != icSigLut8Type && p->ttype != icSigLut16Type) { + sprintf(icp->err,"icmLut_read: Wrong tag type for icmLut"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + + if (p->ttype == icSigLut8Type) { + if (len < 48) { + sprintf(icp->err,"icmLut_read: Tag too small to be legal"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + } else { + if (len < 52) { + sprintf(icp->err,"icmLut_read: Tag too small to be legal"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + } + + /* Read in the info common to 8 and 16 bit Lut */ + p->inputChan = read_UInt8Number(bp+8); + p->outputChan = read_UInt8Number(bp+9); + p->clutPoints = read_UInt8Number(bp+10); + + /* Sanity check */ + if (p->inputChan > MAX_CHAN) { + sprintf(icp->err,"icmLut_read: Can't handle > %d input channels\n",MAX_CHAN); + return icp->errc = 1; + } + + if (p->outputChan > MAX_CHAN) { + sprintf(icp->err,"icmLut_read: Can't handle > %d output channels\n",MAX_CHAN); + return icp->errc = 1; + } + + /* Read 3x3 transform matrix */ + for (j = 0; j < 3; j++) { /* Rows */ + for (i = 0; i < 3; i++) { /* Columns */ + p->e[j][i] = read_S15Fixed16Number(bp + 12 + ((j * 3 + i) * 4)); + } + } + /* Read 16 bit specific stuff */ + if (p->ttype == icSigLut8Type) { + p->inputEnt = 256; /* By definition */ + p->outputEnt = 256; /* By definition */ + bp = buf+48; + } else { + p->inputEnt = read_UInt16Number(bp+48); + p->outputEnt = read_UInt16Number(bp+50); + bp = buf+52; + } + + if (len < icmLut_get_size((icmBase *)p)) { + sprintf(icp->err,"icmLut_read: Tag too small 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 * uipow(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 long of /* File offset to write from */ +) { + icmLut *p = (icmLut *)pp; + icc *icp = p->icp; + unsigned long i,j; + unsigned int len, size; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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 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 ((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 * uipow(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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmLut *p = (icmLut *)pp; + if (verb <= 0) + return; + + if (p->ttype == icSigLut8Type) { + fprintf(op,"Lut8:\n"); + } else { + fprintf(op,"Lut16:\n"); + } + fprintf(op," Input Channels = %u\n",p->inputChan); + fprintf(op," Output Channels = %u\n",p->outputChan); + fprintf(op," CLUT resolution = %u\n",p->clutPoints); + fprintf(op," Input Table entries = %u\n",p->inputEnt); + fprintf(op," Output Table entries = %u\n",p->outputEnt); + fprintf(op," XYZ matrix = %f, %f, %f\n",p->e[0][0],p->e[0][1],p->e[0][2]); + fprintf(op," %f, %f, %f\n",p->e[1][0],p->e[1][1],p->e[1][2]); + fprintf(op," %f, %f, %f\n",p->e[2][0],p->e[2][1],p->e[2][2]); + + if (verb >= 2) { + unsigned int i,size; + int j; + unsigned int ii[MAX_CHAN]; /* maximum no of input channels */ + + fprintf(op," Input table:\n"); + for (i = 0; i < p->inputEnt; i++) { + fprintf(op," %3u: ",i); + for (j = 0; j < p->inputChan; j++) + fprintf(op," %1.10f",p->inputTable[j * p->inputEnt + i]); + fprintf(op,"\n"); + } + + fprintf(op,"\n CLUT table:\n"); + if (p->inputChan > MAX_CHAN) { + fprintf(op," !!Can't dump > %d input channel CLUT table!!\n",MAX_CHAN); + } else { + size = (p->outputChan * uipow(p->clutPoints,p->inputChan)); + for (j = 0; j < p->inputChan; j++) + ii[j] = 0; + for (i = 0; i < size;) { + int k; + /* Print table entry index */ + fprintf(op," "); + for (j = p->inputChan-1; j >= 0; j--) + fprintf(op," %2u",ii[j]); + fprintf(op,":"); + /* Print table entry contents */ + for (k = 0; k < p->outputChan; k++, i++) + fprintf(op," %1.10f",p->clutTable[i]); + fprintf(op,"\n"); + + for (j = 0; j < p->inputChan; j++) { /* Increment index */ + ii[j]++; + if (ii[j] < p->clutPoints) + break; /* No carry */ + ii[j] = 0; + } + } + } + + fprintf(op,"\n Output table:\n"); + for (i = 0; i < p->outputEnt; i++) { + fprintf(op," %3u: ",i); + for (j = 0; j < p->outputChan; j++) + fprintf(op," %1.10f",p->outputTable[j * p->outputEnt + i]); + fprintf(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 > 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; + } + + size = (p->inputChan * p->inputEnt); + if (size != p->inputTable_size) { + if (p->inputTable != NULL) + icp->al->free(icp->al, p->inputTable); + if ((p->inputTable = (double *) icp->al->calloc(icp->al,sizeof(double), size)) == NULL) { + sprintf(icp->err,"icmLut_alloc: calloc() of Lut inputTable data failed"); + return icp->errc = 2; + } + p->inputTable_size = size; + } + size = (p->outputChan * uipow(p->clutPoints,p->inputChan)); + if (size != p->clutTable_size) { + if (p->clutTable != NULL) + icp->al->free(icp->al, p->clutTable); + if ((p->clutTable = (double *) icp->al->calloc(icp->al,sizeof(double), size)) == NULL) { + sprintf(icp->err,"icmLut_alloc: calloc() of Lut clutTable data failed"); + return icp->errc = 2; + } + p->clutTable_size = size; + } + size = (p->outputChan * p->outputEnt); + if (size != p->outputTable_size) { + if (p->outputTable != NULL) + icp->al->free(icp->al, p->outputTable); + if ((p->outputTable = (double *) icp->al->calloc(icp->al,sizeof(double), size)) == 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; + + 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); + icmTable_delete_bwd(icp, &p->rit); + icmTable_delete_bwd(icp, &p->rot); + 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; + + p->rit.inited = 0; + p->rot.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 += 8; /* 8 bytes for tag and padding */ + len += 4; /* 4 for standard observer */ + len += 12; /* 12 for XYZ of measurement backing */ + len += 4; /* 4 for measurement geometry */ + len += 4; /* 4 for measurement flare */ + 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 long len, /* tag length */ + unsigned long 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 long 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 */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmMeasurement *p = (icmMeasurement *)pp; + if (verb <= 0) + return; + + fprintf(op,"Measurement:\n"); + fprintf(op," Standard Observer = %s\n", string_StandardObserver(p->observer)); + fprintf(op," XYZ for Measurement Backing = %s\n", string_XYZNumber_and_Lab(&p->backing)); + fprintf(op," Measurement Geometry = %s\n", string_MeasurementGeometry(p->geometry)); + fprintf(op," Measurement Flare = %5.1f%%\n", p->flare * 100.0); + fprintf(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; + int i; + unsigned int mxl; /* Max possible string length */ + + mxl = (end - bp) < 32 ? (end - bp) : 32; + if (check_null_string(bp,mxl)) { + sprintf(icp->err,"icmNamedColorVal_read: Root name string not terminated"); + return icp->errc = 1; + } + strcpy((void *)p->root, (void *)bp); + bp += strlen(p->root) + 1; + if ((bp + ndc) > end) { + 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 corrds */ +) { + icc *icp = p->icp; + int i; + if ((bp + 32 + 6 + ndc * 2) > end) { + sprintf(icp->err,"icmNamedColorVal2_read: Data too short to read"); + return icp->errc = 1; + } + if (check_null_string(bp,32)) { + sprintf(icp->err,"icmNamedColorVal2_read: Root name string not terminated"); + return icp->errc = 1; + } + memcpy((void *)p->root,(void *)(bp + 0),32); + switch(pcs) { + case icSigXYZData: + p->pcsCoords[0] = read_PCSXYZ16Number(bp+32); + p->pcsCoords[1] = read_PCSXYZ16Number(bp+34); + p->pcsCoords[2] = read_PCSXYZ16Number(bp+36); + break; + case icSigLabData: + p->pcsCoords[0] = read_PCSL16Number(bp+32); + p->pcsCoords[1] = read_PCSab16Number(bp+34); + p->pcsCoords[2] = read_PCSab16Number(bp+36); + 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; + int i, rv = 0; + if (check_null_string(p->root,32) != 0) { + sprintf(icp->err,"icmNamedColorVal_write: Root string names is unterminated"); + return icp->errc = 1; + } + strcpy((void *)d,(void *)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 corrds */ +) { + icc *icp = p->icp; + int i, rv = 0; + if (check_null_string(p->root,32)) { + sprintf(icp->err,"icmNamedColorVal2_write: Root string names is unterminated"); + return icp->errc = 1; + } + memcpy((void *)(bp + 0),(void *)p->root,32); + switch(pcs) { + case icSigXYZData: + rv |= write_PCSXYZ16Number(p->pcsCoords[0], bp+32); + rv |= write_PCSXYZ16Number(p->pcsCoords[1], bp+34); + rv |= write_PCSXYZ16Number(p->pcsCoords[2], bp+36); + break; + case icSigLabData: + rv |= write_PCSL16Number(p->pcsCoords[0], bp+32); + rv |= write_PCSab16Number(p->pcsCoords[1], bp+34); + rv |= write_PCSab16Number(p->pcsCoords[2], bp+36); + 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 += 8; /* 8 bytes for tag and padding */ + len += 4; /* 4 for vendor specific flags */ + len += 4; /* 4 for count of named colors */ + len += strlen(p->prefix) + 1; /* prefix of color names */ + len += strlen(p->suffix) + 1; /* suffix of color names */ + for (i = 0; i < p->count; i++) { + len += strlen(p->data[i].root) + 1; /* color names */ + len += p->nDeviceCoords * 1; /* bytes for each named color */ + } + } else { /* Named Color 2 */ + len += 8; /* 8 bytes for tag and padding */ + len += 4; /* 4 for vendor specific flags */ + len += 4; /* 4 for count of named colors */ + len += 4; /* 4 for number of device coords */ + len += 32; /* 32 for prefix of color names */ + len += 32; /* 32 for suffix of color names */ + len += 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmNamedColor *p = (icmNamedColor *)pp; + icc *icp = p->icp; + unsigned long i; + char *bp, *buf, *end; + int rv = 0; + + 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 */ + mxl = (end - bp) < 32 ? (end - bp) : 32; + if (check_null_string(bp,mxl) != 0) { + sprintf(icp->err,"icmNamedColor_read: Color prefix is not null terminated"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + strcpy((void *)p->prefix, (void *)bp); + bp += strlen(p->prefix) + 1; + + /* Suffix for each color name */ + mxl = (end - bp) < 32 ? (end - bp) : 32; + if (check_null_string(bp,mxl) != 0) { + sprintf(icp->err,"icmNamedColor_read: Color suffix is not null terminated"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + strcpy((void *)p->suffix, (void *)bp); + bp += strlen(p->suffix) + 1; + + if ((rv = p->allocate((void *)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); + + /* Prefix for each color name */ + memcpy((void *)p->prefix, (void *)(bp + 20), 32); + if (check_null_string(p->prefix,32) != 0) { + 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 */ + memcpy((void *)p->suffix, (void *)(bp + 52), 32); + if (check_null_string(p->suffix,32) != 0) { + 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++, bp += (32 + 6 + p->nDeviceCoords * 2)) { + if ((rv = read_NamedColorVal2(p->data+i, bp, end, icp->header->pcs, p->nDeviceCoords)) != 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 icmNamedColor_write( + icmBase *pp, + unsigned long of /* File offset to write from */ +) { + icmNamedColor *p = (icmNamedColor *)pp; + icc *icp = p->icp; + unsigned long i; + unsigned int len; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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)) != 0) { + sprintf(icp->err,"icmNamedColor_write: Color prefix is not null terminated"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + strcpy((void *)bp, (void *)p->prefix); + bp += strlen(p->prefix) + 1; + + /* Suffix for each color name */ + if (check_null_string(p->suffix,32)) { + sprintf(icp->err,"icmNamedColor_write: Color sufix is not null terminated"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + strcpy((void *)bp, (void *)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)) != 0) { + sprintf(icp->err,"icmNamedColor_write: Color prefix is not null terminated"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + memcpy((void *)(bp + 20), (void *)p->prefix, 32); + + /* Suffix for each color name */ + if (check_null_string(p->suffix,32)) { + sprintf(icp->err,"icmNamedColor_write: Color sufix is not null terminated"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + memcpy((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, + FILE *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) + fprintf(op,"NamedColor:\n"); + else + fprintf(op,"NamedColor2:\n"); + fprintf(op," Vendor Flag = 0x%x\n",p->vendorFlag); + fprintf(op," No. colors = %u\n",p->count); + fprintf(op," No. dev. coords = %u\n",p->nDeviceCoords); + fprintf(op," Name prefix = '%s'\n",p->prefix); + fprintf(op," Name suffix = '%s'\n",p->suffix); + if (verb >= 2) { + unsigned long i, n; + icmNamedColorVal *vp; + for (i = 0; i < p->count; i++) { + vp = p->data + i; + fprintf(op," Color %lu:\n",i); + fprintf(op," Name root = '%s'\n",vp->root); + + if (p->ttype == icSigNamedColor2Type) { + switch(icp->header->pcs) { + case icSigXYZData: + fprintf(op," XYZ = %f, %f, %f'\n", + vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]); + break; + case icSigLabData: + fprintf(op," Lab = %f, %f, %f'\n", + vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]); + break; + default: + fprintf(op," Unexpected PCS\n"); + break; + } + } + if (p->nDeviceCoords > 0) { + fprintf(op," Device Coords = "); + for (n = 0; n < p->nDeviceCoords; n++) { + if (n > 0) + printf(", "); + printf("%f",vp->deviceCoords[n]); + } + printf("\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 (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; +} + +/* ---------------------------------------------------------- */ +/* 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 += 8; /* 8 bytes for tag and padding */ + len += 4 + p->size; /* Ascii string length + ascii string */ + len += 8 + 2 * p->ucSize; /* Unicode language code + length + string */ + 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 long len, /* tag length */ + unsigned long 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 = 0; + char *bp = *bpp; + + if ((bp + 8) > end) { + 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 + 4) > end) { + *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 + p->size) > end) { + *bpp = bp; + sprintf(icp->err,"icmTextDescription_read: Data to short to read Ascii string"); + return icp->errc = 1; + } + if (check_null_string(bp,p->size)) { + *bpp = bp; + sprintf(icp->err,"icmTextDescription_read: ascii string is not terminated"); + return icp->errc = 1; + } + if ((rv = p->allocate((icmBase *)p)) != 0) { + return rv; + } + strcpy((void *)p->desc, (void *)bp); + bp += p->size; + } + + /* Read the Unicode string */ + if ((bp + 8) > end) { + *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; + if ((bp + 2 * p->ucSize) > end) { + *bpp = bp; + sprintf(icp->err,"icmTextDescription_read: Data too short to read Unicode string"); + return icp->errc = 1; + } + if (check_null_string16(bp,p->ucSize)) { + *bpp = bp; + sprintf(icp->err,"icmTextDescription_read: Unicode string is not terminated"); + return icp->errc = 1; + } + if ((rv = p->allocate((icmBase *)p)) != 0) { + return rv; + } + for(up = p->ucDesc; bp[0] != 0 || bp[1] != 0; up++, bp += 2) + *up = read_UInt16Number(bp); + *up = 0; /* Unicode null */ + bp += 2; + } + + /* Read the ScriptCode string */ + if ((bp + 3) > end) { + *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 + p->scSize) > end) { + *bpp = bp; + sprintf(icp->err,"icmTextDescription_read: Data too short to read ScriptCode string"); + return icp->errc = 1; + } + if (check_null_string(bp,p->scSize)) { + *bpp = bp; + sprintf(icp->err,"icmTextDescription_read: ScriptCode string is not terminated"); + return icp->errc = 1; + } + memcpy((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 long 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 */ + len = p->get_size((icmBase *)p); + 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 = 0; + + /* 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 (check_null_string(p->desc,p->size)) { + *bpp = bp; + sprintf(icp->err,"icmTextDescription_write: ascii string is not terminated"); + return icp->errc = 1; + } + strcpy((void *)bp, (void *)p->desc); + bp += p->size; + } + + /* 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 (check_null_string16((char *)p->ucDesc,p->ucSize)) { + *bpp = bp; + sprintf(icp->err,"icmTextDescription_write: Unicode string is not terminated"); + 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 (check_null_string((char *)p->scDesc,p->scSize)) { + *bpp = bp; + sprintf(icp->err,"icmTextDescription_write: ScriptCode string is not terminated"); + return icp->errc = 1; + } + memcpy((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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmTextDescription *p = (icmTextDescription *)pp; + unsigned long i, r, c; + + if (verb <= 0) + return; + + fprintf(op,"TextDescription:\n"); + + if (p->size > 0) { + unsigned long size = p->size > 0 ? p->size-1 : 0; + fprintf(op," ASCII data, length %lu chars:\n",p->size); + + i = 0; + for (r = 1;; r++) { /* count rows */ + if (i >= size) { + fprintf(op,"\n"); + break; + } + if (r > 1 && verb < 2) { + fprintf(op,"...\n"); + break; /* Print 1 row if not verbose */ + } + c = 1; + fprintf(op," 0x%04lx: ",i); + c += 10; + while (i < size && c < 75) { + if (isprint(p->desc[i])) { + fprintf(op,"%c",p->desc[i]); + c++; + } else { + fprintf(op,"\\%03o",p->desc[i]); + c += 4; + } + i++; + } + if (i < size) + fprintf(op,"\n"); + } + } else { + fprintf(op," No ASCII data\n"); + } + + /* Can't dump Unicode or ScriptCode as text with portable code */ + if (p->ucSize > 0) { + unsigned long size = p->ucSize; + fprintf(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) { + fprintf(op,"\n"); + break; + } + if (r > 1 && verb < 2) { + fprintf(op,"...\n"); + break; /* Print 1 row if not verbose */ + } + c = 1; + fprintf(op," 0x%04lx: ",i); + c += 10; + while (i < size && c < 75) { + fprintf(op,"%04x ",p->ucDesc[i]); + c += 5; + i++; + } + if (i < size) + fprintf(op,"\n"); + } + } else { + fprintf(op," No Unicode data\n"); + } + if (p->scSize > 0) { + unsigned long size = p->scSize; + fprintf(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) { + fprintf(op,"\n"); + break; + } + if (r > 1 && verb < 2) { + fprintf(op,"...\n"); + break; /* Print 1 row if not verbose */ + } + c = 1; + fprintf(op," 0x%04lx: ",i); + c += 10; + while (i < size && c < 75) { + fprintf(op,"%02x ",p->scDesc[i]); + c += 3; + i++; + } + if (i < size) + fprintf(op,"\n"); + } + } else { + fprintf(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 (p->desc != NULL) + icp->al->free(icp->al, p->desc); + if ((p->desc = (char *) icp->al->malloc(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 (p->ucDesc != NULL) + icp->al->free(icp->al, p->ucDesc); + if ((p->ucDesc = (ORD16 *) icp->al->malloc(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 += 20; /* 20 bytes for header info */ + len += p->device.get_size((icmBase *)&p->device); + len += p->model.get_size((icmBase *)&p->model); + 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 + 20) > end) { + 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 = 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; + + 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; + + /* Write the device text description */ + if ((rv = p->device.core_write(&p->device, bpp)) != 0) { + return rv; + } + + /* Write the model text description */ + if ((rv = p->model.core_write(&p->model, bpp)) != 0) { + return rv; + } + + return 0; +} + +/* Dump a text description of the object */ +static void icmDescStruct_dump( + icmDescStruct *p, + FILE *op, /* Output to dump to */ + int verb, /* Verbosity level */ + int index /* Description index */ +) { + if (verb <= 0) + return; + + fprintf(op,"DescStruct %u:\n",index); + if (verb >= 1) { + fprintf(op," Dev. Mnfctr. = %s\n",tag2str(p->deviceMfg)); /* ~~~ */ + fprintf(op," Dev. Model = %s\n",tag2str(p->deviceModel)); /* ~~~ */ + fprintf(op," Dev. Attrbts = %s\n", string_DeviceAttributes(p->attributes.l)); + fprintf(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); + fprintf(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 += 12; /* 8 bytes for tag, padding and count */ + for (i = 0; i < p->count; i++) { /* All the description structures */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp; + icc *icp = p->icp; + unsigned long 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 long of /* File offset to write from */ +) { + icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp; + icc *icp = p->icp; + unsigned long i; + unsigned int len; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp; + if (verb <= 0) + return; + + fprintf(op,"ProfileSequenceDesc:\n"); + fprintf(op," No. elements = %u\n",p->count); + if (verb >= 2) { + unsigned long 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 (p->data != NULL) + icp->al->free(icp->al, p->data); + if ((p->data = (icmDescStruct *) icp->al->malloc(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 += 8; /* 8 bytes for tag and padding */ + 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 long len, /* tag length */ + unsigned long 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 long 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 */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmSignature *p = (icmSignature *)pp; + if (verb <= 0) + return; + + fprintf(op,"Signature\n"); + fprintf(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 += 16; /* 16 bytes for tag, padding, flag & channeles */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmScreening *p = (icmScreening *)pp; + icc *icp = p->icp; + int rv = 0; + unsigned long 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 + 12) > end) { + 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 long of /* File offset to write from */ +) { + icmScreening *p = (icmScreening *)pp; + icc *icp = p->icp; + unsigned long i; + unsigned int len; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmScreening *p = (icmScreening *)pp; + if (verb <= 0) + return; + + fprintf(op,"Screening:\n"); + fprintf(op," Flags = %s\n", string_ScreenEncodings(p->screeningFlag)); + fprintf(op," No. channels = %u\n",p->channels); + if (verb >= 2) { + unsigned long i; + for (i = 0; i < p->channels; i++) { + fprintf(op," %lu:\n",i); + fprintf(op," Frequency: %f\n",p->data[i].frequency); + fprintf(op," Angle: %f\n",p->data[i].angle); + fprintf(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 (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 += 8; /* 8 bytes for tag and padding */ + len += 4 + p->UCRcount * 2; /* Undercolor Removal */ + len += 4 + p->BGcount * 2; /* Black Generation */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmUcrBg *p = (icmUcrBg *)pp; + icc *icp = p->icp; + unsigned long i; + int rv = 0; + 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 + 2) > end) { + 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 + 4) > end) { + 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 + 2) > end) { + 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 (check_null_string(bp, p->size) != 0) { + 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; + } + memcpy((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 long of /* File offset to write from */ +) { + icmUcrBg *p = (icmUcrBg *)pp; + icc *icp = p->icp; + unsigned long i; + unsigned int len; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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)) != 0) { + sprintf(icp->err,"icmUcrBg_write: text is not null terminated"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + memcpy((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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmUcrBg *p = (icmUcrBg *)pp; + if (verb <= 0) + return; + + fprintf(op,"Undercolor Removal Curve & Black Generation:\n"); + + if (p->UCRcount == 0) { + fprintf(op," UCR: Not specified\n"); + } else if (p->UCRcount == 1) { + fprintf(op," UCR: %f%%\n",p->UCRcurve[0]); + } else { + fprintf(op," UCR curve no. elements = %u\n",p->UCRcount); + if (verb >= 2) { + unsigned long i; + for (i = 0; i < p->UCRcount; i++) + fprintf(op," %3lu: %f\n",i,p->UCRcurve[i]); + } + } + if (p->BGcount == 0) { + fprintf(op," BG: Not specified\n"); + } else if (p->BGcount == 1) { + fprintf(op," BG: %f%%\n",p->BGcurve[0]); + } else { + fprintf(op," BG curve no. elements = %u\n",p->BGcount); + if (verb >= 2) { + unsigned long i; + for (i = 0; i < p->BGcount; i++) + fprintf(op," %3lu: %f\n",i,p->BGcurve[i]); + } + } + + { + unsigned long i, r, c, size; + fprintf(op," Description:\n"); + fprintf(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) { + fprintf(op,"\n"); + break; + } + if (r > 1 && verb < 2) { + fprintf(op,"...\n"); + break; /* Print 1 row if not verbose */ + } + c = 1; + fprintf(op," 0x%04lx: ",i); + c += 10; + while (i < size && c < 73) { + if (isprint(p->string[i])) { + fprintf(op,"%c",p->string[i]); + c++; + } else { + fprintf(op,"\\%03o",p->string[i]); + c += 4; + } + i++; + } + if (i < size) + fprintf(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 (p->UCRcurve != NULL) + icp->al->free(icp->al, p->UCRcurve); + if ((p->UCRcurve = (double *) icp->al->malloc(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 (p->BGcurve != NULL) + icp->al->free(icp->al, p->BGcurve); + if ((p->BGcurve = (double *) icp->al->malloc(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 (p->string != NULL) + icp->al->free(icp->al, p->string); + if ((p->string = (char *) icp->al->malloc(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) */ + +static unsigned int icmVideoCardGamma_get_size( + icmBase *pp +) { + icmVideoCardGamma *p = (icmVideoCardGamma *)pp; + unsigned int len = 0; + + len += 8; /* 8 bytes for tag and padding */ + len += 4; /* 4 for gamma type */ + + /* compute size of remainder */ + if (p->tagType == icmVideoCardGammaTableType) { + len += 2; /* 2 bytes for channels */ + len += 2; /* 2 for entry count */ + len += 2; /* 2 for entry size */ + len += ( p->u.table.channels * /* compute table size */ + p->u.table.entryCount * + p->u.table.entrySize ); + } + else if (p->tagType == icmVideoCardGammaFormulaType) { + len += 12; /* 4 bytes each for red gamma, min, & max */ + len += 12; /* 4 bytes each for green gamma, min & max */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmVideoCardGamma *p = (icmVideoCardGamma *)pp; + icc *icp = p->icp; + int rv, c; + char *bp, *buf; + unsigned char *pchar; + unsigned short *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 = read_UInt32Number(bp+8); + + /* Read remaining gamma data based on format */ + switch ((int)p->tagType) { + case 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 < 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; + } + pchar = (unsigned char*)p->u.table.data; + pshort = (unsigned short*)p->u.table.data; + for (c=0, bp=bp+18; c<p->u.table.channels*p->u.table.entryCount; c++) { + switch (p->u.table.entrySize) { + case 1: + *pchar++ = read_UInt8Number(bp); + bp++; + break; + case 2: + *pshort++ = read_UInt16Number(bp); + bp+=2; + break; + default: + sprintf(icp->err,"icmVideoCardGamma_read: unsupported table entry size"); + pp->del(pp); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + } + break; + case icmVideoCardGammaFormulaType: + if (len < 48) { + sprintf(icp->err,"icmVideoCardGamma_read: Tag too small to be legal"); + return icp->errc = 1; + } + 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); + break; + default: + 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 long 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; + unsigned char *pchar; + unsigned short *pshort; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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 */ + switch ((int)p->tagType) { + case 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 = (unsigned char*)p->u.table.data; + pshort = (unsigned short*)p->u.table.data; + for (c=0, bp=bp+18; c<p->u.table.channels*p->u.table.entryCount; c++) { + switch (p->u.table.entrySize) { + case 1: + write_UInt8Number(*pchar++,bp); + bp++; + break; + case 2: + write_UInt16Number(*pshort++,bp); + bp+=2; + break; + default: + sprintf(icp->err,"icmVideoCardGamma_write: unsupported table entry size"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + } + break; + case 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; + } + break; + default: + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmVideoCardGamma *p = (icmVideoCardGamma *)pp; + int c,i; + + if (verb <= 0) + return; + + switch ((int)p->tagType) { + case icmVideoCardGammaTableType: + fprintf(op,"VideoCardGammaTable:\n"); + fprintf(op," channels = %d\n", p->u.table.channels); + fprintf(op," entries = %d\n", p->u.table.entryCount); + fprintf(op," entrysize = %d\n", p->u.table.entrySize); + if (verb >= 2) { + /* dump array contents also */ + for (c=0; c<p->u.table.channels; c++) { + fprintf(op," channel #%d\n",c); + for (i=0; i<p->u.table.entryCount; i++) { + if (p->u.table.entrySize == 1) { + fprintf(op," %d: %d\n",i,((unsigned char*)p->u.table.data)[c*p->u.table.entryCount+i]); + } + else if (p->u.table.entrySize == 2) { + fprintf(op," %d: %d\n",i,((unsigned short*)p->u.table.data)[c*p->u.table.entryCount+i]); + } + } + } + } + break; + case icmVideoCardGammaFormulaType: + fprintf(op,"VideoCardGammaFormula:\n"); + fprintf(op," red gamma = %f\n", p->u.formula.redGamma); + fprintf(op," red min = %f\n", p->u.formula.redMin); + fprintf(op," red max = %f\n", p->u.formula.redMax); + fprintf(op," green gamma = %f\n", p->u.formula.greenGamma); + fprintf(op," green min = %f\n", p->u.formula.greenMin); + fprintf(op," green max = %f\n", p->u.formula.greenMax); + fprintf(op," blue gamma = %f\n", p->u.formula.blueGamma); + fprintf(op," blue min = %f\n", p->u.formula.blueMin); + fprintf(op," blue max = %f\n", p->u.formula.blueMax); + break; + default: + fprintf(op," Unknown tag format\n"); + } +} + +/* Allocate variable sized data elements */ +static int icmVideoCardGamma_allocate( + icmBase *pp +) { + icmVideoCardGamma *p = (icmVideoCardGamma *)pp; + icc *icp = p->icp; + 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) { + if (p->u.table.data != NULL) + icp->al->free(icp->al, p->u.table.data); + size = (p->u.table.channels * + p->u.table.entryCount); + switch (p->u.table.entrySize) { + case 1: + size *= sizeof(unsigned char); + break; + case 2: + size *= sizeof(unsigned short); + break; + default: + sprintf(icp->err,"icmVideoCardGamma_alloc: unsupported table entry size"); + return icp->errc = 1; + } + 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; +} + +/* 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->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 += 8; /* 8 bytes for tag and padding */ + len += 12; /* 12 for XYZ of illuminant */ + len += 12; /* 12 for XYZ of surround */ + 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 long len, /* tag length */ + unsigned long 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 long 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 */ + len = p->get_size((icmBase *)p); + 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmViewingConditions *p = (icmViewingConditions *)pp; + if (verb <= 0) + return; + + fprintf(op,"Viewing Conditions:\n"); + fprintf(op," XYZ value of illuminant in cd/m^2 = %s\n", string_XYZNumber(&p->illuminant)); + fprintf(op," XYZ value of surround in cd/m^2 = %s\n", string_XYZNumber(&p->surround)); + fprintf(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 += 8; /* 8 bytes for tag and padding */ + len += 4 + p->ppsize; /* Postscript product name */ + for (t = 0; t < 4; t++) { /* For all 4 intents */ + 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 long len, /* tag length */ + unsigned long of /* start offset within file */ +) { + icmCrdInfo *p = (icmCrdInfo *)pp; + icc *icp = p->icp; + unsigned long t; + int rv = 0; + 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 + 4) > end) { + 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 ((bp + p->ppsize) > end) { + sprintf(icp->err,"icmCrdInfo_read: Data to short to read Postscript product string"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + if (check_null_string(bp,p->ppsize)) { + sprintf(icp->err,"icmCrdInfo_read: Postscript product name is not 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; + } + memcpy((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 + 4) > end) { + sprintf(icp->err,"icmCrdInfo_read: Data too short to read CRD%ld 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 ((bp + p->crdsize[t]) > end) { + sprintf(icp->err,"icmCrdInfo_read: Data to short to read CRD%ld string",t); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + if (check_null_string(bp,p->crdsize[t])) { + sprintf(icp->err,"icmCrdInfo_read: CRD%ld name is not terminated",t); + 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; + } + memcpy((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 long of /* File offset to write from */ +) { + icmCrdInfo *p = (icmCrdInfo *)pp; + icc *icp = p->icp; + unsigned long t; + unsigned int len; + char *bp, *buf; /* Buffer to write from */ + int rv = 0; + + /* Allocate a file write buffer */ + len = p->get_size((icmBase *)p); + 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)) != 0) { + sprintf(icp->err,"icmCrdInfo_write: Postscript product name is not terminated"); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + memcpy((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])) != 0) { + sprintf(icp->err,"icmCrdInfo_write: CRD%ld name is not terminated",t); + icp->al->free(icp->al, buf); + return icp->errc = 1; + } + memcpy((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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + icmCrdInfo *p = (icmCrdInfo *)pp; + unsigned long i, r, c, size, t; + + if (verb <= 0) + return; + + fprintf(op,"PostScript Product name and CRD names:\n"); + + fprintf(op," Product name:\n"); + fprintf(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) { + fprintf(op,"\n"); + break; + } + if (r > 1 && verb < 2) { + fprintf(op,"...\n"); + break; /* Print 1 row if not verbose */ + } + c = 1; + fprintf(op," 0x%04lx: ",i); + c += 10; + while (i < size && c < 73) { + if (isprint(p->ppname[i])) { + fprintf(op,"%c",p->ppname[i]); + c++; + } else { + fprintf(op,"\\%03o",p->ppname[i]); + c += 4; + } + i++; + } + if (i < size) + fprintf(op,"\n"); + } + + for (t = 0; t < 4; t++) { /* For all 4 intents */ + fprintf(op," CRD%ld name:\n",t); + fprintf(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) { + fprintf(op,"\n"); + break; + } + if (r > 1 && verb < 2) { + fprintf(op,"...\n"); + break; /* Print 1 row if not verbose */ + } + c = 1; + fprintf(op," 0x%04lx: ",i); + c += 10; + while (i < size && c < 73) { + if (isprint(p->crdname[t][i])) { + fprintf(op,"%c",p->crdname[t][i]); + c++; + } else { + fprintf(op,"\\%03o",p->crdname[t][i]); + c += 4; + } + i++; + } + if (i < size) + fprintf(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 (p->ppname != NULL) + icp->al->free(icp->al, p->ppname); + if ((p->ppname = (char *) icp->al->malloc(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 (p->crdname[t] != NULL) + icp->al->free(icp->al, p->crdname[t]); + if ((p->crdname[t] = (char *) icp->al->malloc(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 long len, /* tag length */ + unsigned long 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; + } + + /* Fill in the in-memory structure */ + p->size = read_UInt32Number(buf + 0); /* Profile size in bytes */ + 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 */ + 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; + } + 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; + } + 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 */ + + icp->al->free(icp->al, buf); + return 0; +} + +/* Write the contents of the object. Return 0 on sucess, error code on failure */ +static int icmHeader_write( + icmHeader *p, + unsigned long of /* File offset to write from */ +) { + icc *icp = p->icp; + char *buf; /* Buffer to write from */ + unsigned int len; + unsigned int tt; + int rv = 0; + + len = p->get_size(p); + 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(p->flags, buf + 44)) != 0) { /* Various 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((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 ( 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, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + if (verb <= 0) + return; + + fprintf(op,"Header:\n"); + fprintf(op," size = %d bytes\n",p->size); + fprintf(op," CMM = %s\n",tag2str(p->cmmId)); + fprintf(op," Version = %d.%d.%d\n",p->majv, p->minv, p->bfv); + fprintf(op," Device Class = %s\n", string_ProfileClassSignature(p->deviceClass)); + fprintf(op," Color Space = %s\n", string_ColorSpaceSignature(p->colorSpace)); + fprintf(op," Conn. Space = %s\n", string_ColorSpaceSignature(p->pcs)); + fprintf(op," Date, Time = %s\n", string_DateTimeNumber(&p->date)); + fprintf(op," Platform = %s\n", string_PlatformSignature(p->platform)); + fprintf(op," Flags = %s\n", string_ProfileHeaderFlags(p->flags)); + fprintf(op," Dev. Mnfctr. = %s\n",tag2str(p->manufacturer)); /* ~~~ */ + fprintf(op," Dev. Model = %s\n",tag2str(p->model)); /* ~~~ */ + fprintf(op," Dev. Attrbts = %s\n", string_DeviceAttributes(p->attributes.l)); + fprintf(op," Rndrng Intnt = %s\n", string_RenderingIntent(p->renderingIntent)); + fprintf(op," Illuminant = %s\n", string_XYZNumber_and_Lab(&p->illuminant)); + fprintf(op," Creator = %s\n",tag2str(p->creator)); /* ~~~ */ + fprintf(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[] = { + {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}}, + {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}}, + {icMaxEnumType, {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, icMaxEnumType}}, + + {icSigInputClass, -3, icMaxEnumData, icSigXYZData, + {icSigProfileDescriptionTag, + icSigRedColorantTag, + icSigGreenColorantTag, + icSigBlueColorantTag, + icSigRedTRCTag, + icSigGreenTRCTag, + icSigBlueTRCTag, + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigInputClass, -100, icMaxEnumData, icSigPCSData, + {icSigProfileDescriptionTag, + icSigAToB0Tag, + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigDisplayClass, -1, icMaxEnumData, icSigPCSData, + {icSigProfileDescriptionTag, + icSigGrayTRCTag, + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigDisplayClass, -3, icSigRgbData, icSigXYZData, /* Rgb or any 3 component space ?? */ + {icSigProfileDescriptionTag, + icSigRedColorantTag, + icSigGreenColorantTag, + icSigBlueColorantTag, + icSigRedTRCTag, + icSigGreenTRCTag, + icSigBlueTRCTag, + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + /* Non-3 component Display device */ + {icSigDisplayClass, -100, icMaxEnumData, icSigPCSData, + {icSigProfileDescriptionTag, + icSigAToB0Tag, /* BToA doesn't seem to be required, which is strange... */ + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigOutputClass, -1, icMaxEnumData, icSigPCSData, + {icSigProfileDescriptionTag, + icSigGrayTRCTag, + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigOutputClass, -1, icMaxEnumData, icSigPCSData, + {icSigProfileDescriptionTag, + icSigAToB0Tag, + icSigBToA0Tag, + icSigGamutTag, + icSigAToB1Tag, + icSigBToA1Tag, + icSigAToB2Tag, + icSigBToA2Tag, + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigOutputClass, -2, icMaxEnumData, icSigPCSData, + {icSigProfileDescriptionTag, + icSigAToB0Tag, + icSigBToA0Tag, + icSigGamutTag, + icSigAToB1Tag, + icSigBToA1Tag, + icSigAToB2Tag, + icSigBToA2Tag, + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigOutputClass, -3, icMaxEnumData, icSigPCSData, + {icSigProfileDescriptionTag, + icSigAToB0Tag, + icSigBToA0Tag, + icSigGamutTag, + icSigAToB1Tag, + icSigBToA1Tag, + icSigAToB2Tag, + icSigBToA2Tag, + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigOutputClass, -4, icMaxEnumData, icSigPCSData, + {icSigProfileDescriptionTag, + icSigAToB0Tag, + icSigBToA0Tag, + icSigGamutTag, + icSigAToB1Tag, + icSigBToA1Tag, + icSigAToB2Tag, + icSigBToA2Tag, + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigOutputClass, -100, icMaxEnumData, icSigPCSData, /* Assumed from Hexachrome examples */ + {icSigProfileDescriptionTag, + icSigBToA0Tag, + icSigBToA1Tag, + icSigBToA2Tag, + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigLinkClass, -100, icMaxEnumData, icMaxEnumData, + {icSigProfileDescriptionTag, + icSigAToB0Tag, + icSigProfileSequenceDescTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigColorSpaceClass, -100, icMaxEnumData, icSigPCSData, + {icSigProfileDescriptionTag, + icSigBToA0Tag, + icSigAToB0Tag, + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigAbstractClass, -100, icSigPCSData, icSigPCSData, + {icSigProfileDescriptionTag, + icSigAToB0Tag, + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigNamedColorClass, -200, icMaxEnumData, icMaxEnumData, + {icSigProfileDescriptionTag, + icSigNamedColor2Tag, + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icSigNamedColorClass, -100, icMaxEnumData, icMaxEnumData, + {icSigProfileDescriptionTag, + icSigNamedColorTag, /* Not strictly V3.4 */ + icSigMediaWhitePointTag, + icSigCopyrightTag, icMaxEnumType}}, + + {icMaxEnumType,-1,icMaxEnumData, icMaxEnumData, {icMaxEnumType}} +}; + +/* ------------------------------------------------------------- */ +/* 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. */ +static int icc_read( + icc *p, + icmFile *fp, /* File to read from */ + unsigned long of /* File offset to read from */ +) { + char tcbuf[4]; /* Tag count read buffer */ + int i; + unsigned int len; + int er = 0; /* Error code */ + + p->fp = fp; + 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); /* Tag count */ + if (p->count > 0) { + char *bp, *buf; + if ((p->data = (icmTag *) p->al->malloc(p->al, p->count * sizeof(icmTag))) == NULL) { + sprintf(p->err,"icc_read: Tag table malloc() failed"); + return p->errc = 2; + } + + len = 4 + 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) != 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 */ + bp = buf+4; + for (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); + 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; + p->al->free(p->al, buf); + return p->errc = 1; + } + p->data[i].ttype = read_SInt32Number(tcbuf); /* Tag type */ + p->data[i].objp = NULL; /* Read on demand */ + } + p->al->free(p->al, buf); + } /* p->count > 0 */ + + return er; +} + +#define DO_ALIGN(val) (((val) + 3) & ~3) + +/* Return the total size needed for the profile */ +/* Return 0 on error. */ +static unsigned int icc_get_size( + icc *p +) { + unsigned int size = 0; + int i; + + /* Check that the right tags etc. are present for a legal ICC profile */ + if (check_icc_legal(p) != 0) { + return 0; + } + + /* 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 += p->header->get_size(p->header); + + size = DO_ALIGN(size); + size += 4 + p->count * 12; /* Tag table length */ + + /* 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 = DO_ALIGN(size); + size += p->data[i].objp->get_size(p->data[i].objp); + p->data[i].objp->touched = 1; /* Don't account for this again */ + } + } + + return size; /* Total size needed */ +} + +/* Write the contents of the object. Return 0 on sucess, error code on failure */ +static int icc_write( + icc *p, + icmFile *fp, /* File to write to */ + unsigned long of /* File offset to write to */ +) { + char *bp, *buf; /* Buffer to write to */ + unsigned int len; + int rv = 0; + int i; + unsigned int size = 0; + + /* Check that the right tags etc. are present for a legal ICC profile */ + if ((rv = check_icc_legal(p)) != 0) { + return rv; + } + + p->fp = fp; /* Open file pointer */ + p->of = of; /* Offset of ICC profile */ + + /* 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 += p->header->get_size(p->header); + + len = 4 + p->count * 12; /* Tag table length */ + size = DO_ALIGN(size); + size += len; + + /* Allocate memory buffer for tag table */ + if ((buf = (char *) p->al->malloc(p->al, len)) == NULL) { + sprintf(p->err,"icc_write malloc() 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 */ + for (i = 0; i < p->count; i++) { + if (p->data[i].objp->touched == 0) { /* Allocate space for tag type */ + size = DO_ALIGN(size); + p->data[i].offset = size; /* Profile relative target */ + p->data[i].size = p->data[i].objp->get_size(p->data[i].objp); + size += p->data[i].size; + p->data[i].objp->touched = 1; /* Allocated space for it */ + } else { /* must be linked - copy allocation */ + 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; + } + /* 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 size */ + + /* Write the header */ + if ((rv = p->header->write(p->header, of)) != 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: fseek() or fwrite() failed"); + p->al->free(p->al, buf); + return p->errc = 1; + } + p->al->free(p->al, buf); + + /* Write all the tag element data */ + 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; + } + p->data[i].objp->touched = 0; /* Written it */ + } + + if (p->fp->flush(p->fp) != 0) { + sprintf(p->err,"icc_write flush() failed"); + p->al->free(p->al, buf); + return p->errc = 2; + } + return rv; +} +#undef DO_ALIGN + +/* 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 */ +static icmBase *icc_add_tag( + icc *p, + icTagSignature sig, /* Tag signature - may be unknown */ + icTagTypeSignature ttype /* Tag type */ +) { + icmBase *tp; + icmBase *nob; + int i, j, ok = 1; + + /* 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 exits */ + /* (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 (p->data == NULL) + tp = p->al->malloc(p->al, (p->count+1) * sizeof(icmTag)); + else + tp = 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; + + /* 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; + int i, j, exi, 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 = p->al->malloc(p->al, (p->count+1) * sizeof(icmTag)); + else + tp = 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 */ +) { + int i,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 tag element data, and return a pointer to the object */ +/** + * Returns NULL if error - icc->errc will contain: + * 1 if found but not handled type + * 2 if not found + **/ +/* NOTE: we don't handle tag duplication - you'll always get the first in the file */ +static icmBase *icc_read_tag( + icc *p, + icTagSignature sig /* Tag signature - may be unknown */ +) { + icmBase *nob; + int i,j,k; + + /* 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; + } + + /* 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) { + sprintf(p->err,"icc_read_tag: Unhandled tag type '%s'",string_TypeSignature(p->data[i].ttype)); + p->errc = 1; + return NULL; + } + + /* Creat and read in the object */ + if ((nob = typetable[j].new_obj(p)) == 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; +} + +/* 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 */ +) { + int i, j, k, 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 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 */ +static int icc_unread_tag( + icc *p, + icTagSignature sig /* Tag signature - may be unknown */ +) { + 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; + } + + /* 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(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; +} + +/* Delete 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 */ +static int icc_delete_tag( + icc *p, + icTagSignature sig /* Tag signature - may be unknown */ +) { + 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; + } + + /* 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; +} + + +/* Read all the tags into memory. */ +/* Returns non-zero on error. */ +static int icc_read_all_tags( + icc *p +) { + int i; + + for (i = 0; i < p->count; i++) { /* For all the tag element data */ + icmBase *ob; + if ((ob = p->read_tag(p, p->data[i].sig)) == NULL) { + return p->errc; + } + } + return 0; +} + + +static void icc_dump( + icc *p, + FILE *op, /* Output to dump to */ + int verb /* Verbosity level */ +) { + int i; + if (verb <= 0) + return; + + fprintf(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; + fprintf(op,"tag %d:\n",i); + fprintf(op," sig %s\n",tag2str(p->data[i].sig)); + fprintf(op," type %s\n",tag2str(p->data[i].ttype)); + fprintf(op," offset %d\n", p->data[i].offset); + fprintf(op," size %d\n", p->data[i].size); + tr = 0; + if ((ob = p->data[i].objp) == NULL) { + /* The object is not loaded, so load it then free it */ + if ((ob = p->read_tag(p, p->data[i].sig)) == NULL) { + fprintf(op,"Unable to read: %d, %s\n",p->errc,p->err); + } + tr = 1; + } + if (ob != NULL) { + /* fprintf(op," refcount %d\n", ob->refcount); */ + ob->dump(ob,op,verb-1); + + if (tr != 0) { /* Cleanup if temporary */ + ob->refcount--; + (ob->del)(ob); + p->data[i].objp = NULL; + } + } + fprintf(op,"\n"); + } +} + +static void icc_delete( + icc *p +) { + 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 */ + 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); + + /* 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 the PCS 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 PCS spaces (XYZ or Lab) */ +/* 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. */ + +/* 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 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 Lut16 table index/value to Lab */ +static void Lut_Lut2Lab16(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_Lab2Lut16(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 Lut8 table index/value to Lab */ +static void Lut_Lut2Lab8(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_Lab2Lut8(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 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 (*fromLut8)(double *out, double *in); /* 8 bit from Lut index/entry */ + void (*fromLut16)(double *out, double *in); /* 16 bit from Lut index/entry */ + void (*toLut8)(double *out, double *in); /* 8 bit to Lut index/entry */ + void (*toLut16)(double *out, double *in); /* 16 bit to Lut index/entry */ +} colnormtable[] = { + {icSigXYZData, NULL, Lut_Lut2XYZ, NULL, Lut_XYZ2Lut }, + {icSigLabData, Lut_Lut2Lab8, Lut_Lut2Lab16, Lut_Lab2Lut8, Lut_Lab2Lut16 }, + {icSigLuvData, Lut_Lut2Luv, Lut_Lut2Luv, Lut_Luv2Lut, Lut_Luv2Lut }, + {icSigYxyData, Lut_3, Lut_3, Lut_3, Lut_3 }, + {icSigRgbData, Lut_3, Lut_3, Lut_3, Lut_3 }, + {icSigGrayData, Lut_1, Lut_1, Lut_1, Lut_1 }, + {icSigHsvData, Lut_3, Lut_3, Lut_3, Lut_3 }, + {icSigHlsData, Lut_3, Lut_3, Lut_3, Lut_3 }, + {icSigCmykData, Lut_4, Lut_4, Lut_4, Lut_4 }, + {icSigCmyData, Lut_3, Lut_3, Lut_3, Lut_3 }, + {icSigMch6Data, Lut_6, Lut_6, Lut_6, Lut_6 }, + {icSig2colorData, Lut_2, Lut_2, Lut_2, Lut_2 }, + {icSig3colorData, Lut_3, Lut_3, Lut_3, Lut_3 }, + {icSig4colorData, Lut_4, Lut_4, Lut_4, Lut_4 }, + {icSig5colorData, Lut_5, Lut_5, Lut_5, Lut_5 }, + {icSig6colorData, Lut_6, Lut_6, Lut_6, Lut_6 }, + {icSig7colorData, Lut_7, Lut_7, Lut_7, Lut_7 }, + {icSig8colorData, Lut_8, Lut_8, Lut_8, Lut_8 }, + {icSig9colorData, Lut_9, Lut_9, Lut_9, Lut_9 }, + {icSig10colorData, Lut_10, Lut_10, Lut_10, Lut_10 }, + {icSig11colorData, Lut_11, Lut_11, Lut_11, Lut_11 }, + {icSig12colorData, Lut_12, Lut_12, Lut_12, Lut_12 }, + {icSig13colorData, Lut_13, Lut_13, Lut_13, Lut_13 }, + {icSig14colorData, Lut_14, Lut_14, Lut_14, Lut_14 }, + {icSig15colorData, Lut_15, Lut_15, Lut_15, Lut_15 }, + {icMaxEnumData, NULL, NULL, NULL, NULL } +}; + +/* Find appropriate conversion functions */ +/* 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( + icColorSpaceSignature csig, + icTagTypeSignature tagSig, + icmNormFlag flag, + void (**nfunc)(double *out, double *in) +) { + int i; + 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 */ + if (tagSig == icSigLut8Type) { + *nfunc = colnormtable[i].fromLut8; + return 0; + } else if (tagSig == icSigLut16Type) { + *nfunc = colnormtable[i].fromLut16; + return 0; + } else { + *nfunc = NULL; + return 1; + } + } else if (flag == icmToLuti || flag == icmToLutv) { /* Table index/value encoding functions */ + if (tagSig == icSigLut8Type) { + *nfunc = colnormtable[i].toLut8; + return 0; + } else if (tagSig == icSigLut16Type) { + *nfunc = colnormtable[i].toLut16; + return 0; + } else { + *nfunc = NULL; + return 1; + } + } else { + *nfunc = NULL; + return 1; + } + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Colorspace ranges - used instead of norm/denorm by Mono & Matrix */ + +/* Function table - match ranges to color spaces. */ +/* Anything not here, we don't know how to convert. */ +/* (ie. YCbCr) */ +static struct { + icColorSpaceSignature csig; + int same; /* Non zero if first entry applies to all channels */ + double min[15]; /* Minimum value for this colorspace */ + double max[15]; /* Maximum value for this colorspace */ +} colorrangetable[] = { + {icSigXYZData, 1, { 0.0 } , { 1.0 + 32767.0/32768.0 } }, + {icSigLabData, 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 } }, + {icSigLuvData, 0, { 0.0, -128.0, -128.0 }, + { 100.0, 127.0 + 255.0/256.0, 127.0 + 255.0/256.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 } }, + {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 } +}; + +/* Find appropriate typical encoding ranges for a */ +/* colorspace given the color space. */ +/* Return 0 on success, 1 on match failure */ +static int getRange( + icColorSpaceSignature csig, + double *min, double *max +) { + int i, e, ee; + 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; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* + 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 = det3x3( 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 | + */ + +static double det3x3(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 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 */ +static int inverse3x3( +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 = det3x3(in); + + if ( fabs(det) < 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; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Multuply XYZ array by 3x3 transform matrix */ +static 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]; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ +/* CIE XYZ to perceptual Lab */ +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; + double L; + + x = X/w->X; + if (x > 0.008856451586) + fx = pow(x,1.0/3.0); + else + fx = 7.787036979 * x + 16.0/116.0; + + y = Y/w->Y; + if (y > 0.008856451586) { + fy = pow(y,1.0/3.0); + L = 116.0 * fy - 16.0; + } else { + fy = 7.787036979 * y + 16.0/116.0; + L = 903.2963058 * y; + } + + z = Z/w->Z; + if (z > 0.008856451586) + fz = pow(z,1.0/3.0); + else + fz = 7.787036979 * z + 16.0/116.0; + + out[0] = L; + out[1] = 500.0 * (fx - fy); + out[2] = 200.0 * (fy - fz); +} + +/* Perceptual Lab 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; + + if (L > 8.0) { + fy = (L + 16.0)/116.0; + y = pow(fy,3.0); + } else { + y = L/903.2963058; + fy = 7.787036979 * y + 16.0/116.0; + } + + fx = a/500.0 + fy; + if (fx > 24.0/116.0) + x = pow(fx,3.0); + else + x = (fx - 16.0/116.0)/7.787036979; + + fz = fy - b/200.0; + 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; +} + +/* available D50 Illuminant */ +icmXYZNumber icmD50 = { /* Profile illuminant - D50 */ + 0.9642, 1.0000, 0.8249 +}; + +/* available D65 Illuminant */ +icmXYZNumber icmD65 = { /* Profile illuminant - D65 */ + 0.9505, 1.0000, 1.0890 +}; + +/* Default black point */ +icmXYZNumber icmBlack = { + 0.0000, 0.0000, 0.0000 +}; + +/* Return the normal Delta E given two Lab values */ +double icmLabDE(double *Lab1, double *Lab2) { + double rv = 0.0, tt; + + tt = Lab1[0] - Lab2[0]; + rv += tt * tt; + tt = Lab1[1] - Lab2[1]; + rv += tt * tt; + tt = Lab1[2] - Lab2[2]; + rv += tt * tt; + + return sqrt(rv); +} + +/* Return the normal Delta E squared, given two Lab values */ +double icmLabDEsq(double *Lab1, double *Lab2) { + double rv = 0.0, tt; + + tt = Lab1[0] - Lab2[0]; + rv += tt * tt; + tt = Lab1[1] - Lab2[1]; + rv += tt * tt; + tt = Lab1[2] - Lab2[2]; + rv += tt * tt; + + return rv; +} + +/* Return the CIE94 Delta E color difference measure */ +double icmCIE94(double Lab1[3], double Lab2[3]) { + double desq, dhsq; + double dlsq, dcsq; + double c12; + + { + double dl, da, db; + dl = Lab1[0] - Lab2[0]; + dlsq = dl * dl; /* dl squared */ + da = Lab1[1] - Lab2[1]; + db = Lab1[2] - Lab2[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(Lab1[1] * Lab1[1] + Lab1[2] * Lab1[2]); + c2 = sqrt(Lab2[1] * Lab2[1] + Lab2[2] * Lab2[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 sqrt(dlsq + dcsq/(sc * sc) + dhsq/(sh * sh)); + } +} + +/* Return the CIE94 Delta E color difference measure, squared */ +double icmCIE94sq(double Lab1[3], double Lab2[3]) { + double desq, dhsq; + double dlsq, dcsq; + double c12; + + { + double dl, da, db; + dl = Lab1[0] - Lab2[0]; + dlsq = dl * dl; /* dl squared */ + da = Lab1[1] - Lab2[1]; + db = Lab1[2] - Lab2[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(Lab1[1] * Lab1[1] + Lab1[2] * Lab1[2]); + c2 = sqrt(Lab2[1] * Lab2[1] + Lab2[2] * Lab2[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); + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Multiply one 3x3 with another */ +static void mul3x3(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]; +} + +/* Chrmatic Adaption transform utility */ +/* Return a 3x3 chromatic adaption matrix */ +void icmChromAdaptMatrix( + int 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 */ + 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 } + }; + double ibradford[3][3]; /* Inverse Bradford */ + + /* Set initial matrix to unity */ + if (!(flags & ICM_CAM_MULMATRIX)) { + mat[0][0] = mat[1][1] = mat[2][2] = 1.0; + mat[0][1] = mat[0][2] = 0.0; + mat[1][0] = mat[1][2] = 0.0; + mat[2][0] = mat[2][1] = 0.0; + } + + 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 adaption 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) { + mul3x3(mat, bradford); + } + + /* Apply chromatic adaption */ + mul3x3(mat, vkmat); + + /* Transform from Bradford space */ + if (flags & ICM_CAM_BRADFORD) { + inverse3x3(ibradford, bradford); + mul3x3(mat, ibradford); + } + + /* We're done */ +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Return information about the native lut in/out 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 */ +) { + 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); +} + +/* Return information about the effective lookup in/out colorspaces, */ +/* including allowance for PCS overide. */ +/* 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 */ +) { + 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; +} + +/* Return the media white and black points in XYZ space. */ +/* Note that if not in the icc, the black point will be returned as 0, 0, 0 */ +/* Any pointer may be NULL if value is not to be returned */ +static void icmLuWh_bk_points( +struct _icmLuBase *p, +icmXYZNumber *wht, +icmXYZNumber *blk +) { + if (wht != NULL) + *wht = p->whitePoint; /* Structure copy */ + + if (blk != NULL) + *blk = p->blackPoint; /* Structure copy */ +} + +/* Get the effective (externally visible) ranges for the Monochrome or Matrix profile */ +/* 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 */ +) { + /* Hmm. we have no way of handlin an error from getRange. */ + /* It shouldn't ever return one unless there is a mismatch between */ + /* getRange and Lu creation... */ + getRange(p->e_inSpace, inmin, inmax); + getRange(p->e_outSpace, outmin, outmax); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Forward and Backward Monochrome type conversion */ +/* 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) { + int i; + for (i = 0; i < 3; i++) /* Don't alter input values */ + out[i] = in[i]; + } + + /* Do absolute conversion */ + if (p->intent == icAbsoluteColorimetric) { + + if (p->pcs == icSigLabData) /* Convert L to Y */ + icmLab2XYZ(&p->pcswht, out, out); + + /* Convert from Relative to Absolute colorometric */ + 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 */ +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; +} + + +/* 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) { + int i; + for (i = 0; i < 3; i++) /* Don't alter input values */ + out[i] = in[i]; + } + + /* Force to monochrome locus in correct space */ + if (p->e_pcs == icSigLabData) { + double wp[3]; + + if (p->intent == icAbsoluteColorimetric) { + 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) { + 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 to */ + if (p->intent == icAbsoluteColorimetric) { + + 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 */ +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; +} + +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 */ + icmXYZNumber whitePoint, /* Profile absolute white point */ + icmXYZNumber blackPoint, /* Profile absolute black point */ + 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->get_ranges = icmLu_get_ranges; + p->wh_bk_points = icmLuWh_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; + } else { + p->ttype = icmMonoFwdType; + p->lookup = icmLuMonoFwd_lookup; + } + + /* 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->whitePoint = whitePoint; + p->blackPoint = blackPoint; + 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; + + /* Create absolute <-> relative conversion matricies */ + icmChromAdaptMatrix(ICM_CAM_BRADFORD, whitePoint, icmD50, p->toAbs); + icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, whitePoint, p->fromAbs); + + 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 */ + icmXYZNumber whitePoint, /* Profile absolute white point */ + icmXYZNumber blackPoint, /* Profile absolute black point */ + icRenderingIntent intent, /* Rendering intent */ + icmLookupFunc func /* Functionality requested */ +) { + return new_icmLuMono(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs, + whitePoint, blackPoint, 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 */ + icmXYZNumber whitePoint, /* Profile absolute white point */ + icmXYZNumber blackPoint, /* Profile absolute black point */ + icRenderingIntent intent, /* Rendering intent */ + icmLookupFunc func /* Functionality requested */ +) { + return new_icmLuMono(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs, + whitePoint, blackPoint, 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) { + int i; + for (i = 0; i < 3; i++) /* Don't alter input values */ + out[i] = in[i]; + } + + /* If required, convert from Relative to Absolute colorometric */ + if (p->intent == icAbsoluteColorimetric) { + 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 */ +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; +} + +/* 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) { + int i; + for (i = 0; i < 3; i++) /* Don't alter input values */ + out[i] = in[i]; + } + + /* 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 colorometric */ + if (p->intent == icAbsoluteColorimetric) { + 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]; + + /* Matrix */ + tt[0] = p->bmx[0][0] * in[0] + p->bmx[0][1] * in[1] + p->bmx[0][2] * in[2]; + tt[1] = p->bmx[1][0] * in[0] + p->bmx[1][1] * in[1] + p->bmx[1][2] * in[2]; + tt[2] = p->bmx[2][0] * in[0] + p->bmx[2][1] * in[1] + p->bmx[2][2] * in[2]; + + out[0] = tt[0]; + out[1] = tt[1]; + out[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],&out[0])) > 1 + || (rv |= p->greenCurve->lookup_bwd(p->greenCurve,&out[1],&out[1])) > 1 + || (rv |= p->blueCurve->lookup_bwd(p->blueCurve,&out[2],&out[2])) > 1) { + sprintf(icp->err,"icc_lookup: Curve->lookup_bwd() failed"); + icp->errc = rv; + return 2; + } + return rv; +} + +/* Overall Bwd conversion */ +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; +} + +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 */ + icmXYZNumber whitePoint, /* Profile absolute white point */ + icmXYZNumber blackPoint, /* Profile absolute black point */ + 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->get_ranges = icmLu_get_ranges; + p->wh_bk_points = icmLuWh_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; + } else { + p->ttype = icmMatrixFwdType; + p->lookup = icmLuMatrixFwd_lookup; + } + + /* 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; + + if (inverse3x3(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->whitePoint = whitePoint; + p->blackPoint = blackPoint; + 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; + + /* Create absolute <-> relative conversion matricies */ + icmChromAdaptMatrix(ICM_CAM_BRADFORD, whitePoint, icmD50, p->toAbs); + icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, whitePoint, p->fromAbs); + + 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 */ + icmXYZNumber whitePoint, /* Profile absolute white point */ + icmXYZNumber blackPoint, /* Profile absolute black point */ + icRenderingIntent intent, /* Rendering intent */ + icmLookupFunc func /* Functionality requested */ +) { + return new_icmLuMatrix(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs, + whitePoint, blackPoint, 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 */ + icmXYZNumber whitePoint, /* Profile absolute white point */ + icmXYZNumber blackPoint, /* Profile absolute black point */ + icRenderingIntent intent, /* Rendering intent */ + icmLookupFunc func /* Functionality requested */ +) { + return new_icmLuMatrix(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs, + whitePoint, blackPoint, 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; + + if (out != in) { + 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) { + + if (p->e_inSpace == icSigLabData) + icmLab2XYZ(&p->pcswht, out, out); + + /* Convert from Absolute to Relative colorometric */ + icmMulBy3x3(out, p->fromAbs, out); + + if (p->inSpace == icSigLabData) + icmXYZ2Lab(&p->pcswht, out, out); + + } else { + + /* Convert from Effective to Native input space */ + if (p->e_inSpace == icSigLabData && p->inSpace == icSigXYZData) + icmLab2XYZ(&p->pcswht, out, out); + else if (p->e_inSpace == icSigXYZData && p->inSpace == icSigLabData) + icmXYZ2Lab(&p->pcswht, out, 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) { + 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; + + if (out != in) { + int i; + for (i = 0; i < lut->inputChan; 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) { + + if (p->outSpace == icSigLabData) + icmLab2XYZ(&p->pcswht, out, out); + + /* Convert from Relative to Absolute colorometric XYZ */ + icmMulBy3x3(out, p->toAbs, out); + + if (p->e_outSpace == icSigLabData) + icmXYZ2Lab(&p->pcswht, out, out); + } else { + + /* Convert from Native to Effective output space */ + if (p->outSpace == icSigLabData && p->e_outSpace == icSigXYZData) + icmLab2XYZ(&p->pcswht, out, out); + else if (p->outSpace == icSigXYZData && p->e_outSpace == icSigLabData) + icmXYZ2Lab(&p->pcswht, out, 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]; + + rv |= p->in_abs(p,temp,in); /* Possible absolute conversion */ + if (p->usematrix) + rv |= lut->lookup_matrix(lut,temp,temp);/* If XYZ, multiply by non-unity matrix */ + p->in_normf(temp, temp); /* Normalize for input color space */ + rv |= lut->lookup_input(lut,temp,temp); /* Lookup though input tables */ + rv |= p->lookup_clut(lut,out,temp); /* Lookup though clut tables */ + rv |= lut->lookup_output(lut,out,out); /* Lookup though output tables */ + p->out_denormf(out,out); /* Normalize for output color space */ + rv |= p->out_abs(p,out,out); /* Possible absolute conversion */ + + 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 */ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* 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; + + if (out != in) { + int i; + for (i = 0; i < lut->inputChan; 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) { + + if (p->e_outSpace == icSigLabData) + icmLab2XYZ(&p->pcswht, out, out); + + /* Convert from Absolute to Relative colorometric */ + icmMulBy3x3(out, p->fromAbs, out); + + if (p->outSpace == icSigLabData) + icmXYZ2Lab(&p->pcswht, out, out); + + } else { + + /* Convert from Effective to Native output space */ + if (p->e_outSpace == icSigLabData && p->outSpace == icSigXYZData) + icmLab2XYZ(&p->pcswht, out, out); + else if (p->e_outSpace == icSigXYZData && p->outSpace == icSigLabData) + icmXYZ2Lab(&p->pcswht, out, 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 rv = 0; + + if (lut->rot.inited == 0) { + rv = icmTable_setup_bwd(icp, &lut->rot, lut->outputEnt, lut->outputTable); + 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 */ + rv |= icmTable_lookup_bwd(&lut->rot, out, out); /* Reverse lookup though output tables */ + 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 rv = 0; + + if (lut->rit.inited == 0) { + rv = icmTable_setup_bwd(icp, &lut->rit, lut->inputEnt, lut->inputTable); + 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 */ + rv |= icmTable_lookup_bwd(&lut->rit, out, out); /* Reverse lookup though input tables */ + 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 (inverse3x3(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) { + 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; + + if (out != in) { + 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) { + + if (p->inSpace == icSigLabData) + icmLab2XYZ(&p->pcswht, out, out); + + /* Convert from Relative to Absolute colorometric XYZ */ + icmMulBy3x3(out, p->toAbs, out); + + if (p->e_inSpace == icSigLabData) + icmXYZ2Lab(&p->pcswht, out, out); + } else { + + /* Convert from Native to Effective input space */ + if (p->inSpace == icSigLabData && p->e_inSpace == icSigXYZData) + icmLab2XYZ(&p->pcswht, out, out); + else if (p->inSpace == icSigXYZData && p->e_inSpace == icSigLabData) + icmXYZ2Lab(&p->pcswht, out, 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 */ +static void +icmLuLut_get_lutranges ( + struct _icmLuLut *p, + double *inmin, double *inmax, /* Return maximum range of inspace values */ + double *outmin, double *outmax /* Return maximum range of outspace values */ +) { + 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 (externally visible) ranges for the LuLut */ +/* Arguments may be NULL */ +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; + double tinmin[MAX_CHAN], tinmax[MAX_CHAN], toutmin[MAX_CHAN], toutmax[MAX_CHAN]; + int i; + + /* fudge NULL arguments so that they don't bomb */ + if (inmin == NULL) + inmin = tinmin; + if (inmax == NULL) + inmax = tinmax; + if (outmin == NULL) + outmin = toutmin; + if (outmax == NULL) + outmax = toutmax; + + for (i = 0; i < p->lut->inputChan; i++) { + inmin[i] = 0.0; /* Normalized range of input space values */ + inmax[i] = 1.0; + } + p->e_in_denormf(inmin,inmin); /* Convert to real colorspace range */ + p->e_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->e_out_denormf(outmin,outmin); /* Convert to real colorspace range */ + p->e_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; + } + } +} + +/* 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 */ + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + if (i == j) + m[i][j] = 1.0; + else + m[i][j] = 0.0; + } + } + } +} + + +static void +icmLuLut_delete( +icmLuBase *p +) { + icc *icp = p->icp; + + icp->al->free(icp->al, p); +} + +static icmLuBase * +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 */ + icColorSpaceSignature e_inSpace, /* Effective Input color space */ + icColorSpaceSignature e_outSpace, /* Effective Output color space */ + icColorSpaceSignature e_pcs, /* Effective PCS */ + icmXYZNumber whitePoint, /* Profile absolute white point */ + icmXYZNumber blackPoint, /* Profile absolute black point */ + icRenderingIntent intent, /* Rendering intent */ + icmLookupFunc func /* Functionality requested */ +) { + 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->wh_bk_points = icmLuWh_bk_points; + + p->lookup = icmLuLut_lookup; + 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->whitePoint = whitePoint; + p->blackPoint = blackPoint; + 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; + p->get_info = icmLuLut_get_info; + p->get_lutranges = icmLuLut_get_lutranges; + p->get_ranges = icmLuLut_get_ranges; + p->get_matrix = icmLuLut_get_matrix; + + /* Create absolute <-> relative conversion matricies */ + icmChromAdaptMatrix(ICM_CAM_BRADFORD, whitePoint, icmD50, p->toAbs); + icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, whitePoint, p->fromAbs); + + /* 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(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(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(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(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; + } + + /* Lookup normalized index to effective input color space function */ + if (getNormFunc(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(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; + } + + /* 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); + + /* 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 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 icSigXYZData: + 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; + } + 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 overide (0 = def) */ + icmLookupOrder order /* Conversion representation search Order */ +) { + int rv; + icmLuBase *luobj = NULL; /* Lookup object to return */ + icmXYZNumber whitePoint, blackPoint; + icColorSpaceSignature pcs, e_pcs; /* PCS and effective PCS */ + + /* Check that the profile is legal, since we depend on it */ + if ((rv = check_icc_legal(p)) != 0) + return NULL; + + /* Figure out the native and effective PCS */ + e_pcs = pcs = p->header->pcs; + if (pcsor != icmSigDefaultData) + e_pcs = pcsor; /* Overide */ + + /* Get White and Black points from the profile */ + { + icmXYZArray *whitePointTag, *blackPointTag; + + if ((whitePointTag = (icmXYZArray *)p->read_tag(p, icSigMediaWhitePointTag)) == NULL + || whitePointTag->ttype != icSigXYZType || whitePointTag->size < 1) { + if (intent == icAbsoluteColorimetric) { + sprintf(p->err,"icc_lookup: Profile is missing Media White Point Tag"); + p->errc = 1; + return NULL; + } + whitePoint = icmD50; /* safe value */ + } else + whitePoint = whitePointTag->data[0]; /* Copy structure */ + + if ((blackPointTag = (icmXYZArray *)p->read_tag(p, icSigMediaBlackPointTag)) == NULL + || blackPointTag->ttype != icSigXYZType || blackPointTag->size < 1) { + blackPoint = icmBlack; /* default */ + } else + blackPoint = blackPointTag->data[0]; /* Copy structure */ + } + + /* How we expect to execute the request depends firstly on the type of profile */ + switch (p->header->deviceClass) { + case icSigInputClass: + case icSigDisplayClass: + /* Look for AToB0 based profile + optional BToA0 reverse */ + /* or three component matrix profile (reversable) */ + /* or momochrome table profile (reversable) */ + /* No intent */ + /* Device <-> PCS */ + /* Determine the algorithm and set its parameters */ + + if (intent != icmDefaultIntent + && intent != icAbsoluteColorimetric) { + sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Input/Display profile"); + p->errc = 1; + return NULL; + } + + switch (func) { + case icmFwd: /* Device to PCS */ + if (order != icmLuOrdRev) { + /* Try Lut type lookup first */ + if ((luobj = new_icmLuLut(p, icSigAToB0Tag, + p->header->colorSpace, pcs, pcs, + p->header->colorSpace, e_pcs, e_pcs, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, intent, func)) != NULL) + break; + + /* Try Lut type lookup last */ + if ((luobj = new_icmLuLut(p, icSigAToB0Tag, + p->header->colorSpace, pcs, pcs, + p->header->colorSpace, e_pcs, e_pcs, + whitePoint, blackPoint, intent, func)) != NULL) + break; + } + break; + + case icmBwd: /* PCS to Device */ + if (order != icmLuOrdRev) { + /* Try Lut type lookup first */ + if ((luobj = new_icmLuLut(p, icSigBToA0Tag, + pcs, p->header->colorSpace, pcs, + e_pcs, p->header->colorSpace, e_pcs, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, intent, func)) != NULL) + break; + + /* Try Lut type lookup last */ + if ((luobj = new_icmLuLut(p, icSigBToA0Tag, + pcs, p->header->colorSpace, pcs, + e_pcs, p->header->colorSpace, e_pcs, + whitePoint, blackPoint, intent, 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 = icRelativeColorimetric; /* Make this the default */ + + switch (intent) { + case icRelativeColorimetric: + case icAbsoluteColorimetric: + ttag = icSigAToB1Tag; + break; + case icPerceptual: + ttag = icSigAToB0Tag; + break; + case icSaturation: + 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 = new_icmLuLut(p, ttag, + p->header->colorSpace, pcs, pcs, + p->header->colorSpace, e_pcs, e_pcs, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, intent, func)) != NULL) + break; + + /* Try Lut type lookup last */ + if ((luobj = new_icmLuLut(p, ttag, + p->header->colorSpace, pcs, pcs, + p->header->colorSpace, e_pcs, e_pcs, + whitePoint, blackPoint, intent, func)) != NULL) + break; + } + break; + + case icmBwd: /* PCS to Device */ + + if (intent == icmDefaultIntent) + intent = icRelativeColorimetric; /* Make this the default */ + + switch (intent) { + case icRelativeColorimetric: + case icAbsoluteColorimetric: + ttag = icSigBToA1Tag; + break; + case icPerceptual: + ttag = icSigBToA0Tag; + break; + case icSaturation: + 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 = new_icmLuLut(p, ttag, + pcs, p->header->colorSpace, pcs, + e_pcs, p->header->colorSpace, e_pcs, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, 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, + whitePoint, blackPoint, intent, func)) != NULL) + break; + + /* Try Lut type lookup last */ + if ((luobj = new_icmLuLut(p, ttag, + pcs, p->header->colorSpace, pcs, + e_pcs, p->header->colorSpace, e_pcs, + whitePoint, blackPoint, intent, func)) != NULL) + break; + } + break; + + case icmGamut: /* PCS to 1D */ + + if (intent != icmDefaultIntent) { + sprintf(p->err,"icc_get_luobj: Intent is inappropriate for type of function"); + p->errc = 1; + return NULL; + } + + /* If the target tag exists, and it is a Lut */ + luobj = new_icmLuLut(p, icSigGamutTag, + pcs, icSigGrayData, pcs, + e_pcs, icSigGrayData, e_pcs, + whitePoint, blackPoint, 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: + sprintf(p->err,"icc_get_luobj: Intent is inappropriate for type of function"); + 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 = new_icmLuLut(p, ttag, + pcs, pcs, pcs, + e_pcs, e_pcs, e_pcs, + whitePoint, blackPoint, 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 = new_icmLuLut(p, icSigAToB0Tag, + p->header->colorSpace, pcs, pcs, + p->header->colorSpace, pcs, pcs, + whitePoint, blackPoint, intent, func); + break; + + case icmBwd: /* PCS (== Device) to Device */ + + luobj = new_icmLuLut(p, icSigBToA0Tag, + pcs, p->header->colorSpace, pcs, + pcs, p->header->colorSpace, pcs, + whitePoint, blackPoint, 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 BToA Lut, no intents */ + /* PCS <-> PCS */ + /* Determine the algorithm and set its parameters */ + + if (intent != icmDefaultIntent) { + 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 = new_icmLuLut(p, icSigAToB0Tag, + p->header->colorSpace, pcs, pcs, + e_pcs, e_pcs, e_pcs, + whitePoint, blackPoint, intent, func); + break; + + case icmBwd: /* PCS to PCS (== Device) */ + + luobj = new_icmLuLut(p, icSigBToA0Tag, + pcs, p->header->colorSpace, pcs, + e_pcs, e_pcs, e_pcs, + whitePoint, blackPoint, intent, func); + break; + + default: + sprintf(p->err,"icc_get_luobj: Inaproptiate function requested"); + p->errc = 1; + return NULL; + } + break; + + case icSigColorSpaceClass: + /* Expect AToB0 Lut and BToA0 Lut, no intents, */ + /* Device <-> PCS */ + + if (intent != icmDefaultIntent) { + sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Colorspace profile"); + p->errc = 1; + return NULL; + } + + /* Determine the algorithm and set its parameters */ + switch (func) { + case icmFwd: /* Device to PCS */ + + luobj = new_icmLuLut(p, icSigAToB0Tag, + p->header->colorSpace, pcs, pcs, + p->header->colorSpace, e_pcs, e_pcs, + whitePoint, blackPoint, intent, func); + break; + + case icmBwd: /* PCS to Device */ + + luobj = new_icmLuLut(p, icSigBToA0Tag, + pcs, p->header->colorSpace, pcs, + e_pcs, p->header->colorSpace, e_pcs, + whitePoint, blackPoint, 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) { + 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; + } + + return luobj; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Create an empty object. Return null on error */ +icc *new_icc_a( +icmAlloc *al /* Optional memory allocator. NULL for default */ +) { + icc *p; + int del_al = 0; + + if (al == NULL) { /* None provided, create default */ + if ((al = new_icmAllocStd()) == NULL) + return NULL; + del_al = 1; /* We need to delete it */ + } + + if ((p = (icc *) al->calloc(al, 1,sizeof(icc))) == NULL) + return NULL; + p->al = al; /* Heap allocator */ + p->del_al = del_al; /* Flag noting whether we delete it */ + + p->get_size = icc_get_size; + p->read = icc_read; + p->write = icc_write; + 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->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->get_luobj = icc_get_luobj; + +#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); + if (del_al) + al->del(al); + 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.1.0 */ + p->header->minv = 1; + p->header->bfv = 0; + setcur_DateTimeNumber(&p->header->date);/* Creation Date */ + p->header->platform = icSigMicrosoft; /* Primary Platform */ + p->header->illuminant = icmD50; /* Profile illuminant - D50 */ + + return p; +} + + +/* For backwards compatibility - a NULL allocator version */ +icc *new_icc(void) { + return new_icc_a(NULL); +} + + +/* ---------------------------------------------------------- */ |