summaryrefslogtreecommitdiff
path: root/icc/lutest.c
diff options
context:
space:
mode:
Diffstat (limited to 'icc/lutest.c')
-rw-r--r--icc/lutest.c3506
1 files changed, 3506 insertions, 0 deletions
diff --git a/icc/lutest.c b/icc/lutest.c
new file mode 100644
index 0000000..e43fbc5
--- /dev/null
+++ b/icc/lutest.c
@@ -0,0 +1,3506 @@
+
+/*
+ * International Color Consortium Format Library (icclib)
+ * Lookup test code, and profile creation examples.
+ *
+ * Author: Graeme W. Gill
+ * Date: 2000/6/18
+ * Version: 2.15
+ *
+ * Copyright 1998 - 2012 Graeme W. Gill
+ *
+ * This material is licensed with an "MIT" free use license:-
+ * see the License.txt file in this directory for licensing details.
+ */
+
+/* TTBD:
+ *
+ */
+
+/*
+
+ This file is intended to serve two purposes. One is to do some
+ basic regression testing of the lookup function of the icc
+ library, by creating profiles with known mapping characteristics,
+ and then checking that lookup displays those characteristics.
+ Given the huge possible number of combinations of color spaces,
+ profile variations, number of input and output channels, intents etc.
+ the tests done here are far from exaustive.
+
+ The other purpose is as a very basic source code example of how
+ to create various styles of ICC profiles from mapping information,
+ since extracting this information from the source code can take some
+ doing. The example code here is still not perfect, since
+ it doesn't cover the finer points of how to handle various intents,
+ gamut compression etc. Such things are really the domain of a
+ CMS, and would be dependent on the exact nature of the
+ profile representation that the ICC file is being created from.
+ (See examples in the Argyll CMS for these sorts of details.)
+
+ (Note XYZ scaling to 1.0, not 100.0, as per ICC)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#if defined(__IBMC__) && defined(_M_IX86)
+#include <float.h>
+#endif
+#include "icc.h"
+
+void error(char *fmt, ...), warning(char *fmt, ...);
+
+/* Some debuging aids */
+#define STOPONERROR /* stop if errors are excessive */
+#undef TESTLIN1 /* test linear in curves */
+#undef TESTLIN2 /* test linear clut (fails with Lab) */
+#undef TESTLIN3 /* test linear out curves */
+
+/* These two assist the accuracy of our BToA Lut tests, using our simplistic test functions */
+/* They probably shouldn't be used on any real profile. */
+#define REVLUTSCALE1 /* Define this for pre-clut gamut bounding box scaling */
+#define REVLUTSCALE2 /* Define this for post-clut gamut quantization scaling */
+
+/*
+ * We start with a mathematically defined transfer characteristic, that
+ * we create profiles from, and then check that the lookup function
+ * matches the mathematical characteristic we started with.
+ *
+ * Use a matrix style 3D to 3D XYZ transfer as the core, to be
+ * compatible with a matrix and Lut style profile.
+ */
+
+/* - - - - - - - - - - - - - */
+/* Some support functions */
+
+/* Clip value to range 0.0 to 1.0 */
+void clip(double inout[3]) {
+ int i;
+ for (i = 0; i < 3; i++) {
+ if (inout[i] < 0.0)
+ inout[i] = 0.0;
+ else if (inout[i] > 1.0)
+ inout[i] = 1.0;
+ }
+}
+
+/* Protected power function */
+double ppow(double num, double p) {
+ if (num < 0.0)
+ return -pow(-num, p);
+ else
+ return pow(num, p);
+}
+
+#define D50_X 0.9642
+#define D50_Y 1.0000
+#define D50_Z 0.8249
+
+#define D50_BX ( 0.8951 * D50_X + 0.2664 * D50_Y + -0.1614 * D50_Z)
+#define D50_BY (-0.7502 * D50_X + 1.7135 * D50_Y + 0.0367 * D50_Z)
+#define D50_BZ ( 0.0389 * D50_X + -0.0685 * D50_Y + 1.0296 * D50_Z)
+
+#define ABS_X 0.83
+#define ABS_Y 0.95
+#define ABS_Z 1.05
+
+#define ABS_BX ( 0.8951 * ABS_X + 0.2664 * ABS_Y + -0.1614 * ABS_Z)
+#define ABS_BY (-0.7502 * ABS_X + 1.7135 * ABS_Y + 0.0367 * ABS_Z)
+#define ABS_BZ ( 0.0389 * ABS_X + -0.0685 * ABS_Y + 1.0296 * ABS_Z)
+
+/* Convert from normalized to relative XYZ */
+static void to_rel(double out[3], double in[3]) {
+ /* Scale to relative white and black points */
+ out[0] = D50_X * in[0];
+ out[1] = D50_Y * in[1];
+ out[2] = D50_Z * in[2];
+}
+
+/* Convert from relative to normalized XYZ */
+static void from_rel(double out[3], double in[3]) {
+ /* Remove white and black points scale */
+ out[0] = in[0]/D50_X;
+ out[1] = in[1]/D50_Y;
+ out[2] = in[2]/D50_Z;
+}
+
+/* Convert from relative to absolute XYZ */
+static void rel_to_abs(double out[3], double in[3]) {
+ double tt[3];
+
+ /* Multiply by bradford */
+ tt[0] = 0.8951 * in[0] + 0.2664 * in[1] + -0.1614 * in[2];
+ tt[1] = -0.7502 * in[0] + 1.7135 * in[1] + 0.0367 * in[2];
+ tt[2] = 0.0389 * in[0] + -0.0685 * in[1] + 1.0296 * in[2];
+
+ /* Scale from D50 to absolute white point */
+ tt[0] = tt[0] * ABS_BX/D50_BX;
+ tt[1] = tt[1] * ABS_BY/D50_BY;
+ tt[2] = tt[2] * ABS_BZ/D50_BZ;
+
+ /* Inverse bradford */
+ out[0] = 0.986993 * tt[0] + -0.147054 * tt[1] + 0.159963 * tt[2];
+ out[1] = 0.432305 * tt[0] + 0.518360 * tt[1] + 0.049291 * tt[2];
+ out[2] = -0.008529 * tt[0] + 0.040043 * tt[1] + 0.968487 * tt[2];
+}
+
+/* Convert from normalized to absolute XYZ */
+static void to_abs(double out[3], double in[3]) {
+
+ to_rel(out, in); /* Convert to relative */
+ rel_to_abs(out, out); /* Convert to absolute */
+
+}
+
+/* Convert from absolute to relative XYZ */
+static void abs_to_rel(double out[3], double in[3]) {
+ double tt[3];
+
+ /* Multiply by bradford */
+ tt[0] = 0.8951 * in[0] + 0.2664 * in[1] + -0.1614 * in[2];
+ tt[1] = -0.7502 * in[0] + 1.7135 * in[1] + 0.0367 * in[2];
+ tt[2] = 0.0389 * in[0] + -0.0685 * in[1] + 1.0296 * in[2];
+
+ /* Scale from absolute white point to D50 */
+ tt[0] = tt[0] * D50_BX/ABS_BX;
+ tt[1] = tt[1] * D50_BY/ABS_BY;
+ tt[2] = tt[2] * D50_BZ/ABS_BZ;
+
+ /* Inverse bradford */
+ out[0] = 0.986993 * tt[0] + -0.147054 * tt[1] + 0.159963 * tt[2];
+ out[1] = 0.432305 * tt[0] + 0.518360 * tt[1] + 0.049291 * tt[2];
+ out[2] = -0.008529 * tt[0] + 0.040043 * tt[1] + 0.968487 * tt[2];
+}
+
+#ifdef NEVER /* Not currently used */
+/* Convert from absolute to normalized XYZ */
+static void from_abs(double out[3], double in[3]) {
+
+ abs_to_rel(out, in); /* Convert to relative */
+ from_rel(out, out); /* Convert to normalised */
+}
+#endif /* NEVER */
+
+/* CIE XYZ to perceptual Lab with ICC D50 white point */
+static void
+XYZ2Lab(double *out, double *in) {
+ double X = in[0], Y = in[1], Z = in[2];
+ double x,y,z,fx,fy,fz;
+ double L;
+
+ x = X/D50_X;
+ if (x > 0.008856451586)
+ fx = pow(x,1.0/3.0);
+ else
+ fx = 7.787036979 * x + 16.0/116.0;
+
+ y = Y/D50_Y;
+ if (y > 0.008856451586) {
+ fy = pow(y,1.0/3.0);
+ L = 116.0 * fy - 16.0;
+ } else {
+ fy = 7.787036979 * y + 16.0/116.0;
+ L = 903.2963058 * y;
+ }
+
+ z = Z/D50_Z;
+ if (z > 0.008856451586)
+ fz = pow(z,1.0/3.0);
+ else
+ fz = 7.787036979 * z + 16.0/116.0;
+
+ out[0] = L;
+ out[1] = 500.0 * (fx - fy);
+ out[2] = 200.0 * (fy - fz);
+}
+
+/* Perceptual Lab with ICC D50 white point to CIE XYZ */
+static void
+Lab2XYZ(double *out, double *in) {
+ double L = in[0], a = in[1], b = in[2];
+ double x,y,z,fx,fy,fz;
+
+ if (L > 8.0) {
+ fy = (L + 16.0)/116.0;
+ y = pow(fy,3.0);
+ } else {
+ y = L/903.2963058;
+ fy = 7.787036979 * y + 16.0/116.0;
+ }
+
+ fx = a/500.0 + fy;
+ if (fx > 24.0/116.0)
+ x = pow(fx,3.0);
+ else
+ x = (fx - 16.0/116.0)/7.787036979;
+
+ fz = fy - b/200.0;
+ if (fz > 24.0/116.0)
+ z = pow(fz,3.0);
+ else
+ z = (fz - 16.0/116.0)/7.787036979;
+
+ out[0] = x * D50_X;
+ out[1] = y * D50_Y;
+ out[2] = z * D50_Z;
+}
+
+/* - - - - - - - - - - - - - */
+
+/* Return maximum difference */
+static double maxdiff(double in1[3], double in2[3]) {
+ double tt, rv = 0.0;
+ if ((tt = fabs(in1[0] - in2[0])) > rv)
+ rv = tt;
+ if ((tt = fabs(in1[1] - in2[1])) > rv)
+ rv = tt;
+ if ((tt = fabs(in1[2] - in2[2])) > rv)
+ rv = tt;
+ return rv;
+}
+
+/* Return absolute difference */
+static double absdiff(double in1[3], double in2[3]) {
+ double tt, rv = 0.0;
+ tt = in1[0] - in2[0];
+ rv += tt * tt;
+ tt = in1[1] - in2[1];
+ rv += tt * tt;
+ tt = in1[2] - in2[2];
+ rv += tt * tt;
+ return sqrt(rv);
+}
+
+/* - - - - - - - - - - - - - - - - - */
+/* Overall Monochrome XYZ device model is */
+/* Gray -> GrayY -> XYZ */
+/* Where GrayY is assumed to directly Scale Y */
+
+/* Gray -> GrayY */
+static double Gray_GrayY(double in) {
+#ifdef TESTLIN1
+ return in;
+#else
+ return ppow(in,1.6);
+#endif
+}
+
+/* GrayY -> Gray */
+static double GrayY_Gray(double in) {
+#ifdef TESTLIN1
+ return in;
+#else
+ return ppow(in,1.0/1.6);
+#endif
+}
+
+/* Gray -> XYZ */
+static void Gray_XYZ(double out[3], double in) {
+ double temp[3];
+ temp[0] = temp[1] = temp[2] = Gray_GrayY(in);
+
+ /* Scale to relative white and black points */
+ to_rel(out, temp);
+}
+
+/* XYZ -> Device gray space */
+static double XYZ_Gray(double in[3]) {
+ double temp[3];
+
+ /* Remove relative white and black points scale */
+ from_rel(temp, in);
+
+ /* Do calculation just from Y value */
+ return GrayY_Gray(temp[1]);
+}
+
+/* Gray -> XYZ absolute */
+static void aGray_XYZ(double out[3], double in) {
+ double temp[3];
+ temp[0] = temp[1] = temp[2] = Gray_GrayY(in);
+
+ /* Scale to absolute white and black points */
+ to_abs(out, temp);
+
+}
+
+/* XYZ -> Gray absolute */
+static double aXYZ_Gray(double in[3]) {
+ double tt, temp[3];
+
+ /* Remove absolute white and black points scale */
+ abs_to_rel(temp, in); /* Convert to relative */
+
+ from_rel(temp, temp); /* Convert to normalised */
+
+ /* Do calculation just from Y value */
+ tt = GrayY_Gray(temp[1]);
+
+ return tt;
+}
+
+/* - - - - - - - - - - - */
+/* Overall Monochrome Lab device model is */
+/* Gray -> GrayL -> Lab */
+/* Where GrayL is assumed to directly scale L */
+
+
+/* Gray -> GrayL */
+static double Gray_GrayL(double in) {
+#ifdef TESTLIN1
+ return in; /* normalized L */
+#else
+ return ppow(in,1.6);
+#endif
+}
+
+/* GrayL -> Gray */
+static double GrayL_Gray(double in) {
+#ifdef TESTLIN1
+ return in;
+#else
+ return ppow(in,1.0/1.6); /* Y */
+#endif
+}
+
+/* Gray -> Lab */
+static void Gray_Lab(double out[3], double in) {
+ double tt, wp[3];
+
+ wp[0] = D50_X;
+ wp[1] = D50_Y;
+ wp[2] = D50_Z;
+ XYZ2Lab(wp, wp); /* Lab white point */
+
+ tt = Gray_GrayL(in); /* Raw L value */
+
+ /* Scale to relative Lab white point */
+ out[0] = wp[0] * tt;
+ out[1] = wp[1] * tt;
+ out[2] = wp[2] * tt;
+}
+
+/* Lab -> Gray */
+static double Lab_Gray(double in[3]) {
+ double tt, wp[3];
+
+ wp[0] = D50_X;
+ wp[1] = D50_Y;
+ wp[2] = D50_Z;
+ XYZ2Lab(wp, wp); /* Lab white point */
+
+ /* Scale from relative Lab white point */
+ tt = in[0]/wp[0];
+
+ return GrayL_Gray(tt); /* Raw L value */
+
+}
+
+/* Gray -> Lab absolute */
+static void aGray_Lab(double out[3], double in) {
+
+ /* Generate relative Lab */
+ Gray_Lab(out, in);
+
+ Lab2XYZ(out, out);
+ rel_to_abs(out, out);
+ XYZ2Lab(out, out);
+}
+
+/* Lab -> Gray absolute */
+static double aLab_Gray(double in[3]) {
+ double tt[3];
+
+ Lab2XYZ(tt, in);
+ abs_to_rel(tt, tt);
+ XYZ2Lab(tt, tt);
+
+ return Lab_Gray(tt);
+}
+
+/* - - - - - - - - - - - - - - - - - */
+/* RGB -> XYZ test transfer curves */
+/* Note that overall model is: */
+/* RBG -> RGB' -> XYZ' -> XYZ */
+
+/* Device space linearization */
+/* RGB -> RGB' */
+static void RGB_RGBp(void *cntx, double out[3], double in[3]) {
+#ifdef TESTLIN1
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+#else
+ out[0] = ppow(in[0],1.6);
+ out[1] = ppow(in[1],1.5);
+ out[2] = ppow(in[2],1.4);
+#endif
+}
+
+/* RGB' -> RGB */
+static void RGBp_RGB(void *cntx, double out[3], double in[3]) {
+#ifdef TESTLIN1
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+#else
+ out[0] = ppow(in[0],1.0/1.6);
+ out[1] = ppow(in[1],1.0/1.5);
+ out[2] = ppow(in[2],1.0/1.4);
+#endif
+}
+
+#ifdef TESTLIN2
+static double matrix[3][3] = {
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+};
+#else
+static double matrix[3][3] = {
+ { 0.4361, 0.3851, 0.1431 },
+ { 0.2225, 0.7169, 0.0606 },
+ { 0.0139, 0.0971, 0.7141 },
+};
+#endif
+
+/* 3x3 matrix conversion */
+/* RGB' -> XYZ' */
+static void RGBp_XYZp(void *cntx, double out[3], double in[3]) {
+ double o0,o1,o2;
+
+#ifdef TESTLIN2
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+#else
+ o0 = matrix[0][0] * in[0] + matrix[0][1] * in[1] + matrix[0][2] * in[2];
+ o1 = matrix[1][0] * in[0] + matrix[1][1] * in[1] + matrix[1][2] * in[2];
+ o2 = matrix[2][0] * in[0] + matrix[2][1] * in[1] + matrix[2][2] * in[2];
+
+ out[0] = o0;
+ out[1] = o1;
+ out[2] = o2;
+#endif
+}
+
+/* XYZ' -> RGB' */
+static void XYZp_RGBp(void *cntx, double out[3], double in[3]) {
+ double o0,o1,o2;
+
+#ifdef TESTLIN2
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+#else
+ o0 = 3.13360257102309000 * in[0] + -1.61682140135654430 * in[1] + -0.49074240441282400 * in[2];
+ o1 = -0.97865031588250000 * in[0] + 1.91606100412532800 * in[1] + 0.03351290204844009 * in[2];
+ o2 = 0.07207655781398956 * in[0] + -0.22906554547222160 * in[1] + 1.40535949675456500 * in[2];
+
+ out[0] = o0;
+ out[1] = o1;
+ out[2] = o2;
+#endif
+}
+
+/* Output linearization curves */
+/* XYZ' -> XYZ */
+static void XYZp_XYZ(void *cntx, double out[3], double in[3]) {
+#ifdef TESTLIN3
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+#else
+ out[0] = ppow(in[0],0.9);
+ out[1] = ppow(in[1],0.8);
+ out[2] = ppow(in[2],1.1);
+#endif
+}
+
+/* XYZ -> XYZ' */
+static void XYZ_XYZp(void *cntx, double out[3], double in[3]) {
+#ifdef TESTLIN3
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+#else
+ out[0] = ppow(in[0],1.0/0.9);
+ out[1] = ppow(in[1],1.0/0.8);
+ out[2] = ppow(in[2],1.0/1.1);
+#endif
+}
+
+/* RGB -> XYZ' */
+static void RGB_XYZp(void *cntx, double out[3], double in[3]) {
+ RGB_RGBp(cntx, out, in);
+ RGBp_XYZp(cntx, out, out);
+}
+
+/* RGB -> XYZ', absolute (for matrix profile test) */
+static void aRGB_XYZp(void *cntx, double out[3], double in[3]) {
+ RGB_RGBp(cntx, out, in);
+ RGBp_XYZp(cntx, out, out);
+ from_rel(out, out);
+ to_abs(out, out);
+}
+
+/* RGB -> XYZ */
+static void RGB_XYZ(void *cntx, double out[3], double in[3]) {
+ RGB_RGBp(cntx, out, in);
+ RGBp_XYZp(cntx, out, out);
+ XYZp_XYZ(cntx, out, out);
+}
+
+/* XYZ -> RGB */
+static void XYZ_RGB(void *cntx, double out[3], double in[3]) {
+ XYZ_XYZp(cntx, out, in);
+ XYZp_RGBp(cntx, out, out);
+ RGBp_RGB(cntx, out, out);
+}
+
+/* RGB -> XYZ, absolute */
+static void aRGB_XYZ(void *cntx, double out[3], double in[3]) {
+ RGB_XYZ(cntx, out, in);
+ from_rel(out, out);
+ to_abs(out, out);
+}
+
+#ifdef NEVER /* Not currently used */
+
+/* XYZ -> RGB, absolute */
+static void aXYZ_RGB(void *cntx, double out[3], double in[3]) {
+ from_abs(out, in);
+ to_rel(out, out);
+ XYZ_RGB(cntx, out, out);
+}
+
+/* XYZ -> RGB, gamut constrained */
+static void cXYZ_RGB(void *cntx, double out[3], double in[3]) {
+ XYZ_RGB(cntx, out, in);
+ clip(out);
+}
+
+#endif /* NEVER */
+
+/* XYZ' -> distance to gamut boundary */
+static void XYZp_BDIST(void *cntx, double out[1], double in[3]) {
+ double gdst; /* Gamut error */
+ int m, mini = 0, outg;
+ double tt, mind;
+ double pcs[3]; /* PCS value of input */
+ double dev[3]; /* Device value */
+ double pgb[3]; /* PCS gamut boundary point */
+ double dgb[3]; /* device gamut boundary point */
+
+ /* Do XYZ' -> XYZ */
+ XYZp_XYZ(cntx, pcs, in);
+
+ /* Do XYZ -> RGB transform */
+ XYZ_RGB(NULL, dev, pcs);
+
+ /* Compute nearest point on gamut boundary, */
+ /* and whether it is in or out of gamut. */
+ /* This should be nearest in PCS space, but */
+ /* we'll cheat and find the nearest point in */
+ /* device space, and then compute the distance in PCS space. */
+
+ for (m = 0; m < 3; m++)
+ dgb[m] = dev[m];
+
+ for (mind = 10000.0, outg = 0, m = 0; m < 3; m++) {
+ if (dev[m] < 0.0) { /* Clip any coordinates outside device limits */
+ dgb[m] = 0.0;
+ outg = 1; /* Out of gamut */
+ } else if (dev[m] > 1.0) {
+ dgb[m] = 1.0;
+ outg = 1; /* Out of gamut */
+ } else { /* Note closest cood to boundary if within limits */
+ if (dev[m] < 0.5)
+ tt = 0.5 - dev[m];
+ else /* >= 0.5 */
+ tt = dev[m] - 0.5;
+ if (tt < mind) {
+ mind = tt;
+ mini = m;
+ }
+ }
+ }
+ if (!outg) { /* If point is within gamut, set to closest point */
+ if (dev[mini] < 0.5)
+ dgb[mini] = 0.0;
+ else
+ dgb[mini] = 1.0;
+ }
+
+ /* Do RGB -> XYZ transform on nearest gamut boundary point */
+ RGB_XYZ(NULL, pgb, dgb);
+
+ /* Distance to nearest gamut point in PCS (XYZ) space */
+ gdst = absdiff(pcs, pgb);
+ if (!outg) /* If within gamut */
+ gdst = -gdst;
+
+ /* Distance in PCS space will be roughly -0.866 -> 0.866 */
+ /* Convert so that 0.5 is on boundary, and then clip. */
+ gdst += 0.5;
+ if (gdst < 0.0)
+ gdst = 0.0;
+ else if (gdst > 1.0)
+ gdst = 1.0;
+
+ out[0] = gdst;
+}
+
+/* The output table is usually a special for the gamut table, returning */
+/* a value of 0 for all inputs <= 0.5, and then outputing between */
+/* 0.0 and 1.0 for the input range 0.5 to 1.0. This is so a graduated */
+/* "gamut boundary distance" number from the multi-d lut can be */
+/* translated into the ICC "0.0 if in gamut, > 0.0 if not" number. */
+static void BDIST_GAMMUT(void *cntx, double out[1], double in[1]) {
+ double iv, ov;
+ iv = in[0];
+ if (iv <= 0.5)
+ ov = 0.0;
+ else
+ ov = (iv - 0.5) * 2.0;
+ out[0] = ov;
+}
+
+/* - - - - - - - - - - - - - */
+/* Lab versions for Lut profile, built on top of XYZ model */
+/* The overall model is: */
+/* RBG -> RGB' -> Lab' -> Lab */
+
+/* 3x3 matrix conversion */
+/* RGB' -> Lab' */
+static void RGBp_Labp(void *cntx, double out[3], double in[3]) {
+ RGBp_XYZp(cntx, out, in);
+ XYZ2Lab(out, out);
+}
+
+/* Lab' -> RGB' */
+static void Labp_RGBp(void *cntx, double out[3], double in[3]) {
+ Lab2XYZ(out, in);
+ XYZp_RGBp(cntx, out, out);
+}
+
+/* Lab' -> Lab */
+/* (We are using linear) */
+static void Labp_Lab(void *cntx, double out[3], double in[3]) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+}
+
+/* Lab -> Lab' */
+static void Lab_Labp(void *cntx, double out[3], double in[3]) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+}
+
+/* RGB -> Lab */
+static void RGB_Lab(void *cntx, double out[3], double in[3]) {
+ RGB_RGBp(cntx, out, in);
+ RGBp_Labp(cntx, out, out);
+ Labp_Lab(cntx, out, out);
+}
+
+/* Lab -> RGB */
+static void Lab_RGB(void *cntx, double out[3], double in[3]) {
+ Lab_Labp(cntx, out, in);
+ Labp_RGBp(cntx, out, out);
+ RGBp_RGB(cntx, out, out);
+}
+
+/* RGB -> Lab, absolute */
+static void aRGB_Lab(void *cntx, double out[3], double in[3]) {
+ RGB_Lab(cntx, out, in);
+ Lab2XYZ(out, out);
+ from_rel(out, out);
+ to_abs(out, out);
+ XYZ2Lab(out, out);
+}
+
+#ifdef NEVER /* Not currently used */
+
+/* Lab -> RGB, absolute */
+static void aLab_RGB(void *cntx, double out[3], double in[3]) {
+ Lab2XYZ(out, in);
+ from_abs(out, out);
+ to_rel(out, out);
+ XYZ2Lab(out, out);
+ Lab_RGB(cntx, out, out);
+}
+
+/* Lab -> RGB, gamut constrained */
+static void cLab_RGB(void *cntx, double out[3], double in[3]) {
+ Lab_RGB(cntx, out, in);
+ clip(out);
+}
+
+#endif /* NEVER */
+
+/* Lab' -> distance to gamut boundary */
+static void Labp_BDIST(void *cntx, double out[1], double in[3]) {
+ double gdst; /* Gamut error */
+ int m, mini = 0, outg;
+ double tt, mind;
+ double pcs[3]; /* PCS value of input */
+ double dev[3]; /* Device value */
+ double pgb[3]; /* PCS gamut boundary point */
+ double dgb[3]; /* device gamut boundary point */
+
+ /* Do Lab' -> Lab */
+ Labp_Lab(cntx, pcs, in);
+
+ /* Do Lab -> RGB transform */
+ Lab_RGB(cntx, dev, pcs);
+
+ /* Compute nearest point on gamut boundary, */
+ /* and whether it is in or out of gamut. */
+ /* This should be nearest in PCS space, but */
+ /* we'll cheat and find the nearest point in */
+ /* device space, and then compute the distance in PCS space. */
+
+ for (m = 0; m < 3; m++)
+ dgb[m] = dev[m];
+
+ for (mind = 10000.0, outg = 0, m = 0; m < 3; m++) {
+ if (dev[m] < 0.0) { /* Clip any coordinates outside device limits */
+ dgb[m] = 0.0;
+ outg = 1; /* Out of gamut */
+ } else if (dev[m] > 1.0) {
+ dgb[m] = 1.0;
+ outg = 1; /* Out of gamut */
+ } else { /* Note closest cood to boundary if within limits */
+ if (dev[m] < 0.5)
+ tt = 0.5 - dev[m];
+ else /* >= 0.5 */
+ tt = dev[m] - 0.5;
+ if (tt < mind) {
+ mind = tt;
+ mini = m;
+ }
+ }
+ }
+ if (!outg) { /* If point is within gamut, set to closest point */
+ if (dev[mini] < 0.5)
+ dgb[mini] = 0.0;
+ else
+ dgb[mini] = 1.0;
+ }
+
+ /* Do RGB -> Lab transform on nearest gamut boundary point */
+ RGB_Lab(NULL, pgb, dgb);
+
+ /* Distance to nearest gamut point in PCS (Lab) space */
+ gdst = absdiff(pcs, pgb);
+ if (!outg) /* If within gamut */
+ gdst = -gdst;
+
+ /* Distance in PCS space will be roughly -86 -> 86 */
+ /* Convert so that 0.5 is on boundary, and then clip. */
+ gdst /= 100.0;
+ gdst += 0.5;
+ if (gdst < 0.0)
+ gdst = 0.0;
+ else if (gdst > 1.0)
+ gdst = 1.0;
+
+ out[0] = gdst;
+}
+
+/* - - - - - - - - - - - - - */
+
+#define TRES 10
+#define MON_POINTS 8101 /* Number of test points in monochrome tests */
+
+int
+main(
+ int argc,
+ char *argv[]
+) {
+ char *file_name;
+ icmFile *wr_fp, *rd_fp;
+ icc *wr_icco, *rd_icco; /* Keep object separate */
+ int rv = 0;
+
+ /* Check variables */
+ int co[3];
+ double in[3], out[3], check[3];
+
+ {
+
+#if defined(__IBMC__) && defined(_M_IX86)
+ _control87(EM_UNDERFLOW, EM_UNDERFLOW);
+#endif
+ printf("Starting lookup function test - V%s\n",ICCLIB_VERSION_STR);
+
+ /* Do a check that our reference function is reversable */
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do device -> XYZ transform */
+ RGB_XYZ(NULL, out, in);
+
+ /* Do XYZ -> Device transform */
+ XYZ_RGB(NULL, check, out);
+
+ /* Check the result */
+ mxd = maxdiff(in, check);
+ if (mxd > 0.00001)
+#ifdef STOPONERROR
+ error ("Excessive error %f > 0.00001",mxd);
+#else
+ warning ("Excessive error %f > 0.00001",mxd);
+#endif /* STOPONERROR */
+ }
+ }
+ }
+ printf("Self check complete\n");
+ }
+
+ /* ---------------------------------------- */
+ /* Create a monochrome XYZ profile to test */
+ /* ---------------------------------------- */
+
+ /* Open up the file for writing */
+ file_name = "xxxx_mono_XYZ.icm";
+ if ((wr_fp = new_icmFileStd_name(file_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",file_name);
+
+ if ((wr_icco = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags required */
+
+ /* The header: */
+ {
+ icmHeader *wh = wr_icco->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigDisplayClass; /* Could use Output or Input too */
+ wh->colorSpace = icSigGrayData; /* It's a gray space */
+ wh->pcs = icSigXYZData; /* Test XYZ monochrome profile */
+ wh->renderingIntent = icRelativeColorimetric;
+
+ /* Values that should be set before writing */
+ wh->manufacturer = str2tag("tst2");
+ wh->model = str2tag("test");
+ }
+ /* Profile Description Tag: */
+ {
+ icmTextDescription *wo;
+ char *dst = "This is a test monochrome XYZ style Display Profile";
+ if ((wo = (icmTextDescription *)wr_icco->add_tag(
+ wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Copyright Tag: */
+ {
+ icmText *wo;
+ char *crt = "Copyright 1998 Graeme Gill";
+ if ((wo = (icmText *)wr_icco->add_tag(
+ wr_icco, icSigCopyrightTag, icSigTextType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, crt); /* Copy the text in */
+ }
+ /* White Point Tag: */
+ {
+ icmXYZArray *wo;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = ABS_X; /* Set some silly numbers */
+ wo->data[0].Y = ABS_Y;
+ wo->data[0].Z = ABS_Z;
+ }
+ /* Black Point Tag: */
+ {
+ icmXYZArray *wo;
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = 0.02; /* Doesn't take part in Absolute anymore */
+ wo->data[0].Y = 0.04;
+ wo->data[0].Z = 0.03;
+ }
+ /* Gray Tone Reproduction Curve Tags: */
+ {
+ icmCurve *wog;
+ unsigned int i;
+ if ((wog = (icmCurve *)wr_icco->add_tag(
+ wr_icco, icSigGrayTRCTag, icSigCurveType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wog->flag = icmCurveSpec; /* Specified version */
+ wog->size = 256; /* Number of entries (min must be 2!) */
+ wog->allocate((icmBase *)wog); /* Allocate space */
+ for (i = 0; i < wog->size; i++) {
+ double vv;
+ vv = i/(wog->size-1.0);
+ wog->data[i] = Gray_GrayY(vv);
+ }
+ }
+
+ /* Write the file out */
+ if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,wr_icco->err);
+
+ wr_icco->del(wr_icco);
+ wr_fp->del(wr_fp);
+
+ /* - - - - - - - - - - - - - - */
+ /* Deal with reading and verifying the monochrome XYZ profile */
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(file_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",file_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Check the lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = co[0]/(MON_POINTS-1.0);
+
+ /* Do reference conversion of device -> XYZ transform */
+ Gray_XYZ(check,in[0]);
+
+ /* Do lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.00005)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome XYZ Fwd %f > 0.00005",mxd);
+#else
+ warning ("Excessive error in Monochrome XYZ Fwd %f > 0.00005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome XYZ fwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse lookup function */
+ {
+ double min[3], range[3];
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Establish the range */
+ Gray_XYZ(min,0.0);
+ Gray_XYZ(range,1.0);
+ range[0] -= min[0];
+ range[1] -= min[1];
+ range[2] -= min[2];
+
+ /* Get a bwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = range[0] * co[0]/(MON_POINTS-1.0) + min[0];
+ in[1] = range[1] * co[0]/(MON_POINTS-1.0) + min[1];
+ in[2] = range[2] * co[0]/(MON_POINTS-1.0) + min[2];
+
+ /* Do reference conversion of XYZ -> device transform */
+ check[0] = XYZ_Gray(in);
+
+ /* Do reverse lookup of XYZ -> device transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = fabs(check[0] - out[0]);
+ if (mxd > 0.00018)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome XYZ Bwd %f > 0.00018",mxd);
+#else
+ warning ("Excessive error in Monochrome XYZ Bwd %f > 0.00018",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome XYZ bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the lookup function, absolute colorimetric */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = co[0]/(MON_POINTS-1.0);
+
+ /* Do reference conversion of device -> XYZ transform */
+ aGray_XYZ(check,in[0]);
+
+ /* Do lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.00005)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome XYZ Abs Fwd %f > 0.00005",mxd);
+#else
+ warning ("Excessive error in Monochrome XYZ Abs Fwd %f > 0.00005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome XYZ fwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse lookup function, absolute colorimetric */
+ {
+ double min[3], range[3];
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Establish the range */
+ /* Establish the range */
+ aGray_XYZ(min,0.0);
+ aGray_XYZ(range,1.0);
+ range[0] -= min[0];
+ range[1] -= min[1];
+ range[2] -= min[2];
+
+ /* Get a bwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = range[0] * co[0]/(MON_POINTS-1.0) + min[0];
+ in[1] = range[1] * co[0]/(MON_POINTS-1.0) + min[1];
+ in[2] = range[2] * co[0]/(MON_POINTS-1.0) + min[2];
+
+ /* Do reference conversion of XYZ -> device transform */
+ check[0] = aXYZ_Gray(in);
+
+ /* Do reverse lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = fabs(check[0] - out[0]);
+ if (mxd > 0.0002)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome XYZ Abs Bwd %f > 0.0002",mxd);
+#else
+ warning ("Excessive error in Monochrome XYZ Abs Bwd %f > 0.0002",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome XYZ bwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ /* ---------------------------------------- */
+ /* Create a monochrome Lab profile to test */
+ /* ---------------------------------------- */
+
+ /* Open up the file for writing */
+ file_name = "xxxx_mono_Lab.icm";
+ if ((wr_fp = new_icmFileStd_name(file_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",file_name);
+
+ if ((wr_icco = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags required */
+
+ /* The header: */
+ {
+ icmHeader *wh = wr_icco->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigDisplayClass; /* Could use Output or Input too */
+ wh->colorSpace = icSigGrayData; /* It's a gray space */
+ wh->pcs = icSigLabData; /* Use Lab for this monochrome profile */
+ wh->renderingIntent = icRelativeColorimetric;
+
+ /* Values that should be set before writing */
+ wh->manufacturer = str2tag("tst2");
+ wh->model = str2tag("test");
+ }
+ /* Profile Description Tag: */
+ {
+ icmTextDescription *wo;
+ char *dst = "This is a test monochrome Lab style Display Profile";
+ if ((wo = (icmTextDescription *)wr_icco->add_tag(
+ wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Copyright Tag: */
+ {
+ icmText *wo;
+ char *crt = "Copyright 1998 Graeme Gill";
+ if ((wo = (icmText *)wr_icco->add_tag(
+ wr_icco, icSigCopyrightTag, icSigTextType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, crt); /* Copy the text in */
+ }
+ /* White Point Tag: */
+ {
+ icmXYZArray *wo;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = ABS_X; /* Set some silly numbers */
+ wo->data[0].Y = ABS_Y;
+ wo->data[0].Z = ABS_Z;
+ }
+ /* Black Point Tag: */
+ {
+ icmXYZArray *wo;
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = 0.02; /* Doesn't take part in Absolute anymore */
+ wo->data[0].Y = 0.04;
+ wo->data[0].Z = 0.03;
+ }
+ /* Gray Tone Reproduction Curve Tags: */
+ {
+ icmCurve *wog;
+ unsigned int i;
+ if ((wog = (icmCurve *)wr_icco->add_tag(
+ wr_icco, icSigGrayTRCTag, icSigCurveType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wog->flag = icmCurveSpec; /* Specified version */
+ wog->size = 256; /* Number of entries (min must be 2!) */
+ wog->allocate((icmBase *)wog); /* Allocate space */
+ for (i = 0; i < wog->size; i++) {
+ double vv;
+ vv = i/(wog->size-1.0);
+ wog->data[i] = Gray_GrayL(vv);
+ }
+ }
+
+ /* Write the file out */
+ if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,wr_icco->err);
+
+ wr_icco->del(wr_icco);
+ wr_fp->del(wr_fp);
+
+ /* - - - - - - - - - - - - - - */
+ /* Deal with reading and verifying the monochrome Lab profile */
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(file_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",file_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Check the lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = co[0]/(MON_POINTS-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ Gray_Lab(check,in[0]);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.0025)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome Lab Fwd %f > 0.0025",mxd);
+#else
+ warning ("Excessive error in Monochrome Lab Fwd %f > 0.0025",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome Lab fwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse lookup function */
+ {
+ double min, range;
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Establish the range */
+ Gray_Lab(out,0.0);
+ min = out[0];
+ Gray_Lab(out,1.0);
+ range = out[0] - min;
+
+ /* Get a bwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = range * co[0]/(MON_POINTS-1.0) + min;
+
+ /* Do reference conversion of Lab -> device transform */
+ check[0] = Lab_Gray(in);
+
+ /* Do reverse lookup of Lab -> device transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = fabs(check[0] - out[0]);
+ if (mxd > 0.0002)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome Lab Bwd %f > 0.0002",mxd);
+#else
+ warning ("Excessive error in Monochrome Lab Bwd %f > 0.0002",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome Lab bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+#ifdef NEVER
+ /* Check the fwd/bwd accuracy */
+ {
+ double merr = 0.0;
+ icmLuBase *luof, *luob;
+
+ /* Get a fwd conversion object */
+ if ((luof = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ /* Get a bwd conversion object */
+ if ((luob = rd_icco->get_luobj(rd_icco, icmBwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ /* Check it out */
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = co[0]/(MON_POINTS-1.0);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luof->lookup(luof, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Do reverse lookup of device -> Lab transform */
+ if ((rv = luob->lookup(luob, check, out)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ mxd = fabs(in[0] - check[0]);
+ if (mxd > 1e-6) {
+ printf("co %d, in %f, out %f, check %f, err %f\n",
+ co[0],in[0],out[0],check[0], fabs(in[0] - check[0]));
+ }
+
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome Lab fwd/bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup objects */
+ luof->del(luof);
+ luob->del(luob);
+ }
+
+ /* Benchmark the routines */
+ {
+ int ii;
+ icmLuBase *luo;
+ double no_pixels = 0.0;
+ clock_t stime,ttime;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ stime = clock();
+ no_pixels = 1000.0 * 2048.0;
+
+ for (ii = 0; ii < 1000; ii++) {
+ for (co[0] = 0; co[0] < 2048; co[0]++) {
+ double mxd;
+ in[0] = co[0]/(2048-1.0);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+ }
+ }
+ ttime = clock() - stime;
+ printf("Done - %f seconds, rate = %f Mpix/sec\n",
+ (double)ttime/CLOCKS_PER_SEC,no_pixels * CLOCKS_PER_SEC/ttime);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ {
+ int ii;
+ icmLuBase *luo;
+ double no_pixels = 0.0;
+ clock_t stime,ttime;
+
+ /* Get a bwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ stime = clock();
+ no_pixels = 1000.0 * 2048.0;
+
+ for (ii = 0; ii < 1000; ii++) {
+ for (co[0] = 0; co[0] < 2048; co[0]++) {
+ double mxd;
+ in[0] = 100.0 * co[0]/(2048-1.0);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+ }
+ }
+ ttime = clock() - stime;
+ printf("Done - %f seconds, rate = %f Mpix/sec\n",
+ (double)ttime/CLOCKS_PER_SEC,no_pixels * CLOCKS_PER_SEC/ttime);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+#endif /* NEVER */
+
+ /* Check the lookup function, absolute colorimetric */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = co[0]/(MON_POINTS-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ aGray_Lab(check,in[0]);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.003)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome Lab Fwd Abs %f > 0.003",mxd);
+#else
+ warning ("Excessive error in Monochrome Lab Fwd Abs %f > 0.003",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome Lab fwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse lookup function, absolute colorimetric*/
+ {
+ double min, range;
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Establish the range */
+ aGray_Lab(out,0.0);
+ min = out[0];
+ aGray_Lab(out,1.0);
+ range = out[0] - min;
+
+ /* Get a bwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = range * co[0]/(MON_POINTS-1.0) + min;
+ in[1] = in[2] = 0.0;
+
+ /* Do reference conversion of Lab -> device transform */
+ check[0] = aLab_Gray(in);
+
+ /* Do reverse lookup of Lab -> device transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = fabs(check[0] - out[0]);
+ if (mxd > 0.005)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome Lab Bwd Abs %f > 0.005",mxd);
+#else
+ warning ("Excessive error in Monochrome Lab Bwd Abs %f > 0.005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome Lab bwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ /* ---------------------------------------- */
+ /* Create a matrix based profile to test */
+ /* ---------------------------------------- */
+
+ /* Open up the file for writing */
+ file_name = "xxxx_matrix.icm";
+ if ((wr_fp = new_icmFileStd_name(file_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",file_name);
+
+ if ((wr_icco = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags required */
+
+ /* The header: */
+ {
+ icmHeader *wh = wr_icco->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigDisplayClass; /* Could use Output or Input too */
+ wh->colorSpace = icSigRgbData; /* It's and RGBish space */
+ wh->pcs = icSigXYZData; /* Must be XYZ for matrix based profile */
+ wh->renderingIntent = icRelativeColorimetric;
+
+ /* Values that should be set before writing */
+ wh->manufacturer = str2tag("tst2");
+ wh->model = str2tag("test");
+ }
+ /* Profile Description Tag: */
+ {
+ icmTextDescription *wo;
+ char *dst = "This is a test matrix style Display Profile";
+ if ((wo = (icmTextDescription *)wr_icco->add_tag(
+ wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Copyright Tag: */
+ {
+ icmText *wo;
+ char *crt = "Copyright 1998 Graeme Gill";
+ if ((wo = (icmText *)wr_icco->add_tag(
+ wr_icco, icSigCopyrightTag, icSigTextType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, crt); /* Copy the text in */
+ }
+ /* White Point Tag: */
+ {
+ icmXYZArray *wo;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = ABS_X; /* Set some silly numbers */
+ wo->data[0].Y = ABS_Y;
+ wo->data[0].Z = ABS_Z;
+ }
+ /* Black Point Tag: */
+ {
+ icmXYZArray *wo;
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = 0.02; /* Doesn't take part in Absolute anymore */
+ wo->data[0].Y = 0.04;
+ wo->data[0].Z = 0.03;
+ }
+ /* Red, Green and Blue Colorant Tags: */
+ {
+ icmXYZArray *wor, *wog, *wob;
+ if ((wor = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigRedColorantTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+ if ((wog = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigGreenColorantTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+ if ((wob = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigBlueColorantTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wor->size = wog->size = wob->size = 1;
+ wor->allocate((icmBase *)wor); /* Allocate space */
+ wog->allocate((icmBase *)wog);
+ wob->allocate((icmBase *)wob);
+ wor->data[0].X = matrix[0][0]; wor->data[0].Y = matrix[1][0]; wor->data[0].Z = matrix[2][0];
+ wog->data[0].X = matrix[0][1]; wog->data[0].Y = matrix[1][1]; wog->data[0].Z = matrix[2][1];
+ wob->data[0].X = matrix[0][2]; wob->data[0].Y = matrix[1][2]; wob->data[0].Z = matrix[2][2];
+ }
+ /* Red, Green and Blue Tone Reproduction Curve Tags: */
+ {
+ icmCurve *wor, *wog, *wob;
+ unsigned int i;
+ if ((wor = (icmCurve *)wr_icco->add_tag(
+ wr_icco, icSigRedTRCTag, icSigCurveType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+ if ((wog = (icmCurve *)wr_icco->add_tag(
+ wr_icco, icSigGreenTRCTag, icSigCurveType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+ if ((wob = (icmCurve *)wr_icco->add_tag(
+ wr_icco, icSigBlueTRCTag, icSigCurveType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wor->flag = wog->flag = wob->flag = icmCurveSpec; /* Specified version */
+ wor->size = wog->size = wob->size = 256; /* Number of entries (min must be 2!) */
+ wor->allocate((icmBase *)wor); /* Allocate space */
+ wog->allocate((icmBase *)wog);
+ wob->allocate((icmBase *)wob);
+ for (i = 0; i < wor->size; i++) {
+ double vv[3];
+ vv[0] = vv[1] = vv[2] = i/(wor->size-1.0);
+ RGB_RGBp(NULL, vv, vv); /* Transfer function we want */
+ wor->data[i] = vv[0]; /* Curve values 0.0 - 1.0 */
+ wog->data[i] = vv[1];
+ wob->data[i] = vv[2];
+ }
+ }
+
+ /* Write the file out */
+ if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,wr_icco->err);
+
+ wr_icco->del(wr_icco);
+ wr_fp->del(wr_fp);
+
+ /* - - - - - - - - - - - - - - */
+ /* Deal with reading and verifying the Matrix based profile */
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(file_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",file_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Check the forward lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> XYZ transform */
+ RGB_XYZp(NULL, check,in);
+
+ /* Do lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.00005)
+#ifdef STOPONERROR
+ error ("Excessive error in Matrix Fwd %f > 0.00005",mxd);
+#else
+ warning ("Excessive error in Matrix Fwd %f > 0.00005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Matrix fwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> XYZ */
+ RGB_XYZp(NULL, check,in);
+
+ /* Do reverse lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.0002)
+#ifdef STOPONERROR
+ error ("Excessive error in Matrix Bwd %f > 0.0002",mxd);
+#else
+ warning ("Excessive error in Matrix Bwd %f > 0.0002",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Matrix bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the forward absolute lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> abs XYZ transform */
+ aRGB_XYZp(NULL, check,in);
+
+ /* Do lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.00005)
+#ifdef STOPONERROR
+ error ("Excessive error in Abs Matrix Fwd %f > 0.00005",mxd);
+#else
+ warning ("Excessive error in Abs Matrix Fwd %f > 0.00005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Matrix fwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse absolute lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> abs XYZ */
+ aRGB_XYZp(NULL, check,in);
+
+ /* Do reverse lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.001)
+#ifdef STOPONERROR
+ error ("Excessive error in Abs Matrix Bwd %f > 0.001",mxd);
+#else
+ warning ("Excessive error in Abs Matrix Bwd %f > 0.001",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Matrix bwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ /* ---------------------------------------- */
+ /* Create a Lut16 based XYZ profile to test */
+ /* ---------------------------------------- */
+
+ /* Open up the file for writing */
+ file_name = "xxxx_lut16_XYZ.icm";
+ if ((wr_fp = new_icmFileStd_name(file_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",file_name);
+
+ if ((wr_icco = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags required */
+
+ /* The header: */
+ {
+ icmHeader *wh = wr_icco->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigOutputClass;
+ wh->colorSpace = icSigRgbData; /* It's and RGBish space */
+ wh->pcs = icSigXYZData;
+ wh->renderingIntent = icRelativeColorimetric; /* For want of something */
+
+ /* Values that should be set before writing */
+ wh->manufacturer = str2tag("tst2");
+ wh->model = str2tag("test");
+ }
+ /* Profile Description Tag: */
+ {
+ icmTextDescription *wo;
+ char *dst = "This is a test Lut XYZ style Output Profile";
+ if ((wo = (icmTextDescription *)wr_icco->add_tag(
+ wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Copyright Tag: */
+ {
+ icmText *wo;
+ char *crt = "Copyright 1998 Graeme Gill";
+ if ((wo = (icmText *)wr_icco->add_tag(
+ wr_icco, icSigCopyrightTag, icSigTextType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, crt); /* Copy the text in */
+ }
+ /* White Point Tag: */
+ {
+ icmXYZArray *wo;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = ABS_X; /* Set some silly numbers */
+ wo->data[0].Y = ABS_Y;
+ wo->data[0].Z = ABS_Z;
+ }
+ /* Black Point Tag: */
+ {
+ icmXYZArray *wo;
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = 0.02; /* Doesn't take part in Absolute anymore */
+ wo->data[0].Y = 0.04;
+ wo->data[0].Z = 0.03;
+ }
+
+ /* 16 bit dev -> pcs lut: */
+ {
+ icmLut *wo;
+ double xyzmin[3] = {0.0, 0.0, 0.0};
+ double xyzmax[3] = {1.0, 1.0, 1.0}; /* Override default XYZ max of 1.999969482422 */
+
+ /* Intent 1 = relative colorimetric */
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigAToB1Tag, icSigLut16Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 3;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 4096;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* So we can't use it for this lut. */
+
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigRgbData, /* Input color space */
+ icSigXYZData, /* Output color space */
+ RGB_RGBp, /* Input transfer function, RGB->RGB' (NULL = default) */
+ NULL, NULL, /* Use default Maximum range of RGB' values */
+ RGBp_XYZp, /* RGB' -> XYZ' transfer function */
+ xyzmin, xyzmax, /* Make XYZ' range 0.0 - 1.0 for better precision */
+ XYZp_XYZ) != 0) /* Output transfer function, XYZ'->XYZ (NULL = deflt) */
+ error("Setting 16 bit RGB->XYZ Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 bit dev -> pcs lut - link intent 0 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 0 = perceptual */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigAToB0Tag, icSigAToB1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 dev -> pcs bit lut - link intent 2 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 2 = saturation */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigAToB2Tag, icSigAToB1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 bit pcs -> dev lut: */
+ {
+ icmLut *wo;
+ double xyzmin[3] = {0.0, 0.0, 0.0}; /* XYZ' range */
+ double xyzmax[3] = {1.0, 1.0, 1.0}; /* Override default XYZ max of 1.999969482422 */
+ double rgbmin[3] = {0.0, 0.0, 0.0}; /* RGB' range */
+ double rgbmax[3] = {1.0, 1.0, 1.0};
+
+ /* Intent 1 = relative colorimetric */
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigBToA1Tag, icSigLut16Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 3;
+ wo->clutPoints = 33;
+ wo->inputEnt = 1024; /* (power curves are hard to represent in tables at small values) */
+ wo->outputEnt = 4096;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* (so it could be used here) */
+ /* Matrix not tested:
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ wo->e[i][j] = ??;
+ */
+
+#ifdef REVLUTSCALE1
+ {
+ /* In any any real profile, you will probably be providing a clut */
+ /* function that carefully maps out of gamut PCS values to in-gamut */
+ /* device values, so the scaling done here won't be appropriate. */
+ /* */
+ /* For this regresion test, we are interested in maximizing accuracy */
+ /* over the known gamut of the device. It is an advantage therefore */
+ /* to scale the internal lut values to this end. */
+
+ /* We'll do a really simple sampling search of the device gamut to */
+ /* establish the XYZ' bounding box. */
+
+ int co[3];
+ double in[3], out[3];
+
+ xyzmin[0] = xyzmin[1] = xyzmin[2] = 1.0;
+ xyzmax[0] = xyzmax[1] = xyzmax[2] = 0.0;
+ for (co[0] = 0; co[0] < 11; co[0]++) {
+ in[0] = co[0]/(11-1.0);
+ for (co[1] = 0; co[1] < 11; co[1]++) {
+ in[1] = co[1]/(11-1.0);
+ for (co[2] = 0; co[2] < 11; co[2]++) {
+ in[2] = co[2]/(11-1.0);
+
+ /* Do RGB -> XYZ' transform */
+ RGB_XYZp(NULL, out,in);
+ if (out[0] < xyzmin[0])
+ xyzmin[0] = out[0];
+ if (out[0] > xyzmax[0])
+ xyzmax[0] = out[0];
+ if (out[1] < xyzmin[1])
+ xyzmin[1] = out[1];
+ if (out[1] > xyzmax[1])
+ xyzmax[1] = out[1];
+ if (out[2] < xyzmin[2])
+ xyzmin[2] = out[2];
+ if (out[2] > xyzmax[2])
+ xyzmax[2] = out[2];
+ }
+ }
+ }
+ }
+#endif
+
+#ifdef REVLUTSCALE2
+ {
+ /* In any any real profile, you will probably be providing a clut */
+ /* function that carefully maps out of gamut PCS values to in-gamut */
+ /* device values, so the scaling done here won't be appropriate. */
+ /* */
+ /* For this regresion test, we are interested in maximizing accuracy */
+ /* over the known gamut of the device. */
+ /* By setting the min/max to a larger range than will actually be */
+ /* used, we can make sure that the extreme table values of the */
+ /* clut are not actually used, and therefore we won't see the */
+ /* rounding effects of these extreme values being clipped */
+ /* by the numerical limits of the ICC representation. */
+ /* Instead the extreme values will be clipped by the the */
+ /* higher resolution output table. */
+ /* */
+ /* This all assumes that the multi-d reverse transform we are trying */
+ /* to represent in the profile extrapolates beyond the legal device */
+ /* value range. */
+ /* */
+ /* The scaling was chosen by experiment to make sure that the full */
+ /* gamut is surrounded by one row of extrapolated, unclipped clut */
+ /* table entries. */
+
+ int i;
+ for (i = 0; i < 3; i++) {
+ rgbmin[i] = -0.1667; /* Magic numbers */
+ rgbmax[i] = 1.1667;
+ }
+ }
+#endif
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigXYZData, /* Input color space */
+ icSigRgbData, /* Output color space */
+ XYZ_XYZp, /* Input transfer function, XYZ->XYZ' (NULL = default) */
+ xyzmin, xyzmax, /* Make XYZ' range 0.0 - 1.0 for better precision */
+ XYZp_RGBp, /* XYZ' -> RGB' transfer function */
+ rgbmin, rgbmax, /* Make RGB' range 0.0 - 1.333 for less clip rounding */
+ RGBp_RGB) != 0) /* Output transfer function, RGB'->RGB (NULL = deflt) */
+ error("Setting 16 bit XYZ->RGB Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ }
+ /* 16 bit pcs -> dev lut - link intent 0 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 0 = perceptual */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigBToA0Tag, icSigBToA1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 pcs -> dev bit lut - link intent 2 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 2 = saturation */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigBToA2Tag, icSigBToA1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+
+ /* 16 bit pcs -> gamut lut: */
+ {
+ icmLut *wo;
+ double xyzmin[3] = {0.0, 0.0, 0.0}; /* XYZ' range */
+ double xyzmax[3] = {1.0, 1.0, 1.0}; /* Override default XYZ max of 1.999969482422 */
+
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigGamutTag, icSigLut16Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 1;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 256;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* (so it could be used here) */
+ /* Matrix not tested:
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ wo->e[i][j] = ??;
+ */
+
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigXYZData, /* Input color space */
+ icSigGrayData, /* Output color space */
+ XYZ_XYZp, /* Input transfer function, XYZ->XYZ' (NULL = default) */
+ xyzmin, xyzmax, /* Make XYZ' range 0.0 - 1.0 for better precision */
+ XYZp_BDIST, /* XYZ' -> Boundary Distance transfer function */
+ NULL, NULL, /* Default range from clut to output table */
+ BDIST_GAMMUT /* Boundary Distance -> Out of gamut distance */
+ ) != 0)
+ error("Setting 16 bit XYZ->Gammut Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+
+ /* Write the file out */
+ if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,wr_icco->err);
+
+ wr_icco->del(wr_icco);
+ wr_fp->del(wr_fp);
+
+ /* - - - - - - - - - - - - - - */
+ /* Deal with reading and verifying the Lut XYZ style profile */
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(file_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",file_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Check the Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icRelativeColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> XYZ transform */
+ RGB_XYZ(NULL, check, in);
+
+ /* Do lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.00005)
+#ifdef STOPONERROR
+ error ("Excessive error in XYZ Lut Fwd %f > 0.00005",mxd);
+#else
+ warning ("Excessive error in XYZ Lut Fwd %f > 0.00005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut XYZ fwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+ int co[3];
+ double in[3], out[3], check[3];
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icRelativeColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of XYZ -> device transform */
+ RGB_XYZ(NULL, check, in);
+
+ /* Do reverse lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.002) {
+#ifdef STOPONERROR
+ error ("Excessive error in XYZ Lut Bwd %f > 0.002",mxd);
+#else
+ warning ("Excessive error in XYZ Lut Bwd %f > 0.002",mxd);
+#endif /* STOPONERROR */
+ }
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut XYZ bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Absolute Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> XYZ transform */
+ aRGB_XYZ(NULL, check,in);
+
+ /* Do lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.00005)
+#ifdef STOPONERROR
+ error ("Excessive error in XYZ Abs Lut Fwd %f > 0.00005",mxd);
+#else
+ warning ("Excessive error in XYZ Abs Lut Fwd %f > 0.00005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut XYZ fwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Absolute reverse Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of XYZ -> device transform */
+ aRGB_XYZ(NULL, check,in);
+
+ /* Do reverse lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.002)
+#ifdef STOPONERROR
+ error ("Excessive error in XYZ Abs Lut Bwd %f > 0.002",mxd);
+#else
+ warning ("Excessive error in XYZ Abs Lut Bwd %f > 0.002",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut XYZ bwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the XYZ gamut function */
+ {
+ int ino,ono,iok,ook;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmGamut, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ ino = ono = iok = ook = 0;
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ int outgamut;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do gamut lookup of XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Do reference conversion of XYZ -> RGB */
+ XYZ_RGB(NULL, check,in);
+
+ /* Check the result */
+ outgamut = 1; /* assume on edge */
+ if (check[0] < -0.01 || check[0] > 1.01
+ || check[1] < -0.01 || check[1] > 1.01
+ || check[2] < -0.01 || check[2] > 1.01)
+ outgamut = 2; /* Definitely out of gamut */
+ if (check[0] > 0.01 && check[0] < 0.99
+ && check[1] > 0.01 && check[1] < 0.99
+ && check[2] > 0.01 && check[2] < 0.99)
+ outgamut = 0; /* Definitely in gamut */
+
+ /* Keep record of agree/disagree */
+ if (outgamut <= 1) {
+ ino++;
+ if (out[0] <= 0.01)
+ iok++;
+ } else {
+ ono++;
+ if (out[0] > 0.01)
+ ook++;
+ }
+ }
+ }
+ }
+ printf("Lut XYZ gamut check inside correct = %f%%\n",100.0 * iok/ino);
+ printf("Lut XYZ gamut check outside correct = %f%%\n",100.0 * ook/ono);
+ printf("Lut XYZ gamut check total correct = %f%%\n",100.0 * (iok+ook)/(ino+ono));
+ if (((double)iok/ino) < 0.99 || ((double)ook/ono) < 0.98)
+#ifdef STOPONERROR
+ error ("Gamut XYZ lookup has excessive error");
+#else
+ warning ("Gamut XYZ lookup has excessive error");
+#endif /* STOPONERROR */
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ /* ---------------------------------------- */
+ /* Create a Lut16 based Lab profile to test */
+ /* ---------------------------------------- */
+
+ /* Open up the file for writing */
+ file_name = "xxxx_lut16_Lab.icm";
+ if ((wr_fp = new_icmFileStd_name(file_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",file_name);
+
+ if ((wr_icco = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags required */
+
+ /* The header: */
+ {
+ icmHeader *wh = wr_icco->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigOutputClass;
+ wh->colorSpace = icSigRgbData; /* It's and RGBish space */
+ wh->pcs = icSigLabData;
+ wh->renderingIntent = icRelativeColorimetric; /* For want of something */
+
+ /* Values that should be set before writing */
+ wh->manufacturer = str2tag("tst2");
+ wh->model = str2tag("test");
+ }
+ /* Profile Description Tag: */
+ {
+ icmTextDescription *wo;
+ char *dst = "This is a test Lut style Lab Output Profile";
+ if ((wo = (icmTextDescription *)wr_icco->add_tag(
+ wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Copyright Tag: */
+ {
+ icmText *wo;
+ char *crt = "Copyright 1998 Graeme Gill";
+ if ((wo = (icmText *)wr_icco->add_tag(
+ wr_icco, icSigCopyrightTag, icSigTextType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, crt); /* Copy the text in */
+ }
+ /* White Point Tag: */
+ {
+ icmXYZArray *wo;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = ABS_X; /* Set some silly numbers */
+ wo->data[0].Y = ABS_Y;
+ wo->data[0].Z = ABS_Z;
+ }
+ /* Black Point Tag: */
+ {
+ icmXYZArray *wo;
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = 0.02; /* Doesn't take part in Absolute anymore */
+ wo->data[0].Y = 0.04;
+ wo->data[0].Z = 0.03;
+ }
+ /* 16 bit dev -> pcs lut: */
+ {
+ icmLut *wo;
+
+ /* Intent 1 = relative colorimetric */
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigAToB1Tag, icSigLut16Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 3;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 256; /* I'm not going to use the output Lut */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* so it is not used here. */
+
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigRgbData, /* Input color space */
+ icSigLabData, /* Output color space */
+ RGB_RGBp, /* Input transfer function, RGB->RGB' (NULL = default) */
+ NULL, NULL, /* Use default Maximum range of RGB' values */
+ RGBp_Labp, /* RGB' -> Lab' transfer function */
+ NULL, NULL, /* Use default Maximum range of Lab' values */
+ Labp_Lab /* Linear output transform Lab'->Lab */
+ ) != 0)
+ error("Setting 16 bit RGB->Lab Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 bit dev -> pcs lut - link intent 0 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 0 = perceptual */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigAToB0Tag, icSigAToB1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 dev -> pcs bit lut - link intent 2 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 2 = saturation */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigAToB2Tag, icSigAToB1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 bit pcs -> dev lut: */
+ {
+ icmLut *wo;
+ double rgbmin[3] = {0.0, 0.0, 0.0}; /* RGB' range */
+ double rgbmax[3] = {1.0, 1.0, 1.0};
+
+ /* Intent 1 = relative colorimetric */
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigBToA1Tag, icSigLut16Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 3;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256; /* Not using this for Lab test */
+ wo->outputEnt = 4096;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* so it is not used here. */
+
+
+ /* REVLUTSCALE1 could be used here, but in this case it hardly */
+ /* makes any difference. */
+
+#ifdef REVLUTSCALE2
+ {
+ /* In any any real profile, you will probably be providing a clut */
+ /* function that carefully maps out of gamut PCS values to in-gamut */
+ /* device values, so the scaling done here won't be appropriate. */
+ /* */
+ /* For this regresion test, we are interested in maximizing accuracy */
+ /* over the known gamut of the device. */
+ /* By setting the min/max to a larger range than will actually be */
+ /* used, we can make sure that the extreme table values of the */
+ /* clut are not actually used, and therefore we won't see the */
+ /* rounding effects of these extreme values being clipped to */
+ /* by the numerical limits of the ICC representation. */
+ /* Instead the extreme values will be clipped by the the higher */
+ /* higher resolution output table. */
+ /* */
+ /* This all assumes that the multi-d reverse transform we are trying */
+ /* to represent in the profile extrapolates beyond the legal device */
+ /* value range. */
+ /* */
+ /* The scaling was chosen by experiment to make sure that the full */
+ /* gamut is surrounded by one row of extrapolated, unclipped clut */
+ /* table entries. */
+
+ int i;
+ for (i = 0; i < 3; i++) {
+ rgbmin[i] = -0.1667; /* Magic numbers */
+ rgbmax[i] = 1.1667;
+ }
+ }
+#endif
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigLabData, /* Input color space */
+ icSigRgbData, /* Output color space */
+ Lab_Labp, /* Linear input transform Lab->Lab' */
+ NULL, NULL, /* Use default Lab' range */
+ Labp_RGBp, /* Lab' -> RGB' transfer function */
+ rgbmin, rgbmax, /* Make RGB' range 0.0 - 1.333 for less clip rounding */
+ RGBp_RGB) != 0) /* Output transfer function, RGB'->RGB (NULL = deflt) */
+ error("Setting 16 bit Lab->RGB Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ }
+ /* 16 bit pcs -> dev lut - link intent 0 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 0 = perceptual */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigBToA0Tag, icSigBToA1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 pcs -> dev bit lut - link intent 2 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 2 = saturation */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigBToA2Tag, icSigBToA1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+
+ /* 16 bit pcs -> gamut lut: */
+ {
+ icmLut *wo;
+
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigGamutTag, icSigLut16Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 1;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 256;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* so it can't be used here. */
+
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigLabData, /* Input color space */
+ icSigGrayData, /* Output color space */
+ Lab_Labp, /* Linear input transform Lab->Lab' */
+ NULL, NULL , /* Default Lab' range */
+ Labp_BDIST, /* Lab' -> Boundary Distance transfer function */
+ NULL, NULL, /* Default range from clut to output table */
+ BDIST_GAMMUT /* Boundary Distance -> Out of gamut distance */
+ ) != 0)
+ error("Setting 16 bit Lab->Gammut Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+
+ /* Write the file out */
+ if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,wr_icco->err);
+
+ wr_icco->del(wr_icco);
+ wr_fp->del(wr_fp);
+
+ /* - - - - - - - - - - - - - - */
+ /* Deal with reading and verifying the Lut Lab 16bit style profile */
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(file_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",file_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Check the Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icRelativeColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ RGB_Lab(NULL, check,in);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 1.0)
+#ifdef STOPONERROR
+ error ("Excessive error in Lab16 Lut Fwd %f",mxd);
+#else
+ warning ("Excessive error in Lab16 Lut Fwd %f",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab16 fwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icRelativeColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab */
+ RGB_Lab(NULL, check,in);
+
+ /* Do reverse lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.02)
+#ifdef STOPONERROR
+ error ("Excessive error in Lab16 Lut Bwd %f > 0.02",mxd);
+#else
+ warning ("Excessive error in Lab16 Lut Bwd %f > 0.02",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab16 bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Absolute Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ aRGB_Lab(NULL, check,in);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 1.0)
+#ifdef STOPONERROR
+ error ("Excessive error in Abs Lab16 Lut Fwd %f > 1.0",mxd);
+#else
+ warning ("Excessive error in Abs Lab16 Lut Fwd %f > 1.0",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab16 fwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Absolute reverse Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ aRGB_Lab(NULL, check,in);
+
+ /* Do reverse lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.02)
+#ifdef STOPONERROR
+ error ("Excessive error in Abs Lab16 Lut Bwd %f > 0.02",mxd);
+#else
+ warning ("Excessive error in Abs Lab16 Lut Bwd %f > 0.02",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab16 bwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Lab gamut function */
+ {
+ int ino,ono,iok,ook;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmGamut, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ ino = ono = iok = ook = 0;
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = (co[0]/(TRES-1.0)) * 100.0; /* L */
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = ((co[1]/(TRES-1.0)) - 0.5) * 256.0; /* a */
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ int outgamut;
+ in[2] = ((co[2]/(TRES-1.0)) - 0.5) * 256.0; /* b */
+
+ /* Do gamut lookup of Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Do reference conversion of Lab -> RGB */
+ Lab_RGB(NULL, check,in);
+
+ /* Check the result */
+ outgamut = 1; /* assume on edge */
+ if (check[0] < -0.01 || check[0] > 1.01
+ || check[1] < -0.01 || check[1] > 1.01
+ || check[2] < -0.01 || check[2] > 1.01)
+ outgamut = 2; /* Definitely out of gamut */
+ if (check[0] > 0.01 && check[0] < 0.99
+ && check[1] > 0.01 && check[1] < 0.99
+ && check[2] > 0.01 && check[2] < 0.99)
+ outgamut = 0; /* Definitely in gamut */
+
+ /* Keep record of agree/disagree */
+ if (outgamut <= 1) {
+ ino++;
+ if (out[0] <= 0.01)
+ iok++;
+ } else {
+ ono++;
+ if (out[0] > 0.01)
+ ook++;
+ }
+ }
+ }
+ }
+ printf("Lut Lab16 gamut check inside correct = %f%%\n",100.0 * iok/ino);
+ printf("Lut Lab16 gamut check outside correct = %f%%\n",100.0 * ook/ono);
+ printf("Lut Lab16 gamut check total correct = %f%%\n",100.0 * (iok+ook)/(ino+ono));
+ if (((double)iok/ino) < 0.98 || ((double)ook/ono) < 0.98)
+#ifdef STOPONERROR
+ error ("Gamut Lab16 lookup has excessive error");
+#else
+ warning ("Gamut Lab16 lookup has excessive error");
+#endif /* STOPONERROR */
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ /* ---------------------------------------- */
+ /* Create a Lut8 based Lab profile to test */
+ /* ---------------------------------------- */
+
+ /* Open up the file for writing */
+ file_name = "xxxx_lut8_Lab.icm";
+ if ((wr_fp = new_icmFileStd_name(file_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",file_name);
+
+ if ((wr_icco = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags required */
+
+ /* The header: */
+ {
+ icmHeader *wh = wr_icco->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigOutputClass;
+ wh->colorSpace = icSigRgbData; /* It's and RGBish space */
+ wh->pcs = icSigLabData;
+ wh->renderingIntent = icRelativeColorimetric; /* For want of something */
+
+ /* Values that should be set before writing */
+ wh->manufacturer = str2tag("tst2");
+ wh->model = str2tag("test");
+ }
+ /* Profile Description Tag: */
+ {
+ icmTextDescription *wo;
+ char *dst = "This is a test Lut style Lab Output Profile";
+ if ((wo = (icmTextDescription *)wr_icco->add_tag(
+ wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Copyright Tag: */
+ {
+ icmText *wo;
+ char *crt = "Copyright 1998 Graeme Gill";
+ if ((wo = (icmText *)wr_icco->add_tag(
+ wr_icco, icSigCopyrightTag, icSigTextType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, crt); /* Copy the text in */
+ }
+ /* White Point Tag: */
+ {
+ icmXYZArray *wo;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = ABS_X; /* Set some silly numbers */
+ wo->data[0].Y = ABS_Y;
+ wo->data[0].Z = ABS_Z;
+ }
+ /* Black Point Tag: */
+ {
+ icmXYZArray *wo;
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = 0.02; /* Doesn't take part in Absolute anymore */
+ wo->data[0].Y = 0.04;
+ wo->data[0].Z = 0.03;
+ }
+ /* 8 bit dev -> pcs lut: */
+ {
+ icmLut *wo;
+
+ /* Intent 1 = relative colorimetric */
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigAToB1Tag, icSigLut8Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 3;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 256;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* so it is not used here. */
+
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigRgbData, /* Input color space */
+ icSigLabData, /* Output color space */
+ RGB_RGBp, /* Input transfer function, RGB->RGB' (NULL = default) */
+ NULL, NULL, /* Use default Maximum range of RGB' values */
+ RGBp_Labp, /* RGB' -> Lab' transfer function */
+ NULL, NULL, /* Use default Maximum range of Lab' values */
+ Labp_Lab /* Linear output transform Lab'->Lab */
+ ) != 0)
+ error("Setting 8 bit RGB->Lab Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 8 bit dev -> pcs lut - link intent 0 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 0 = perceptual */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigAToB0Tag, icSigAToB1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 8 dev -> pcs bit lut - link intent 2 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 2 = saturation */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigAToB2Tag, icSigAToB1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 8 bit pcs -> dev lut: */
+ {
+ icmLut *wo;
+ double rgbmin[3] = {0.0, 0.0, 0.0}; /* RGB' range */
+ double rgbmax[3] = {1.0, 1.0, 1.0};
+
+ /* Intent 1 = relative colorimetric */
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigBToA1Tag, icSigLut8Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 3;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 256;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* so it is not used here. */
+
+
+ /* REVLUTSCALE1 could be used here, but in this case it hardly */
+ /* makes any difference. */
+
+#ifdef REVLUTSCALE2
+ {
+ /* In any any real profile, you will probably be providing a clut */
+ /* function that carefully maps out of gamut PCS values to in-gamut */
+ /* device values, so the scaling done here won't be appropriate. */
+ /* */
+ /* For this regresion test, we are interested in maximizing accuracy */
+ /* over the known gamut of the device. */
+ /* By setting the min/max to a larger range than will actually be */
+ /* used, we can make sure that the extreme table values of the */
+ /* clut are not actually used, and therefore we won't see the */
+ /* rounding effects of these extreme values being clipped to */
+ /* by the numerical limits of the ICC representation. */
+ /* Instead the extreme values will be clipped by the the higher */
+ /* higher resolution output table. */
+ /* */
+ /* This all assumes that the multi-d reverse transform we are trying */
+ /* to represent in the profile extrapolates beyond the legal device */
+ /* value range. */
+ /* */
+ /* The scaling was chosen by experiment to make sure that the full */
+ /* gamut is surrounded by one row of extrapolated, unclipped clut */
+ /* table entries. */
+
+ int i;
+ for (i = 0; i < 3; i++) {
+ rgbmin[i] = -0.1667; /* Magic numbers */
+ rgbmax[i] = 1.1667;
+ }
+ }
+#endif
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigLabData, /* Input color space */
+ icSigRgbData, /* Output color space */
+ Lab_Labp, /* Linear input transform Lab->Lab' */
+ NULL, NULL, /* Use default Lab' range */
+ Labp_RGBp, /* Lab' -> RGB' transfer function */
+ rgbmin, rgbmax, /* Make RGB' range 0.0 - 1.333 for less clip rounding */
+ RGBp_RGB) != 0) /* Output transfer function, RGB'->RGB (NULL = deflt) */
+ error("Setting 8 bit Lab->RGB Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ }
+ /* 8 bit pcs -> dev lut - link intent 0 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 0 = perceptual */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigBToA0Tag, icSigBToA1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 8 pcs -> dev bit lut - link intent 2 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 2 = saturation */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigBToA2Tag, icSigBToA1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+
+ /* 8 bit pcs -> gamut lut: */
+ {
+ icmLut *wo;
+
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigGamutTag, icSigLut8Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 1;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 256;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* so it can't be used here. */
+
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigLabData, /* Input color space */
+ icSigGrayData, /* Output color space */
+ Lab_Labp, /* Linear input transform Lab->Lab' */
+ NULL, NULL , /* Default Lab' range */
+ Labp_BDIST, /* Lab' -> Boundary Distance transfer function */
+ NULL, NULL, /* Default range from clut to output table */
+ BDIST_GAMMUT /* Boundary Distance -> Out of gamut distance */
+ ) != 0)
+ error("Setting 16 bit Lab->Gammut Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+
+ /* Write the file out */
+ if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,wr_icco->err);
+
+ wr_icco->del(wr_icco);
+ wr_fp->del(wr_fp);
+
+ /* - - - - - - - - - - - - - - */
+ /* Deal with reading and verifying the Lut Lab 8bit style profile */
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(file_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",file_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Check the Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icRelativeColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ RGB_Lab(NULL, check,in);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 2.1)
+#ifdef STOPONERROR
+ error ("Excessive error in Lab8 Lut Fwd %f > 2.1",mxd);
+#else
+ warning ("Excessive error in Lab8 Lut Fwd %f > 2.1",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab8 fwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icRelativeColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab */
+ RGB_Lab(NULL, check,in);
+
+ /* Do reverse lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.03)
+#ifdef STOPONERROR
+ error ("Excessive error in Lab8 Lut Bwd %f > 0.03",mxd);
+#else
+ warning ("Excessive error in Lab8 Lut Bwd %f > 0.03",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab8 bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Absolute Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ aRGB_Lab(NULL, check,in);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 2.3)
+#ifdef STOPONERROR
+ error ("Excessive error in Abs Lab8 Lut Fwd %f > 2.3",mxd);
+#else
+ warning ("Excessive error in Abs Lab8 Lut Fwd %f > 2.3",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab8 fwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Absolute reverse Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ aRGB_Lab(NULL, check,in);
+
+ /* Do reverse lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.03)
+#ifdef STOPONERROR
+ error ("Excessive error in Abs Lab8 Lut Bwd %f > 0.03",mxd);
+#else
+ warning ("Excessive error in Abs Lab8 Lut Bwd %f > 0.03",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab8 bwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Lab gamut function */
+ {
+ int ino,ono,iok,ook;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmGamut, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ ino = ono = iok = ook = 0;
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = (co[0]/(TRES-1.0)) * 100.0; /* L */
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = ((co[1]/(TRES-1.0)) - 0.5) * 256.0; /* a */
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ int outgamut;
+ in[2] = ((co[2]/(TRES-1.0)) - 0.5) * 256.0; /* b */
+
+ /* Do gamut lookup of Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Do reference conversion of Lab -> RGB */
+ Lab_RGB(NULL, check,in);
+
+ /* Check the result */
+ outgamut = 1; /* assume on edge */
+ if (check[0] < -0.01 || check[0] > 1.01
+ || check[1] < -0.01 || check[1] > 1.01
+ || check[2] < -0.01 || check[2] > 1.01)
+ outgamut = 2; /* Definitely out of gamut */
+ if (check[0] > 0.01 && check[0] < 0.99
+ && check[1] > 0.01 && check[1] < 0.99
+ && check[2] > 0.01 && check[2] < 0.99)
+ outgamut = 0; /* Definitely in gamut */
+
+ /* Keep record of agree/disagree */
+ if (outgamut <= 1) {
+ ino++;
+ if (out[0] <= 0.01)
+ iok++;
+ } else {
+ ono++;
+ if (out[0] > 0.01)
+ ook++;
+ }
+ }
+ }
+ }
+ printf("Lut Lab8 gamut check inside correct = %f%%\n",100.0 * iok/ino);
+ printf("Lut Lab8 gamut check outside correct = %f%%\n",100.0 * ook/ono);
+ printf("Lut Lab8 gamut check total correct = %f%%\n",100.0 * (iok+ook)/(ino+ono));
+ if (((double)iok/ino) < 0.98 || ((double)ook/ono) < 0.98)
+#ifdef STOPONERROR
+ error ("Gamut Lab8 lookup has excessive error");
+#else
+ warning ("Gamut Lab8 lookup has excessive error");
+#endif /* STOPONERROR */
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ /* ---------------------------------------- */
+
+ printf("Lookup test completed OK\n");
+ return 0;
+}
+
+/* ------------------------------------------------ */
+/* Basic printf type error() and warning() routines */
+
+void
+error(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"lutest: Error - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ exit (-1);
+}
+
+void
+warning(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"lutest: Warning - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}