1 /*
2 * Argyll Color Correction System
3 * DTP92/Spectrolino display target reader
4 *
5 * Author: Graeme W. Gill
6 * Date: 4/10/96
7 *
8 * Copyright 1996 - 2013 Graeme W. Gill
9 * All rights reserved.
10 *
11 * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
12 * see the License.txt file for licencing details.
13 */
14
15 /* This program displays test patches, and takes readings from a display device */
16
17 /* TTBD
18
19 Add support for black recalibration using i1pro or munki.
20 Setable timeout ? Need to allow placing instrument back
21 on screen. Need this to properly handle ss anyway ?
22
23 Add bell at end of readings ?
24
25 Ideally this should be changed to always create non-normalized (absolute)
26 readings, since normalised readings are not natural, but everything
27 that deals with .ti3 data then needs fixing to deal with non-normalized
28 readings !
29 */
30
31 #undef DEBUG
32 #undef DEBUG_OFFSET /* Keep test window out of the way */
33
34 /* Invoke with -dfake for testing with a fake device */
35 /* Will use fake.icm/.icc if present */
36
37 #define COMPORT 1 /* Default com port 1..4 */
38
39 #ifdef __MINGW32__
40 # define WINVER 0x0500
41 #endif
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <stdarg.h>
46 #include <math.h>
47 #include <sys/types.h>
48 #include <time.h>
49 #include <string.h>
50 #if defined (NT)
51 #include <conio.h>
52 #endif
53 #include "copyright.h"
54 #include "aconfig.h"
55 #include "numlib.h"
56 #include "xspect.h"
57 #include "cgats.h"
58 #include "insttypes.h"
59 #include "conv.h"
60 #include "icoms.h"
61 #include "inst.h"
62 #include "ccmx.h"
63 #include "ccss.h"
64 #include "ccast.h"
65 #include "dispwin.h"
66 #include "dispsup.h"
67 #include "sort.h"
68 #include "instappsup.h"
69 #ifdef ENABLE_USB
70 # include "spyd2.h"
71 #endif
72 #include "ui.h"
73
74 /* ------------------------------------------------------------------- */
75 #if defined(__APPLE__) && defined(__POWERPC__)
76
77 /* Workaround for a PPC gcc 3.3 optimiser bug... */
78 /* It seems to cause a segmentation fault instead of */
79 /* converting an integer loop index into a float, */
80 /* when there are sufficient variables in play. */
gcc_bug_fix(int i)81 static int gcc_bug_fix(int i) {
82 static int nn;
83 nn += i;
84 return nn;
85 }
86 #endif /* APPLE */
87
88 /* ------------------------------------------------------------------- */
89
90 /*
91
92 Flags used:
93
94 ABCDEFGHIJKLMNOPQRSTUVWXYZ
95 upper .... .... .. .. .....
96 lower .. . . . . .. .
97
98 */
99
100 /* Flag = 0x0000 = default */
101 /* Flag & 0x0001 = list ChromCast's */
usage(int flag,char * diag,...)102 void usage(int flag, char *diag, ...) {
103 int i;
104 disppath **dp;
105 icompaths *icmps;
106 inst2_capability cap2 = inst2_none;
107
108 fprintf(stderr,"Read a Display, Version %s\n",ARGYLL_VERSION_STR);
109 fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
110 if (diag != NULL) {
111 va_list args;
112 fprintf(stderr,"Diagnostic: ");
113 va_start(args, diag);
114 vfprintf(stderr, diag, args);
115 va_end(args);
116 fprintf(stderr,"\n");
117 }
118 fprintf(stderr,"usage: dispread [options] outfile\n");
119 fprintf(stderr," -v Verbose mode\n");
120 #if defined(UNIX_X11)
121 fprintf(stderr," -display displayname Choose X11 display name\n");
122 fprintf(stderr," -d n[,m] Choose the display n from the following list (default 1)\n");
123 fprintf(stderr," Optionally choose different display m for VideoLUT access\n");
124 #else
125 fprintf(stderr," -d n Choose the display from the following list (default 1)\n");
126 #endif
127 dp = get_displays();
128 if (dp == NULL || dp[0] == NULL)
129 fprintf(stderr," ** No displays found **\n");
130 else {
131 int i;
132 for (i = 0; ; i++) {
133 if (dp[i] == NULL)
134 break;
135 fprintf(stderr," %d = '%s'\n",i+1,dp[i]->description);
136 }
137 }
138 free_disppaths(dp);
139 fprintf(stderr," -dweb[:port] Display via a web server at port (default 8080)\n");
140 fprintf(stderr," -dcc[:n] Display via n'th ChromeCast (default 1, ? for list)\n");
141 if (flag & 0x001) {
142 ccast_id **ids;
143 if ((ids = get_ccids()) == NULL) {
144 fprintf(stderr," ** Error discovering ChromCasts **\n");
145 } else {
146 if (ids[0] == NULL)
147 fprintf(stderr," ** No ChromCasts found **\n");
148 else {
149 int i;
150 for (i = 0; ids[i] != NULL; i++)
151 fprintf(stderr," %d = '%s'\n",i+1,ids[i]->name);
152 free_ccids(ids);
153 }
154 }
155 }
156 #ifdef NT
157 fprintf(stderr," -dmadvr Display via MadVR Video Renderer\n");
158 #endif
159 // fprintf(stderr," -d fake Use a fake display device for testing, fake%s if present\n",ICC_FILE_EXT);
160 fprintf(stderr," -c listno Set communication port from the following list (default %d)\n",COMPORT);
161 if ((icmps = new_icompaths(g_log)) != NULL) {
162 icompath **paths;
163 if ((paths = icmps->paths) != NULL) {
164 int i;
165 for (i = 0; ; i++) {
166 if (paths[i] == NULL)
167 break;
168 if ((paths[i]->itype == instSpyder1 && setup_spyd2(0) == 0)
169 || (paths[i]->itype == instSpyder2 && setup_spyd2(1) == 0))
170 fprintf(stderr," %d = '%s' !! Disabled - no firmware !!\n",i+1,paths[i]->name);
171 else
172 fprintf(stderr," %d = '%s'\n",i+1,paths[i]->name);
173 }
174 } else
175 fprintf(stderr," ** No ports found **\n");
176 }
177 fprintf(stderr," -p Use telephoto mode (ie. for a projector) (if available)\n");
178 cap2 = inst_show_disptype_options(stderr, " -y ", icmps, 0);
179 fprintf(stderr," -k file.cal Load calibration file into display while reading\n");
180 fprintf(stderr," -K file.cal Apply calibration file to test values while reading\n");
181 #ifdef NT
182 fprintf(stderr," -V Enable MadVR color management (3dLut)\n");
183 #endif
184 fprintf(stderr," -s Save spectral information (default don't save)\n");
185 fprintf(stderr," -P ho,vo,ss[,vs] Position test window and scale it\n");
186 fprintf(stderr," ho,vi: 0.0 = left/top, 0.5 = center, 1.0 = right/bottom etc.\n");
187 fprintf(stderr," ss: 0.5 = half, 1.0 = normal, 2.0 = double etc.\n");
188 fprintf(stderr," -F Fill whole screen with black background\n");
189 #if defined(UNIX_X11)
190 fprintf(stderr," -n Don't set override redirect on test window\n");
191 #endif
192 fprintf(stderr," -E Encode the test values for video range 16..235/255\n");
193 fprintf(stderr," -Z nbits Quantize test values to fit in nbits\n");
194 fprintf(stderr," -J Run instrument calibration first (used rarely)\n");
195 fprintf(stderr," -N Disable initial calibration of instrument if possible\n");
196 fprintf(stderr," -H Use high resolution spectrum mode (if available)\n");
197 // fprintf(stderr," -V Use adaptive measurement mode (if available)\n");
198 fprintf(stderr," -w Disable normalisation of white to Y = 100\n");
199 if (cap2 & inst2_ccmx)
200 fprintf(stderr," -X file.ccmx Apply Colorimeter Correction Matrix\n");
201 if (cap2 & inst2_ccss) {
202 fprintf(stderr," -X file.ccss Use Colorimeter Calibration Spectral Samples for calibration\n");
203 fprintf(stderr," -Q observ Choose CIE Observer for spectrometer or CCSS colorimeter data:\n");
204 fprintf(stderr," 1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2, 1964_10c\n");
205 }
206 fprintf(stderr," -I b|w Drift compensation, Black: -Ib, White: -Iw, Both: -Ibw\n");
207 fprintf(stderr," -Y R:rate Override measured refresh rate with rate Hz\n");
208 fprintf(stderr," -Y A Use non-adaptive integration time mode (if available).\n");
209 fprintf(stderr," -Y p Don't wait for the instrument to be placed on the display\n");
210 fprintf(stderr," -C \"command\" Invoke shell \"command\" each time a color is set\n");
211 fprintf(stderr," -M \"command\" Invoke shell \"command\" each time a color is measured\n");
212 // fprintf(stderr," -x [lx] Take manually entered values, either L*a*b* (-xl) or XYZ (-xx).\n");
213 fprintf(stderr," -x x Take manually entered XYZ values\n");
214 fprintf(stderr," -W n|h|x Override serial port flow control: n = none, h = HW, x = Xon/Xoff\n");
215 fprintf(stderr," -D [level] Print debug diagnostics to stderr\n");
216 fprintf(stderr," outfile Base name for input[ti1]/output[ti3] file\n");
217 if (icmps != NULL)
218 icmps->del(icmps);
219 exit(1);
220 }
221
main(int argc,char * argv[])222 int main(int argc, char *argv[]) {
223 int i, j;
224 int fa, nfa, mfa; /* current argument we're looking at */
225 disppath *disp = NULL; /* Display being used */
226 double hpatscale = 1.0, vpatscale = 1.0; /* scale factor for test patch size */
227 double ho = 0.0, vo = 0.0; /* Test window offsets, -1.0 to 1.0 */
228 int out_tvenc = 0; /* 1 to use RGB Video Level encoding */
229 int qbits = 0; /* Quantization bits, 0 = not set */
230 int fullscreen = 0; /* NZ if whole screen should be filled with black */
231 int verb = 0;
232 int debug = 0;
233 int fake = 0; /* Use the fake device for testing */
234 int override = 1; /* Override redirect on X11 */
235 int comport = COMPORT; /* COM port used */
236 icompaths *icmps = NULL;
237 icompath *ipath = NULL;
238 flow_control fc = fc_nc; /* Default flow control */
239 int docalib = 0; /* Do a calibration */
240 int highres = 0; /* Use high res mode if available */
241 double refrate = 0.0; /* 0.0 = default, > 0.0 = override refresh rate */
242 int nadaptive = 0; /* Use non-adaptive mode if available */
243 int bdrift = 0; /* Flag, nz for black drift compensation */
244 int wdrift = 0; /* Flag, nz for white drift compensation */
245 int dtype = 0; /* Display type selection charater */
246 int tele = 0; /* NZ if telephoto mode */
247 int noautocal = 0; /* Disable auto calibration */
248 int noplace = 0; /* Disable user instrument placement */
249 int donorm = 1; /* Enable Y = 100 normalisation */
250 char ccxxname[MAXNAMEL+1] = "\000"; /* Colorimeter Correction Matrix name */
251 ccmx *cmx = NULL; /* Colorimeter Correction Matrix */
252 ccss *ccs = NULL; /* Colorimeter Calibration Spectral Samples */
253 int spec = 0; /* Don't save spectral information */
254 icxObserverType obType = icxOT_default;
255 int webdisp = 0; /* NZ for web display, == port number */
256 int ccdisp = 0; /* NZ for ChromeCast, == list index */
257 ccast_id **ccids = NULL;
258 ccast_id *ccid = NULL;
259 #ifdef NT
260 int madvrdisp = 0; /* NZ for MadVR display */
261 #endif
262 char *ccallout = NULL; /* Change color Shell callout */
263 char *mcallout = NULL; /* Measure color Shell callout */
264 int xtern = 0; /* Use external (user supplied) values rather than */
265 /* instument read 1 = Lab, 2 = XYZ */
266 char inname[MAXNAMEL+1] = "\000"; /* Input cgats file base name */
267 char outname[MAXNAMEL+1] = "\000"; /* Output cgats file base name */
268 char calname[MAXNAMEL+1] = "\000"; /* Calibration file name (if any) */
269 int native = 2; /* X0 = use current per channel calibration curve */
270 /* X1 = set native linear output and use ramdac high prec */
271 /* 0X = use current color management cLut (MadVR) */
272 /* 1X = disable color management cLUT (MadVR) */
273 double cal[3][MAX_CAL_ENT]; /* Display calibration */
274 int ncal = 256; /* Default number of cal entries used */
275 cgats *icg; /* input cgats structure */
276 cgats *ocg; /* output cgats structure */
277 time_t clk = time(0);
278 struct tm *tsp = localtime(&clk);
279 char *atm = asctime(tsp); /* Ascii time */
280 col *cols; /* Internal storage of all the patch colors */
281 int dim = 0; /* Dimensionality - 1, 3, or 4 */
282 int npat; /* Number of patches/colors */
283 int xpat = 0; /* Set to number of extra patches */
284 int wpat; /* Set to index of white patch */
285 int si; /* Sample id index */
286 int ti; /* Temp index */
287 int fi; /* Colorspace index */
288 int nsetel = 0;
289 cgats_set_elem *setel; /* Array of set value elements */
290 disprd *dr; /* Display patch read object */
291 int noramdac = 0; /* Will be set to nz if can't set ramdac */
292 int nocm = 0; /* Will be set to nz if can't set color management */
293 int errc; /* Return value from new_disprd() */
294 int rv;
295
296 set_exe_path(argv[0]); /* Set global exe_path and error_program */
297 check_if_not_interactive();
298
299 #ifdef DEBUG_OFFSET
300 ho = 0.8;
301 vo = -0.8;
302 #endif
303
304 #if defined(DEBUG) || defined(DEBUG_OFFSET)
305 printf("!!!!!! Debug turned on !!!!!!\n");
306 #endif
307
308 if (argc <= 1)
309 usage(0,"Too few arguments");
310
311 if (ncal > MAX_CAL_ENT)
312 error("Internal, ncal = %d > MAX_CAL_ENT %d\n",ncal,MAX_CAL_ENT);
313
314 /* Process the arguments */
315 mfa = 1; /* Minimum final arguments */
316 for (fa = 1;fa < argc;fa++) {
317 nfa = fa; /* skip to nfa if next argument is used */
318 if (argv[fa][0] == '-') { /* Look for any flags */
319 char *na = NULL; /* next argument after flag, null if none */
320
321 if (argv[fa][2] != '\000')
322 na = &argv[fa][2]; /* next is directly after flag */
323 else {
324 if ((fa+1+mfa) < argc) {
325 if (argv[fa+1][0] != '-') {
326 nfa = fa + 1;
327 na = argv[nfa]; /* next is seperate non-flag argument */
328 }
329 }
330 }
331
332 if (argv[fa][1] == '?' || argv[fa][1] == '-') {
333 usage(0,"Usage requested");
334
335 } else if (argv[fa][1] == 'v') {
336 verb = 1;
337 g_log->verb = verb;
338
339 /* Display number */
340 } else if (argv[fa][1] == 'd') {
341 if (strncmp(na,"web",3) == 0
342 || strncmp(na,"WEB",3) == 0) {
343 webdisp = 8080;
344 if (na[3] == ':') {
345 webdisp = atoi(na+4);
346 if (webdisp == 0 || webdisp > 65535)
347 usage(0,"Web port number must be in range 1..65535");
348 }
349 fa = nfa;
350 } else if (strncmp(na,"cc",2) == 0
351 || strncmp(na,"CC",2) == 0) {
352 ccdisp = 1;
353 if (na[2] == ':') {
354 if (na[3] < '0' || na[3] > '9')
355 usage(0x0001,"Available ChromeCasts");
356
357 ccdisp = atoi(na+3);
358 if (ccdisp <= 0)
359 usage(0,"ChromCast number must be in range 1..N");
360 }
361 fa = nfa;
362 #ifdef NT
363 } else if (strncmp(na,"madvr",5) == 0
364 || strncmp(na,"MADVR",5) == 0) {
365 madvrdisp = 1;
366 fa = nfa;
367 #endif
368 } else {
369 #if defined(UNIX_X11)
370 int ix, iv;
371
372 if (strcmp(&argv[fa][2], "isplay") == 0 || strcmp(&argv[fa][2], "ISPLAY") == 0) {
373 if (++fa >= argc || argv[fa][0] == '-') usage(0,"Parameter expected following -display");
374 setenv("DISPLAY", argv[fa], 1);
375 } else {
376 if (na == NULL) usage(0,"Parameter expected following -d");
377 fa = nfa;
378 if (strcmp(na,"fake") == 0) {
379 fake = 1;
380 } else {
381 if (sscanf(na, "%d,%d",&ix,&iv) != 2) {
382 ix = atoi(na);
383 iv = 0;
384 }
385 if (disp != NULL)
386 free_a_disppath(disp);
387 if ((disp = get_a_display(ix-1)) == NULL)
388 usage(0,"-d parameter %d out of range",ix);
389 if (iv > 0)
390 disp->rscreen = iv-1;
391 }
392 }
393 #else
394 int ix;
395 if (na == NULL) usage(0,"Parameter expected following -d");
396 fa = nfa;
397 if (strcmp(na,"fake") == 0) {
398 fake = 1;
399 } else {
400 ix = atoi(na);
401 if (disp != NULL)
402 free_a_disppath(disp);
403 if ((disp = get_a_display(ix-1)) == NULL)
404 usage(0,"-d parameter %d out of range",ix);
405 }
406 #endif
407 }
408 #if defined(UNIX_X11)
409 } else if (argv[fa][1] == 'n') {
410 override = 0;
411 #endif /* UNIX */
412
413 /* COM port */
414 } else if (argv[fa][1] == 'c') {
415 fa = nfa;
416 if (na == NULL) usage(0,"Paramater expected following -c");
417 comport = atoi(na);
418 if (comport < 1 || comport > 50) usage(0,"-c parameter %d out of range",comport);
419
420 /* Telephoto */
421 } else if (argv[fa][1] == 'p') {
422 tele = 1;
423
424 /* Display type */
425 } else if (argv[fa][1] == 'y') {
426 fa = nfa;
427 if (na == NULL) usage(0,"Parameter expected after -y");
428 dtype = na[0];
429
430 /* Calibration file */
431 } else if (argv[fa][1] == 'k'
432 || argv[fa][1] == 'K') {
433 if (na == NULL) usage(0,"Parameter expected after -%c",argv[fa][1]);
434 strncpy(calname,na,MAXNAMEL); calname[MAXNAMEL] = '\000';
435 if (argv[fa][1] == 'K')
436 native |= 1; /* Use native linear & soft cal */
437 else
438 native &= ~1; /* Use HW cal */
439 fa = nfa;
440
441 #ifdef NT
442 /* MadVR verify mode */
443 } else if (argv[fa][1] == 'V') {
444 native &= ~2;
445 #endif
446
447 /* Save spectral data */
448 } else if (argv[fa][1] == 's') {
449 spec = 1;
450
451 /* Test patch offset and size */
452 } else if (argv[fa][1] == 'P') {
453 fa = nfa;
454 if (na == NULL) usage(0,"Parameter expected after -P");
455 if (sscanf(na, " %lf,%lf,%lf,%lf ", &ho, &vo, &hpatscale, &vpatscale) == 4) {
456 ;
457 } else if (sscanf(na, " %lf,%lf,%lf ", &ho, &vo, &hpatscale) == 3) {
458 vpatscale = hpatscale;
459 } else {
460 usage(0,"-P parameter '%s' not recognised",na);
461 }
462 if (ho < 0.0 || ho > 1.0
463 || vo < 0.0 || vo > 1.0
464 || hpatscale <= 0.0 || hpatscale > 50.0
465 || vpatscale <= 0.0 || vpatscale > 50.0)
466 usage(0,"-P parameters %f %f %f %f out of range",ho,vo,hpatscale,vpatscale);
467 ho = 2.0 * ho - 1.0;
468 vo = 2.0 * vo - 1.0;
469
470 /* Full screen black background */
471 } else if (argv[fa][1] == 'F') {
472 fullscreen = 1;
473
474 /* Video encoded output */
475 } else if (argv[fa][1] == 'E') {
476 out_tvenc = 1;
477 if (qbits == 0)
478 qbits = 8;
479
480 /* Specify quantization bits */
481 } else if (argv[fa][1] == 'Z') {
482 fa = nfa;
483 if (na == NULL) usage(0,"Expected argument to -Z");
484 qbits = atoi(na);
485 if (qbits < 1 || qbits > 32)
486 usage(0,"Argument to -Q must be between 1 and 32");
487
488 /* Force calibration */
489 } else if (argv[fa][1] == 'J') {
490 docalib = 1;
491
492 /* No auto-cal */
493 } else if (argv[fa][1] == 'N') {
494 noautocal = 1;
495
496 /* High res mode */
497 } else if (argv[fa][1] == 'H') {
498 highres = 1;
499
500 /* Disable normalisation of values to white Y = 100 */
501 } else if (argv[fa][1] == 'w') {
502 donorm = 0;
503
504 /* Colorimeter Correction Matrix */
505 /* or Colorimeter Calibration Spectral Samples */
506 } else if (argv[fa][1] == 'X') {
507 int ix;
508 fa = nfa;
509 if (na == NULL) usage(0,"Parameter expected following -X");
510 strncpy(ccxxname,na,MAXNAMEL-1); ccxxname[MAXNAMEL-1] = '\000';
511
512 } else if (argv[fa][1] == 'I') {
513 fa = nfa;
514 if (na == NULL || na[0] == '\000') usage(0,"Parameter expected after -I");
515 for (i=0; ; i++) {
516 if (na[i] == '\000')
517 break;
518 if (na[i] == 'b' || na[i] == 'B')
519 bdrift = 1;
520 else if (na[i] == 'w' || na[i] == 'W')
521 wdrift = 1;
522 else
523 usage(0,"-I parameter '%c' not recognised",na[i]);
524 }
525
526 /* Spectral Observer type */
527 } else if (argv[fa][1] == 'Q') {
528 fa = nfa;
529 if (na == NULL) usage(0,"Parameter expecte after -Q");
530 if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */
531 obType = icxOT_CIE_1931_2;
532 } else if (strcmp(na, "1964_10") == 0) { /* Classic 10 degree */
533 obType = icxOT_CIE_1964_10;
534 } else if (strcmp(na, "1964_10c") == 0) { /* 10 degree corrected */
535 obType = icxOT_CIE_1964_10c;
536 } else if (strcmp(na, "1955_2") == 0) { /* Stiles and Burch 1955 2 degree */
537 obType = icxOT_Stiles_Burch_2;
538 } else if (strcmp(na, "1978_2") == 0) { /* Judd and Voss 1978 2 degree */
539 obType = icxOT_Judd_Voss_2;
540 } else if (strcmp(na, "shaw") == 0) { /* Shaw and Fairchilds 1997 2 degree */
541 obType = icxOT_Shaw_Fairchild_2;
542 } else
543 usage(0,"-Q parameter '%s' not recognised",na);
544
545
546 /* Change color callout */
547 } else if (argv[fa][1] == 'C') {
548 fa = nfa;
549 if (na == NULL) usage(0,"Parameter expected after -C");
550 ccallout = na;
551
552 /* Measure color callout */
553 } else if (argv[fa][1] == 'M') {
554 fa = nfa;
555 if (na == NULL) usage(0,"Parameter expected after -M");
556 mcallout = na;
557
558 /* Request external values */
559 } else if (argv[fa][1] == 'x') {
560 fa = nfa;
561 if (na == NULL) usage(0, "Parameter expected after -x");
562
563 if (na[0] == 'x' || na[0] == 'X')
564 xtern = 2;
565 // else if (na[0] == 'l' || na[0] == 'L')
566 // xtern = 1;
567 else
568 usage(0, "Unexpected parameter -x %c",na[0]);
569
570 /* Serial port flow control */
571 } else if (argv[fa][1] == 'W') {
572 fa = nfa;
573 if (na == NULL) usage(0,"Parameter expected after -W");
574 if (na[0] == 'n' || na[0] == 'N')
575 fc = fc_None;
576 else if (na[0] == 'h' || na[0] == 'H')
577 fc = fc_Hardware;
578 else if (na[0] == 'x' || na[0] == 'X')
579 fc = fc_XonXOff;
580 else
581 usage(0,"-W parameter '%s' not recognised",na);
582
583 } else if (argv[fa][1] == 'D') {
584 debug = 1;
585 if (na != NULL && na[0] >= '0' && na[0] <= '9') {
586 debug = atoi(na);
587 fa = nfa;
588 }
589 g_log->debug = debug;
590 callback_ddebug = 1; /* dispwin global */
591
592 /* Extra flags */
593 } else if (argv[fa][1] == 'Y') {
594 if (na == NULL)
595 usage(0,"Flag '-Y' expects extra flag");
596
597 if (na[0] == 'R') {
598 if (na[1] != ':')
599 usage(0,"-Y R:rate syntax incorrect");
600 refrate = atof(na+2);
601 if (refrate < 5.0 || refrate > 150.0)
602 usage(0,"-Y R:rate %f Hz not in valid range",refrate);
603 } else if (na[0] == 'p') {
604 noplace = 1;
605 } else if (na[0] == 'A') {
606 nadaptive = 1;
607 } else {
608 usage(0,"Flag '-Y %c' not recognised",na[0]);
609 }
610 fa = nfa;
611
612 } else
613 usage(0,"Flag '-%c' not recognised",argv[fa][1]);
614 }
615 else
616 break;
617 }
618
619 /* No explicit display has been set */
620 if (!fake
621 #ifdef NT
622 && madvrdisp == 0
623 #endif
624 && webdisp == 0
625 && ccdisp == 0
626 && disp == NULL) {
627 int ix = 0;
628 #if defined(UNIX_X11)
629 char *dn, *pp;
630
631 if ((dn = getenv("DISPLAY")) != NULL) {
632 if ((pp = strrchr(dn, ':')) != NULL) {
633 if ((pp = strchr(pp, '.')) != NULL) {
634 if (pp[1] != '\000')
635 ix = atoi(pp+1);
636 }
637 }
638 }
639 #endif
640 if ((disp = get_a_display(ix)) == NULL)
641 error("Unable to open the default display");
642 }
643
644 /* See if there is an environment variable ccxx */
645 if (ccxxname[0] == '\000') {
646 char *na;
647 if ((na = getenv("ARGYLL_COLMTER_CAL_SPEC_SET")) != NULL) {
648 strncpy(ccxxname,na,MAXNAMEL-1); ccxxname[MAXNAMEL-1] = '\000';
649
650 } else if ((na = getenv("ARGYLL_COLMTER_COR_MATRIX")) != NULL) {
651 strncpy(ccxxname,na,MAXNAMEL-1); ccxxname[MAXNAMEL-1] = '\000';
652 }
653 }
654
655 /* Load up CCMX or CCSS */
656 if (ccxxname[0] != '\000') {
657 if ((cmx = new_ccmx()) == NULL
658 || cmx->read_ccmx(cmx, ccxxname)) {
659 if (cmx != NULL) {
660 cmx->del(cmx);
661 cmx = NULL;
662 }
663
664 /* CCMX failed, try CCSS */
665 if ((ccs = new_ccss()) == NULL
666 || ccs->read_ccss(ccs, ccxxname)) {
667 if (ccs != NULL) {
668 ccs->del(ccs);
669 ccs = NULL;
670 error("Reading CCMX/CCSS File '%s' failed\n", ccxxname);
671 }
672 }
673 }
674 }
675
676 if (fake)
677 comport = FAKE_DEVICE_PORT;
678 if ((icmps = new_icompaths(g_log)) == NULL)
679 error("Finding instrument paths failed");
680 if ((ipath = icmps->get_path(icmps, comport)) == NULL)
681 error("No instrument at port %d",comport);
682
683 /* If we've requested ChromeCast, look it up */
684 if (ccdisp) {
685 if ((ccids = get_ccids()) == NULL)
686 error("discovering ChromCasts failed");
687 if (ccids[0] == NULL)
688 error("There are no ChromCasts to use\n");
689 for (i = 0; ccids[i] != NULL; i++)
690 ;
691 if (ccdisp < 1 || ccdisp > i)
692 error("Chosen ChromCasts (%d) is outside list (1..%d)\n",ccdisp,i);
693 ccid = ccids[ccdisp-1];
694 }
695
696 if (docalib) {
697 if ((rv = disprd_calibration(ipath, fc, dtype, -1, 0, tele, nadaptive, noautocal,
698 disp, webdisp, ccid,
699 #ifdef NT
700 madvrdisp,
701 #endif
702 out_tvenc, fullscreen, override,
703 100.0 * hpatscale, 100.0 * vpatscale, ho, vo,
704 g_log)) != 0) {
705 error("docalibration failed with return value %d\n",rv);
706 }
707 }
708
709 /* Get the file name argument */
710 if (fa >= argc || argv[fa][0] == '-') usage(0,"Filname parameter not found");
711 strncpy(inname,argv[fa++],MAXNAMEL-4); inname[MAXNAMEL-4] = '\000';
712 strcpy(outname,inname);
713 strcat(inname,".ti1");
714 strcat(outname,".ti3");
715
716 icg = new_cgats(); /* Create a CGATS structure */
717 icg->add_other(icg, "CTI1"); /* our special input type is Calibration Target Information 1 */
718
719 if (icg->read_name(icg, inname))
720 error("CGATS file read error : %s",icg->err);
721
722 if (icg->ntables == 0 || icg->t[0].tt != tt_other || icg->t[0].oi != 0)
723 error ("Input file '%s' isn't a CTI1 format file",inname);
724 if (icg->ntables < 1) /* We don't use second table at the moment */
725 error ("Input file '%s' doesn't contain at least one table",inname);
726
727 if ((npat = icg->t[0].nsets) <= 0)
728 error ("Input file '%s' has no sets of data",inname);
729
730 /* Setup output cgats file */
731 ocg = new_cgats(); /* Create a CGATS structure */
732 ocg->add_other(ocg, "CTI3"); /* our special type is Calibration Target Information 3 */
733 ocg->add_table(ocg, tt_other, 0); /* Start the first table */
734
735 ocg->add_kword(ocg, 0, "DESCRIPTOR", "Argyll Calibration Target chart information 3",NULL);
736 ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll dispread", NULL);
737 atm[strlen(atm)-1] = '\000'; /* Remove \n from end */
738 ocg->add_kword(ocg, 0, "CREATED",atm, NULL);
739 ocg->add_kword(ocg, 0, "DEVICE_CLASS","DISPLAY", NULL); /* What sort of device this is */
740
741 if ((ti = icg->find_kword(icg, 0, "SINGLE_DIM_STEPS")) >= 0)
742 ocg->add_kword(ocg, 0, "SINGLE_DIM_STEPS",icg->t[0].kdata[ti], NULL);
743
744 if ((ti = icg->find_kword(icg, 0, "COMP_GREY_STEPS")) >= 0)
745 ocg->add_kword(ocg, 0, "COMP_GREY_STEPS",icg->t[0].kdata[ti], NULL);
746
747 if ((ti = icg->find_kword(icg, 0, "MULTI_DIM_STEPS")) >= 0)
748 ocg->add_kword(ocg, 0, "MULTI_DIM_STEPS",icg->t[0].kdata[ti], NULL);
749
750 if ((ti = icg->find_kword(icg, 0, "FULL_SPREAD_PATCHES")) >= 0)
751 ocg->add_kword(ocg, 0, "FULL_SPREAD_PATCHES",icg->t[0].kdata[ti], NULL);
752
753 if ((ti = icg->find_kword(icg, 0, "DARK_REGION_EMPHASIS")) >= 0)
754 ocg->add_kword(ocg, 0, "DARK_REGION_EMPHASIS",icg->t[0].kdata[ti], NULL);
755
756 if (verb) {
757 printf("Number of patches = %d\n",npat);
758 }
759
760 /* Fields we want */
761 ocg->add_field(ocg, 0, "SAMPLE_ID", nqcs_t);
762
763 if ((si = icg->find_field(icg, 0, "SAMPLE_ID")) < 0)
764 error ("Input file '%s' doesn't contain field SAMPLE_ID",inname);
765 if (icg->t[0].ftype[si] != nqcs_t)
766 error ("Input file %s' field SAMPLE_ID is wrong type",inname);
767
768 if ((cols = (col *)malloc(sizeof(col) * (npat+1))) == NULL)
769 error("Malloc failed!");
770
771 /* Figure out the color space */
772 /* Read all the test patches in, and quantize them */
773 if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0)
774 error ("Input file '%s' doesn't contain keyword COLOR_REP",inname);
775 if (strcmp(icg->t[0].kdata[fi],"RGB") == 0) {
776 int ri, gi, bi;
777 double rgb[3];
778 double qscale = (1 << qbits) - 1.0;
779 dim = 3;
780 if ((ri = icg->find_field(icg, 0, "RGB_R")) < 0)
781 error ("Input file '%s' doesn't contain field RGB_R",inname);
782 if (icg->t[0].ftype[ri] != r_t)
783 error ("Input file '%s' field RGB_R is wrong type - expect float",inname);
784 if ((gi = icg->find_field(icg, 0, "RGB_G")) < 0)
785 error ("Input file '%s' doesn't contain field RGB_G",inname);
786 if (icg->t[0].ftype[gi] != r_t)
787 error ("Input file '%s' field RGB_G is wrong type - expect float",inname);
788 if ((bi = icg->find_field(icg, 0, "RGB_B")) < 0)
789 error ("Input file '%s' doesn't contain field RGB_B",inname);
790 if (icg->t[0].ftype[bi] != r_t)
791 error ("Input file '%s' field RGB_B is wrong type - expect float",inname);
792 ocg->add_field(ocg, 0, "RGB_R", r_t);
793 ocg->add_field(ocg, 0, "RGB_G", r_t);
794 ocg->add_field(ocg, 0, "RGB_B", r_t);
795 ocg->add_kword(ocg, 0, "COLOR_REP","RGB_XYZ", NULL);
796 ocg->add_field(ocg, 0, "XYZ_X", r_t);
797 ocg->add_field(ocg, 0, "XYZ_Y", r_t);
798 ocg->add_field(ocg, 0, "XYZ_Z", r_t);
799 for (i = 0; i < npat; i++) {
800 cols[i].id = ((char *)icg->t[0].fdata[i][si]);
801 rgb[0] = *((double *)icg->t[0].fdata[i][ri]) / 100.0;
802 rgb[1] = *((double *)icg->t[0].fdata[i][gi]) / 100.0;
803 rgb[2] = *((double *)icg->t[0].fdata[i][bi]) / 100.0;
804 if (qbits > 0) {
805 double vr;
806 for (j = 0; j < 3; j++) {
807 rgb[j] *= qscale;
808 vr = floor(rgb[j] + 0.5);
809 if ((vr - rgb[j]) == 0.5 && (((int)vr) & 1) != 0) /* Round to even */
810 vr -= 1.0;
811 rgb[j] = vr/qscale;
812 }
813 }
814 cols[i].r = rgb[0];
815 cols[i].g = rgb[1];
816 cols[i].b = rgb[2];
817 cols[i].XYZ[0] = cols[i].XYZ[1] = cols[i].XYZ[2] = -1.0;
818 }
819 } else
820 error ("Input file '%s' keyword COLOR_REP has illegal value (RGB colorspace expected)",inname);
821
822 /* Check that there is a white patch, and if not, add one, */
823 /* so that we can normalize the values to white. */
824 for (wpat = 0; wpat < npat; wpat++) {
825 if (cols[wpat].r > 0.9999999 &&
826 cols[wpat].g > 0.9999999 &&
827 cols[wpat].b > 0.9999999) {
828 break;
829 }
830 }
831 if (wpat >= npat) { /* Create a white patch */
832 if (verb)
833 printf("Adding one white patch\n");
834 xpat = 1;
835 cols[wpat].r = cols[wpat].g = cols[wpat].b = 1.0;
836 }
837
838 /* Setup a display calibration set if we are given one */
839 /* (Should switch to xcal ?) */
840 if (calname[0] != '\000') {
841 cgats *ccg; /* calibration cgats structure */
842 int ii, ri, gi, bi;
843
844 ccg = new_cgats(); /* Create a CGATS structure */
845 ccg->add_other(ccg, "CAL"); /* our special calibration type */
846
847 if (ccg->read_name(ccg, calname))
848 error("CGATS calibration file read error %s on file '%s'",ccg->err,calname);
849
850 if (ccg->ntables == 0 || ccg->t[0].tt != tt_other || ccg->t[0].oi != 0)
851 error ("Calibration file isn't a CAL format file");
852 if (ccg->ntables < 1)
853 error ("Calibration file '%s' doesn't contain at least one table",calname);
854
855 if ((ncal = ccg->t[0].nsets) <= 0)
856 error ("No data in set of file '%s'",calname);
857
858 if (ncal < 2 || ncal > MAX_CAL_ENT)
859 error("Data set size %d is out of range for '%s'",ncal,calname);
860
861 if (ncal > MAX_CAL_ENT)
862 error ("Cant handle %d data sets in file '%s', max is %d",ncal,calname,MAX_CAL_ENT);
863
864 if ((fi = ccg->find_kword(ccg, 0, "DEVICE_CLASS")) < 0)
865 error ("Calibration file '%s' doesn't contain keyword DEVICE_CLASS",calname);
866 if (strcmp(ccg->t[0].kdata[fi],"DISPLAY") != 0)
867 error ("Calibration file '%s' doesn't have DEVICE_CLASS of DISPLAY",calname);
868
869 if ((fi = ccg->find_kword(ccg, 0, "VIDEO_LUT_CALIBRATION_POSSIBLE")) >= 0) {
870 if (stricmp(ccg->t[0].kdata[fi],"NO") == 0) {
871 native = 1;
872 if (verb) printf("Switching to soft cal because there is no access to VideoLUTs\n");
873 }
874 }
875
876 if ((fi = ccg->find_kword(ccg, 0, "TV_OUTPUT_ENCODING")) >= 0) {
877 if (!out_tvenc && (strcmp(ccg->t[0].kdata[fi], "YES") == 0
878 || strcmp(ccg->t[0].kdata[fi], "yes")) == 0) {
879 if (verb) printf("Using Video range (16-235)/255 range encoding because cal. used it\n");
880 out_tvenc = 1;
881 }
882 }
883
884 if ((fi = ccg->find_kword(ccg, 0, "COLOR_REP")) < 0)
885 error ("Calibration file '%s' doesn't contain keyword COLOR_REP",calname);
886 if (strcmp(ccg->t[0].kdata[fi],"RGB") != 0)
887 error ("Calibration file '%s' doesn't have COLOR_REP of RGB",calname);
888
889 if ((ii = ccg->find_field(ccg, 0, "RGB_I")) < 0)
890 error ("Calibration file '%s' doesn't contain field RGB_I",calname);
891 if (ccg->t[0].ftype[ii] != r_t)
892 error ("Field RGB_R in file '%s' is wrong type - expect float",calname);
893 if ((ri = ccg->find_field(ccg, 0, "RGB_R")) < 0)
894 error ("Calibration file '%s' doesn't contain field RGB_R",calname);
895 if (ccg->t[0].ftype[ri] != r_t)
896 error ("Field RGB_R in file '%s' is wrong type - expect float",calname);
897 if ((gi = ccg->find_field(ccg, 0, "RGB_G")) < 0)
898 error ("Calibration file '%s' doesn't contain field RGB_G",calname);
899 if (ccg->t[0].ftype[gi] != r_t)
900 error ("Field RGB_G in file '%s' is wrong type - expect float",calname);
901 if ((bi = ccg->find_field(ccg, 0, "RGB_B")) < 0)
902 error ("Calibration file '%s' doesn't contain field RGB_B",calname);
903 if (ccg->t[0].ftype[bi] != r_t)
904 error ("Field RGB_B in file '%s' is wrong type - expect float",calname);
905 for (i = 0; i < ncal; i++) {
906 cal[0][i] = *((double *)ccg->t[0].fdata[i][ri]);
907 cal[1][i] = *((double *)ccg->t[0].fdata[i][gi]);
908 cal[2][i] = *((double *)ccg->t[0].fdata[i][bi]);
909 }
910 ccg->del(ccg);
911 } else {
912 cal[0][0] = -1.0; /* Not used */
913 }
914
915 if ((dr = new_disprd(&errc, ipath, fc, dtype, -1, 0, tele, nadaptive, noautocal, noplace,
916 highres, refrate, native, &noramdac, &nocm, cal, ncal, disp,
917 out_tvenc, fullscreen, override, webdisp, ccid,
918 #ifdef NT
919 madvrdisp,
920 #endif
921 ccallout, mcallout, xtern,
922 100.0 * hpatscale, 100.0 * vpatscale, ho, vo,
923 ccs != NULL ? ccs->dtech : cmx != NULL ? cmx->dtech : disptech_unknown,
924 cmx != NULL ? cmx->cc_cbid : 0,
925 cmx != NULL ? cmx->matrix : NULL,
926 ccs != NULL ? ccs->samples : NULL, ccs != NULL ? ccs->no_samp : 0,
927 spec, obType, NULL, bdrift, wdrift,
928 "fake" ICC_FILE_EXT, g_log)) == NULL)
929 error("new_disprd failed with '%s'\n",disprd_err(errc));
930
931 if (cmx != NULL)
932 cmx->del(cmx);
933 if (ccs != NULL)
934 ccs->del(ccs);
935
936 /* Test the CRT with all of the test points */
937 if ((rv = dr->read(dr, cols, npat + xpat, 1, npat + xpat, 1, 0, instNoClamp)) != 0) {
938 dr->del(dr);
939 error("dispd->read returned error code %d\n",rv);
940 }
941 /* Note what instrument the chart was read with */
942 if (dr->it != NULL) {
943 int refrmode, cbid;
944 ocg->add_kword(ocg, 0, "TARGET_INSTRUMENT", inst_name(dr->it->get_itype(dr->it)) , NULL);
945 dr->get_disptype(dr, &refrmode, &cbid);
946 if (refrmode >= 0)
947 ocg->add_kword(ocg, 0, "DISPLAY_TYPE_REFRESH", refrmode ? "YES" : "NO", NULL);
948 if (cbid != 0) {
949 char buf[100];
950 sprintf(buf, "%d", cbid);
951 ocg->add_kword(ocg, 0, "DISPLAY_TYPE_BASE_ID", buf, NULL);
952 }
953 } else {
954 ocg->add_kword(ocg, 0, "TARGET_INSTRUMENT", "Fake" , NULL);
955 }
956
957 /* Note if the instrument is natively spectral or not */
958 if (dr->it != NULL) {
959 inst_mode cap;
960 dr->it->capabilities(dr->it, &cap, NULL, NULL);
961
962 if (dr->it != NULL && cap & inst_mode_spectral)
963 ocg->add_kword(ocg, 0, "INSTRUMENT_TYPE_SPECTRAL", "YES" , NULL);
964 else
965 ocg->add_kword(ocg, 0, "INSTRUMENT_TYPE_SPECTRAL", "NO" , NULL);
966 } else {
967 ocg->add_kword(ocg, 0, "INSTRUMENT_TYPE_SPECTRAL", "NO" , NULL);
968 }
969
970 dr->del(dr);
971
972 /* And save the result: */
973
974 /* Convert from absolute XYZ to relative XYZ */
975 if (npat > 0) {
976 double nn;
977
978 /* Make sure there is a copy of the white patch beyond npat, */
979 /* so that it can be left absolute. */
980 if (wpat != npat) {
981 cols[npat].r = cols[npat].g = cols[npat].b = 1.0;
982 cols[npat].XYZ[0] = cols[wpat].XYZ[0];
983 cols[npat].XYZ[1] = cols[wpat].XYZ[1];
984 cols[npat].XYZ[2] = cols[wpat].XYZ[2];
985 cols[npat].XYZ_v = cols[wpat].XYZ_v;
986 wpat = npat;
987 }
988
989 if (donorm) {
990 if (cols[wpat].XYZ_v == 0)
991 error("XYZ of white patch is not valid!\nCan't normalise value to white",i);
992
993 nn = 100.0 / cols[wpat].XYZ[1]; /* Normalise Y of white to 100 */
994 } else
995 nn = 1.0;
996
997 for (i = 0; i < npat; i++) {
998
999 if (cols[i].XYZ_v == 0)
1000 warning("XYZ patch %d is not valid!",i+1);
1001
1002 for (j = 0; j < 3; j++)
1003 cols[i].XYZ[j] = nn * cols[i].XYZ[j];
1004
1005 /* Keep spectral aligned with normalised XYZ */
1006 if (cols[i].sp.spec_n > 0) {
1007 for (j = 0; j < cols[i].sp.spec_n; j++)
1008 cols[i].sp.spec[j] *= nn;
1009 }
1010 }
1011 }
1012
1013 nsetel += 1; /* For id */
1014 nsetel += dim; /* For device values */
1015 nsetel += 3; /* For XYZ */
1016
1017 /* If we have spectral information, output it too */
1018 if (npat > 0 && cols[0].sp.spec_n > 0) {
1019 char buf[100];
1020
1021 nsetel += cols[0].sp.spec_n; /* Spectral values */
1022 sprintf(buf,"%d", cols[0].sp.spec_n);
1023 ocg->add_kword(ocg, 0, "SPECTRAL_BANDS",buf, NULL);
1024 sprintf(buf,"%f", cols[0].sp.spec_wl_short);
1025 ocg->add_kword(ocg, 0, "SPECTRAL_START_NM",buf, NULL);
1026 sprintf(buf,"%f", cols[0].sp.spec_wl_long);
1027 ocg->add_kword(ocg, 0, "SPECTRAL_END_NM",buf, NULL);
1028
1029 /* Generate fields for spectral values */
1030 for (i = 0; i < cols[0].sp.spec_n; i++) {
1031 int nm;
1032
1033 /* Compute nearest integer wavelength */
1034 nm = (int)(cols[0].sp.spec_wl_short + ((double)i/(cols[0].sp.spec_n-1.0))
1035 * (cols[0].sp.spec_wl_long - cols[0].sp.spec_wl_short) + 0.5);
1036
1037 sprintf(buf,"SPEC_%03d",nm);
1038 ocg->add_field(ocg, 0, buf, r_t);
1039 }
1040 }
1041
1042 if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * nsetel)) == NULL)
1043 error("Malloc failed!");
1044
1045 /* Write out the patch info to the output CGATS file */
1046 for (i = 0; i < npat; i++) {
1047 int k = 0;
1048
1049 if (cols[i].XYZ_v == 0) {
1050 warning("Omitting patch %d from .ti3 file!",i+1);
1051 continue;
1052 }
1053
1054 setel[k++].c = cols[i].id;
1055 setel[k++].d = 100.0 * cols[i].r;
1056 setel[k++].d = 100.0 * cols[i].g;
1057 setel[k++].d = 100.0 * cols[i].b;
1058
1059 setel[k++].d = cols[i].XYZ[0];
1060 setel[k++].d = cols[i].XYZ[1];
1061 setel[k++].d = cols[i].XYZ[2];
1062
1063 for (j = 0; j < cols[i].sp.spec_n; j++) {
1064 setel[k++].d = cols[i].sp.spec[j];
1065 }
1066
1067 ocg->add_setarr(ocg, 0, setel);
1068 }
1069
1070 free(setel);
1071
1072 /* If we have the absolute brightness of the display, record it */
1073 if (cols[wpat].XYZ_v != 0) {
1074 char buf[100];
1075
1076 sprintf(buf,"%f %f %f", cols[wpat].XYZ[0], cols[wpat].XYZ[1], cols[wpat].XYZ[2]);
1077 ocg->add_kword(ocg, 0, "LUMINANCE_XYZ_CDM2",buf, NULL);
1078 }
1079
1080 if (donorm)
1081 ocg->add_kword(ocg, 0, "NORMALIZED_TO_Y_100","YES", NULL);
1082 else
1083 ocg->add_kword(ocg, 0, "NORMALIZED_TO_Y_100","NO", NULL);
1084
1085 /* Write out the calibration if we have it */
1086 if (cal != NULL && cal[0][0] >= 0.0) {
1087 ocg->add_other(ocg, "CAL"); /* our special type is Calibration file */
1088 ocg->add_table(ocg, tt_other, 1); /* Add another table for RAMDAC values */
1089 ocg->add_kword(ocg, 1, "DESCRIPTOR", "Argyll Device Calibration State",NULL);
1090 ocg->add_kword(ocg, 1, "ORIGINATOR", "Argyll dispread", NULL);
1091 ocg->add_kword(ocg, 1, "CREATED",atm, NULL);
1092
1093 ocg->add_kword(ocg, 1, "DEVICE_CLASS","DISPLAY", NULL);
1094 ocg->add_kword(ocg, 1, "COLOR_REP","RGB", NULL);
1095 ocg->add_kword(ocg, 0, "VIDEO_LUT_CALIBRATION_POSSIBLE",noramdac ? "NO" : "YES", NULL);
1096
1097 ocg->add_field(ocg, 1, "RGB_I", r_t);
1098 ocg->add_field(ocg, 1, "RGB_R", r_t);
1099 ocg->add_field(ocg, 1, "RGB_G", r_t);
1100 ocg->add_field(ocg, 1, "RGB_B", r_t);
1101
1102 if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * 4)) == NULL)
1103 error("Malloc failed!");
1104
1105 for (i = 0; i < ncal; i++) {
1106 double vv;
1107
1108 #if defined(__APPLE__) && defined(__POWERPC__)
1109 gcc_bug_fix(i);
1110 #endif
1111 vv = i/(ncal-1.0);
1112
1113 setel[0].d = vv;
1114 setel[1].d = cal[0][i];
1115 setel[2].d = cal[1][i];
1116 setel[3].d = cal[2][i];
1117
1118 ocg->add_setarr(ocg, 1, setel);
1119 }
1120
1121 free(setel);
1122 }
1123
1124 if (ocg->write_name(ocg, outname))
1125 error("Write error : %s",ocg->err);
1126
1127 if (verb)
1128 printf("Written '%s'\n",outname);
1129
1130 icmps->del(icmps);
1131 free(cols);
1132 ocg->del(ocg); /* Clean up */
1133 icg->del(icg); /* Clean up */
1134 free_a_disppath(disp);
1135 free_ccids(ccids);
1136
1137 return 0;
1138 }
1139
1140
1141