1 
2 /*
3  * International Color Consortium Format Library (icclib)
4  * Check the device chanel to PCS monotonicity.
5  *
6  * Author:  Graeme W. Gill
7  * Date:    2000/12/11
8  * Version: 2.15
9  *
10  * Copyright 2000 - 2012 Graeme W. Gill
11  *
12  * This material is licensed with an "MIT" free use license:-
13  * see the License4.txt file in this directory for licensing details.
14  */
15 
16 /* TTBD:
17  *
18  * Make general device input, not just CMYK
19  */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <math.h>
27 #include "vrml.h"
28 #include "icc.h"
29 
30 void error(char *fmt, ...), warning(char *fmt, ...);
31 
usage(void)32 void usage(void) {
33 	fprintf(stderr,"Check device to PCS monotonicity of a CMYK ICC file, V%s\n",ICCLIB_VERSION_STR);
34 	fprintf(stderr,"Author: Graeme W. Gill\n");
35 	fprintf(stderr,"usage: mcheck [-v] [-w] infile\n");
36 	fprintf(stderr," -v        verbose\n");
37 	fprintf(stderr," -c        Check just Cyan monotonicity\n");
38 	fprintf(stderr," -m        Check just Magenta monotonicity\n");
39 	fprintf(stderr," -y        Check just Yellow monotonicity\n");
40 	fprintf(stderr," -k        Check just Black monotonicity\n");
41 	fprintf(stderr," -w        create %s visualisation\n",vrml_format());
42 	fprintf(stderr," -x        use %s axes\n",vrml_format());
43 	exit(1);
44 }
45 
46 #define MGR 50			/* Maximum grid resolution handled */
47 
48 int
main(int argc,char * argv[])49 main(
50 	int argc,
51 	char *argv[]
52 ) {
53 	int fa,nfa;				/* argument we're looking at */
54 	int verb = 0;
55 	int cchan = -1;			/* default all */
56 	int dovrml = 0;
57 	int doaxes = 0;
58 	char in_name[500];
59 	char out_name[500], *xl;
60 	icmFile *rd_fp;
61 	icc *wr_icco, *rd_icco;		/* Keep object separate */
62 	int rv = 0;
63 
64 	/* Check variables */
65 	icmLuBase *luo;
66 	icmLuLut *luluto;	/* Lookup xLut type object */
67 	int gres;			/* Grid resolution */
68 	icColorSpaceSignature ins, outs;	/* Type of input and output spaces */
69 	int inn;							/* Number of input chanels */
70 	icmLuAlgType alg;
71 	vrml *wrl;
72 	int dx[4];			/* Device index mapping */
73 	int chan, cs, ce;
74 
75 	if (argc < 2)
76 		usage();
77 
78 	/* Process the arguments */
79 	for(fa = 1;fa < argc;fa++) {
80 		nfa = fa;					/* skip to nfa if next argument is used */
81 		if (argv[fa][0] == '-')	{	/* Look for any flags */
82 			char *na = NULL;		/* next argument after flag, null if none */
83 
84 			if (argv[fa][2] != '\000')
85 				na = &argv[fa][2];		/* next is directly after flag */
86 			else {
87 				if ((fa+1) < argc) {
88 					if (argv[fa+1][0] != '-') {
89 						nfa = fa + 1;
90 						na = argv[nfa];		/* next is seperate non-flag argument */
91 					}
92 				}
93 			}
94 
95 			/* Verbosity */
96 			if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
97 				verb = 1;
98 			}
99 			/* VRML/X3D */
100 			else if (argv[fa][1] == 'w' || argv[fa][1] == 'W') {
101 				dovrml = 1;
102 			}
103 			/* Cyan */
104 			else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') {
105 				cchan = 0;
106 			}
107 			/* Magenta */
108 			else if (argv[fa][1] == 'm' || argv[fa][1] == 'M') {
109 				cchan = 1;
110 			}
111 			/* Yellow */
112 			else if (argv[fa][1] == 'y' || argv[fa][1] == 'Y') {
113 				cchan = 2;
114 			}
115 			/* Black */
116 			else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') {
117 				cchan = 3;
118 			}
119 			else if (argv[fa][1] == 'x') {
120 				doaxes = 1;
121 			}
122 			else if (argv[fa][1] == '?')
123 				usage();
124 			else
125 				usage();
126 		}
127 		else
128 			break;
129 	}
130 
131 	if (fa >= argc || argv[fa][0] == '-') usage();
132 	strcpy(in_name,argv[fa]);
133 
134 	strcpy(out_name, in_name);
135 	if ((xl = strrchr(out_name, '.')) == NULL)	/* Figure where extention is */
136 		xl = out_name + strlen(out_name);
137 	xl[0] = '\000';			/* Remove extension */
138 
139 	/* Open up the file for reading */
140 	if ((rd_fp = new_icmFileStd_name(in_name,"r")) == NULL)
141 		error ("Read: Can't open file '%s'",in_name);
142 
143 	if ((rd_icco = new_icc()) == NULL)
144 		error ("Read: Creation of ICC object failed");
145 
146 	/* Read the header and tag list */
147 	if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
148 		error ("Read: %d, %s",rv,rd_icco->err);
149 
150 	/* Get a Device to PCS conversion object */
151 	if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icRelativeColorimetric, icSigLabData, icmLuOrdNorm)) == NULL) {
152 		if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent, icSigLabData, icmLuOrdNorm)) == NULL)
153 			error ("%d, %s",rd_icco->errc, rd_icco->err);
154 	}
155 	/* Get details of conversion */
156 	luo->spaces(luo, &ins, &inn, &outs, NULL, &alg, NULL, NULL, NULL, NULL);
157 
158 	if (alg != icmLutType) {
159 		error("Expecting Lut based profile");
160 	}
161 
162 	if (ins != icSigCmykData) {
163 		error("Expecting CMYK device");
164 	}
165 
166 	if (outs != icSigLabData) {
167 		error("Expecting Lab PCS");
168 	}
169 
170 	luluto = (icmLuLut *)luo;	/* Lookup xLut type object */
171 
172 	gres = luluto->lut->clutPoints;
173 	if (gres > MGR) {
174 		error("Can't handle grid resolution greater than %d\n",MGR);
175 	}
176 
177 	if (dovrml) {
178 		wrl = new_vrml(out_name, doaxes, vrml_lab);
179 		if (wrl == NULL)
180 			error("new_vrml for '%s%s' failed",out_name,vrml_ext());
181 		wrl->start_line_set(wrl, 0);
182 	}
183 
184 	/* For all the device chanels chosen */
185 	if (cchan < 0) {
186 		cs = 0;
187 		ce = inn;
188 	} else {
189 		cs = cchan;
190 		ce = cs + 1;
191 	}
192 	for (chan = cs; chan < ce; chan++) {
193 
194 		/* Check the monotonicity of the output for a given device input */
195 		int co[4];
196 		if (chan == 0) {
197 			dx[0] = 1;
198 			dx[1] = 2;
199 			dx[2] = 3;
200 			dx[3] = 0;		/* Cyan is variable */
201 		} else if (chan == 1) {
202 			dx[0] = 0;
203 			dx[1] = 2;
204 			dx[2] = 3;
205 			dx[3] = 1;		/* Magenta is variable */
206 		} else if (chan == 2) {
207 			dx[0] = 0;
208 			dx[1] = 1;
209 			dx[2] = 3;
210 			dx[3] = 2;		/* Yellow is variable */
211 		} else if (chan == 3) {
212 			dx[0] = 0;
213 			dx[1] = 1;
214 			dx[2] = 2;
215 			dx[3] = 3;		/* Black is variable */
216 		}
217 
218 		/* Itterate throught the CMY clut grid points */
219 		for (co[0] = 0; co[0] < gres; co[0]++) {
220 			for (co[1] = 0; co[1] < gres; co[1]++) {
221 				for (co[2] = 0; co[2] < gres; co[2]++) {
222 					int j, k, ck, nm;
223 					double dev[MGR][4];
224 					double pcs[MGR][3];
225 					double apcs[3], ss;
226 
227 					/* Run up the variable axis */
228 					for (ck = 0; ck < gres; ck++) {
229 
230 						dev[ck][dx[0]] = co[0]/(gres-1.0);
231 						dev[ck][dx[1]] = co[1]/(gres-1.0);
232 						dev[ck][dx[2]] = co[2]/(gres-1.0);
233 						dev[ck][dx[3]] = ck/(gres-1.0);
234 
235 						/* Device to PCS */
236 						if ((rv = luluto->clut(luluto, pcs[ck], dev[ck])) > 1)
237 							error ("%d, %s",rd_icco->errc,rd_icco->err);
238 
239 //						if (dovrml)
240 //							wrl->add_vertex(wrl, 0, pcs[ck]);
241 					}
242 
243 					/* Compute average vector direction */
244 					for (ss = 0.0, k = 0; k < 3; k++) {
245 						double tt;
246 						tt = pcs[gres-1][k] - pcs[0][k];
247 						ss += tt * tt;
248 						apcs[k] = tt;
249 					}
250 					for (k = 0; k < 3; k++)
251 						apcs[k] /= ss;
252 
253 					/* Now compute the dot product for each vector, */
254 					/* and check for reversals. */
255 					j = 0;
256 //printf("Checking          CMYK %f %f %f %f Lab %f %f %f\n",
257 //       dev[j][0], dev[j][1], dev[j][2], dev[j][3],
258 //       pcs[j][0], pcs[j][1], pcs[j][2]);
259 					for (nm = 0, j = 1; j < gres; j++) {
260 						for (ss = 0.0, k = 0; k < 3; k++)	/* Dot product */
261 							ss += (pcs[j][k] - pcs[j-1][k]) * apcs[k];
262 
263 //printf("Checking %f CMYK %f %f %f %f Lab %f %f %f\n",
264 //       ss, dev[j][0], dev[j][1], dev[j][2], dev[j][3],
265 //       pcs[j][0], pcs[j][1], pcs[j][2]);
266 
267 						if (ss <= 0.0) {
268 							nm = 1;
269 							printf("NonMon %f at CMYK %f %f %f %f Lab %f %f %f\n",
270 							       ss, dev[j][0], dev[j][1], dev[j][2], dev[j][3],
271 							       pcs[j][0], pcs[j][1], pcs[j][2]);
272 						}
273 					}
274 //printf("\n");
275 
276 					/* Display just the non mono threads */
277 					if (nm && dovrml) {
278 						for (j = 0; j < gres; j++)
279 							wrl->add_vertex(wrl, 0, pcs[j]);
280 					}
281 					if (verb) {
282 						printf("."); fflush(stdout);
283 					}
284 				}
285 			}
286 		}
287 	}
288 
289 	if (dovrml) {
290 		wrl->make_lines(wrl, 0, gres);
291 		wrl->del(wrl);
292 	}
293 
294 	/* Done with lookup object */
295 	luo->del(luo);
296 
297 	rd_icco->del(rd_icco);
298 	rd_fp->del(rd_fp);
299 
300 	return 0;
301 }
302 
303 /* ------------------------------------------------ */
304 /* Basic printf type error() and warning() routines */
305 
306 void
error(char * fmt,...)307 error(char *fmt, ...)
308 {
309 	va_list args;
310 
311 	fprintf(stderr,"icctest: Error - ");
312 	va_start(args, fmt);
313 	vfprintf(stderr, fmt, args);
314 	va_end(args);
315 	fprintf(stderr, "\n");
316 	exit (-1);
317 }
318 
319 void
warning(char * fmt,...)320 warning(char *fmt, ...)
321 {
322 	va_list args;
323 
324 	fprintf(stderr,"icctest: Warning - ");
325 	va_start(args, fmt);
326 	vfprintf(stderr, fmt, args);
327 	va_end(args);
328 	fprintf(stderr, "\n");
329 }
330