summaryrefslogtreecommitdiff
path: root/xicc/xdevlin.c
diff options
context:
space:
mode:
Diffstat (limited to 'xicc/xdevlin.c')
-rw-r--r--xicc/xdevlin.c299
1 files changed, 299 insertions, 0 deletions
diff --git a/xicc/xdevlin.c b/xicc/xdevlin.c
new file mode 100644
index 0000000..a5f7bd6
--- /dev/null
+++ b/xicc/xdevlin.c
@@ -0,0 +1,299 @@
+
+/*
+ * 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 <sys/types.h>
+#ifdef __sun
+#include <unistd.h>
+#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;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+