#ifndef ICC_H
#define ICC_H

/* 
 * International Color Consortium Format Library (icclib)
 *
 * Author:  Graeme W. Gill
 * Date:    1999/11/29
 * Version: 2.15
 *
 * Copyright 1997 - 2013 Graeme W. Gill
 *
 * This material is licensed with an "MIT" free use license:-
 * see the License4.txt file in this directory for licensing details.
 */

/* We can get some subtle errors if certain headers aren't included */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <math.h>
#include <time.h>
#include <limits.h>
#include <sys/types.h>

#ifdef __cplusplus
	extern "C" {
#endif

/* Version of icclib release */

#define ICCLIB_VERSION 0x020021
#define ICCLIB_VERSION_STR "2.21"

#undef ENABLE_V4		/* [und] V4 is not fully implemented, but enables parsing */

/*
 *  Note XYZ scaling to 1.0, not 100.0
 */

#undef ICC_DEBUG_MALLOC		/* Turns on partial support for filename and linenumber capture */

/* Make allowance for shared library use */
#ifdef ICCLIB_SHARED		/* Compiling or Using shared library version */
# ifdef ICCLIB_EXPORTS		/* Compiling shared library */
#  ifdef NT
#   define ICCLIB_API __declspec(dllexport)
#  endif /* NT */
# else						/* Using shared library */
#  ifdef NT
#   define ICCLIB_API __declspec(dllimport)
#   ifdef ICCLIB_DEBUG
#    pragma comment (lib, "icclibd.lib")
#   else
#    pragma comment (lib, "icclib.lib")
#   endif	/* DEBUG */
#  endif /* NT */
# endif
#else						/* Using static library */
# define ICCLIB_API	/* empty */
#endif

#if UINT_MAX < 0xffffffff
# error "icclib: integer size is too small, must be at least 32 bit"
#endif

/* =========================================================== */
/* Platform specific primitive defines. */
/* This really needs checking for each different platform. */
/* Using C99 and MSC covers a lot of cases, */
/* and the fallback default is pretty reliable with modern compilers and machines. */
/* Note that MSWin is LLP64 == 32 bit long, while OS X/Linux is LP64 == 64 bit long. */
/* so long shouldn't really be used in any code.... */
/* (duplicated in icc.h) */ 

/* Use __P64__ as cross platform 64 bit pointer #define */
#if defined(__LP64__) || defined(__ILP64__) || defined(__LLP64__) || defined(_WIN64)
# define __P64__ 1
#endif

#ifndef ORD32			/* If not defined elsewhere */

#if (__STDC_VERSION__ >= 199901L)	/* C99 */		\
 || defined(_STDINT_H_) || defined(_STDINT_H)		\
 || defined(_SYS_TYPES_H)

#include <stdint.h> 

#define INR8   int8_t		/* 8 bit signed */
#define INR16  int16_t		/* 16 bit signed */
#define INR32  int32_t		/* 32 bit signed */
#define INR64  int64_t		/* 64 bit signed - not used in icclib */
#define ORD8   uint8_t		/* 8 bit unsigned */
#define ORD16  uint16_t		/* 16 bit unsigned */
#define ORD32  uint32_t		/* 32 bit unsigned */
#define ORD64  uint64_t		/* 64 bit unsigned - not used in icclib */

#define PNTR intptr_t

#define PF64PREC "ll"		/* printf format precision specifier */
#define CF64PREC "LL"		/* Constant precision specifier */

#ifndef ATTRIBUTE_NORETURN
# ifdef _MSC_VER
#  define ATTRIBUTE_NORETURN __declspec(noreturn)
# else
#  define ATTRIBUTE_NORETURN __attribute__((noreturn))
# endif
#endif

#else  /* !__STDC_VERSION__ */

#ifdef _MSC_VER

#define INR8   __int8				/* 8 bit signed */
#define INR16  __int16				/* 16 bit signed */
#define INR32  __int32				/* 32 bit signed */
#define INR64  __int64				/* 64 bit signed - not used in icclib */
#define ORD8   unsigned __int8		/* 8 bit unsigned */
#define ORD16  unsigned __int16		/* 16 bit unsigned */
#define ORD32  unsigned __int32		/* 32 bit unsigned */
#define ORD64  unsigned __int64		/* 64 bit unsigned - not used in icclib */

#define PNTR UINT_PTR

#define vsnprintf _vsnprintf
#define snprintf _snprintf

#define PF64PREC "I64"				/* printf format precision specifier */
#define CF64PREC "LL"				/* Constant precision specifier */

#ifndef ATTRIBUTE_NORETURN
# define ATTRIBUTE_NORETURN __declspec(noreturn)
#endif

#else  /* !_MSC_VER */

/* The following works on a lot of modern systems, including */
/* LLP64 and LP64 models, but won't work with ILP64 which needs int32 */

#define INR8   signed char		/* 8 bit signed */
#define INR16  signed short		/* 16 bit signed */
#define INR32  signed int		/* 32 bit signed */
#define ORD8   unsigned char	/* 8 bit unsigned */
#define ORD16  unsigned short	/* 16 bit unsigned */
#define ORD32  unsigned int		/* 32 bit unsigned */

#ifdef __GNUC__
# ifdef __LP64__	/* long long could be 128 bit ? */
#  define INR64  long				/* 64 bit signed - not used in icclib */
#  define ORD64  unsigned long		/* 64 bit unsigned - not used in icclib */
#  define PF64PREC "l"			/* printf format precision specifier */
#  define CF64PREC "L"			/* Constant precision specifier */
# else
#  define INR64  long long			/* 64 bit signed - not used in icclib */
#  define ORD64  unsigned long long	/* 64 bit unsigned - not used in icclib */
#  define PF64PREC "ll"			/* printf format precision specifier */
#  define CF64PREC "LL"			/* Constant precision specifier */
# endif /* !__LP64__ */
#endif /* __GNUC__ */

#define PNTR unsigned long 

#ifndef ATTRIBUTE_NORETURN
# define ATTRIBUTE_NORETURN __attribute__((noreturn))
#endif

#endif /* !_MSC_VER */
#endif /* !__STDC_VERSION__ */
#endif /* !ORD32 */

/* =========================================================== */
#include "iccV43.h"	/* ICC Version 4.3 definitions. */ 

/* Note that the prefix icm is used for the native Machine */
/* equivalents of the ICC binary file structures, and other icclib */
/* specific definitions. */

/* ---------------------------------------------- */
/* System interface objects. The defaults can be replaced */
/* for adaption to different system environments */

/* - - - - - - - - - - - - - - - - - - - - -  */
/* Heap allocator class interface definition */
#ifdef ICC_DEBUG_MALLOC

#define ICM_ALLOC_BASE																		\
	/* Public: */																			\
																							\
	void *(*dmalloc) (struct _icmAlloc *p, size_t size, char *file, int line);				\
	void *(*dcalloc) (struct _icmAlloc *p, size_t num, size_t size, char *file, int line);	\
	void *(*drealloc)(struct _icmAlloc *p, void *ptr, size_t size, char *file, int line);	\
	void  (*dfree)   (struct _icmAlloc *p, void *ptr, char *file, int line);				\
																							\
	/* we're done with the allocator object */												\
	void (*del)(struct _icmAlloc *p);														\

#ifdef _ICC_C_		/* only inside icc.c */
#define malloc( p, size )	    dmalloc( p, size, __FILE__, __LINE__ )
#define calloc( p, num, size )	dcalloc( p, num, size, __FILE__, __LINE__ )
#define realloc( p, ptr, size )	drealloc( p, ptr, size, __FILE__, __LINE__ )
#define free( p, ptr )	        dfree( p, ptr , __FILE__, __LINE__ )
#endif /* _ICC_C */

#else /* !ICC_DEBUG_MALLOC */

/* Heap allocator class interface definition */
#define ICM_ALLOC_BASE																		\
	/* Public: */																			\
																							\
	void *(*malloc) (struct _icmAlloc *p, size_t size);										\
	void *(*calloc) (struct _icmAlloc *p, size_t num, size_t size);							\
	void *(*realloc)(struct _icmAlloc *p, void *ptr, size_t size);							\
	void  (*free)   (struct _icmAlloc *p, void *ptr);										\
																							\
	/* we're done with the allocator object */												\
	void (*del)(struct _icmAlloc *p);														\

#endif /* !ICC_DEBUG_MALLOC */

/* Common heap allocator interface class */
struct _icmAlloc {
	ICM_ALLOC_BASE
}; typedef struct _icmAlloc icmAlloc;

/* - - - - - - - - - - - - - - - - - - - - -  */

/* Implementation of heap class based on standard system malloc */
struct _icmAllocStd {
	ICM_ALLOC_BASE
}; typedef struct _icmAllocStd icmAllocStd;

/* Create a standard alloc object */
icmAlloc *new_icmAllocStd(void);

/* File access class interface definition */
#define ICM_FILE_BASE																		\
	/* Public: */																			\
																							\
	/* Get the size of the file (Typically valid after read only) */						\
	size_t (*get_size) (struct _icmFile *p);												\
																							\
	/* Set current position to offset. Return 0 on success, nz on failure. */				\
	int    (*seek) (struct _icmFile *p, unsigned int offset);								\
																							\
	/* Read count items of size length. Return number of items successfully read. */ 		\
	size_t (*read) (struct _icmFile *p, void *buffer, size_t size, size_t count);			\
																							\
	/* write count items of size length. Return number of items successfully written. */ 	\
	size_t (*write)(struct _icmFile *p, void *buffer, size_t size, size_t count);			\
																							\
	/* printf to the file */																\
	int (*gprintf)(struct _icmFile *p, const char *format, ...);							\
																							\
	/* flush all write data out to secondary storage. Return nz on failure. */				\
	int (*flush)(struct _icmFile *p);														\
																							\
	/* Return the memory buffer. Error if not icmFileMem */									\
	int (*get_buf)(struct _icmFile *p, unsigned char **buf, size_t *len);					\
																							\
	/* we're done with the file object, return nz on failure */								\
	int (*del)(struct _icmFile *p);															\

/* Common file interface class */
struct _icmFile {
	ICM_FILE_BASE
}; typedef struct _icmFile icmFile;


/* - - - - - - - - - - - - - - - - - - - - -  */

/* These are avalailable if SEPARATE_STD is not defined: */

/* Implementation of file access class based on standard file I/O */
struct _icmFileStd {
	ICM_FILE_BASE

	/* Private: */
	icmAlloc *al;		/* Heap allocator */
	int      del_al;	/* NZ if heap allocator should be deleted */
	FILE     *fp;
	int   doclose;		/* nz if free should close */

	/* Private: */
	size_t size;    /* Size of the file (For read) */

}; typedef struct _icmFileStd icmFileStd;

/* Create given a file name */
icmFile *new_icmFileStd_name(char *name, char *mode);

/* Create given a (binary) FILE* */
icmFile *new_icmFileStd_fp(FILE *fp);

/* Create given a file name with allocator */
icmFile *new_icmFileStd_name_a(char *name, char *mode, icmAlloc *al);

/* Create given a (binary) FILE* and allocator */
icmFile *new_icmFileStd_fp_a(FILE *fp, icmAlloc *al);


/* - - - - - - - - - - - - - - - - - - - - -  */
/* Implementation of file access class based on a memory image */
/* The buffer is assumed to be allocated with the given heap allocator */
/* Pass base = NULL, length = 0 for no initial buffer */

struct _icmFileMem {
	ICM_FILE_BASE

	/* Private: */
	icmAlloc *al;		/* Heap allocator */
	int      del_al;	/* NZ if heap allocator should be deleted */
	int      del_buf;	/* NZ if memory file buffer should be deleted */
	unsigned char *start, *cur, *end, *aend;

}; typedef struct _icmFileMem icmFileMem;

/* Create a memory image file access class with given allocator */
/* The buffer is not delete with the object. */
icmFile *new_icmFileMem_a(void *base, size_t length, icmAlloc *al);

/* Create a memory image file access class with given allocator */
/* and delete buffer when icmFile is deleted. */
icmFile *new_icmFileMem_ad(void *base, size_t length, icmAlloc *al);

/* This is avalailable if SEPARATE_STD is not defined: */

/* Create a memory image file access class */
icmFile *new_icmFileMem(void *base, size_t length);

/* Create a memory image file access class */
/* and delete buffer when icmFile is deleted. */
icmFile *new_icmFileMem_d(void *base, size_t length);

/* --------------------------------- */
/* Assumed constants                 */

#define MAX_CHAN 15		/* Maximum number of color channels */

/* --------------------------------- */
/* tag and other compound structures */

typedef int icmSig;	/* Otherwise un-enumerated 4 byte signature */

/* ArgyllCMS Private tagSignature: */
/* Absolute to Media Relative Transformation Space matrix. */
/* Uses the icSigS15Fixed16ArrayType */
/* (Unit matrix for default ICC, Bradford matrix for Argll default) */
#define icmSigAbsToRelTransSpace ((icTagSignature) icmMakeTag('a','r','t','s'))

/* Non-standard Platform Signature for Linux and similiar */
#define icmSig_nix ((icPlatformSignature) icmMakeTag('*','n','i','x'))

/* Non-standard Color Space Signatures - will be incompatible outside icclib! */

/* Y'u'v' perceptual CIE 1976 UCS diagram Yu'v' */
#define icmSigYuvData ((icColorSpaceSignature) icmMakeTag('Y','u','v',' '))

/* A monochrome CIE L* space */
#define icmSigLData ((icColorSpaceSignature) icmMakeTag('L',' ',' ',' '))

/* A monochrome CIE Y space */
#define icmSigYData ((icColorSpaceSignature) icmMakeTag('Y',' ',' ',' '))

/* A modern take on Lab */
#define icmSigLptData ((icColorSpaceSignature) icmMakeTag('L','p','t',' '))


/* Pseudo Color Space Signatures - just used within icclib */

/* Pseudo PCS colospace of profile */
#define icmSigPCSData ((icColorSpaceSignature) icmMakeTag('P','C','S',' '))

/* Pseudo PCS colospace to signal 8 bit Lab */
#define icmSigLab8Data ((icColorSpaceSignature) icmMakeTag('L','a','b','8'))

/* Pseudo PCS colospace to signal V2 16 bit Lab */
#define icmSigLabV2Data ((icColorSpaceSignature) icmMakeTag('L','a','b','2'))

/* Pseudo PCS colospace to signal V4 16 bit Lab */
#define icmSigLabV4Data ((icColorSpaceSignature) icmMakeTag('L','a','b','4'))

/* Pseudo PCS colospace to signal 8 bit L */
#define icmSigL8Data ((icColorSpaceSignature) icmMakeTag('L',' ',' ','8'))

/* Pseudo PCS colospace to signal V2 16 bit L */
#define icmSigLV2Data ((icColorSpaceSignature) icmMakeTag('L',' ',' ','2'))

/* Pseudo PCS colospace to signal V4 16 bit L */
#define icmSigLV4Data ((icColorSpaceSignature) icmMakeTag('L',' ',' ','4'))

/* Alias for icSigColorantTableType found in LOGO profiles (Byte swapped clrt !) */ 
#define icmSigAltColorantTableType ((icTagTypeSignature)icmMakeTag('t','r','l','c'))

/* Tag Type signature, used to handle any tag that */
/* isn't handled with a specific type object. */
/* Also used in manufacturer & model header fields */
/* Old: #define icmSigUnknownType ((icTagTypeSignature)icmMakeTag('?','?','?','?')) */
#define icmSigUnknownType ((icTagTypeSignature) 0)


typedef struct {
	ORD32 l;			/* High and low components of signed 64 bit */
	INR32 h;
} icmInt64;

typedef struct {
	ORD32 l,h;			/* High and low components of unsigned 64 bit */
} icmUint64;

/* XYZ Number */
typedef struct {
    double  X;
    double  Y;
    double  Z;
} icmXYZNumber;

/* Response 16 number */
typedef struct {
	double	       deviceValue;	/* The device value in range 0.0 - 1.0 */
	double	       measurement;	/* The reading value */
} icmResponse16Number;

/*
 *  read and write method error codes:
 *  0 = sucess
 *  1 = file format/logistical error
 *  2 = system error
 */

#define ICM_BASE_MEMBERS																\
	/* Private: */																		\
	icTagTypeSignature  ttype;		/* The tag type signature */						\
	struct _icc    *icp;			/* Pointer to ICC we're a part of */				\
	int	           touched;			/* Flag for write bookeeping */						\
    int            refcount;		/* Reference count for sharing */					\
	unsigned int   (*get_size)(struct _icmBase *p);										\
	int            (*read)(struct _icmBase *p, unsigned int len, unsigned int of);		\
	int            (*write)(struct _icmBase *p, unsigned int of);						\
	void           (*del)(struct _icmBase *p);											\
																						\
	/* Public: */																		\
	void           (*dump)(struct _icmBase *p, icmFile *op, int verb);					\
	int            (*allocate)(struct _icmBase *p);									

/* Base tag element data object */
struct _icmBase {
	ICM_BASE_MEMBERS
}; typedef struct _icmBase icmBase;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* Tag type to hold an unknown tag type, */
/* so that it can be read and copied. */
struct _icmUnknown {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int      _size;	/* size of data currently allocated */

	/* Public: */
	icTagTypeSignature uttype;	/* The unknown tag type signature */
	unsigned int	  size;		/* Allocated and used size of the array */
    unsigned char	  *data;  	/* tag data */
}; typedef struct _icmUnknown icmUnknown;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* UInt8 Array */
struct _icmUInt8Array {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int   _size;		/* Size currently allocated */

	/* Public: */
	unsigned int	size;		/* Allocated and used size of the array */
    unsigned int   *data;		/* Pointer to array of data */ 
}; typedef struct _icmUInt8Array icmUInt8Array;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* uInt16 Array */
struct _icmUInt16Array {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int   _size;		/* Size currently allocated */

	/* Public: */
	unsigned int	size;		/* Allocated and used size of the array */
    unsigned int	*data;		/* Pointer to array of data */ 
}; typedef struct _icmUInt16Array icmUInt16Array;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* uInt32 Array */
struct _icmUInt32Array {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int   _size;		/* Size currently allocated */

	/* Public: */
	unsigned int	size;		/* Allocated and used size of the array */
    unsigned int	*data;		/* Pointer to array of data */ 
}; typedef struct _icmUInt32Array icmUInt32Array;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* UInt64 Array */
struct _icmUInt64Array {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int   _size;		/* Size currently allocated */

	/* Public: */
	unsigned int	size;		/* Allocated and used size of the array */
    icmUint64		*data;		/* Pointer to array of hight data */ 
}; typedef struct _icmUInt64Array icmUInt64Array;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* u16Fixed16 Array */
struct _icmU16Fixed16Array {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int   _size;		/* Size currently allocated */

	/* Public: */
	unsigned int	size;		/* Allocated and used size of the array */
    double			*data;		/* Pointer to array of hight data */ 
}; typedef struct _icmU16Fixed16Array icmU16Fixed16Array;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* s15Fixed16 Array */
struct _icmS15Fixed16Array {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int   _size;		/* Size currently allocated */

	/* Public: */
	unsigned int	size;		/* Allocated and used size of the array */
    double			*data;		/* Pointer to array of hight data */ 
}; typedef struct _icmS15Fixed16Array icmS15Fixed16Array;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* XYZ Array */
struct _icmXYZArray {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int   _size;		/* Size currently allocated */

	/* Public: */
	unsigned int	size;		/* Allocated and used size of the array */
    icmXYZNumber	*data;		/* Pointer to array of data */ 
}; typedef struct _icmXYZArray icmXYZArray;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* Curve */
typedef enum {
    icmCurveUndef           = -1, /* Undefined curve */
    icmCurveLin             = 0,  /* Linear transfer curve */
    icmCurveGamma           = 1,  /* Gamma power transfer curve */
    icmCurveSpec            = 2   /* Specified curve */
} icmCurveStyle;

/* Curve reverse lookup information */
typedef struct {
	int inited;				/* Flag */
	double rmin, rmax;		/* Range of reverse grid */
	double qscale;			/* Quantising scale factor */
	int rsize;				/* Number of reverse lists */
	unsigned int **rlists;	/* Array of list of fwd values that may contain output value */
							/* Offset 0 = allocated size */
							/* Offset 1 = next free index */
							/* Offset 2 = first fwd index */
	unsigned int size;		/* Copy of forward table size */
	double       *data;		/* Copy of forward table data */
} icmRevTable;

struct _icmCurve {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int   _size;		/* Size currently allocated */
	icmRevTable  rt;			/* Reverse table information */

	/* Public: */
    icmCurveStyle   flag;		/* Style of curve */
	unsigned int	size;		/* Allocated and used size of the array */
    double         *data;  		/* Curve data scaled to range 0.0 - 1.0 */
								/* or data[0] = gamma value */
	/* Translate a value through the curve, return warning flags */
	int (*lookup_fwd) (struct _icmCurve *p, double *out, double *in);	/* Forwards */
	int (*lookup_bwd) (struct _icmCurve *p, double *out, double *in);	/* Backwards */

}; typedef struct _icmCurve icmCurve;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* Data */
typedef enum {
    icmDataUndef           = -1, /* Undefined data */
    icmDataASCII           = 0,  /* ASCII data */
    icmDataBin             = 1   /* Binary data */
} icmDataStyle;

struct _icmData {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int   _size;		/* Size currently allocated */

	/* Public: */
    icmDataStyle	flag;		/* Style of data */
	unsigned int	size;		/* Allocated and used size of the array (inc ascii null) */
    unsigned char	*data;  	/* data or string, NULL if size == 0 */
}; typedef struct _icmData icmData;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* text */
struct _icmText {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int   _size;		/* Size currently allocated */

	/* Public: */
	unsigned int	 size;		/* Allocated and used size of data, inc null */
	char             *data;		/* ascii string (null terminated), NULL if size==0 */

}; typedef struct _icmText icmText;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* The base date time number */
struct _icmDateTimeNumber {
	ICM_BASE_MEMBERS

	/* Public: */
    unsigned int      year;
    unsigned int      month;
    unsigned int      day;
    unsigned int      hours;
    unsigned int      minutes;
    unsigned int      seconds;
}; typedef struct _icmDateTimeNumber icmDateTimeNumber;

#ifdef NEW
/* - - - - - - - - - - - - - - - - - - - - -  */
/* DeviceSettings */

/*
   I think this all works like this:

Valid setting = (   (platform == platform1 and platform1.valid)
                 or (platform == platform2 and platform2.valid)
                 or ...
                )

where
	platformN.valid = (   platformN.combination1.valid
	                   or platformN.combination2.valid
	                   or ...
	                  )

where
	platformN.combinationM.valid = (    platformN.combinationM.settingstruct1.valid
	                                and platformN.combinationM.settingstruct2.valid
	                                and ...
	                               )

where
	platformN.combinationM.settingstructP.valid = (   platformN.combinationM.settingstructP.setting1.valid
	                                               or platformN.combinationM.settingstructP.setting2.valid
	                                               or ...
	                                              )

 */

/* The Settings Structure holds an array of settings of a particular type */
struct _icmSettingStruct {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int   _num;				/* Size currently allocated */

	/* Public: */
	icSettingsSig       settingSig;		/* Setting identification */
	unsigned int        numSettings; 	/* number of setting values */
	union {								/* Setting values - type depends on Sig */
		icUInt64Number      *resolution;
		icDeviceMedia       *media;
		icDeviceDither      *halftone;
	}
}; typedef struct _icmSettingStruct icmSettingStruct;

/* A Setting Combination holds all arrays of different setting types */
struct _icmSettingComb {
	/* Private: */
	unsigned int   _num;			/* number currently allocated */

	/* Public: */
	unsigned int        numStructs;   /* num of setting structures */
	icmSettingStruct    *data;
}; typedef struct _icmSettingComb icmSettingComb;

/* A Platform Entry holds all setting combinations */
struct _icmPlatformEntry {
	/* Private: */
	unsigned int   _num;			/* number currently allocated */

	/* Public: */
	icPlatformSignature platform;
	unsigned int        numCombinations;    /* num of settings and allocated array size */
	icmSettingComb      *data; 
}; typedef struct _icmPlatformEntry icmPlatformEntry;

/* The Device Settings holds all platform settings */
struct _icmDeviceSettings {
	/* Private: */
	unsigned int   _num;			/* number currently allocated */

	/* Public: */
	unsigned int        numPlatforms;	/* num of platforms and allocated array size */
	icmPlatformEntry    *data;			/* Array of pointers to platform entry data */
}; typedef struct _icmDeviceSettings icmDeviceSettings;

#endif /* NEW */

/* - - - - - - - - - - - - - - - - - - - - -  */
/* optimised simplex Lut lookup axis flipping structure */
typedef struct {
	double fth;		/* Flip threshold */
	char bthff;		/* Below threshold flip flag value */
	char athff;		/* Above threshold flip flag value */
} sx_flip_info;
 
/* Set method flags */
#define ICM_CLUT_SET_EXACT 0x0000	/* Set clut node values exactly from callback */
#define ICM_CLUT_SET_APXLS 0x0001	/* Set clut node values to aproximate least squares fit */

#define ICM_CLUT_SET_FILTER 0x0002	/* Post filter values (icmSetMultiLutTables() only) */


/* lut */
struct _icmLut {
	ICM_BASE_MEMBERS

	/* Private: */
	/* Cache appropriate normalization routines */
	int dinc[MAX_CHAN];				/* Dimensional increment through clut (in doubles) */
	int dcube[1 << MAX_CHAN];		/* Hyper cube offsets (in doubles) */
	icmRevTable rit[MAX_CHAN];		/* Reverse input table information */
	icmRevTable rot[MAX_CHAN];		/* Reverse output table information */
	sx_flip_info finfo[MAX_CHAN];	/* Optimised simplex flip information */

	unsigned int inputTable_size;	/* size allocated to input table */
	unsigned int clutTable_size;	/* size allocated to clut table */
	unsigned int outputTable_size;	/* size allocated to output table */

	/* Optimised simplex orientation information. oso_ffa is NZ if valid. */
	/* Only valid if inputChan > 1 && clutPoints > 1 */
									/* oso_ff[01] will be NULL if not valid */
	unsigned short *oso_ffa;		/* Flip flags for inputChan-1 dimensions, organised */
									/* [inputChan 0, 0..cp-2]..[inputChan ic-2, 0..cp-2] */
	unsigned short *oso_ffb;		/* Flip flags for dimemension inputChan-1, organised */
									/* [0..cp-2] */
	int odinc[MAX_CHAN];			/* Dimensional increment through oso_ffa */
	
	/* return the minimum and maximum values of the given channel in the clut */
	void (*min_max) (struct _icmLut *pp, double *minv, double *maxv, int chan);

	/* Translate color values through 3x3 matrix, input tables only, multi-dimensional lut, */
	/* or output tables, */
	int (*lookup_matrix)  (struct _icmLut *pp, double *out, double *in);
	int (*lookup_input)   (struct _icmLut *pp, double *out, double *in);
	int (*lookup_clut_nl) (struct _icmLut *pp, double *out, double *in);
	int (*lookup_clut_sx) (struct _icmLut *pp, double *out, double *in);
	int (*lookup_output)  (struct _icmLut *pp, double *out, double *in);

	/* Public: */

	/* return non zero if matrix is non-unity */
	int (*nu_matrix) (struct _icmLut *pp);

    unsigned int	inputChan;      /* Num of input channels */
    unsigned int	outputChan;     /* Num of output channels */
    unsigned int	clutPoints;     /* Num of grid points */
    unsigned int	inputEnt;       /* Num of in-table entries (must be 256 for Lut8) */
    unsigned int	outputEnt;      /* Num of out-table entries (must be 256 for Lut8) */
    double			e[3][3];		/* 3 * 3 array */
	double	        *inputTable;	/* The in-table: [inputChan * inputEnt] */
	double	        *clutTable;		/* The clut: [(clutPoints ^ inputChan) * outputChan] */
	double	        *outputTable;	/* The out-table: [outputChan * outputEnt] */
	/* inputTable  is organized [inputChan 0..ic-1][inputEnt 0..ie-1] */
	/* clutTable   is organized [inputChan 0, 0..cp-1]..[inputChan ic-1, 0..cp-1]
	                                                                [outputChan 0..oc-1] */
	/* outputTable is organized [outputChan 0..oc-1][outputEnt 0..oe-1] */

	/* Helper function to setup a Lut tables contents */
	int (*set_tables) (
		struct _icmLut *p,						/* Pointer to Lut object */
		int     flags,							/* Setting flags */
		void   *cbctx,							/* Opaque callback context pointer value */
		icColorSpaceSignature insig, 			/* Input color space */
		icColorSpaceSignature outsig, 			/* Output color space */
		void (*infunc)(void *cbctx, 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 *cbntx, double *out, double *in),
								/* inspace' -> outspace' transfer function */
		double *clutmin, double *clutmax,		/* Maximum range of outspace' values */
												/* (NULL = default) */
		void (*outfunc)(void *cbntx, double *out, double *in),
								/* Output transfer function, outspace'->outspace (NULL = deflt) */
		int *apxls_gmin, int *apxls_gmax	/* If not NULL, the grid indexes not to be affected */
										/* by ICM_CLUT_SET_APXLS, defaulting to 0..>clutPoints-1 */
	);

	/* Helper function to fine tune a single value interpolation */
	/* Return 0 on success, 1 if input clipping occured, 2 if output clipping occured */
	/* To guarantee the optimal function, an icmLuLut needs to be create. */
	/* The default will be to assume simplex interpolation will be used. */
	int (*tune_value) (
		struct _icmLut *p,				/* Pointer to Lut object */
		double *out,					/* Target value */
		double *in);					/* Input value */
		
}; typedef struct _icmLut icmLut;

/* Helper function to set multiple Lut tables simultaneously. */
/* Note that these tables all have to be compatible in */
/* having the same configuration and resolutions, and the */
/* same per channel input and output curves. */
/* Set errc and return error number in underlying icc */
/* Note that clutfunc in[] value has "index under". */
/* If ICM_CLUT_SET_FILTER is set, the per grid node filtering radius */
/* is returned in clutfunc out[-1], out[-2] etc for each table */
int icmSetMultiLutTables(
	int ntables,						/* Number of tables to be set, 1..n */
	struct _icmLut **p,					/* Pointer to Lut object */
	int     flags,						/* Setting flags */
	void   *cbctx,						/* Opaque callback context pointer value */
	icColorSpaceSignature insig, 		/* Input color space */
	icColorSpaceSignature outsig, 		/* Output color space */
	void (*infunc)(void *cbctx, double *out, double *in),
							/* Input transfer function, inspace->inspace' (NULL = default) */
							/* Will be called ntables times each input grid value */
	double *inmin, double *inmax,		/* Maximum range of inspace' values */
										/* (NULL = default) */
	void (*clutfunc)(void *cbntx, double *out, double *in),
							/* inspace' -> outspace[ntables]' transfer function */
							/* will be called once for each input' grid value, and */
							/* ntables output values should be written consecutively */
							/* to out[]. */
	double *clutmin, double *clutmax,	/* Maximum range of outspace' values */
										/* (NULL = default) */
	void (*outfunc)(void *cbntx, double *out, double *in),
								/* Output transfer function, outspace'->outspace (NULL = deflt) */
								/* Will be called ntables times on each output value */
	int *apxls_gmin, int *apxls_gmax	/* If not NULL, the grid indexes not to be affected */
										/* by ICM_CLUT_SET_APXLS, defaulting to 0..>clutPoints-1 */
);
		
/* - - - - - - - - - - - - - - - - - - - - -  */
/* Measurement Data */
struct _icmMeasurement {
	ICM_BASE_MEMBERS

	/* Public: */
    icStandardObserver           observer;       /* Standard observer */
    icmXYZNumber                 backing;        /* XYZ for backing */
    icMeasurementGeometry        geometry;       /* Meas. geometry */
    double                       flare;          /* Measurement flare */
    icIlluminant                 illuminant;     /* Illuminant */
}; typedef struct _icmMeasurement icmMeasurement;

#ifdef NEW
/* - - - - - - - - - - - - - - - - - - - - -  */
/* MultiLocalizedUnicode */

struct _icmMultiLocalizedUnicode {
	ICM_BASE_MEMBERS

	/* Private: */
	~~~999

	/* Public: */
	~~~999

}; typedef struct _icmMultiLocalizedUnicode icmMultiLocalizedUnicode;

#endif /* NEW */

/* - - - - - - - - - - - - - - - - - - - - -  */
/* Named color */

/* Structure that holds each named color data */
typedef struct {
	struct _icc      *icp;				/* Pointer to ICC we're a part of */
	char              root[32];			/* Root name for color */
	double            pcsCoords[3];		/* icmNC2: PCS coords of color */
	double            deviceCoords[MAX_CHAN];	/* Dev coords of color */
} icmNamedColorVal;

struct _icmNamedColor {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int      _count;			/* Count currently allocated */

	/* Public: */
    unsigned int      vendorFlag;		/* Bottom 16 bits for IC use */
    unsigned int      count;			/* Count of named colors */
    unsigned int      nDeviceCoords;	/* Num of device coordinates */
    char              prefix[32];		/* Prefix for each color name (null terminated) */
    char              suffix[32];		/* Suffix for each color name (null terminated) */
    icmNamedColorVal  *data;			/* Array of [count] color values */
}; typedef struct _icmNamedColor icmNamedColor;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* Colorant table */
/* (Contribution from Piet Vandenborre, derived from NamedColor) */

/* Structure that holds colorant table data */
typedef struct {
	struct _icc         *icp;			/* Pointer to ICC we're a part of */
	char                 name[32];		/* Name for colorant */
	double               pcsCoords[3];	/* PCS coords of colorant */
} icmColorantTableVal;

struct _icmColorantTable {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int         _count;		/* Count currently allocated */

	/* Public: */
    unsigned int         count;			/* Count of colorants */
    icmColorantTableVal *data;			/* Array of [count] colorants */
}; typedef struct _icmColorantTable icmColorantTable;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* textDescription */
struct _icmTextDescription {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int   _size;			/* Size currently allocated */
	unsigned int   uc_size;			/* uc Size currently allocated */
	int            (*core_read)(struct _icmTextDescription *p, char **bpp, char *end);
	int            (*core_write)(struct _icmTextDescription *p, char **bpp);

	/* Public: */
	unsigned int 	  size;			/* Allocated and used size of desc, inc null */
	char              *desc;		/* ascii string (null terminated) */

	unsigned int      ucLangCode;	/* UniCode language code */
	unsigned int 	  ucSize;		/* Allocated and used size of ucDesc in wchars, inc null */
	ORD16             *ucDesc;		/* The UniCode description (null terminated) */

	/* See <http://unicode.org/Public/MAPPINGS/VENDORS/APPLE/ReadMe.txt> */
	ORD16             scCode;		/* ScriptCode code */
	unsigned int 	  scSize;		/* Used size of scDesc in bytes, inc null */
	ORD8              scDesc[67];	/* ScriptCode Description (null terminated, max 67) */
}; typedef struct _icmTextDescription icmTextDescription;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* Profile sequence structure */
struct _icmDescStruct {
	/* Private: */
	struct _icc      *icp;				/* Pointer to ICC we're a part of */

	/* Public: */
	int             (*allocate)(struct _icmDescStruct *p);	/* Allocate method */
    icmSig            deviceMfg;		/* Dev Manufacturer */
    unsigned int      deviceModel;		/* Dev Model */
    icmUint64         attributes;		/* Dev attributes */
    icTechnologySignature technology;	/* Technology sig */
	icmTextDescription device;			/* Manufacturer text (sub structure) */
	icmTextDescription model;			/* Model text (sub structure) */
}; typedef struct _icmDescStruct icmDescStruct;

/* Profile sequence description */
struct _icmProfileSequenceDesc {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int	 _count;			/* number currently allocated */

	/* Public: */
    unsigned int      count;			/* Number of descriptions */
	icmDescStruct     *data;			/* array of [count] descriptions */
}; typedef struct _icmProfileSequenceDesc icmProfileSequenceDesc;

#ifdef NEW
/* - - - - - - - - - - - - - - - - - - - - -  */
/* ResponseCurveSet16 */

struct _icmResponseCurveSet16 {
	ICM_BASE_MEMBERS

	/* Private: */
	~~~999

	/* Public: */
	~~~999

}; typedef struct _icmResponseCurveSet16 icmResponseCurveSet16;

#endif /* NEW */

/* - - - - - - - - - - - - - - - - - - - - -  */
/* signature (only ever used for technology ??) */
struct _icmSignature {
	ICM_BASE_MEMBERS

	/* Public: */
    icTechnologySignature sig;	/* Signature */
}; typedef struct _icmSignature icmSignature;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* Per channel Screening Data */
typedef struct {
	/* Public: */
    double            frequency;		/* Frequency */
    double            angle;			/* Screen angle */
    icSpotShape       spotShape;		/* Spot Shape encodings below */
} icmScreeningData;

struct _icmScreening {
	ICM_BASE_MEMBERS

	/* Private: */
	unsigned int   _channels;			/* number currently allocated */

	/* Public: */
    unsigned int      screeningFlag;	/* Screening flag */
    unsigned int      channels;			/* Number of channels */
    icmScreeningData  *data;			/* Array of screening data */
}; typedef struct _icmScreening icmScreening;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* Under color removal, black generation */
struct _icmUcrBg {
	ICM_BASE_MEMBERS

	/* Private: */
    unsigned int      UCR_count;		/* Currently allocated UCR count */
    unsigned int      BG_count;			/* Currently allocated BG count */
	unsigned int 	  _size;			/* Currently allocated string size */

	/* Public: */
    unsigned int      UCRcount;			/* Undercolor Removal Curve length */
    double           *UCRcurve;		    /* The array of UCR curve values, 0.0 - 1.0 */
										/* or 0.0 - 100 % if count = 1 */
    unsigned int      BGcount;			/* Black generation Curve length */
    double           *BGcurve;			/* The array of BG curve values, 0.0 - 1.0 */
										/* or 0.0 - 100 % if count = 1 */
	unsigned int 	  size;				/* Allocated and used size of desc, inc null */
	char              *string;			/* UcrBg description (null terminated) */
}; typedef struct _icmUcrBg icmUcrBg;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* viewingConditionsType */
struct _icmViewingConditions {
	ICM_BASE_MEMBERS

	/* Public: */
    icmXYZNumber    illuminant;		/* In candelas per sq. meter */
    icmXYZNumber    surround;		/* In candelas per sq. meter */
    icIlluminant    stdIlluminant;	/* See icIlluminant defines */
}; typedef struct _icmViewingConditions icmViewingConditions;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* Postscript Color Rendering Dictionary names type */
struct _icmCrdInfo {
	ICM_BASE_MEMBERS
	/* Private: */
    unsigned int     _ppsize;		/* Currently allocated size */
	unsigned int     _crdsize[4];	/* Currently allocated sizes */

	/* Public: */
    unsigned int     ppsize;		/* Postscript product name size (including null) */
    char            *ppname;		/* Postscript product name (null terminated) */
	unsigned int     crdsize[4];	/* Rendering intent 0-3 CRD names sizes (icluding null) */
	char            *crdname[4];	/* Rendering intent 0-3 CRD names (null terminated) */
}; typedef struct _icmCrdInfo icmCrdInfo;

/* - - - - - - - - - - - - - - - - - - - - -  */
/* Apple ColorSync 2.5 video card gamma type (vcgt) */
struct _icmVideoCardGammaTable {
	unsigned short   channels;		/* No of gamma channels (1 or 3) */
	unsigned short   entryCount; 	/* Number of entries per channel */
	unsigned short   entrySize;		/* Size in bytes of each entry */
	void            *data;			/* Variable size data */
}; typedef struct _icmVideoCardGammaTable icmVideoCardGammaTable;

struct _icmVideoCardGammaFormula {
	unsigned short   channels;		/* No of gamma channels (always 3) */
	double           redGamma;		/* must be > 0.0 */
	double           redMin;		/* must be > 0.0 and < 1.0 */
	double           redMax;		/* must be > 0.0 and < 1.0 */
	double           greenGamma;   	/* must be > 0.0 */
	double           greenMin;		/* must be > 0.0 and < 1.0 */
	double           greenMax;		/* must be > 0.0 and < 1.0 */
	double           blueGamma;		/* must be > 0.0 */
	double           blueMin;		/* must be > 0.0 and < 1.0 */
	double           blueMax;		/* must be > 0.0 and < 1.0 */
}; typedef struct _icmVideoCardGammaFormula icmVideoCardGammaFormula;

typedef enum {
	icmVideoCardGammaTableType = 0,
	icmVideoCardGammaFormulaType = 1
} icmVideoCardGammaTagType;

struct _icmVideoCardGamma {
	ICM_BASE_MEMBERS
	icmVideoCardGammaTagType tagType;	/* eg. table or formula, use above enum */
	union {
		icmVideoCardGammaTable   table;
		icmVideoCardGammaFormula formula;
	} u;

	double (*lookup)(struct _icmVideoCardGamma *p, int chan, double iv); /* Read a value */

}; typedef struct _icmVideoCardGamma icmVideoCardGamma;

/* ------------------------------------------------- */
/* The Profile header */
/* NOTE that the deviceClass should be set up before calling chromAdaptMatrix()! */ 
struct _icmHeader {

  /* Private: */
	unsigned int           (*get_size)(struct _icmHeader *p);
	int                    (*read)(struct _icmHeader *p, unsigned int len, unsigned int of);
	int                    (*write)(struct _icmHeader *p, unsigned int of, int doid);
	void                   (*del)(struct _icmHeader *p);
	struct _icc            *icp;			/* Pointer to ICC we're a part of */
    unsigned int            size;			/* Profile size in bytes */

  /* public: */
	void                   (*dump)(struct _icmHeader *p, icmFile *op, int verb);

	/* Values that must be set before writing */
    icProfileClassSignature deviceClass;	/* Type of profile */
    icColorSpaceSignature   colorSpace;		/* Clr space of data (Cannonical input space) */
    icColorSpaceSignature   pcs;			/* PCS: XYZ or Lab (Cannonical output space) */
    icRenderingIntent       renderingIntent;/* Rendering intent */

	/* Values that should be set before writing */
    icmSig                  manufacturer;	/* Dev manufacturer */
    icmSig		            model;			/* Dev model */
    icmUint64               attributes;		/* Device attributes */
    unsigned int            flags;			/* Various bits */

	/* Values that may optionally be set before writing */
    /* icmUint64            attributes;		   Device attributes.h (see above) */
    icmSig                  creator;		/* Profile creator */

	/* Values that are not normally set, since they have defaults */
    icmSig                  cmmId;			/* CMM for profile */
    int            			majv, minv, bfv;/* Format version - major, minor, bug fix */
											/* Default is majv = 2, alternate value is 4 */
    icmDateTimeNumber       date;			/* Creation Date */
    icPlatformSignature     platform;		/* Primary Platform */
    icmXYZNumber            illuminant;		/* Profile illuminant */

	/* Values that are created automatically */
    unsigned char           id[16];			/* MD5 fingerprint value, lsB to msB  <V4.0+> */

}; typedef struct _icmHeader icmHeader;

/* ---------------------------------------------------------- */
/* Objects for accessing lookup functions. */
/* Note that the "effective" PCS colorspace is the one specified by the */
/* PCS override, and visible in the overal profile conversion. */
/* The "native" PCS colorspace is the one that the underlying tags actually */
/* represent, and the PCS that the individual stages within each profile type handle. */
/* The conversion between the native and effective PCS is done in the to/from_abs() */
/* conversions. */

/* Public: Parameter to get_luobj function */
typedef enum {
    icmFwd           = 0,  /* Device to PCS, or Device 1 to Last Device */
    icmBwd           = 1,  /* PCS to Device, or Last Device to Device */
    icmGamut         = 2,  /* PCS Gamut check */
    icmPreview       = 3   /* PCS to PCS preview */
} icmLookupFunc;

/* Public: Parameter to get_luobj function */
typedef enum {
    icmLuOrdNorm     = 0,  /* Normal profile preference: Lut, matrix, monochrome */
    icmLuOrdRev      = 1   /* Reverse profile preference: monochrome, matrix, monochrome */
} icmLookupOrder;

/* Public: Lookup algorithm object type */
typedef enum {
    icmMonoFwdType       = 0,	/* Monochrome, Forward */
    icmMonoBwdType       = 1,	/* Monochrome, Backward */
    icmMatrixFwdType     = 2,	/* Matrix, Forward */
    icmMatrixBwdType     = 3,	/* Matrix, Backward */
    icmLutType           = 4,	/* Multi-dimensional Lookup Table */
    icmNamedType         = 5	/* Named color data */
} icmLuAlgType;

/* Lookup class members common to named and non-named color types */
#define LU_ICM_BASE_MEMBERS																\
	/* Private: */																		\
	icmLuAlgType   ttype;		    	/* The object tag */							\
	struct _icc    *icp;				/* Pointer to ICC we're a part of */			\
	icRenderingIntent intent;			/* Effective (externaly visible) intent */		\
	icmLookupFunc function;				/* Functionality being used */					\
	icmLookupOrder order;        		/* Conversion representation search Order */ 	\
	icmXYZNumber pcswht, whitePoint, blackPoint;	/* White and black point info (absolute XYZ) */	\
	int blackisassumed;					/* nz if black point tag is missing from profile */ \
	double toAbs[3][3];					/* Matrix to convert from relative to absolute */ \
	double fromAbs[3][3];				/* Matrix to convert from absolute to relative */ \
    icColorSpaceSignature inSpace;		/* Native Clr space of input */					\
    icColorSpaceSignature outSpace;		/* Native Clr space of output */				\
	icColorSpaceSignature pcs;			/* Native PCS */								\
    icColorSpaceSignature e_inSpace;	/* Effective Clr space of input */				\
    icColorSpaceSignature e_outSpace;	/* Effective Clr space of output */				\
	icColorSpaceSignature e_pcs;		/* Effective PCS */								\
																						\
	/* Public: */																		\
	void           (*del)(struct _icmLuBase *p);										\
																						\
	               /* Internal native colorspaces: */	     							\
	void           (*lutspaces) (struct _icmLuBase *p, icColorSpaceSignature *ins, int *inn,	\
	                                                   icColorSpaceSignature *outs, int *outn,	\
	                                                   icColorSpaceSignature *pcs);			\
	                                                                                        \
	               /* External effecive colorspaces */										\
	void           (*spaces) (struct _icmLuBase *p, icColorSpaceSignature *ins, int *inn,	\
	                                 icColorSpaceSignature *outs, int *outn,				\
	                                 icmLuAlgType *alg, icRenderingIntent *intt, 			\
	                                 icmLookupFunc *fnc, icColorSpaceSignature *pcs,		\
	                                 icmLookupOrder *ord);							 		\
																							\
	               /* Relative to Absolute for this WP in XYZ */   							\
	void           (*XYZ_Rel2Abs)(struct _icmLuBase *p, double *xyzout, double *xyzin);		\
																							\
	               /* Absolute to Relative for this WP in XYZ */   							\
	void           (*XYZ_Abs2Rel)(struct _icmLuBase *p, double *xyzout, double *xyzin);		\


/* Non-algorithm specific lookup class. Used as base class of algorithm specific class. */
#define LU_ICM_NN_BASE_MEMBERS															\
    LU_ICM_BASE_MEMBERS                                                                 \
																						\
	/* Public: */																		\
																							\
	/* Get the native input space and output space ranges */								\
	/* This is an accurate number of what the underlying profile can hold. */				\
	void (*get_lutranges) (struct _icmLuBase *p,											\
		double *inmin, double *inmax,		/* Range of inspace values */					\
		double *outmin, double *outmax);	/* Range of outspace values */					\
																							\
	/* Get the effective input space and output space ranges */								\
	/* This may not be accurate when the effective type is different to the native type */	\
	void (*get_ranges) (struct _icmLuBase *p,												\
		double *inmin, double *inmax,		/* Range of inspace values */					\
		double *outmin, double *outmax);	/* Range of outspace values */					\
																							\
	/* Initialise the white and black points from the ICC tags */							\
	/* and the corresponding absolute<->relative conversion matrices */						\
	/* Return nz on error */																\
	int (*init_wh_bk)(struct _icmLuBase *p);												\
																							\
	/* Get the LU white and black points in absolute XYZ space. */							\
	/* Return nz if the black point is being assumed to be 0,0,0 rather */					\
	/* than being from the tag. */															\
	int (*wh_bk_points)(struct _icmLuBase *p, double *wht, double *blk);					\
																							\
	/* Get the LU white and black points in LU PCS space, converted to XYZ. */				\
	/* (ie. white and black will be relative if LU is relative intent etc.) */				\
	/* Return nz if the black point is being assumed to be 0,0,0 rather */					\
	/* than being from the tag. */															\
	int (*lu_wh_bk_points)(struct _icmLuBase *p, double *wht, double *blk);					\
																							\
	/* Translate color values through profile in effective in and out colorspaces, */		\
	/* return values: */																	\
	/* 0 = success, 1 = warning: clipping occured, 2 = fatal: other error */				\
	/* Note that clipping is not a reliable means of detecting out of gamut */				\
	/* in the lookup(bwd) call for clut based profiles. */									\
	int (*lookup) (struct _icmLuBase *p, double *out, double *in);							\
																							\
																							\
	/* Alternate to above, splits color conversion into three steps. */						\
	/* Colorspace of _in and _out and _core are the effective in and out */					\
	/* colorspaces. Note that setting colorspace overrides will probably. */				\
	/* force _in and/or _out to be dumy (unity) transforms. */								\
																							\
	/* Per channel input lookup (may be unity): */											\
	int (*lookup_in) (struct _icmLuBase *p, double *out, double *in);						\
																							\
	/* Intra channel conversion: */															\
	int (*lookup_core) (struct _icmLuBase *p, double *out, double *in);						\
																							\
	/* Per channel output lookup (may be unity): */											\
	int (*lookup_out) (struct _icmLuBase *p, double *out, double *in);						\
																							\
	/* Inverse versions of above - partially implemented */									\
	/* Inverse per channel input lookup (may be unity): */									\
	int (*lookup_inv_in) (struct _icmLuBase *p, double *out, double *in);					\
																							\


/* Base lookup object */
struct _icmLuBase {
	LU_ICM_NN_BASE_MEMBERS
}; typedef struct _icmLuBase icmLuBase;


/* Algorithm specific lookup classes */

/* Monochrome  Fwd & Bwd type object */
struct _icmLuMono {
	LU_ICM_NN_BASE_MEMBERS
	icmCurve    *grayCurve;

	/* Overall lookups */
	int (*fwd_lookup) (struct _icmLuBase *p, double *out, double *in);
	int (*bwd_lookup) (struct _icmLuBase *p, double *out, double *in);

	/* Components of lookup */
	int (*fwd_curve) (struct _icmLuMono *p, double *out, double *in);
	int (*fwd_map)   (struct _icmLuMono *p, double *out, double *in);
	int (*fwd_abs)   (struct _icmLuMono *p, double *out, double *in);
	int (*bwd_abs)   (struct _icmLuMono *p, double *out, double *in);
	int (*bwd_map)   (struct _icmLuMono *p, double *out, double *in);
	int (*bwd_curve) (struct _icmLuMono *p, double *out, double *in);

}; typedef struct _icmLuMono icmLuMono;

/* 3D Matrix Fwd & Bwd type object */
struct _icmLuMatrix {
	LU_ICM_NN_BASE_MEMBERS
	icmCurve    *redCurve, *greenCurve, *blueCurve;
	icmXYZArray *redColrnt, *greenColrnt, *blueColrnt;
    double		mx[3][3];	/* 3 * 3 conversion matrix */
    double		bmx[3][3];	/* 3 * 3 backwards conversion matrix */

	/* Overall lookups */
	int (*fwd_lookup) (struct _icmLuBase *p, double *out, double *in);
	int (*bwd_lookup) (struct _icmLuBase *p, double *out, double *in);

	/* Components of lookup */
	int (*fwd_curve)  (struct _icmLuMatrix *p, double *out, double *in);
	int (*fwd_matrix) (struct _icmLuMatrix *p, double *out, double *in);
	int (*fwd_abs)    (struct _icmLuMatrix *p, double *out, double *in);
	int (*bwd_abs)    (struct _icmLuMatrix *p, double *out, double *in);
	int (*bwd_matrix) (struct _icmLuMatrix *p, double *out, double *in);
	int (*bwd_curve)  (struct _icmLuMatrix *p, double *out, double *in);

}; typedef struct _icmLuMatrix icmLuMatrix;

/* Multi-D. Lut type object */
struct _icmLuLut {
	LU_ICM_NN_BASE_MEMBERS

	/* private: */
	icmLut *lut;								/* Lut to use */
	int    usematrix;							/* non-zero if matrix should be used */
    double imx[3][3];							/* 3 * 3 inverse conversion matrix */
	int    imx_valid;							/* Inverse matrix is valid */
	void (*in_normf)(double *out, double *in);	/* Lut input data normalizing function */
	void (*in_denormf)(double *out, double *in);/* Lut input data de-normalizing function */
	void (*out_normf)(double *out, double *in);	/* Lut output data normalizing function */
	void (*out_denormf)(double *out, double *in);/* Lut output de-normalizing function */
	void (*e_in_denormf)(double *out, double *in);/* Effective input de-normalizing function */
	void (*e_out_denormf)(double *out, double *in);/* Effecive output de-normalizing function */
	/* function chosen out of lut->lookup_clut_sx and lut->lookup_clut_nl to imp. clut() */
	int (*lookup_clut) (struct _icmLut *pp, double *out, double *in);	/* clut function */

	/* public: */

	/* Components of lookup */
	int (*in_abs)  (struct _icmLuLut *p, double *out, double *in);	/* Should be in icmLut ? */
	int (*matrix)  (struct _icmLuLut *p, double *out, double *in);
	int (*input)   (struct _icmLuLut *p, double *out, double *in);
	int (*clut)    (struct _icmLuLut *p, double *out, double *in);
	int (*output)  (struct _icmLuLut *p, double *out, double *in);
	int (*out_abs) (struct _icmLuLut *p, double *out, double *in);	/* Should be in icmLut ? */

	/* Some inverse components, in reverse order */
	/* Should be in icmLut ??? */
	int (*inv_out_abs) (struct _icmLuLut *p, double *out, double *in);
	int (*inv_output)  (struct _icmLuLut *p, double *out, double *in);
	/* inv_clut is beyond scope of icclib. See argyll for solution! */
	int (*inv_input)   (struct _icmLuLut *p, double *out, double *in);
	int (*inv_matrix)  (struct _icmLuLut *p, double *out, double *in);
	int (*inv_in_abs)  (struct _icmLuLut *p, double *out, double *in);

	/* Get various types of information about the LuLut */
	/* (Note that there's no reason why white/black point info */
	/*  isn't being made available for other Lu types) */
	void (*get_info) (struct _icmLuLut *p, icmLut **lutp,
	                 icmXYZNumber *pcswhtp, icmXYZNumber *whitep,
	                 icmXYZNumber *blackp);

	/* Get the matrix contents */
	void (*get_matrix) (struct _icmLuLut *p, double m[3][3]);

}; typedef struct _icmLuLut icmLuLut;

/* Named colors lookup object */
struct _icmLuNamed {
	LU_ICM_BASE_MEMBERS

  /* Private: */

  /* Public: */

	/* Get various types of information about the Named lookup */
	void (*get_info) (struct _icmLuLut *p, 
	                 icmXYZNumber *pcswhtp, icmXYZNumber *whitep,
	                 icmXYZNumber *blackp,
	                 int *maxnamesize,
	                 int *num_colors, char *prefix, char *suffix);


	/* Lookup a name that includes prefix and suffix */
	int (*fullname_lookup) (struct _icmLuNamed *p, double *pcs, double *dev, char *in);

	/* Lookup a name that doesn't include prefix and suffix */
	int (*name_lookup) (struct _icmLuNamed *p, double *pcs, double *dev, char *in);

	/* Fill in a list with the nout closest named colors to the pcs target */
	int (*pcs_lookup) (struct _icmLuNamed *p, char **out, int nout, double *in);

	/* Fill in a list with the nout closest named colors to the device target */
	int (*dev_lookup) (struct _icmLuNamed *p, char **out, int nout, double *in);

}; typedef struct _icmLuNamed icmLuNamed;

/* ========================================================== */

/* A table entry that maps a signature to the possible tag types */
struct _sig2type {
	icTagSignature      sig;
	icTagTypeSignature  ttypes[6];			/* Arbitrary max of 6 */
}; typedef struct _sig2type sig2type;

/* A tag */
typedef struct {
    icTagSignature      sig;			/* The tag signature */
	icTagTypeSignature  ttype;			/* The tag type signature */
    unsigned int        offset;			/* File offset to start header */
    unsigned int        size;			/* Size in bytes (not including padding) */
    unsigned int        pad;			/* Padding in bytes */
	icmBase            *objp;			/* In memory data structure */
} icmTag;

/* Pseudo enumerations valid as parameter to get_luobj(): */

/* Special purpose Perceptual intent */
#define icmAbsolutePerceptual ((icRenderingIntent)97)

/* Special purpose Saturation intent */
#define icmAbsoluteSaturation ((icRenderingIntent)98)

/* To be specified where an intent is not appropriate */
#define icmDefaultIntent ((icRenderingIntent)99)

/* Pseudo PCS colospace used to indicate the native PCS */
#define icmSigDefaultData ((icColorSpaceSignature) 0x0)

/* Pseudo colospace used to indicate a named colorspace */
#define icmSigNamedData ((icColorSpaceSignature) 0x1)


/* Arguments to set a non-default creation version */
typedef enum {
    icmVersionDefault       = 0,	/* Version 2.2.0 is default */
    icmVersion2_3           = 1,	/* Version 2.3.0 - Chromaticity Tag */
    icmVersion2_4           = 2,	/* Version 2.4.0 - Display etc. have intents */
    icmVersion4_1           = 3,	/* Version 4.1.0 - General V4 features */
} icmICCVersion;

/* The ICC object */
struct _icc {
  /* Public: */
	icmFile     *(*get_rfp)(struct _icc *p);			/* Return the current read fp (if any) */
	int          (*set_version)(struct _icc *p, icmICCVersion ver);
	                                                       /* For creation, use ICC V4 etc. */
	unsigned int (*get_size)(struct _icc *p);				/* Return total size needed, 0 = err. */
															/* Will add auto tags ('arts' etc.) */
	int          (*read)(struct _icc *p, icmFile *fp, unsigned int of);	/* Returns error code */
	int          (*read_x)(struct _icc *p, icmFile *fp, unsigned int of, int take_fp);
	int          (*write)(struct _icc *p, icmFile *fp, unsigned int of);/* Returns error code */
	int          (*write_x)(struct _icc *p, icmFile *fp, unsigned int of, int take_fp);
	void         (*dump)(struct _icc *p, icmFile *op, int verb);	/* Dump whole icc */
	void         (*del)(struct _icc *p);						/* Free whole icc */
	int          (*find_tag)(struct _icc *p, icTagSignature sig);
							/* Returns 0 if found, 1 if found but not readable, 2 of not found */
	icmBase *    (*read_tag)(struct _icc *p, icTagSignature sig);
															/* Returns pointer to object */
	icmBase *    (*read_tag_any)(struct _icc *p, icTagSignature sig);
								/* Returns pointer to object, but may be icmSigUnknownType! */
	icmBase *    (*add_tag)(struct _icc *p, icTagSignature sig, icTagTypeSignature ttype);
															/* Returns pointer to object */
	int          (*rename_tag)(struct _icc *p, icTagSignature sig, icTagSignature sigNew);
															/* Rename and existing tag */
	icmBase *    (*link_tag)(struct _icc *p, icTagSignature sig, icTagSignature ex_sig);
															/* Returns pointer to object */
	int          (*unread_tag)(struct _icc *p, icTagSignature sig);
														/* Unread a tag (free on refcount == 0 */
	int          (*read_all_tags)(struct _icc *p); /* Read all the tags, non-zero on error. */

	int          (*delete_tag)(struct _icc *p, icTagSignature sig);
															/* Returns 0 if deleted OK */
	int          (*check_id)(struct _icc *p, ORD8 *id); /* Returns 0 if ID is OK, 1 if not present etc. */
	double       (*get_tac)(struct _icc *p, double *chmax,
	                        void (*calfunc)(void *cntx, double *out, double *in), void *cntx);
 	                           /* Returns total ink limit and channel maximums */
	                           /* calfunc is optional. */
	void         (*set_illum)(struct _icc *p, double ill_wp[3]);
								/* Clear any existing 'chad' matrix, and if Output type profile */
	                            /* and ARGYLL_CREATE_OUTPUT_PROFILE_WITH_CHAD set and */
								/* ill_wp != NULL, create a 'chad' matrix. */
	void         (*chromAdaptMatrix)(struct _icc *p, int flags, double imat[3][3],
	                                 double mat[3][3], icmXYZNumber d_wp, icmXYZNumber s_wp);
	                            /* Return an overall Chromatic Adaptation Matrix */
	                            /* for the given source and destination white points. */
	                            /* This will depened on the icc profiles current setup */
	                            /* for Abs->Rel conversion (wpchtmx[][] set to wrong Von */
	                            /* Kries or not, whether 'arts' tag has been read), and */
	                            /* whether an Output profile 'chad' tag has bean read */
	                            /* or will be created. (i.e. on writing assumes */
	                            /* icc->set_illum() called or not). */
	                            /* Set header->deviceClass before calling this! */
	                            /* ICM_CAM_NONE or ICM_CAM_MULMATRIX to mult by mat. */
	                            /* Return invers of matrix if imat != NULL. */
	                            /* Use icmMulBy3x3(dst, mat, src). */
	                            /* NOTE that to transform primaries they */
	                            /* must be mat[XYZ][RGB] format! */

	/* Get a particular color conversion function */
	icmLuBase *  (*get_luobj) (struct _icc *p,
                               icmLookupFunc func,			/* Functionality */
	                           icRenderingIntent intent,	/* Intent */
	                           icColorSpaceSignature pcsor,	/* PCS override (0 = def) */
	                           icmLookupOrder order);		/* Search Order */
	                           /* Return appropriate lookup object */
	                           /* NULL on error, check errc+err for reason */

	/* Low level - load specific cLUT conversion */
	icmLuBase *  (*new_clutluobj)(struct _icc *p,
	                            icTagSignature        ttag,		  /* Target Lut tag */
                                icColorSpaceSignature inSpace,	  /* Native Input color space */
                                icColorSpaceSignature outSpace,	  /* Native Output color space */
                                icColorSpaceSignature pcs,		  /* Native PCS (from header) */
                                icColorSpaceSignature e_inSpace,  /* Override Inpt color space */
                                icColorSpaceSignature e_outSpace, /* Override Outpt color space */
                                icColorSpaceSignature e_pcs,	  /* Override PCS */
	                            icRenderingIntent     intent,	  /* Rendering intent (For abs.) */
	                            icmLookupFunc         func);	  /* For icmLuSpaces() */
	                           /* Return appropriate lookup object */
	                           /* NULL on error, check errc+err for reason */
	
    icmHeader       *header;			/* The header */
	char             err[512];			/* Error message */
	int              errc;				/* Error code */
	int              warnc;				/* Warning code */

	/* - - - - - - - - tweaks - - - - - - - */
	int              allowclutPoints256; /* Non standard - allow 256 res cLUT */

	int              useLinWpchtmx;		/* Force Wrong Von Kries for output class (default false) */
										/* Could be set by code, and is set set by */
										/* ARGYLL_CREATE_WRONG_VON_KRIES_OUTPUT_CLASS_REL_WP env. */
    icProfileClassSignature wpchtmx_class;	/* Class of profile wpchtmx was set for. */
										/* Set wpchtmx automatically on ->chromAdaptMatrix() */
										/* or ->get_size() or ->write() if the profile class */
										/* doesn't match wpchtmx_class. */
	double           wpchtmx[3][3];		/* Absolute to Media Relative Transformation Space (i.e. */
										/* Cone Space transformation) matrix. */
	double           iwpchtmx[3][3];	/* Inverse of wpchtmx[][] */
										/* (Default is Bradford matrix) */
	int				 useArts;			/* Save ArgyllCMS private 'arts' tag (default yes). */
										/* This creates 'arts' tag on writing. */
										/* This is cleared if 'arts' tag is not found on reading. */

	double           illwp[3];			/* Output type profile illuminant white point, */
										/* used to create 'chad' tag if */
										/* ARGYLL_CREATE_OUTPUT_PROFILE_WITH_CHAD is set. */
	int              illwpValid;		/* illwp[] has been set. */

	int              naturalChad;		/* nz if 'chad' tag is naturally present in the profile, */
										/* because it was read or added. wrD/OChad will be */
										/* ignored if this is the case. */

	int              chadmxValid;		/* nz if 'chad' tag has been read, or created */
										/* using ->set_illum() and chadmx is valid. */
	double			 chadmx[3][3];		/* 'chad' tag matrix if read or created. */
										/* (This is used to restore full absolute intent for */
	                                    /* Display and Output type profiles.) */

	int				 wrDChad;			/* Create 'chad' tag for Display profiles (default no). */
										/* Override with ARGYLL_CREATE_DISPLAY_PROFILE_WITH_CHAD. */
										/* On writing Display profiles this sets the media white */
										/* point tag to D50 and creates 'chad' tag from the white */
										/* point to D50. Ignored if naturalChad. */
		
	int				 wrOChad;			/* Create 'chad' tag for Output profiles (default no). */
										/* Override with ARGYLL_CREATE_DISPLAY_PROFILE_WITH_CHAD. */
										/* On writing an Output profile, this  Creates a 'chad' */
										/* tag frp, illwp[] to D50, and sets the white point */
										/* tag to be the real value transformed by the */
										/* 'chad' matrix. Ignored if naturalChad. */

	int              tempChad;			/* nz while temporary chad tag is in place and */
										/* white point has been modified, between */
										/* ->get_size() to ->write() calls when wrD/OChad active. */
	icmXYZNumber	 tempWP;			/* Save real wp tag value here between ->get_size() */
										/* to ->write() calls when wrD/OChad active. */
		
  /* Private: ? */
	icmAlloc        *al;				/* Heap allocator */
	int              del_al;			/* NZ if heap allocator should be deleted */
	icmFile         *fp;				/* File associated with object */
	int              del_fp;			/* NZ if File should be deleted */
	unsigned int     of;				/* Offset of the profile within the file */
    unsigned int     count;				/* Num tags in the profile */
    icmTag          *data;    			/* The tagTable and tagData */
	icmICCVersion    ver;				/* Version class, see icmICCVersion enum */

	/* sig to valid types for current ver */ 
	sig2type *sigtypetable;

	}; typedef struct _icc icc;

/* ========================================================== */
/* Utility structures and declarations */

/* Type of primitives that can be read or written */
typedef enum {
	icmUInt8Number,
	icmUInt16Number,
	icmUInt32Number,
	icmUInt64Number,
	icmU8Fixed8Number,
	icmU16Fixed16Number,
	icmSInt8Number,
	icmSInt16Number,
	icmSInt32Number,
	icmSInt64Number,
	icmS15Fixed16Number,
	icmDCS8Number,
	icmDCS16Number,
	icmPCSNumber,			/* 16 bit PCS of profile */
	icmPCSXYZNumber,		/* 16 bit XYZ */
	icmPCSLab8Number,		/* 8 bit Lab */
	icmPCSLabNumber,		/* 16 bit Lab of profile */
	icmPCSLabV2Number,		/* 16 bit Lab Version 2 */
	icmPCSLabV4Number		/* 16 bit Lab Version 4 */
} icmPrimType;

/* Structure to hold pseudo-hilbert counter info */
struct _psh {
	int      di;	/* Dimensionality */
	unsigned int res;	/* Resolution per coordinate */
	unsigned int bits;	/* Bits per coordinate */
	unsigned int ix;	/* Current binary index */
	unsigned int tmask;	/* Total 2^n count mask */
	unsigned int count;	/* Usable count */
}; typedef struct _psh psh;

/* Type of encoding to be returned as a string */
typedef enum {
    icmScreenEncodings,
    icmDeviceAttributes,
	icmProfileHeaderFlags,
	icmAsciiOrBinaryData,
	icmTagSignature,
	icmTechnologySignature,
	icmTypeSignature,
	icmColorSpaceSignature,
	icmProfileClassSignature,
	icmPlatformSignature,
	icmMeasurementFlare,
	icmMeasurementGeometry,
	icmRenderingIntent,
	icmTransformLookupFunc,
	icmSpotShape,
	icmStandardObserver,
	icmIlluminant,
	icmLuAlg
} icmEnumType;


/* A helper object that computes MD5 checksums */
struct _icmMD5 {
  /* Private: */
//	icc *icp;				/* ICC we're part of */
	icmAlloc *al;			/* Heap allocator */
	int del_al;				/* NZ if heap allocator should be deleted */
	int fin;				/* Flag, nz if final has been called */
	ORD32 sum[4];			/* Current/final checksum */
	unsigned int tlen;		/* Total length added in bytes */
	ORD8 buf[64];			/* Partial buffer */

  /* Public: */
	void (*reset)(struct _icmMD5 *p);	/* Reset the checksum */
	void (*add)(struct _icmMD5 *p, ORD8 *buf, unsigned int len);	/* Add some bytes */
	void (*get)(struct _icmMD5 *p, ORD8 chsum[16]);		/* Finalise and get the checksum */
	void (*del)(struct _icmMD5 *p);		/* We're done with the object */

}; typedef struct _icmMD5 icmMD5;

/* Create a new MD5 checksumming object, with a reset checksum value */
/* Return it or NULL if there is an error. */
extern ICCLIB_API icmMD5 *new_icmMD5_a(icmAlloc *al);

/* If SEPARATE_STD not defined: */
extern ICCLIB_API icmMD5 *new_icmMD5(void);

/* Implementation of file access class to compute an MD5 checksum */
struct _icmFileMD5 {
	ICM_FILE_BASE
	int (*get_errc)(struct _icmFile *p);		/* Check if there was an error */

	/* Private: */
	icmAlloc *al;		/* Heap allocator */
	icmMD5 *md5;		/* MD5 object */
	unsigned int of;	/* Current offset */
	int errc;			/* Error code, 0 for OK */

	/* Private: */
	size_t size;    /* Size of the file (For read) */

}; typedef struct _icmFileMD5 icmFileMD5;

/* Create a dumy file access class with allocator */
icmFile *new_icmFileMD5_a(icmMD5 *md5, icmAlloc *al);


/* ========================================================== */
/* Public function declarations */
/* Create an empty object. Return null on error */
extern ICCLIB_API icc *new_icc_a(icmAlloc *al);		/* With allocator class */

/* If SEPARATE_STD not defined: */
extern ICCLIB_API icc *new_icc(void);				/* Default allocator */

/* - - - - - - - - - - - - - */
/* Some useful utilities: */

/* Default ICC profile file extension string */

#if defined (UNIX) || defined(__APPLE__)
#define ICC_FILE_EXT ".icc"
#define ICC_FILE_EXT_ND "icc"
#else
#define ICC_FILE_EXT ".icm"
#define ICC_FILE_EXT_ND "icm"
#endif

/* Read a given primitive type. Return non-zero on error */
extern ICCLIB_API int read_Primitive(icc *icpp, icmPrimType ptype, void *prim, char *p);

/* Write a given primitive type. Return non-zero on error */
extern ICCLIB_API int write_Primitive(icc *icp, icmPrimType ptype, char *p, void *prim);

/* Return a string that represents a tag */
extern ICCLIB_API char *tag2str(int tag);

/* Return a tag created from a string */
extern ICCLIB_API unsigned int str2tag(const char *str);

/* Utility to define a non-standard tag, arguments are 4 character constants */
#define icmMakeTag(A,B,C,D)	\
	(   (((ORD32)(ORD8)(A)) << 24)	\
	  | (((ORD32)(ORD8)(B)) << 16)	\
	  | (((ORD32)(ORD8)(C)) << 8)	\
	  |  ((ORD32)(ORD8)(D)) )

/* Return a string description of the given enumeration value */
extern ICCLIB_API const char *icm2str(icmEnumType etype, int enumval);

/* Return the number of channels for the given color space. Return 0 if unknown. */
extern ICCLIB_API unsigned int icmCSSig2nchan(icColorSpaceSignature sig);

/* Return the individual channel names and number of channels give a colorspace signature. */
/* Return 0 if it is not a colorspace that itself defines particular channels, */ 
/* 1 if it is a colorant based colorspace, and 2 if it is not a colorant based space */
extern ICCLIB_API unsigned int icmCSSig2chanNames( icColorSpaceSignature sig, char *cvals[]);

/* - - - - - - - - - - - - - - */

/* Set a 3 vector to the same value */
#define icmSet3(d_ary, s_val) ((d_ary)[0] = (s_val), (d_ary)[1] = (s_val), \
                              (d_ary)[2] = (s_val))

/* Set a 3 vector to 3 values */
#define icmInit3(d_ary, s1, s2, s3) ((d_ary)[0] = (s1), (d_ary)[1] = (s2), \
                              (d_ary)[2] = (s3))

/* Some 2 vector macros: */
#define icmSet2(d_ary, s_val) ((d_ary)[0] = (s_val), (d_ary)[1] = (s_val))

#define icmCpy2(d_ary, s_ary) ((d_ary)[0] = (s_ary)[0], (d_ary)[1] = (s_ary)[1])

#define icmAdd2(d_ary, s1_ary, s2_ary) ((d_ary)[0] = (s1_ary)[0] + (s2_ary)[0], \
                                        (d_ary)[1] = (s1_ary)[1] + (s2_ary)[1])

#define icmSub2(d_ary, s1_ary, s2_ary) ((d_ary)[0] = (s1_ary)[0] - (s2_ary)[0], \
                                        (d_ary)[1] = (s1_ary)[1] - (s2_ary)[1])

/* Copy a 3 vector */
#define icmCpy3(d_ary, s_ary) ((d_ary)[0] = (s_ary)[0], (d_ary)[1] = (s_ary)[1], \
                              (d_ary)[2] = (s_ary)[2])

/* Copy a 4 vector */
#define icmCpy4(d_ary, s_ary) ((d_ary)[0] = (s_ary)[0], (d_ary)[1] = (s_ary)[1], \
                              (d_ary)[2] = (s_ary)[2], (d_ary)[3] = (s_ary)[3])

/* - - - - - - - - - - - - - - */

/* Clamp a 3 vector to be +ve */
void icmClamp3(double out[3], double in[3]);

/* Invert (negate) a 3 vector */
void icmInv3(double out[3], double in[3]);

/* Add two 3 vectors */
void icmAdd3(double out[3], double in1[3], double in2[3]);

#define ICMADD3(o, i, j) ((o)[0] = (i)[0] + (j)[0], (o)[1] = (i)[1] + (j)[1], (o)[2] = (i)[2] + (j)[2])

/* Subtract two 3 vectors, out = in1 - in2 */
void icmSub3(double out[3], double in1[3], double in2[3]);

#define ICMSUB3(o, i, j) ((o)[0] = (i)[0] - (j)[0], (o)[1] = (i)[1] - (j)[1], (o)[2] = (i)[2] - (j)[2])

/* Divide two 3 vectors, out = in1/in2 */
void icmDiv3(double out[3], double in1[3], double in2[3]);

#define ICMDIV3(o, i, j) ((o)[0] = (i)[0]/(j)[0], (o)[1] = (i)[1]/(j)[1], (o)[2] = (i)[2]/(j)[2])

/* Multiply two 3 vectors, out = in1 * in2 */
void icmMul3(double out[3], double in1[3], double in2[3]);

#define ICMMUL3(o, i, j) ((o)[0] = (i)[0] * (j)[0], (o)[1] = (i)[1] * (j)[1], (o)[2] = (i)[2] * (j)[2])

/* Take (signed) values to power */
void icmPow3(double out[3], double in[3], double p);

/* Square values */
void icmSqr3(double out[3], double in[3]);

/* Suqare root of values */
void icmSqrt3(double out[3], double in[3]);

/* Take absolute of a 3 vector */
void icmAbs3(double out[3], double in[3]);

/* Compute sum of the vectors components */
#define icmSum3(vv) ((vv)[0] + (vv)[1] + (vv)[2]) 

/* Compute the dot product of two 3 vectors */
double icmDot3(double in1[3], double in2[3]);

#define ICMDOT3(o, i, j) ((o) = (i)[0] * (j)[0] + (i)[1] * (j)[1] + (i)[2] * (j)[2])

/* Compute the cross product of two 3D vectors, out = in1 x in2 */
void icmCross3(double out[3], double in1[3], double in2[3]);

/* Compute the norm squared (length squared) of a 3 vector */
double icmNorm3sq(double in[3]);

#define ICMNORM3SQ(i) ((i)[0] * (i)[0] + (i)[1] * (i)[1] + (i)[2] * (i)[2])

/* Compute the norm (length) of a 3 vector */
double icmNorm3(double in[3]);

#define ICMNORM3(i) sqrt((i)[0] * (i)[0] + (i)[1] * (i)[1] + (i)[2] * (i)[2])

/* Scale a 3 vector by the given ratio */
void icmScale3(double out[3], double in[3], double rat);

#define ICMSCALE3(o, i, j) ((o)[0] = (i)[0] * (j), (o)[1] = (i)[1] * (j), (o)[2] = (i)[2] * (j))

/* Scale a 3 vector by the given ratio and add it */
void icmScaleAdd3(double out[3], double in2[3], double in1[3], double rat);

/* Compute a blend between in0 and in1 for bl 0..1 */
void icmBlend3(double out[3], double in0[3], double in1[3], double bf);

/* Clip a vector to the range 0.0 .. 1.0 */
void icmClip3(double out[3], double in[3]);

/* Clip a vector to the range 0.0 .. 1.0 */
/* and return nz if clipping occured */
int icmClip3sig(double out[3], double in[3]);

/* Clip a vector to the range 0.0 .. 1.0 */
/* and return any clipping margine */
double icmClip3marg(double out[3], double in[3]);

/* Normalise a 3 vector to the given length. Return nz if not normalisable */
int icmNormalize3(double out[3], double in[3], double len);

/* Compute the norm squared (length squared) of a vector defined by two points */
double icmNorm33sq(double in1[3], double in0[3]);

/* Compute the norm (length) of of a vector defined by two points */
double icmNorm33(double in1[3], double in0[3]);

/* Scale a vector from 0->1 by the given ratio. in0[] is the origin. */
void icmScale33(double out[3], double in1[3], double in0[3], double rat);

/* Normalise a vector from 0->1 to the given length. */
/* The new location of in1[] is returned in out[], in0[] is the origin. */
/* Return nz if not normalisable */
int icmNormalize33(double out[3], double in1[3], double in0[3], double len);

/* Set a 3x3 matrix to a value */
void icmSetVal3x3(double mat[3][3], double val);

/* Set a 3x3 matrix to unity */
void icmSetUnity3x3(double mat[3][3]);

/* Copy a 3x3 transform matrix */
void icmCpy3x3(double out[3][3], double mat[3][3]);

/* Add a 3x3 transform matrix to another */
void icmAdd3x3(double dst[3][3], double src1[3][3], double src2[3][3]);

/* Scale each element of a 3x3 transform matrix */
void icmScale3x3(double dst[3][3], double src[3][3], double scale);

/* Multiply 3 vector by 3x3 transform matrix */
/* Organization is mat[out][in] */
void icmMulBy3x3(double out[3], double mat[3][3], double in[3]);

/* Tensor product. Multiply two 3 vectors to form a 3x3 matrix */
/* src1[] forms the colums, and src2[] forms the rows in the result */
void icmTensMul3(double dst[3][3], double src1[3], double src2[3]);

/* Multiply one 3x3 with another */
/* dst = src * dst */
void icmMul3x3(double dst[3][3], double src[3][3]);

/* Multiply one 3x3 with another #2 */
/* dst = src1 * src2 */
void icmMul3x3_2(double dst[3][3], double src1[3][3], double src2[3][3]);

/* Compute the determinant of a 3x3 matrix */
double icmDet3x3(double in[3][3]);

/* Invert a 3x3 transform matrix. Return 1 if error. */
int icmInverse3x3(double out[3][3], double in[3][3]);

/* Transpose a 3x3 matrix */
void icmTranspose3x3(double out[3][3], double in[3][3]);

/* Given two 3D points, create a matrix that rotates */
/* and scales one onto the other about the origin 0,0,0. */
/* Use icmMulBy3x3() to apply this to other points */
void icmRotMat(double mat[3][3], double src[3], double targ[3]);

/* Copy a 3x4 transform matrix */
void icmCpy3x4(double out[3][4], double mat[3][4]);

/* Multiply 3 array by 3x4 transform matrix */
void icmMul3By3x4(double out[3], double mat[3][4], double in[3]);

/* Given two 3D vectors, create a matrix that translates, */
/* rotates and scales one onto the other. */
/* Use icmMul3By3x4 to apply this to other points */
void icmVecRotMat(double m[3][4], double s1[3], double s0[3], double t1[3], double t0[3]);


/* Compute the intersection of a vector and a plane */
/* return nz if there is no intersection */
int icmVecPlaneIsect(double isect[3], double pl_const, double pl_norm[3], double ve_1[3], double ve_0[3]);

/* Compute the closest point on a line to a point. */
/* Return closest points and parameter values if not NULL. */
int icmLinePointClosest(double ca[3], double *pa,
                       double la0[3], double la1[3], double pp[3]);

/* Compute the closest points between two lines a and b. */
/* Return closest points and parameter values if not NULL. */
/* Return nz if they are parallel. */
int icmLineLineClosest(double ca[3], double cb[3], double *pa, double *pb,
                       double la0[3], double la1[3], double lb0[3], double lb1[3]);

/* Given 3 3D points, compute a plane equation. */
/* The normal will be right handed given the order of the points */
/* The plane equation will be the 3 normal components and the constant. */
/* Return nz if any points are cooincident or co-linear */
int icmPlaneEqn3(double eq[4], double p0[3], double p1[3], double p2[3]);

/* Given a 3D point and a plane equation, return the signed */
/* distance from the plane */
double icmPlaneDist3(double eq[4], double p[3]);

/* - - - - - - - - - - - - - - - - - - - - - - - */

/* Multiply 4 vector by 4x4 transform matrix */
/* Organization is mat[out][in] */
void icmMulBy4x4(double out[4], double mat[4][4], double in[4]);

/* Transpose a 4x4 matrix */
void icmTranspose4x4(double out[4][4], double in[4][4]);

/* Clip a vector to the range 0.0 .. 1.0 */
/* and return any clipping margine */
double icmClip4marg(double out[4], double in[4]);

/* - - - - - - - - - - - - - - - - - - - - - - - */

#define icmNorm2(i) sqrt((i)[0] * (i)[0] + (i)[1] * (i)[1])

#define icmNorm2sq(i) ((i)[0] * (i)[0] + (i)[1] * (i)[1])

/* Compute the norm (length) of of a vector defined by two points */
double icmNorm22(double in1[2], double in0[2]);

/* Compute the norm (length) squared of of a vector defined by two points */
double icmNorm22sq(double in1[2], double in0[2]);

/* Compute the dot product of two 2 vectors */
double icmDot2(double in1[2], double in2[2]);

#define ICMDOT2(o, i, j) ((o) = (i)[0] * (j)[0] + (i)[1] * (j)[1])

/* Compute the dot product of two 2 vectors defined by 4 points */
/* 1->2 and 3->4 */
double icmDot22(double in1[2], double in2[2], double in3[2], double in4[2]);

/* Normalise a 2 vector to the given length. Return nz if not normalisable */
int icmNormalize2(double out[2], double in[2], double len);

/* Return an orthogonal vector */
void icmOrthog2(double out[2], double in[2]);

/* Given 2 2D points, compute a plane equation. */
/* The normal will be right handed given the order of the points */
/* The plane equation will be the 2 normal components and the constant. */
/* Return nz if any points are cooincident or co-linear */
int icmPlaneEqn2(double eq[3], double p0[2], double p1[2]);

/* Given a 2D point and a plane equation, return the signed */
/* distance from the plane */
double icmPlaneDist2(double eq[3], double p[2]);

/* Return the closest point on an implicit line to a point. */
/* Also return the absolute distance */
double icmImpLinePointClosest2(double cp[2], double eq[3], double pp[2]);

/* Return the point of intersection of two implicit lines . */
/* Return nz if there is no intersection (lines are parallel) */
int icmImpLineIntersect2(double res[2], double eq1[3], double eq2[3]);


/* Compute the closest point on a line to a point. */
/* Return closest point and parameter value if not NULL. */
/* Return nz if the line length is zero */
int icmLinePointClosest2(double cp[2], double *pa,
                       double la0[2], double la1[2], double pp[2]);

/* Given two infinite 2D lines define by two pairs of points, compute the intersection. */
/* Return nz if there is no intersection (lines are parallel) */
int icmLineIntersect2(double res[2], double p1[2], double p2[2], double p3[2], double p4[2]);

/* Given two finite 2D lines define by 4 points, compute their paramaterized intersection. */
/* res[] and/or aprm[] may be NULL. Param is prop. from p1 -> p2, p3 -> p4 */
/* Return 2 if there is no intersection (lines are parallel) */
/* Return 1 lines do not cross within their length */
int icmParmLineIntersect2(double ares[2], double aprm[2], double p1[2], double p2[2], double p3[2], double p4[2]);

/* Invert a 2x2 transform matrix. Return 1 if error. */
int icmInverse2x2(double out[2][2], double in[2][2]);

/* Multiply 2 array by 2x2 transform matrix */
void icmMulBy2x2(double out[2], double mat[2][2], double in[2]);

/* Compute a blend between in0 and in1 for bl 0..1 */
void icmBlend2(double out[2], double in0[2], double in1[2], double bf);

/* Scale a 2 vector by the given ratio */
void icmScale2(double out[2], double in[2], double rat);

/* Scale a 2 vector by the given ratio and add it */
void icmScaleAdd2(double out[2], double in2[3], double in1[2], double rat);

/* - - - - - - - - - - - - - - */

/* Simple macro to transfer an array to an XYZ number */
#define icmAry2XYZ(xyz, ary) ((xyz).X = (ary)[0], (xyz).Y = (ary)[1], (xyz).Z = (ary)[2])

/* And the reverse */
#define icmXYZ2Ary(ary, xyz) ((ary)[0] = (xyz).X, (ary)[1] = (xyz).Y, (ary)[2] = (xyz).Z)

/* Simple macro to transfer an XYZ number to an XYZ number */
#define icmXYZ2XYZ(d_xyz, s_xyz) ((d_xyz).X = (s_xyz).X, (d_xyz).Y = (s_xyz).Y, \
                                  (d_xyz).Z = (s_xyz).Z)

/* Simple macro to transfer an 3array to 3array */
/* Hmm. Same as icmCpy3 */
#define icmAry2Ary(d_ary, s_ary) ((d_ary)[0] = (s_ary)[0], (d_ary)[1] = (s_ary)[1], \
                                  (d_ary)[2] = (s_ary)[2])

/* CIE Y (range 0 .. 1) to perceptual CIE 1976 L* (range 0 .. 100) */
double icmY2L(double val);

/* Perceptual CIE 1976 L* (range 0 .. 100) to CIE Y (range 0 .. 1) */
double icmL2Y(double val);

/* CIE XYZ to perceptual Lab */
extern ICCLIB_API void icmXYZ2Lab(icmXYZNumber *w, double *out, double *in);

/* Perceptual Lab to CIE XYZ */
extern ICCLIB_API void icmLab2XYZ(icmXYZNumber *w, double *out, double *in);

/* CIE XYZ to perceptual Lpt */
extern ICCLIB_API void icmXYZ2Lpt(icmXYZNumber *w, double *out, double *in);

/* Perceptual Lpt to CIE XYZ */
extern ICCLIB_API void icmLpt2XYZ(icmXYZNumber *w, double *out, double *in);

/* LCh to Lab (general) */
extern ICCLIB_API void icmLCh2Lab(double *out, double *in);

/* Lab to LCh (general) */
extern ICCLIB_API void icmLab2LCh(double *out, double *in);


/* CIE XYZ to perceptual Luv */
extern ICCLIB_API void icmXYZ2Luv(icmXYZNumber *w, double *out, double *in);

/* Perceptual Luv to CIE XYZ */
extern ICCLIB_API void icmLuv2XYZ(icmXYZNumber *w, double *out, double *in);


/* XYZ to Yxy */
extern ICCLIB_API void icmXYZ2Yxy(double *out, double *in);

/* Yxy to XYZ */
extern ICCLIB_API void icmYxy2XYZ(double *out, double *in);

/* XYZ to xy */
extern ICCLIB_API void icmXYZ2xy(double *out, double *in);

/* Y & xy to XYZ */
extern ICCLIB_API void icmY_xy2XYZ(double *out, double *xy, double Y);


/* CIE XYZ to perceptual CIE 1976 UCS diagram Yu'v'*/
/* (Yu'v' is a better linear chromaticity space than Yxy) */
extern ICCLIB_API void icmXYZ21976UCS(double *out, double *in);

/* Perceptual CIE 1976 UCS diagram Yu'v' to CIE XYZ */
extern ICCLIB_API void icm1976UCS2XYZ(double *out, double *in);

/* CIE XYZ to perceptual CIE 1976 UCS diagram u'v'*/
/* (u'v' is a better linear chromaticity space than xy) */
extern ICCLIB_API void icmXYZ21976UCSuv(double *out, double *in);

/* CIE 1976 UCS diagram Y & u'v' to XYZ */
extern ICCLIB_API void icm1976UCSY_uv2XYZ(double *out, double *uv, double Y);

/* Aliases for above */
#define icmXYZ2Yuv   icmXYZ21976UCS
#define icmYuv2XYZ   icm1976UCS2XYZ
#define icmXYZ2uv    icmXYZ21976UCSuv
#define icmY_uv2XYZ  icm1976UCSY_uv2XYZ


/* CIE XYZ to perceptual CIE 1960 UCS */
/* (This was obsoleted by the 1976UCS, but is still used */
/*  in computing color temperatures.) */
extern ICCLIB_API void icmXYZ21960UCS(double *out, double *in);

/* Perceptual CIE 1960 UCS to CIE XYZ */
extern ICCLIB_API void icm1960UCS2XYZ(double *out, double *in);


/* CIE XYZ to perceptual CIE 1964 WUV (U*V*W*) */
/* (This is obsolete but still used in computing CRI) */
extern ICCLIB_API void icmXYZ21964WUV(icmXYZNumber *w, double *out, double *in);

/* Perceptual CIE 1964 WUV (U*V*W*) to CIE XYZ */
extern ICCLIB_API void icm1964WUV2XYZ(icmXYZNumber *w, double *out, double *in);

/* CIE CIE1960 UCS to perceptual CIE 1964 WUV (U*V*W*) */
extern ICCLIB_API void icm1960UCS21964WUV(icmXYZNumber *w, double *out, double *in);


/* NOTE :- that these values are for the 1931 standard observer */

/* The standard D50 illuminant value */
extern icmXYZNumber icmD50;
extern icmXYZNumber icmD50_100;		/* Scaled to 100 */
extern double icmD50_ary3[3];		/* As an array */
extern double icmD50_100_ary3[3];	/* Scaled to 100 as an array */

/* The standard D65 illuminant value */
extern icmXYZNumber icmD65;
extern icmXYZNumber icmD65_100;		/* Scaled to 100 */
extern double icmD65_ary3[3];		/* As an array */
extern double icmD65_100_ary3[3];	/* Scaled to 100 as an array */


/* The default black value */
extern icmXYZNumber icmBlack;

/* The Standard ("wrong Von-Kries") chromatic transform matrix */
extern double icmWrongVonKries[3][3];

/* The Bradford chromatic transform matrix */
extern double icmBradford[3][3];

/* Initialise a pseudo-hilbert grid counter, return total usable count. */
extern ICCLIB_API unsigned psh_init(psh *p, int di, unsigned int res, int co[]);

/* Reset the counter */
extern ICCLIB_API void psh_reset(psh *p);

/* Increment pseudo-hilbert coordinates */
/* Return non-zero if count rolls over to 0 */
extern ICCLIB_API int psh_inc(psh *p, int co[]);

/* - - - - - - - - - - - - - - - - - - - - - - - */

/* Return the normal Delta E given two Lab values */
extern ICCLIB_API double icmLabDE(double *in0, double *in1);

/* Return the normal Delta E squared, given two Lab values */
extern ICCLIB_API double icmLabDEsq(double *in0, double *in1);

/* Return the normal Delta E squared given two XYZ values */
extern ICCLIB_API double icmXYZLabDEsq(icmXYZNumber *w, double *in0, double *in1);

/* Return the normal Delta E given two XYZ values */
extern ICCLIB_API double icmXYZLabDE(icmXYZNumber *w, double *in0, double *in1);

/* Return the normal Delta E squared given two XYZ values */
extern ICCLIB_API double icmXYZLptDEsq(icmXYZNumber *w, double *in0, double *in1);

/* Return the normal Delta E given two XYZ values */
extern ICCLIB_API double icmXYZLptDE(icmXYZNumber *w, double *in0, double *in1);


/* Return the CIE94 Delta E color difference measure for two Lab values */
extern ICCLIB_API double icmCIE94(double *in0, double *in1);

/* Return the CIE94 Delta E color difference measure squared, for two Lab values */
extern ICCLIB_API double icmCIE94sq(double *in0, double *in1);

/* Return the CIE94 Delta E color difference measure for two XYZ values */
extern ICCLIB_API double icmXYZCIE94(icmXYZNumber *w, double *in0, double *in1);


/* Return the CIEDE2000 Delta E color difference measure for two Lab values */
extern ICCLIB_API double icmCIE2K(double *in0, double *in1);

/* Return the CIEDE2000 Delta E color difference measure squared, for two Lab values */
extern ICCLIB_API double icmCIE2Ksq(double *in0, double *in1);

/* Return the CIEDE2000 Delta E color difference measure for two XYZ values */
extern ICCLIB_API double icmXYZCIE2K(icmXYZNumber *w, double *in0, double *in1);


/* - - - - - - - - - - - - - - - - - - - - - - - */
/* Clip Lab, while maintaining hue angle. */
/* Return nz if clipping occured */
int icmClipLab(double out[3], double in[3]);

/* Clip XYZ, while maintaining hue angle */
/* Return nz if clipping occured */
int icmClipXYZ(double out[3], double in[3]);

/* - - - - - - - - - - - - - - - - - - - - - - - */

/* RGB XYZ primaries to device to RGB->XYZ transform matrix */
/* Return non-zero if matrix would be singular */
/* Use icmMulBy3x3(dst, mat, src) */
int icmRGBXYZprim2matrix(
	double red[3],		/* Red colorant */
	double green[3],	/* Green colorant */
	double blue[3],		/* Blue colorant */
	double white[3],	/* White point */
	double mat[3][3]	/* Destination matrix[RGB][XYZ] */
);

/* RGB Yxy primaries to device to RGB->XYZ transform matrix */
/* Return non-zero if matrix would be singular */
/* Use icmMulBy3x3(dst, mat, src) */
int icmRGBYxyprim2matrix(
	double red[3],		/* Red colorant */
	double green[3],	/* Green colorant */
	double blue[3],		/* Blue colorant */
	double white[3],	/* White point */
	double mat[3][3],	/* Return matrix[RGB][XYZ] */
	double wXYZ[3]		/* Return white XYZ */
);

/* Chromatic Adaption transform utility */
/* Return a 3x3 chromatic adaption matrix */
/* Use icmMulBy3x3(dst, mat, src) */
/* [ use icc->chromAdaptMatrix() to use the profiles cone space matrix rather */
/*   than specify a CAM ] */

#define ICM_CAM_NONE	    0x0000	/* No flags */
#define ICM_CAM_BRADFORD	0x0001	/* Use Bradford sharpened response space */
#define ICM_CAM_MULMATRIX	0x0002	/* Transform the given matrix rather than */
									/* create a transform from scratch. */
									/* NOTE that to transform primaries they */
									/* must be mat[XYZ][RGB] format! */

void icmChromAdaptMatrix(
	int flags,				/* Flags as defined below */
	icmXYZNumber d_wp,		/* Destination white point */
	icmXYZNumber s_wp,		/* Source white point */
	double mat[3][3]		/* Destination matrix */
);

/* Pre-round RGB device primary values to ensure that */
/* the sum of the quantized primaries is the same as */
/* the quantized sum. */
void quantizeRGBprimsS15Fixed16(
	double mat[3][3]		/* matrix[RGB][XYZ] */
);

/* Pre-round a 3x3 matrix to ensure that the product of */
/* the matrix and the input value is the same as */
/* the quantized matrix product. This is used to improve accuracy */
/* of 'chad' tag in computing absolute white point. */ 
void icmQuantize3x3S15Fixed16(
	double targ[3],			/* Target of product */
	double mat[3][3],		/* matrix[][] to be quantized */
	double in[3]			/* Input of product - must not be 0.0! */
);

/* - - - - - - - - - - - - - - */
/* Video functions */

/* Convert Lut table index/value to YCbCr */
void icmLut2YCbCr(double *out, double *in);

/* Convert YCbCr to Lut table index/value */
void icmYCbCr2Lut(double *out, double *in);


/* Convert Rec601 RGB' into YPbPr, (== "full range YCbCr") */
/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */
void icmRec601_RGBd_2_YPbPr(double out[3], double in[3]);

/* Convert Rec601 YPbPr to RGB' (== "full range YCbCr") */
/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */
void icmRec601_YPbPr_2_RGBd(double out[3], double in[3]);


/* Convert Rec709 1150/60/2:1 RGB' into YPbPr, or "full range YCbCr" */
/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */
void icmRec709_RGBd_2_YPbPr(double out[3], double in[3]);

/* Convert Rec709 1150/60/2:1 YPbPr to RGB' (== "full range YCbCr") */
/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */
void icmRec709_YPbPr_2_RGBd(double out[3], double in[3]);

/* Convert Rec709 1250/50/2:1 RGB' into YPbPr, or "full range YCbCr" */
/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */
void icmRec709_50_RGBd_2_YPbPr(double out[3], double in[3]);

/* Convert Rec709 1250/50/2:1 YPbPr to RGB' (== "full range YCbCr") */
/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */
void icmRec709_50_YPbPr_2_RGBd(double out[3], double in[3]);


/* Convert Rec2020 RGB' into Non-constant liminance YPbPr, or "full range YCbCr" */
/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */
void icmRec2020_NCL_RGBd_2_YPbPr(double out[3], double in[3]);

/* Convert Rec2020 Non-constant liminance YPbPr into RGB' (== "full range YCbCr") */
/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */
void icmRec2020_NCL_YPbPr_2_RGBd(double out[3], double in[3]);

/* Convert Rec2020 RGB' into Constant liminance YPbPr, or "full range YCbCr" */
/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */
void icmRec2020_CL_RGBd_2_YPbPr(double out[3], double in[3]);

/* Convert Rec2020 Constant liminance YPbPr into RGB' (== "full range YCbCr") */
/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */
void icmRec2020_CL_YPbPr_2_RGBd(double out[3], double in[3]);


/* Convert Rec601/709/2020 YPbPr to YCbCr range. */
/* input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, */
/* output 16/255 .. 235/255, 16/255 .. 240/255, 16/255 .. 240/255 */ 
void icmRecXXX_YPbPr_2_YCbCr(double out[3], double in[3]);

/* Convert Rec601/709/2020 YCbCr to YPbPr range. */
/* input 16/255 .. 235/255, 16/255 .. 240/255, 16/255 .. 240/255 */ 
/* output 0..1, -0.5 .. 0.5, -0.5 .. 0.5, */
void icmRecXXX_YCbCr_2_YPbPr(double out[3], double in[3]);


/* Convert full range RGB to Video range 16..235 RGB */
void icmRGB_2_VidRGB(double out[3], double in[3]);

/* Convert Video range 16..235 RGB to full range RGB */
void icmVidRGB_2_RGB(double out[3], double in[3]);

/* ---------------------------------------------------------- */
/* PS 3.14-2009, Digital Imaging and Communications in Medicine */
/* (DICOM) Part 14: Grayscale Standard Display Function */

/* JND index value 1..1023 to L 0.05 .. 3993.404 cd/m^2 */
double icmDICOM_fwd(double jnd);

/* L 0.05 .. 3993.404 cd/m^2 to JND index value 1..1023 */
double icmDICOM_bwd(double L);


/* ---------------------------------------------------------- */
/* Some utility functions */

/* Convert an angle in radians into chromatic RGB values */
/* in a simple geometric fashion with 0 = Red */
void icmRad2RGB(double rgb[3], double ang);

/* ---------------------------------------------------------- */
/* Print an int vector to a string. */
/* Returned static buffer is re-used every 5 calls. */
char *icmPiv(int di, int *p);

/* Print a double color vector to a string. */
/* Returned static buffer is re-used every 5 calls. */
char *icmPdv(int di, double *p);

/* Print a float color vector to a string. */
/* Returned static buffer is re-used every 5 calls. */
char *icmPfv(int di, float *p);

/* Print an 0..1 range XYZ as a D50 Lab string */
/* Returned static buffer is re-used every 5 calls. */
char *icmPLab(double *p);
/* - - - - - - - - - - - - - - - - - - - - - - - */


#ifdef __cplusplus
	}
#endif

#endif /* ICC_H */

