#ifndef MPP_H
#define MPP_H

/* 
 * Argyll Color Correction System
 * Model Printer Profile object.
 *
 * Author: Graeme W. Gill
 * Date:   24/2/2002
 *
 * Copyright 2002 Graeme W. Gill
 * All rights reserved.
 * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
 * see the License.txt file for licencing details.
 *
 */

/* This version (based on mpp_x1) has n * 2^(n-1) shape params, */
/* used for linear interpolation of the shaping correction. */

/*
 * This object provides a model based printer forward profile
 * functionality, to support forward profiling and optimised
 * separation of printing devices with more than 4 inks.
 */

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

#define MPP_MXINKS 8		/* Would like to be ICX_MXINKS but need more dynamic allocation */
#define MPP_MXTCORD 10		/* Maxkimum shaper harmonic orders to use */
#define MPP_MXCCOMB (1 << MPP_MXINKS)	/* Maximum number of primary combinations */
#define MPP_MXPARMS (MPP_MXINKS * MPP_MXTCORD + (MPP_MXINKS * MPP_MXCCOMB/2) + MPP_MXCCOMB)
							/* Maximum total parameters for a band */
#define MPP_MXBANDS 61		/* Maximum number of spectral bands (enought for 10nm) */

/* A test patch value */
typedef struct {
  /* public: */
	double *nv;			/* [MPP_MXINKS] Device values */
	double *band;		/* [3+MPP_MXBANDS];	Target XYZ & Spectral reflectance values */

  /* private: */
	double w;			/* Weight for this pass */
	double *lband;		/* [3+MPP_MXBANDS];	L* scale Target XYZ & Spectral reflectance values */
	double Lab[3];		/* Target Lab value */
	double tpcnv, tpcnv1;	/* Intermediate values for band oba without channel och */
	double *tcnv;		/* [MPP_MXINKS] Transfer curve corrected device values */
	double *scnv;		/* [MPP_MXINKS] Ideal shape correction values for device input */
	double *pcnv;		/* [MPP_MXCCOMB] Primary combination values (pre or post shape) */
	double *fcnv;		/* [MPP_MXINKS * MPP_MXCCOMB/2] shape interpolation weights for och */
	double cXYZ[3];		/* Current model XYZ value */
	double err;			/* Delta E squared */
} mppcol;

struct _mpp {

  /* Public: */
	void (*del)(struct _mpp *p);


	/* Create the mpp from scattered data points */
	/* Returns nz on error */
	int (*create) (struct _mpp *p,
	               int verb,				/* Vebosity level, 0 = none */
	               int quality,				/* Profile quality, 0..3 */
	               int display,				/* non-zero if display device */
	               double limit,			/* Total ink limit (not %) (if not display) */
	               inkmask imask,			/* Inkmask describing device colorspace */
	               int    spec_n,			/* Number of spectral bands, 0 if not valid */
	               double spec_wl_short,	/* First reading wavelength in nm (shortest) */
	               double spec_wl_long,		/* Last reading wavelength in nm (longest) */
	               double norm,				/* Normalising scale value */
	               instType itype,			/* Spectral instrument type (if not display) */
	               int no,					/* Number of points */
	               mppcol *points);			/* Array of input points */

	int (*write_mpp)(struct _mpp *p, char *filename, int lab);	/* write to a CGATS .mpp file */
	int (*read_mpp)(struct _mpp *p, char *filename);	/* read from a CGATS .mpp file */

	/* Get various types of information about the mpp */
	void (*get_info) (struct _mpp *p,
	                  inkmask *imask,		/* Inkmask, describing device colorspace */
	                  int *nodchan,			/* Number of device channels */
	                  double *limit,		/* Total ink limit (0.0 .. devchan) */
	                  int *spec_n,			/* Number of spectral bands, 0 if none */
	                  double *spec_wl_short,/* First reading wavelength in nm (shortest) */
	                  double *spec_wl_long, /* Last reading wavelength in nm (longest) */
	                  instType *itype,		/* Instrument type */
					  int *display);		/* NZ if display type */

	/* Set an illuminant and observer to use spectral model */
	/* for CIE lookup with optional FWA. Set both to default for XYZ mpp model. */
	/* Return 0 on OK, 1 on spectral not supported */
	/* If the model is for a display, the illuminant will be ignored. */
	int (*set_ilob) (struct _mpp *p,
		icxIllumeType ilType,			/* Illuminant type (icxIT_default for none) */
		xspect        *custIllum,		/* Custom illuminant (NULL for none) */
		icxObserverType obType,			/* Observer type (icxOT_default for none) */	
		xspect        *custObserver[3],	/* Custom observer (NULL for none)  */
		icColorSpaceSignature  rcs,		/* Return color space, icSigXYZData or icSigLabData */
		int           use_fwa			/* NZ to involke FWA. */
	);

	/* Get the white and black points for this profile (default XYZ) */
	void (*get_wb) (struct _mpp *p,
	                  double *white,
	                  double *black,
	                  double *kblack);


	/* Lookup an XYZ or Lab color (default XYZ) */
	/* [will use spectral and FWA if configured] */
	void (*lookup) (struct _mpp *p,
	               double *out,					/* Returned XYZ or Lab */
	               double *in);					/* Input device values */

	/* Lookup an XYZ or Lab color with its partial derivative. */
	/* [will ignore spectral and FWA even if configured] */
	void (*dlookup)(struct _mpp *p,
	               double *out,					/* Return the XYZ or Lab */
	               double **dout,				/* Return the partial derivative dout[3][n] */
	               double *dev);

	/* Lookup an XYZ value. (never FWA corrected) */
	void (*lookup_xyz) (struct _mpp *p,
	               double *out,					/* Returned XYZ value */
	               double *in);					/* Input device values */

	/* Lookup a spectral value. (never FWA corrected) */
	void (*lookup_spec) (struct _mpp *p,
	               xspect *out,					/* Returned spectral value */
	               double *in);					/* Input device values */

	/* Return a gamut object, return NULL on error */
	gamut *(*get_gamut)(struct _mpp *p, double detail);	/* detail level 0.0 = default */

  /* Private: */
	int verb;				/* Verbose */

	/* General model information */
	int    display;			/* Non-zero if display profile rather than output */
	inkmask imask;			/* Inkmask describing device space */
	double limit;			/* Total ink limit (If output device) */
	int    spec_n;			/* Number of spectral bands, 0 if not valid */
	double spec_wl_short;	/* First reading wavelength in nm (shortest) */
	double spec_wl_long;	/* Last reading wavelength in nm (longest) */
	double norm;			/* Normalising scale value (will be 1.0) */
	instType itype;			/* Spectral instrument type (If output device) */
	mppcol white, black;	/* White and black points */
	mppcol kblack;			/* K only black point */

	/* Foward model parameters */
	int n;					/* Number of chanels */
	int nn;					/* Number of primary combinations = 1 << n  */
	int nnn2;				/* Total shape combinations = n * nn/2 */

	int cord;				/* Device transfer curve order (must be 1 <= cord <= MPP_MXTCORD) */
	double tc[MPP_MXINKS][3+MPP_MXBANDS][MPP_MXTCORD];	/* Device transfer curve parameters */
	int useshape;			/* Flag, NZ if shape parameters are being used */
	double ***shape;		/* [MPP_MXINKS][MPP_MXCCOMB][3+MPP_MXBANDS] Extra shaping parameters */
	double pc[MPP_MXCCOMB][3+MPP_MXBANDS];	/* Primary overlay combinations color values */

	/* Model housekeeping */
	/* Translate sparse shape color combo to compacted parameters and back */
	int f2c[MPP_MXINKS][MPP_MXCCOMB];		/* Full to Compact */
	struct { int ink; int comb; } c2f[MPP_MXINKS * MPP_MXCCOMB/2];	/* Compact to Full */

	/* Optimisation state */
	mppcol *otp;			/* Optimisation test point */
	int oit, ott;			/* Optimisation itteration and total itterations */
	int och;				/* Optimisation device channel 0..n-1 */
	int oba;				/* Optimisation band, 0..2 are XYZ, 3..spec_n+3 are spectral */
	double lpca[MPP_MXCCOMB][MPP_MXBANDS];	/* Primary combinations anchor L* band values */
	int nodp;				/* Number of device data points */
	mppcol *cols;			/* List of test points */
	double spmax;			/* Maximum spectral value of any sample and band */

	/* Lookup */
	icColorSpaceSignature pcs;	/* PCS to return, XYZ, Lab */
	xsp2cie *spc;			/* Spectral to CIE converter (NULL if using XYZ model) */

	/* Houskeeping */
	int errc;				/* Error code */
	char err[200];			/* Error message */
}; typedef struct _mpp mpp;

/* Create a new, uninitialised mpp */
mpp *new_mpp(void);


/* - - - - - - - - - - - - - */
/* mppcol utility functions */
/* Allocate the data portion of an mppcol */
/* Return NZ if malloc error */
int new_mppcol(
mppcol *p,			/* mppcol to allocate */
int n,				/* Number of inks */
int nb				/* Number of spectral bands */
);

/* Free the data allocation of an mppcol */
void del_mppcol(
mppcol *p,			/* mppcol to free */
int n,				/* Number of inks */
int nb				/* Number of spectral bands */
);

/* Copy the contents of one mppcol to another */
void copy_mppcol(
	mppcol *d,			/* Destination */
	mppcol *s,			/* Source */
	int n,				/* Number of inks */
	int nb				/* Number of spectral bands */
);

/* Allocate an array of mppcol, */
/* Return NULL if malloc error */
mppcol *new_mppcols(
	int no,				/* Number in array */
	int n,				/* Number of inks */
	int nb				/* Number of spectral bands */
);

/* Free an array of mppcol */
void del_mppcols(
	mppcol *p,			/* mppcol array to be free'd */
	int no,				/* Number in array */
	int n,				/* Number of inks */
	int nb				/* Number of spectral bands */
);

#endif /* MPP_H */




































