Files
argyllcms/icc/icc_pe.h
2026-08-20 20:28:40 +01:00

694 lines
23 KiB
C

/*
* International Color Consortium Format Library (icclib)
*
* Author: Graeme W. Gill
* Date: 2022/12/15
* Version: 3.0.0
*
* Copyright 1997 - 2023 Graeme W. Gill
*
* This material is licensed with an "MIT" free use license:-
* see the License4.txt file in this directory for licensing details.
*/
/* icmPe transform declarations */
/* This is #included in icc.h */
/* Sig (etype) of processing elements */
/* (Update icmPeSig2str() too) */
typedef enum {
icmSigPeNone = icmMakeTag(0,0,0,0), /* Not an icmPe */
icmSigPeCurveSet = icmMakeTag('P','e','c','s'), /* A set of N x 1d Curves */
icmSigPeCurve = icmMakeTag('P','e','c','u'), /* A linear/gamma/table curve */
icmSigPeMatrix = icmMakeTag('P','e','m','a'), /* An N x M + F matrix */
icmSigPeClut = icmMakeTag('P','e','c','l'), /* An N x M cLUT */
icmSigPeLut816 = icmMakeTag('P','e','l','8'),
icmSigPeContainer = icmMakeTag('P','e','c','r'), /* A sequence of other icmPe's */
icmSigPeInverter = icmMakeTag('P','e','i','v'), /* An inverter */
icmSigPeMono = icmMakeTag('P','e','m','o'), /* Mono conversion part of ShaperMono */
icmSigPeShaperMatrix = icmMakeTag('P','e','s','m'), /* A shaper/matrix sequence */
icmSigPeShaperMono = icmMakeTag('P','e','s','o'), /* A shaper/mono sequence */
/* Format conversions */
icmSigPeXYZ2Lab = icmMakeTag('P','e','x','l'),
icmSigPeAbs2Rel = icmMakeTag('P','e','a','r'),
icmSigPeXYZ2XYZ8 = icmMakeTag('P','e','x','1'),
icmSigPeXYZ2XYZ16 = icmMakeTag('P','e','x','2'),
icmSigPeLab2Lab8 = icmMakeTag('P','e','l','1'),
icmSigPeLab2LabV2 = icmMakeTag('P','e','l','2'),
icmSigPeGeneric2Norm = icmMakeTag('P','e','G','e'), /* Generic normalization */
icmSigPeGridAlign = icmMakeTag('P','e','G','a'), /* Grid align */
icmSigPeNOP = icmMakeTag('P','e','N','O'), /* A NOP */
icmPeMaxEnum = icMaxTagVal
} icmPeSignature;
/* See icmPeSig2str() for text */
/* Lookup return values */
typedef enum {
icmPe_lurv_OK = 0x00, /* Lookup is OK */
icmPe_lurv_clip = 0x01, /* Clipping warning */
icmPe_lurv_bwclp = 0x02, /* lookup_bwd on clipping PeCurveFmlaSeg */
icmPe_lurv_num = 0x04, /* Numerical limit reached warning */
icmPe_lurv_imp = 0x08, /* Not implemented error */
icmPe_lurv_cfg = 0x10, /* Configuration error */
icmPe_lurv_err = 0x18 /* Error mask */
} icmPe_lurv;
char *icmPe_lurv2str(icmPe_lurv err);
/* PE operation classification */
typedef enum { /* Dominant operation type */
icmPeOp_NOP = 0x0, /* No operation */
icmPeOp_perch = 0x1, /* A per-channel operation */
icmPeOp_matrix = 0x2, /* A matrix interchannel operation (maybe abs<->rel or Mono) */
icmPeOp_cLUT = 0x3, /* A cLUT interchannel operation */
icmPeOp_fmt = 0x4, /* PCS format transform (i.e. XYZ <->Lab) */
icmPeOp_complex = 0x5 /* A complex set of interchannel operations */
} icmPeOp;
char *icmPe_Op2str(icmPeOp op);
/* icmPe attributes. */
typedef struct {
char comp; /* nz if Compound element contains other Pe's (for trace) */
char inv; /* nz if element was created with inverted function (for trace) */
char norm; /* nz if normalization type operation */
icmPeOp op; /* Operation type */
char fwd; /* nz if fwd transform is implemented */
char bwd; /* nz if bwd transform is implemented */
} icmPeAttr;
char *icmPe_Attr2Str(icmPeAttr *attr);
#define ICM_PE_SETATTR(ATTR, COMP, INV, NORM, OP, FWD, BWD) \
ATTR.comp = COMP; \
ATTR.inv = INV; \
ATTR.norm = NORM; \
ATTR.op = OP; \
ATTR.fwd = FWD; \
ATTR.bwd = BWD; \
/* Processing Element base class definition */
/* This inherets from icmBase */
#define ICM_PE_MEMBERS(ECLASS) \
ICM_BASE_MEMBERS(ECLASS) \
unsigned int inputChan; /* No. of input channels */ \
unsigned int outputChan; /* No. of output channels */ \
icmPeAttr attr; /* attributes of transforms */ \
int isPeSeq; /* nz if is superclass of icmPeSeq */ \
\
int trace; /* Trace level, 1 = top, > 1 = recursion level */ \
\
/* Init anything that depends on the processing (i.e. attr, inverse comp etc.) */ \
/* Return nz on error */ \
int (*init) (ECLASS *p); \
\
/* Translate a value through the Pe, return any warning flags */ \
/* (Where are warning flags defined ? Clip ?, F.P. error ?) */ \
icmPe_lurv (*lookup_fwd) (ECLASS *p, double *out, double *in); /* Forwards */ \
icmPe_lurv (*lookup_bwd) (ECLASS *p, double *out, double *in); /* Backwards */ \
/* Processing Element Initialisation */
#define ICM_PE_INIT(ECLASS, ESIG) \
p->etype = ESIG; \
p->init = (int (*)(ECLASS *))icmPeDummy_init; \
/* Processing Element allocation and initialisation */
/* Returns NULL on error */
#define ICM_PE_ALLOCINIT(ECLASS, ESIG, TSIG) \
ICM_BASE_ALLOCINIT(ECLASS, TSIG) \
ICM_PE_INIT(ECLASS, ESIG) \
/* Non-serialisable init */
#define ICM_PE_NS_ALLOCINIT(ECLASS, ESIG) \
ICM_BASE_NS_ALLOCINIT(ECLASS) \
ICM_PE_INIT(ECLASS, ESIG) \
/* A Processing Element */
struct _icmPe {
ICM_PE_MEMBERS(struct _icmPe)
}; typedef struct _icmPe icmPe;
/* Structure for managing Pe's */
struct _icmPePrivStr {
int dup; /* Duplicate Pe flag */
icmPositionNumber pn; /* Offset and size of sub-tag */
}; typedef struct _icmPePrivStr icmPePrivStr;
/* ----------------------- */
/* A sequence of icmPe's */
/* Processing Element Sequence base class definition */
/* This inherets from icmPe */
#define ICM_PE_SEQ_MEMBERS(ECLASS) \
ICM_PE_MEMBERS(ECLASS) \
\
/* Private: */ \
unsigned int _count; /* Count currently allocated */ \
unsigned int nncount; /* Count of non-NULL pe's */ \
\
/* Public: */ \
unsigned int count; /* Count of processing elements */ \
icmPe **pe; /* Array of [count] processing elements */ \
/* Processing Element Sequenece allocation and initialisation. */
/* (It's assumed that the parent class provides all methods) */
/* Returns NULL on error */
#define ICM_PE_SEQ_ALLOCINIT(ECLASS, ESIG, TSIG) \
ICM_PE_ALLOCINIT(ECLASS, ESIG, TSIG) \
p->isPeSeq = 1; \
p->init = (int (*)(ECLASS *))icmPeSeq_init; \
p->lookup_fwd = (icmPe_lurv (*)(ECLASS *, double *, double *))icmPeSeq_lookup_fwd; \
p->lookup_bwd = (icmPe_lurv (*)(ECLASS *, double *, double *))icmPeSeq_lookup_bwd; \
/* Non-serialisable init */
#define ICM_PE_SEQ_NS_ALLOCINIT(ECLASS, ESIG) \
ICM_PE_NS_ALLOCINIT(ECLASS, ESIG) \
p->isPeSeq = 1; \
p->init = (int (*)(ECLASS *))icmPeSeq_init; \
p->lookup_fwd = (icmPe_lurv (*)(ECLASS *, double *, double *))icmPeSeq_lookup_fwd; \
p->lookup_bwd = (icmPe_lurv (*)(ECLASS *, double *, double *))icmPeSeq_lookup_bwd; \
/* A Processing Element Sequence */
struct _icmPeSeq {
ICM_PE_SEQ_MEMBERS(struct _icmPeSeq)
}; typedef struct _icmPeSeq icmPeSeq;
/* ================================================================================== */
/* ----------------------- */
/* A set of N x 1d Curves */
/* etype icmSigPeCurveSet */
/* ttype icmSig816Curves */
struct _icmPeCurveSet {
ICM_PE_MEMBERS(struct _icmPeCurveSet)
/* Private: */
icmPePrivStr pei[MAX_CHAN]; /* Management info. */
unsigned int bpv; /* Bytes per value (Lut1 only) */
/* Public: */
icmPe *pe[MAX_CHAN]; /* 1D curves */
}; typedef struct _icmPeCurveSet icmPeCurveSet;
/* A fake internal Sig 'l1vs' for icmLut8 and icmLut16 curves */
#define icmSig816Curves ((icTagTypeSignature)icmMakeTag('l','1','v','s'))
/* ----------------------- */
/* A linear/gamma/table (sample) curve */
/* etype icmSigPeCurve */
/* ttype icSigCurveType */
/* ttype icmSig816Curve */
/* icmPeCurve 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 count; /* Copy of forward table size */
double *data; /* Copy of forward table data */
} icmRevTable;
typedef enum {
icmCurveUndef = -1, /* Undefined curve */
icmCurveLin = 0, /* Linear transfer curve */
icmCurveGamma = 1, /* Gamma power transfer curve */
icmCurveSpec = 2 /* Specified curve */
} icmCurveType;
struct _icmPeCurve {
ICM_PE_MEMBERS(struct _icmPeCurve)
/* Private: */
int inited; /* Flag */
unsigned int _count; /* Size currently allocated */
unsigned int bpv; /* Bytes per value (Lut1 only) */
icmRevTable rt; /* Reverse table information */
/* Public: */
icmCurveType ctype; /* Type of curve */
unsigned int count; /* Allocated and used size of the array */
double *data; /* Curve data scaled to range 0.0 - 1.0 */
/* or data[0] = gamma value */
/* or count == 0 for linear. */
}; typedef struct _icmPeCurve icmPeCurve;
typedef icmPeCurve icmCurve;
/* A fake internal Sig 'l1cv' for a icmLut8 and icmLut16 curve */
#define icmSig816Curve ((icTagTypeSignature)icmMakeTag('l','1','c','v'))
/* ----------------------- */
/* An N x M + F matrix */
/* etype icmSigPeMatrix */
/* ttype icmSig816Matrix */
struct _icmPeMatrix {
ICM_PE_MEMBERS(struct _icmPeMatrix)
/* Private: */
int inited; /* flag - if initiliase (imx, flags) */
int ivalid; /* flag - if imx is valid */
int mx_unity; /* flag - if mx[] is unity */
int ct_zero; /* flag - if ct[] is zero */
double imx[MAX_CHAN][MAX_CHAN]; /* [inn][outn] Inverse matrix values */
/* Public: */
double mx[MAX_CHAN][MAX_CHAN]; /* [outn][inn] Matrix values */
double ct[MAX_CHAN]; /* [outn] Constant values added after mx[][] multiply */
}; typedef struct _icmPeMatrix icmPeMatrix;
/* A fake internal Sig 'l1mx' for icmLut8 and icmLut16 Matrix */
#define icmSig816Matrix ((icTagTypeSignature)icmMakeTag('l','1','m','x'))
/* ----------------------- */
/* An N x M cLUT */
/* etype icmSigPeClut */
/* ttype icmSig816CLUT */
struct _icmLu4Base;
struct _icmLu4Space;
struct _icmPeClut {
ICM_PE_MEMBERS(struct _icmPeClut)
/* Private: */
unsigned int _clutsize; /* Current clut size in doubles */
int inited; /* flag - if initialised (dinc[]) */
unsigned int dinc[MAX_CHAN]; /* [inn] grid resolution values */
int dcube[1 << MAX_CHAN]; /* Hyper cube offsets (in doubles) */
int use_sx; /* flag - use simplex interpolation */
/* Public: */
unsigned int bpv; /* Bytes per value */
unsigned int clutPoints[MAX_CHAN]; /* [inn] grid resolution values */
/* (Must be all the same for icmLut) */
double *clutTable; /* clut values organized as: */
/* [inputChan 0: 0..clutPoints[0]-1].. */
/* [inputChan inn-1: 0..clutPoints[inn-1]-1] */
/* [outputChan: 0..outn-1] */
/* return the minimum and maximum values of the given channel in the clut */
void (*min_max) (struct _icmPeClut *p, double *minv, double *maxv, int chan);
/* Determine appropriate clut lookup algorithm */
void (*choose_alg) (struct _icmPeClut *p, struct _icmLu4Space *lu);
/* Return total ink limit and channel maximums. */
double (*get_tac) (
struct _icmPeClut *p,
double *chmax, /* device return channel sums. May be NULL */
icmPe *tail, /* lu tail transform from clut to device output */
void (*calfunc)(void *cntx, double *out, double *in), /* Optional calibration func. */
void *cntx
);
/* ~8 missing API's:
tune_value() cLut single value fine tune)
*/
}; typedef struct _icmPeClut icmPeClut;
/* A fake internal Sig 'l1L1' for icmLut8 and icmLut16 */
#define icmSig816CLUT ((icTagTypeSignature)icmMakeTag('l','1','L','U'))
/* ----------------------- */
/* Create an empty icmPe object. Return null on error */
/* Set ttype to the serialisation type of this Pe or its */
/* parent ttype if it doesn't use a sig in its serialisation. */
/* Use ttype = icmSigUnknownType if not a serialisable object. */
static icmPe *new_icmPe(struct _icc *icp, icTagTypeSignature ttype, icmPeSignature pesig);
/* =========================================================================== */
/* Client code populates data[i].pe etc using icc->new_pe(). */
/* ------------------------------------------------------------ */
/* Lut8Type and Lut16Type */
/* etype icmSigPeLut816 */
/* ttype icSigLut8Type */
/* ttype icSigLut16Type */
/* Indexes into underlying Pe's */
typedef enum {
icmLut816_ix_Matrix = 0,
icmLut816_ix_in = 1,
icmLut816_ix_CLUT = 2,
icmLut816_ix_out = 3
} icmLut1_ix;
struct _icmLut1 {
ICM_PE_SEQ_MEMBERS(struct _icmLut1)
/* Private: */
unsigned int bpv; /* Bytes per value for curves and cLUT */
/* Public: */
/* unsigned int inputChan; No. of input channels (in icpPeSeq) */
/* unsigned int outputChan; No. of output channels (in icpPeSeq) */
unsigned int inputEnt; /* Num of in-table entries (must be 256 for Lut8) */
unsigned int clutPoints; /* Num of grid points */
unsigned int outputEnt; /* Num of out-table entries (must be 256 for Lut8) */
/* Processing Element aliases: */
icmPeMatrix *pe_mx;
icmPeCurve **pe_ic;
icmPeClut *pe_cl;
icmPeCurve **pe_oc;
}; typedef struct _icmLut1 icmLut1;
typedef struct _icmLut1 icmLut8;
typedef struct _icmLut1 icmLut16;
/* ------------------------------------------------------------ */
/* A Pe that encodes an RGB shaper-matrix transform. */
/* This transform is always XYZ PCS. */
/* (This is not a serialisable tagtype) */
struct _icmShaperMatrix {
ICM_PE_SEQ_MEMBERS(struct _icmShaperMatrix)
/* icSigCurveType */
icmBase *redCurve, *greenCurve, *blueCurve;
icmXYZArray *redColrnt, *greenColrnt, *blueColrnt;
}; typedef struct _icmShaperMatrix icmShaperMatrix;
/* Create an empty object. Return null on error. */
/* Doesn't set icc error if tags not present. */
static icmPeSeq *new_icmShaperMatrix(
struct _icc *icp,
int invert /* If nz, setup inverse of shape-matrix */
);
/* ------------------------------------------------------------ */
/* A Pe that encodes an Monochrome shaper-mono transform. */
/* (This is not a serialisable tagtype) */
struct _icmShaperMono {
ICM_PE_SEQ_MEMBERS(struct _icmShaperMono)
/* icSigCurveType */
icmBase *grayCurve;
}; typedef struct _icmShaperMono icmShaperMono;
/* Create an empty object. Return null on error. */
/* Doesn't set icc error if tags not present. */
static icmPeSeq *new_icmShaperMono(
struct _icc *icp,
int invert /* If nz, setup inverse of shape-mono */
);
/* ------------------------------------------------------------ */
/* A Pe that implements the mono<->PCS conversion. */
/* (This is not a serialisable tagtype) */
struct _icmPeMono {
ICM_PE_MEMBERS(struct _icmPeMono)
}; typedef struct _icmPeMono icmPeMono;
/* Create the Pe */
static icmPe *new_icmPeMono(
struct _icc *icp
);
/* =========================================================== */
/* A Pe that inverts another Pe. */
/* (This is not a serialisable tagtype) */
struct _icmPeInverter {
ICM_PE_MEMBERS(struct _icmPeInverter)
icmPe *pe; /* Pe that's being inverted */
}; typedef struct _icmPeInverter icmPeInverter;
/* Make Pe appear to be its inverse. */
static icmPe *new_icmPeInverter(
struct _icc *icp,
icmPe *pe
);
/* ------------------------------------------------------------ */
/* XYZ <-> Lab Pe (This is not a serialisable tagtype) */
struct _icmPeXYZ2Lab {
ICM_PE_MEMBERS(struct _icmPeXYZ2Lab)
struct _icmLu4Space *lu;
}; typedef struct _icmPeXYZ2Lab icmPeXYZ2Lab;
static icmPe *new_icmPeXYZ2Lab(
struct _icc *icp,
struct _icmLu4Space *lu, /* pcswht */
int invert /* Lab->XYZ */
);
/* ------------------------------------------------------------ */
/* XYZ abs <-> XYZ rel Pe (This is not a serialisable tagtype) */
struct _icmPeAbs2Rel {
ICM_PE_MEMBERS(struct _icmPeAbs2Rel)
struct _icmLu4Space *lu;
}; typedef struct _icmPeAbs2Rel icmPeAbs2Rel;
static icmPe *new_icmPeAbs2Rel(
struct _icc *icp,
struct _icmLu4Space *lu, /* wp, toAbs, fromAbs */
int invert /* Lab->XYZ */
);
/* ------------------------------------------------------------ */
/* XYZ <-> XYZ8 Pe (This is not a serialisable tagtype) */
struct _icmPeXYZ2XYZ8 {
ICM_PE_MEMBERS(struct _icmPeXYZ2XYZ8)
}; typedef struct _icmPeXYZ2XYZ8 icmPeXYZ2XYZ8;
static icmPe *new_icmPeXYZ2XYZ8(
struct _icc *icp,
int invert
);
/* ------------------------------------------------------------ */
/* XYZ <-> XYZ16 Pe (This is not a serialisable tagtype) */
struct _icmPeXYZ2XYZ16 {
ICM_PE_MEMBERS(struct _icmPeXYZ2XYZ16)
}; typedef struct _icmPeXYZ2XYZ16 icmPeXYZ2XYZ16;
static icmPe *new_icmPeXYZ2XYZ16(
struct _icc *icp,
int invert
);
/* ------------------------------------------------------------ */
/* Lab <-> Lab8 Pe (This is not a serialisable tagtype) */
struct _icmPeLab2Lab8 {
ICM_PE_MEMBERS(struct _icmPeLab2Lab8)
}; typedef struct _icmPeLab2Lab8 icmPeLab2Lab8;
static icmPe *new_icmPeLab2Lab8(
struct _icc *icp,
int invert
);
/* ------------------------------------------------------------ */
/* Lab <-> LabV2 Pe (This is not a serialisable tagtype) */
struct _icmPeLab2LabV2 {
ICM_PE_MEMBERS(struct _icmPeLab2LabV2)
}; typedef struct _icmPeLab2LabV2 icmPeLab2LabV2;
static icmPe *new_icmPeLab2LabV2(
struct _icc *icp,
int invert
);
/* ------------------------------------------------------------ */
/* Generic <-> Normalized Pe (This is not a serialisable tagtype) */
/* min/max difference is sanity limited to be at least 1e-4. */
/* NOTE that these conversions don't clip. */
struct _icmPeGeneric2Norm {
ICM_PE_MEMBERS(struct _icmPeGeneric2Norm)
double min[MAX_CHAN]; /* Full range min & max */
double max[MAX_CHAN];
double nmin[MAX_CHAN]; /* Normalize range min & max */
double nmax[MAX_CHAN];
char ident[50];
}; typedef struct _icmPeGeneric2Norm icmPeGeneric2Norm;
static icmPe *new_icmPeFullyGeneric2Norm(
struct _icc *icp,
unsigned int nchan,
double *min, double *max, /* Per channel range to be normalised */
double *nmin, double *nmax, /* Per channel range to normalize to (NULL = 0..1) */
char *ident, /* Encoding identifier */
int invert
);
static icmPe *new_icmPeGeneric2Norm(
struct _icc *icp,
unsigned int nchan,
double *min, double *max, /* Per channel range to be normalised */
char *ident, /* Encoding identifier */
int invert
);
/* ------------------------------------------------------------ */
/* A point to grid alignment transform. */
/* This creates a per channel transform that piecwize bends */
/* the input so as to land the given point on a grid point. */
/* The values are assumed in the range 0..1 */
struct _icmPeGridAlign {
ICM_PE_MEMBERS(struct _icmPeGridAlign)
double src[MAX_CHAN];
double dst[MAX_CHAN];
double fwdlow[MAX_CHAN];
double fwdhigh[MAX_CHAN];
double bwdlow[MAX_CHAN];
double bwdlowoff[MAX_CHAN];
double bwdhigh[MAX_CHAN];
double bwdhighoff[MAX_CHAN];
}; typedef struct _icmPeGridAlign icmPeGridAlign;
static icmPe *new_icmPeGridAlign(
struct _icc *icp,
unsigned int nchan,
double *point, /* Point to aim for, must be 0..1 */
unsigned int *clutPoints, /* Grid resolution for each channel */
int invert
);
/* ------------------------------------------------------------ */
/* A NOP (This is not a serialisable tagtype) */
struct _icmPeNOP {
ICM_PE_MEMBERS(struct _icmPeNOP)
}; typedef struct _icmPeNOP icmPeNOP;
static icmPe *new_icmPeNOP(
struct _icc *icp,
unsigned int nchan
);
/* ========================================================= */
/* A Processing Element Container (This is not a serialisable tagtype) */
struct _icmPeContainer {
ICM_PE_SEQ_MEMBERS(struct _icmPeContainer)
/* Added Pe's will have their reference counts incremented, */
/* so if we adding a new_PeXXX, we need to delete it after adding. */
/* Removed Pe's will have their reference counts decremented. */
/* All these will return an error code. */
/* Add a Pe to the end of the sequence */
int (*append) (struct _icmPeContainer *p, icmPe *pe);
/* Add a Pe to the start of the sequence */
int (*prepend) (struct _icmPeContainer *p, icmPe *pe);
/* Insert a Pe before the given index */
int (*insert) (struct _icmPeContainer *p, unsigned int ix, icmPe *pe);
/* Replace the Pe at the given index */
int (*replace) (struct _icmPeContainer *p, unsigned int ix, icmPe *pe);
/* Remove the Pe at the given index */
int (*remove) (struct _icmPeContainer *p, unsigned int ix);
/* Append Pe's from another icmPeContainer, */
/* starting at six and ending before eix. */
/* Any empty or attr == NOP are skipped. */
/* This will flatten any sub-icmPeSeq, resulting in a sequence */
/* of direct Pe's or direct Pe's inside an Inverter. */
int (*append_pes) (struct _icmPeContainer *p, struct _icmPeSeq *src, int six, int eix);
/* Various icmPeContainer/icmPeSeq characteristic analysis methods: */
/* These all assume that the icmPeContainer is in a flattened state. */
/* Find the largest per channel input lut resolution. */
/* res may be NULL */
/* Returns 0 if there is no per channel lut in the input sequence. */
unsigned int (*max_in_res)(struct _icmPeContainer *p, int res[MAX_CHAN]);
/* Find the largest cLUT clutPoints[] value. */
/* res may be NULL */
/* Returns 0 if there is no cLut transform in the sequence. */
unsigned int (*max_clut_res)(struct _icmPeContainer *p, int res[MAX_CHAN]);
/* Find the largest per channel output lut resolution. */
/* res may be NULL */
/* Returns 0 if there is no per channel lut in the output sequence. */
unsigned int (*max_out_res)(struct _icmPeContainer *p, int res[MAX_CHAN]);
/* Determine if the per channel input or output is likely to be */
/* in a linear light space. We do this by seeing if the first */
/* non-per channel or format element is a matrix or matrix like element. */
int (*linear_light_inout)(struct _icmPeContainer *p, int dir);
/* Return a pointer to the last icmPeLut and a Seq containing */
/* the transforms from the output of the Lut */
/* Return NULL if no icmPeLut */
/* Delete both objects when done */
icmPeClut *(*get_lut)(
struct _icmPeContainer *p,
struct _icmPeContainer **ptail /* If not NULL return tail process, NULL if none */
);
/* Return total ink limit and channel maximums. */
/* return -1.0 if this lu doesn't have an icmPeClut in it */
double (*get_tac) (
struct _icmPeContainer *p,
double *chmax, /* device return channel maximums. May be NULL */
void (*calfunc)(void *cntx, double *out, double *in), /* Optional calibration func. */
void *cntx
);
}; typedef struct _icmPeContainer icmPeContainer;
/* Create an empty object. Return null on error */
static icmPeContainer *new_icmPeContainer(
struct _icc *icp,
int dinch, /* Default number of input channels, 0 OK if will add non-NULL memb. Pe. */
int doutch /* Default number of output channels, 0 OK if will add non-NULL memb. Pe. */
);