/* * International Color Consortium color transform expanded support * * Author: Graeme W. Gill * Date: 8/9/01 * Version: 1.00 * * Copyright 2001 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. * */ /* TTBD: * * Should switch this over to the linearisation used in * the current xlut.c (== xlut2.c), rather than linearise * w.r.t. the Y value. */ /* * This class handles the creation of device chanel linearisation * curves, given a callback function that maps the device chanels * to the value that should be linearised. * * This class is independent of other icc or icx classes. * * Its usual use is to create an Lab linerisation curve * for native XYZ profiles. */ #undef DEBUG /* Plot 1d Luts */ #include #ifdef __sun #include #endif #include "numlib.h" #include "rspl.h" #include "xdevlin.h" /* definitions for this class */ #ifdef DEBUG #include "plot.h" #endif /* Free up the xdevlin */ static void xdevlin_del(struct _xdevlin *p) { int e; for (e = 0; e < p->di; e++) { if (p->curves[e] != NULL) p->curves[e]->del(p->curves[e]); } free (p); } /* Return the linearisation values given the device values */ static void xdevlin_lin( struct _xdevlin *p, /* this */ double *out, /* di input */ double *in /* di output */ ) { co tc; int e; for (e = 0; e < p->di; e++) { tc.p[0] = in[e]; p->curves[e]->interp(p->curves[e], &tc); out[e] = tc.v[0]; } } #define MAX_INVSOLN 5 /* Return the inverse linearisation */ static void xdevlin_invlin( struct _xdevlin *p, /* this */ double *out, /* di input */ double *in /* di output */ ) { int i, j; int nsoln; /* Number of solutions found */ co pp[MAX_INVSOLN]; /* Room for all the solutions found */ double cdir; for (i = 0; i < p->di; i++) { pp[0].p[0] = p->clipc[i]; pp[0].v[0] = in[i]; cdir = p->clipc[i] - in[i]; /* Clip towards output range */ nsoln = p->curves[i]->rev_interp ( p->curves[i], /* this */ 0, /* No flags */ MAX_INVSOLN, /* Maximum number of solutions allowed for */ NULL, /* No auxiliary input targets */ &cdir, /* Clip vector direction and length */ pp); /* Input and output values */ nsoln &= RSPL_NOSOLNS; /* Get number of solutions */ if (nsoln == 1) { /* Exactly one solution */ j = 0; } else if (nsoln == 0) { /* Zero solutions. This is unexpected. */ error("~~~1 Unexpected failure to find reverse solution for linearisation curve"); return; } else { /* Multiple solutions */ /* Use a simple minded resolution - choose the one closest to the center */ double bdist = 1e300; int bsoln = 0; /* Don't expect this - 1D luts are meant to be monotonic */ printf("~~~1 got %d reverse solutions\n",nsoln); printf("~~~1 solution 0 = %f\n",pp[0].p[0]); printf("~~~1 solution 1 = %f\n",pp[1].p[0]); for (j = 0; j < nsoln; j++) { double tt; tt = pp[i].p[0] - p->clipc[i]; tt *= tt; if (tt < bdist) { /* Better solution */ bdist = tt; bsoln = j; } } j = bsoln; } out[i] = pp[j].p[0]; } } /* Callback function that is used to set each chanels grid value */ static void set_curve(void *cbntx, double *out, double *in) { xdevlin *p = (xdevlin *)cbntx; int e, ee = p->setch; double tin[MXDI], tout[MXDO]; double tt; /* setup input value */ for (e = 0; e < p->di; e++) tin[e] = p->pol ? p->max[e] : p->min[e]; tin[ee] = in[0]; p->lookup(p->lucntx, tout, tin); tt = (tout[0] - p->lmin)/(p->lmax - p->lmin); /* Normalise from L */ out[0] = tt * (p->max[ee] - p->min[ee]) + p->min[ee]; /* Back to device range */ } /* Create an appropriate linearisation from the callback */ /* This code is very similar to that in xlut.c when creating */ /* a device profiles linearisation curves. */ xdevlin *new_xdevlin( int di, /* Device dimenstionality */ double *min, double *max, /* Min & max range of device values, NULL = 0.0 - 1.0 */ void *lucntx, /* Context for callback */ void (*lookup) (void *lucntx, double *lin, double *dev) ) { int ee, e; xdevlin *p; /* Do the basic class initialisation */ if ((p = (xdevlin *) calloc(1,sizeof(xdevlin))) == NULL) return NULL; p->del = xdevlin_del; p->lin = xdevlin_lin; p->invlin = xdevlin_invlin; /* And then set it up */ p->di = di; p->lucntx = lucntx; p->lookup = lookup; /* Setup the clipping center */ for (e = 0; e < p->di; e++) { p->min[e] = min[e]; p->max[e] = max[e]; p->clipc[e] = 0.5 * (min[e] + max[e]); } /* Determine what level to set the chanels we're not interested in */ { double tin[MXDI], tout[MXDO]; double l00, l01, l10, l11; /* Resulting levels */ for (e = 0; e < p->di; e++) tin[e] = min[e]; lookup(lucntx, tout, tin); l00 = tout[0]; /* All minimum */ tin[0] = max[0]; lookup(lucntx, tout, tin); l01 = tout[0]; /* First chanel max, rest min */ for (e = 0; e < p->di; e++) tin[e] = max[e]; lookup(lucntx, tout, tin); l11 = tout[0]; /* All maximum */ tin[0] = min[0]; lookup(lucntx, tout, tin); l10 = tout[0]; /* First chanel min, rest max */ if (fabs(l11 - l10) > fabs(l00 - l01)) p->pol = 1; /* Set other chanels to max */ else p->pol = 0; /* Set other chanels to min */ } /* For each chanel, create an rspl */ for (ee = 0; ee < p->di; ee++) { double tin[MXDI], tout[MXDO]; int gres = 100; // 4096 #ifdef DEBUG #define XRES 100 double xx[XRES]; double y1[XRES]; #endif /* DEBUG */ if ((p->curves[ee] = new_rspl(RSPL_NOFLAGS, 1, 1)) == NULL) { error("Creation of rspl failed in xdevlin"); } p->setch = ee; /* Figure the L min and max */ for (e = 0; e < p->di; e++) tin[e] = p->pol ? max[e] : min[e]; tin[ee] = min[ee]; lookup(lucntx, tout, tin); p->lmin = tout[0]; tin[ee] = max[ee]; lookup(lucntx, tout, tin); p->lmax = tout[0]; p->curves[ee]->set_rspl( p->curves[ee], 0, p, /* Opaque function context */ set_curve, /* Function to set from */ min, max, /* Grid low scale, grid high scale */ &gres, /* Grid resolution */ min, max /* Data value normalsie low and high */ ); #ifdef DEBUG { int i; /* Display the result curve */ for (i = 0; i < XRES; i++) { double x; co c; x = i/(double)(XRES-1); xx[i] = x; c.p[0] = x; p->curves[ee]->interp(p->curves[ee], &c); y1[i] = c.v[0]; } do_plot(xx,y1,NULL,NULL,XRES); } #endif /* DEBUG */ } p->lookup = NULL; /* Not valid after function return */ return p; }