| 1 | – | /* Copyright (c) 1995 Regents of the University of California */ | 
| 2 | – |  | 
| 1 |  | #ifndef lint | 
| 2 | < | static char SCCSid[] = "$SunId$ LBL"; | 
| 2 | > | static const char       RCSid[] = "$Id$"; | 
| 3 |  | #endif | 
| 6 | – |  | 
| 4 |  | /* | 
| 5 |  | * Calibrate a scanned MacBeth Color Checker Chart | 
| 6 |  | * | 
| 7 | < | * Produce a .cal file suitable for use with pcomb. | 
| 7 | > | * Produce a .cal file suitable for use with pcomb, | 
| 8 | > | * or .cwp file suitable for use with pcwarp. | 
| 9 | > | * | 
| 10 | > | * Warping code depends on conformance of COLOR and W3VEC types. | 
| 11 |  | */ | 
| 12 |  |  | 
| 13 |  | #include <stdio.h> | 
| 14 | < | #ifdef MSDOS | 
| 15 | < | #include <fcntl.h> | 
| 16 | < | #endif | 
| 14 | > | #include <math.h> | 
| 15 | > | #include <time.h> | 
| 16 | > |  | 
| 17 | > | #include "platform.h" | 
| 18 |  | #include "color.h" | 
| 19 |  | #include "resolu.h" | 
| 20 |  | #include "pmap.h" | 
| 21 | + | #include "warp3d.h" | 
| 22 |  |  | 
| 23 |  | /* MacBeth colors */ | 
| 24 |  | #define DarkSkin        0 | 
| 79 |  | #define NMBNEU          6       /* Number of MacBeth neutral colors */ | 
| 80 |  | short   mbneu[NMBNEU] = {Black,Neutral35,Neutral5,Neutral65,Neutral8,White}; | 
| 81 |  |  | 
| 82 | < | #define NMBMOD          17      /* Number of MacBeth unsaturated colors */ | 
| 83 | < | short   mbmod[NMBMOD] = { | 
| 82 | < | DarkSkin,LightSkin,BlueSky,Foliage,BlueFlower,BluishGreen, | 
| 83 | < | Orange,PurplishBlue,ModerateRed,Purple,YellowGreen, | 
| 84 | < | Black,Neutral35,Neutral5,Neutral65,Neutral8,White | 
| 85 | < | }; | 
| 82 | > | #define  NEUFLGS        (1L<<White|1L<<Neutral8|1L<<Neutral65| \ | 
| 83 | > | 1L<<Neutral5|1L<<Neutral35|1L<<Black) | 
| 84 |  |  | 
| 85 | < | #define NMBSAT          8       /* Number of MacBeth saturated colors */ | 
| 86 | < | short   mbsat[NMBSAT] = { | 
| 87 | < | Red,Green,Blue,Magenta,Yellow,Cyan, | 
| 90 | < | Orange,OrangeYellow | 
| 91 | < | }; | 
| 85 | > | #define  SATFLGS        (1L<<Red|1L<<Green|1L<<Blue|1L<<Magenta|1L<<Yellow| \ | 
| 86 | > | 1L<<Cyan|1L<<Orange|1L<<Purple|1L<<PurplishBlue| \ | 
| 87 | > | 1L<<YellowGreen|1<<OrangeYellow|1L<<BlueFlower) | 
| 88 |  |  | 
| 89 | + | #define  UNSFLGS        (1L<<DarkSkin|1L<<LightSkin|1L<<BlueSky|1L<<Foliage| \ | 
| 90 | + | 1L<<BluishGreen|1L<<ModerateRed) | 
| 91 | + |  | 
| 92 | + | #define  REQFLGS        NEUFLGS                 /* need these colors */ | 
| 93 | + | #define  MODFLGS        (NEUFLGS|UNSFLGS)       /* should be in gamut */ | 
| 94 | + |  | 
| 95 | + | #define  RG_BORD        0       /* patch border */ | 
| 96 | + | #define  RG_CENT        01      /* central region of patch */ | 
| 97 | + | #define  RG_ORIG        02      /* original color region */ | 
| 98 | + | #define  RG_CORR        04      /* corrected color region */ | 
| 99 | + |  | 
| 100 | + | #ifndef  DISPCOM | 
| 101 | + | #define  DISPCOM        "ximage -op %s" | 
| 102 | + | #endif | 
| 103 | + |  | 
| 104 | + | int     scanning = 1;           /* scanned input (or recorded output)? */ | 
| 105 | + | double  irrad = 1.0;            /* irradiance multiplication factor */ | 
| 106 | + | int     rawmap = 0;             /* put out raw color mapping? */ | 
| 107 | + |  | 
| 108 |  | int     xmax, ymax;             /* input image dimensions */ | 
| 109 |  | int     bounds[4][2];           /* image coordinates of chart corners */ | 
| 110 |  | double  imgxfm[3][3];           /* coordinate transformation matrix */ | 
| 111 |  |  | 
| 112 | < | COLOR   picRGB[24];             /* picture colors */ | 
| 112 | > | COLOR   inpRGB[24];             /* measured or scanned input colors */ | 
| 113 | > | long    inpflags = 0;           /* flags of which colors were input */ | 
| 114 | > | long    gmtflags = 0;           /* flags of out-of-gamut colors */ | 
| 115 |  |  | 
| 116 |  | COLOR   bramp[NMBNEU][2];       /* brightness ramp (per primary) */ | 
| 117 | < | double  solmat[3][3];           /* color mapping matrix */ | 
| 117 | > | COLORMAT        solmat;         /* color mapping matrix */ | 
| 118 | > | COLOR   colmin, colmax;         /* gamut limits */ | 
| 119 |  |  | 
| 120 | + | WARP3D  *wcor = NULL;           /* color space warp */ | 
| 121 | + |  | 
| 122 |  | FILE    *debugfp = NULL;        /* debug output picture */ | 
| 123 |  | char    *progname; | 
| 124 |  |  | 
| 105 | – | extern char     *malloc(); | 
| 125 |  |  | 
| 107 | – |  | 
| 126 |  | main(argc, argv) | 
| 127 |  | int     argc; | 
| 128 |  | char    **argv; | 
| 130 |  | int     i; | 
| 131 |  |  | 
| 132 |  | progname = argv[0]; | 
| 133 | < | if (argc > 2 && !strcmp(argv[1], "-d")) {       /* debug output */ | 
| 134 | < | if ((debugfp = fopen(argv[2], "w")) == NULL) { | 
| 135 | < | perror(argv[2]); | 
| 136 | < | exit(1); | 
| 133 | > | for (i = 1; i < argc && argv[i][0] == '-'; i++) | 
| 134 | > | switch (argv[i][1]) { | 
| 135 | > | case 'd':                               /* debug output */ | 
| 136 | > | i++; | 
| 137 | > | if (badarg(argc-i, argv+i, "s")) | 
| 138 | > | goto userr; | 
| 139 | > | if ((debugfp = fopen(argv[i], "w")) == NULL) { | 
| 140 | > | perror(argv[i]); | 
| 141 | > | exit(1); | 
| 142 | > | } | 
| 143 | > | SET_FILE_BINARY(debugfp); | 
| 144 | > | newheader("RADIANCE", debugfp);         /* start */ | 
| 145 | > | printargs(argc, argv, debugfp);         /* header */ | 
| 146 | > | break; | 
| 147 | > | case 'p':                               /* picture position */ | 
| 148 | > | if (badarg(argc-i-1, argv+i+1, "iiiiiiii")) | 
| 149 | > | goto userr; | 
| 150 | > | bounds[0][0] = atoi(argv[++i]); | 
| 151 | > | bounds[0][1] = atoi(argv[++i]); | 
| 152 | > | bounds[1][0] = atoi(argv[++i]); | 
| 153 | > | bounds[1][1] = atoi(argv[++i]); | 
| 154 | > | bounds[2][0] = atoi(argv[++i]); | 
| 155 | > | bounds[2][1] = atoi(argv[++i]); | 
| 156 | > | bounds[3][0] = atoi(argv[++i]); | 
| 157 | > | bounds[3][1] = atoi(argv[++i]); | 
| 158 | > | scanning = 2; | 
| 159 | > | break; | 
| 160 | > | case 'P':                               /* pick position */ | 
| 161 | > | scanning = 3; | 
| 162 | > | break; | 
| 163 | > | case 'i':                               /* irradiance factor */ | 
| 164 | > | i++; | 
| 165 | > | if (badarg(argc-i, argv+i, "f")) | 
| 166 | > | goto userr; | 
| 167 | > | irrad = atof(argv[i]); | 
| 168 | > | break; | 
| 169 | > | case 'm':                               /* raw map output */ | 
| 170 | > | rawmap = 1; | 
| 171 | > | break; | 
| 172 | > | case 'c':                               /* color input */ | 
| 173 | > | scanning = 0; | 
| 174 | > | break; | 
| 175 | > | default: | 
| 176 | > | goto userr; | 
| 177 |  | } | 
| 178 | < | #ifdef MSDOS | 
| 179 | < | setmode(fileno(debugfp), O_BINARY); | 
| 180 | < | #endif | 
| 123 | < | newheader("RADIANCE", debugfp); | 
| 124 | < | printargs(argc, argv, debugfp); | 
| 125 | < | argv += 2; | 
| 126 | < | argc -= 2; | 
| 127 | < | } | 
| 128 | < | if (argc != 3 && argc != 11) | 
| 129 | < | goto userr; | 
| 130 | < | if (strcmp(argv[1], "-") && freopen(argv[1], "r", stdin) == NULL) { | 
| 131 | < | perror(argv[1]); | 
| 178 | > | /* open files */ | 
| 179 | > | if (i < argc && freopen(argv[i], "r", stdin) == NULL) { | 
| 180 | > | perror(argv[i]); | 
| 181 |  | exit(1); | 
| 182 |  | } | 
| 183 | < | if (strcmp(argv[2], "-") && freopen(argv[2], "w", stdout) == NULL) { | 
| 184 | < | perror(argv[2]); | 
| 183 | > | if (i+1 < argc && freopen(argv[i+1], "w", stdout) == NULL) { | 
| 184 | > | perror(argv[i+1]); | 
| 185 |  | exit(1); | 
| 186 |  | } | 
| 187 | < | #ifdef MSDOS | 
| 188 | < | setmode(fileno(stdin), O_BINARY); | 
| 189 | < | #endif | 
| 190 | < | if (checkheader(stdin, COLRFMT, NULL) < 0 || | 
| 191 | < | fgetresolu(&xmax, &ymax, stdin) < 0) { | 
| 192 | < | fprintf(stderr, "%s: bad input picture\n", progname); | 
| 193 | < | exit(1); | 
| 194 | < | } | 
| 195 | < | /* get chart boundaries */ | 
| 147 | < | if (argc == 11) { | 
| 148 | < | for (i = 0; i < 4; i++) { | 
| 149 | < | if (!isint(argv[2*i+3]) | !isint(argv[2*i+4])) | 
| 187 | > | if (scanning) {                 /* load input picture header */ | 
| 188 | > | SET_FILE_BINARY(stdin); | 
| 189 | > | if (checkheader(stdin, COLRFMT, NULL) < 0 || | 
| 190 | > | fgetresolu(&xmax, &ymax, stdin) < 0) { | 
| 191 | > | fprintf(stderr, "%s: bad input picture\n", progname); | 
| 192 | > | exit(1); | 
| 193 | > | } | 
| 194 | > | if (scanning == 3) { | 
| 195 | > | if (i >= argc) | 
| 196 |  | goto userr; | 
| 197 | < | bounds[i][0] = atoi(argv[2*i+3]); | 
| 198 | < | bounds[i][1] = atoi(argv[2*i+4]); | 
| 197 | > | pickchartpos(argv[i]); | 
| 198 | > | scanning = 2; | 
| 199 |  | } | 
| 200 | < | } else { | 
| 200 | > | } else {                        /* else set default xmax and ymax */ | 
| 201 | > | xmax = 512; | 
| 202 | > | ymax = 2*512/3; | 
| 203 | > | } | 
| 204 | > | if (scanning != 2) {            /* use default boundaries */ | 
| 205 |  | bounds[0][0] = bounds[2][0] = .029*xmax + .5; | 
| 206 |  | bounds[0][1] = bounds[1][1] = .956*ymax + .5; | 
| 207 |  | bounds[1][0] = bounds[3][0] = .971*xmax + .5; | 
| 208 |  | bounds[2][1] = bounds[3][1] = .056*ymax + .5; | 
| 209 |  | } | 
| 210 |  | init();                         /* initialize */ | 
| 211 | < | getcolors();                    /* get picture colors */ | 
| 211 | > | if (scanning)                   /* get picture colors */ | 
| 212 | > | getpicture(); | 
| 213 | > | else | 
| 214 | > | getcolors(); | 
| 215 |  | compute();                      /* compute color mapping */ | 
| 216 | < | /* print comment */ | 
| 217 | < | printf("{ Color correction file computed by %s }\n", progname); | 
| 218 | < | printf("{ from scanned MacBetch color chart %s }\n", argv[1]); | 
| 219 | < | putmapping();                   /* put out color mapping */ | 
| 220 | < | putdebug();                     /* put out debug picture */ | 
| 216 | > | if (rawmap) {                   /* print out raw correspondence */ | 
| 217 | > | register int    j; | 
| 218 | > |  | 
| 219 | > | printf("# Color correspondence produced by:\n#\t\t"); | 
| 220 | > | printargs(argc, argv, stdout); | 
| 221 | > | printf("#\tUsage: pcwarp %s uncorrected.pic > corrected.pic\n", | 
| 222 | > | i+1 < argc ? argv[i+1] : "{this_file}"); | 
| 223 | > | printf("#\t   Or: pcond [options] -m %s orig.pic > output.pic\n", | 
| 224 | > | i+1 < argc ? argv[i+1] : "{this_file}"); | 
| 225 | > | for (j = 0; j < 24; j++) | 
| 226 | > | printf("%f %f %f    %f %f %f\n", | 
| 227 | > | colval(inpRGB[j],RED), colval(inpRGB[j],GRN), | 
| 228 | > | colval(inpRGB[j],BLU), colval(mbRGB[j],RED), | 
| 229 | > | colval(mbRGB[j],GRN), colval(mbRGB[j],BLU)); | 
| 230 | > | if (scanning && debugfp != NULL) | 
| 231 | > | cwarp();                /* color warp for debugging */ | 
| 232 | > | } else {                        /* print color mapping */ | 
| 233 | > | /* print header */ | 
| 234 | > | printf("{\n\tColor correction file computed by:\n\t\t"); | 
| 235 | > | printargs(argc, argv, stdout); | 
| 236 | > | printf("\n\tUsage: pcomb -f %s uncorrected.pic > corrected.pic\n", | 
| 237 | > | i+1 < argc ? argv[i+1] : "{this_file}"); | 
| 238 | > | if (!scanning) | 
| 239 | > | printf("\t   Or: pcond [options] -f %s orig.pic > output.pic\n", | 
| 240 | > | i+1 < argc ? argv[i+1] : "{this_file}"); | 
| 241 | > | printf("}\n"); | 
| 242 | > | putmapping();                   /* put out color mapping */ | 
| 243 | > | } | 
| 244 | > | if (debugfp != NULL)            /* put out debug picture */ | 
| 245 | > | if (scanning) | 
| 246 | > | picdebug(); | 
| 247 | > | else | 
| 248 | > | clrdebug(); | 
| 249 |  | exit(0); | 
| 250 |  | userr: | 
| 251 | < | fprintf(stderr, "Usage: %s [-d dbg.pic] input.pic output.cal [xul yul xur yur xll yll xlr ylr]\n", | 
| 251 | > | fprintf(stderr, | 
| 252 | > | "Usage: %s [-d dbg.pic][-P | -p xul yul xur yur xll yll xlr ylr][-i irrad][-m] input.pic [output.{cal|cwp}]\n", | 
| 253 |  | progname); | 
| 254 | + | fprintf(stderr, "   or: %s [-d dbg.pic][-i irrad][-m] -c [xyY.dat [output.{cal|cwp}]]\n", | 
| 255 | + | progname); | 
| 256 |  | exit(1); | 
| 257 |  | } | 
| 258 |  |  | 
| 260 |  | init()                          /* initialize */ | 
| 261 |  | { | 
| 262 |  | double  quad[4][2]; | 
| 263 | + | register int    i; | 
| 264 |  | /* make coordinate transformation */ | 
| 265 |  | quad[0][0] = bounds[0][0]; | 
| 266 |  | quad[0][1] = bounds[0][1]; | 
| 275 |  | fprintf(stderr, "%s: bad chart boundaries\n", progname); | 
| 276 |  | exit(1); | 
| 277 |  | } | 
| 278 | + | /* map MacBeth colors to RGB space */ | 
| 279 | + | for (i = 0; i < 24; i++) { | 
| 280 | + | xyY2RGB(mbRGB[i], mbxyY[i]); | 
| 281 | + | scalecolor(mbRGB[i], irrad); | 
| 282 | + | } | 
| 283 |  | } | 
| 284 |  |  | 
| 285 |  |  | 
| 286 |  | int | 
| 287 | < | chartndx(x, y)                          /* find color number for position */ | 
| 287 | > | chartndx(x, y, np)                      /* find color number for position */ | 
| 288 |  | int     x, y; | 
| 289 | + | int     *np; | 
| 290 |  | { | 
| 291 |  | double  ipos[3], cpos[3]; | 
| 292 |  | int     ix, iy; | 
| 299 |  | cpos[0] /= cpos[2]; | 
| 300 |  | cpos[1] /= cpos[2]; | 
| 301 |  | if (cpos[0] < 0. || cpos[0] >= 6. || cpos[1] < 0. || cpos[1] >= 4.) | 
| 302 | < | return(-1); | 
| 302 | > | return(RG_BORD); | 
| 303 |  | ix = cpos[0]; | 
| 304 |  | iy = cpos[1]; | 
| 305 |  | fx = cpos[0] - ix; | 
| 306 |  | fy = cpos[1] - iy; | 
| 307 | < | if (fx < .35 || fx >= .65 || fy < .35 || fy >= .65) | 
| 308 | < | return(-1); | 
| 309 | < | return(iy*6 + ix); | 
| 307 | > | *np = iy*6 + ix; | 
| 308 | > | if (fx >= 0.35 && fx < 0.65 && fy >= 0.35 && fy < 0.65) | 
| 309 | > | return(RG_CENT); | 
| 310 | > | if (fx < 0.05 || fx >= 0.95 || fy < 0.05 || fy >= 0.95) | 
| 311 | > | return(RG_BORD); | 
| 312 | > | if (fx >= 0.5)                  /* right side is corrected */ | 
| 313 | > | return(RG_CORR); | 
| 314 | > | return(RG_ORIG);                /* left side is original */ | 
| 315 |  | } | 
| 316 |  |  | 
| 317 |  |  | 
| 318 | < | getcolors()                             /* load in picture colors */ | 
| 318 | > | getpicture()                            /* load in picture colors */ | 
| 319 |  | { | 
| 320 |  | COLR    *scanln; | 
| 321 |  | COLOR   pval; | 
| 322 |  | int     ccount[24]; | 
| 323 |  | double  d; | 
| 324 | < | int     y; | 
| 325 | < | register int    x, i; | 
| 324 | > | int     y, i; | 
| 325 | > | register int    x; | 
| 326 |  |  | 
| 327 |  | scanln = (COLR *)malloc(xmax*sizeof(COLR)); | 
| 328 |  | if (scanln == NULL) { | 
| 330 |  | exit(1); | 
| 331 |  | } | 
| 332 |  | for (i = 0; i < 24; i++) { | 
| 333 | < | setcolor(picRGB[i], 0., 0., 0.); | 
| 333 | > | setcolor(inpRGB[i], 0., 0., 0.); | 
| 334 |  | ccount[i] = 0; | 
| 335 |  | } | 
| 336 |  | for (y = ymax-1; y >= 0; y--) { | 
| 339 |  | progname); | 
| 340 |  | exit(1); | 
| 341 |  | } | 
| 342 | < | for (x = 0; x < xmax; x++) { | 
| 343 | < | i = chartndx(x, y); | 
| 248 | < | if (i >= 0) { | 
| 342 | > | for (x = 0; x < xmax; x++) | 
| 343 | > | if (chartndx(x, y, &i) == RG_CENT) { | 
| 344 |  | colr_color(pval, scanln[x]); | 
| 345 | < | addcolor(picRGB[i], pval); | 
| 345 | > | addcolor(inpRGB[i], pval); | 
| 346 |  | ccount[i]++; | 
| 347 |  | } | 
| 348 | + | } | 
| 349 | + | for (i = 0; i < 24; i++) {              /* compute averages */ | 
| 350 | + | if (ccount[i] == 0) | 
| 351 | + | continue; | 
| 352 | + | d = 1./ccount[i]; | 
| 353 | + | scalecolor(inpRGB[i], d); | 
| 354 | + | inpflags |= 1L<<i; | 
| 355 | + | } | 
| 356 | + | free((void *)scanln); | 
| 357 | + | } | 
| 358 | + |  | 
| 359 | + |  | 
| 360 | + | getcolors()                     /* get xyY colors from standard input */ | 
| 361 | + | { | 
| 362 | + | int     gotwhite = 0; | 
| 363 | + | COLOR   whiteclr; | 
| 364 | + | int     n; | 
| 365 | + | float   xyYin[3]; | 
| 366 | + |  | 
| 367 | + | while (fgetval(stdin, 'i', &n) == 1) {          /* read colors */ | 
| 368 | + | if (n < 0 | n > 24 || | 
| 369 | + | fgetval(stdin, 'f', &xyYin[0]) != 1 || | 
| 370 | + | fgetval(stdin, 'f', &xyYin[1]) != 1 || | 
| 371 | + | fgetval(stdin, 'f', &xyYin[2]) != 1 || | 
| 372 | + | xyYin[0] < 0. | xyYin[1] < 0. || | 
| 373 | + | xyYin[0] + xyYin[1] > 1.) { | 
| 374 | + | fprintf(stderr, "%s: bad color input data\n", | 
| 375 | + | progname); | 
| 376 | + | exit(1); | 
| 377 |  | } | 
| 378 | + | if (n == 0) {                           /* calibration white */ | 
| 379 | + | xyY2RGB(whiteclr, xyYin); | 
| 380 | + | gotwhite++; | 
| 381 | + | } else {                                /* standard color */ | 
| 382 | + | n--; | 
| 383 | + | xyY2RGB(inpRGB[n], xyYin); | 
| 384 | + | inpflags |= 1L<<n; | 
| 385 | + | } | 
| 386 |  | } | 
| 387 | < | for (i = 0; i < 24; i++) { | 
| 388 | < | if (ccount[i] == 0) { | 
| 389 | < | fprintf(stderr, "%s: bad chart boundaries\n", | 
| 387 | > | /* normalize colors */ | 
| 388 | > | if (!gotwhite) { | 
| 389 | > | if (!(inpflags & 1L<<White)) { | 
| 390 | > | fprintf(stderr, "%s: missing input for White\n", | 
| 391 |  | progname); | 
| 392 |  | exit(1); | 
| 393 |  | } | 
| 394 | < | d = 1.0/ccount[i]; | 
| 395 | < | scalecolor(picRGB[i], d); | 
| 394 | > | setcolor(whiteclr, | 
| 395 | > | colval(inpRGB[White],RED)/colval(mbRGB[White],RED), | 
| 396 | > | colval(inpRGB[White],GRN)/colval(mbRGB[White],GRN), | 
| 397 | > | colval(inpRGB[White],BLU)/colval(mbRGB[White],BLU)); | 
| 398 |  | } | 
| 399 | < | free((char *)scanln); | 
| 399 | > | for (n = 0; n < 24; n++) | 
| 400 | > | if (inpflags & 1L<<n) | 
| 401 | > | setcolor(inpRGB[n], | 
| 402 | > | colval(inpRGB[n],RED)/colval(whiteclr,RED), | 
| 403 | > | colval(inpRGB[n],GRN)/colval(whiteclr,GRN), | 
| 404 | > | colval(inpRGB[n],BLU)/colval(whiteclr,BLU)); | 
| 405 |  | } | 
| 406 |  |  | 
| 407 |  |  | 
| 408 |  | bresp(y, x)             /* piecewise linear interpolation of primaries */ | 
| 409 |  | COLOR   y, x; | 
| 410 |  | { | 
| 271 | – | double  cv[3]; | 
| 411 |  | register int    i, n; | 
| 412 |  |  | 
| 413 |  | for (i = 0; i < 3; i++) { | 
| 414 | < | n = NMBNEU; | 
| 415 | < | while (n > 0 && colval(x,i) < colval(bramp[--n][0],i)) | 
| 416 | < | ; | 
| 417 | < | cv[i] = ((colval(bramp[n+1][0],i) - colval(x,i)) * | 
| 414 | > | for (n = 0; n < NMBNEU-2; n++) | 
| 415 | > | if (colval(x,i) < colval(bramp[n+1][0],i)) | 
| 416 | > | break; | 
| 417 | > | colval(y,i) = ((colval(bramp[n+1][0],i) - colval(x,i)) * | 
| 418 |  | colval(bramp[n][1],i) + | 
| 419 |  | (colval(x,i) - colval(bramp[n][0],i)) * | 
| 420 |  | colval(bramp[n+1][1],i)) / | 
| 421 |  | (colval(bramp[n+1][0],i) - colval(bramp[n][0],i)); | 
| 283 | – | if (cv[i] < 0.) cv[i] = 0.; | 
| 422 |  | } | 
| 285 | – | setcolor(y, cv[0], cv[1], cv[2]); | 
| 423 |  | } | 
| 424 |  |  | 
| 425 |  |  | 
| 426 |  | compute()                       /* compute color mapping */ | 
| 427 |  | { | 
| 428 | < | COLOR   clrin[NMBMOD], clrout[NMBMOD]; | 
| 428 | > | COLOR   clrin[24], clrout[24]; | 
| 429 | > | long    cflags; | 
| 430 |  | COLOR   ctmp; | 
| 431 | < | double  d; | 
| 432 | < | register int    i; | 
| 433 | < | /* map MacBeth colors to RGB space */ | 
| 434 | < | for (i = 0; i < 24; i++) { | 
| 435 | < | d = mbxyY[i][2] / mbxyY[i][1]; | 
| 436 | < | ctmp[0] = mbxyY[i][0] * d; | 
| 299 | < | ctmp[1] = mbxyY[i][2]; | 
| 300 | < | ctmp[2] = (1. - mbxyY[i][0] - mbxyY[i][1]) * d; | 
| 301 | < | cie_rgb(mbRGB[i], ctmp); | 
| 431 | > | register int    i, n; | 
| 432 | > | /* did we get what we need? */ | 
| 433 | > | if ((inpflags & REQFLGS) != REQFLGS) { | 
| 434 | > | fprintf(stderr, "%s: missing required input colors\n", | 
| 435 | > | progname); | 
| 436 | > | exit(1); | 
| 437 |  | } | 
| 438 |  | /* compute piecewise luminance curve */ | 
| 439 |  | for (i = 0; i < NMBNEU; i++) { | 
| 440 | < | copycolor(bramp[i][0], picRGB[mbneu[i]]); | 
| 440 | > | copycolor(bramp[i][0], inpRGB[mbneu[i]]); | 
| 441 |  | copycolor(bramp[i][1], mbRGB[mbneu[i]]); | 
| 442 |  | } | 
| 443 | < | /* compute color matrix */ | 
| 444 | < | for (i = 0; i < NMBMOD; i++) { | 
| 445 | < | bresp(clrin[i], picRGB[mbmod[i]]); | 
| 446 | < | copycolor(clrout[i], mbRGB[mbmod[i]]); | 
| 447 | < | } | 
| 448 | < | compsoln(clrin, clrout, NMBMOD); | 
| 443 | > | /* compute color space gamut */ | 
| 444 | > | if (scanning) { | 
| 445 | > | copycolor(colmin, cblack); | 
| 446 | > | copycolor(colmax, cwhite); | 
| 447 | > | scalecolor(colmax, irrad); | 
| 448 | > | } else | 
| 449 | > | for (i = 0; i < 3; i++) { | 
| 450 | > | colval(colmin,i) = colval(bramp[0][0],i) - | 
| 451 | > | colval(bramp[0][1],i) * | 
| 452 | > | (colval(bramp[1][0],i)-colval(bramp[0][0],i)) / | 
| 453 | > | (colval(bramp[1][1],i)-colval(bramp[1][0],i)); | 
| 454 | > | colval(colmax,i) = colval(bramp[NMBNEU-2][0],i) + | 
| 455 | > | (1.-colval(bramp[NMBNEU-2][1],i)) * | 
| 456 | > | (colval(bramp[NMBNEU-1][0],i) - | 
| 457 | > | colval(bramp[NMBNEU-2][0],i)) / | 
| 458 | > | (colval(bramp[NMBNEU-1][1],i) - | 
| 459 | > | colval(bramp[NMBNEU-2][1],i)); | 
| 460 | > | } | 
| 461 | > | /* compute color mapping */ | 
| 462 | > | do { | 
| 463 | > | cflags = inpflags & ~gmtflags; | 
| 464 | > | n = 0;                          /* compute transform matrix */ | 
| 465 | > | for (i = 0; i < 24; i++) | 
| 466 | > | if (cflags & 1L<<i) { | 
| 467 | > | bresp(clrin[n], inpRGB[i]); | 
| 468 | > | copycolor(clrout[n], mbRGB[i]); | 
| 469 | > | n++; | 
| 470 | > | } | 
| 471 | > | compsoln(clrin, clrout, n); | 
| 472 | > | if (irrad > 0.99 && irrad < 1.01)       /* check gamut */ | 
| 473 | > | for (i = 0; i < 24; i++) | 
| 474 | > | if (cflags & 1L<<i && cvtcolor(ctmp, mbRGB[i])) | 
| 475 | > | gmtflags |= 1L<<i; | 
| 476 | > | } while (cflags & gmtflags); | 
| 477 | > | if (gmtflags & MODFLGS) | 
| 478 | > | fprintf(stderr, | 
| 479 | > | "%s: warning - some moderate colors are out of gamut\n", | 
| 480 | > | progname); | 
| 481 |  | } | 
| 482 |  |  | 
| 483 |  |  | 
| 484 | < | putmapping()                    /* put out color mapping for pcomb -f */ | 
| 484 | > | putmapping()                    /* put out color mapping */ | 
| 485 |  | { | 
| 486 |  | static char     cchar[3] = {'r', 'g', 'b'}; | 
| 487 |  | register int    i, j; | 
| 495 |  | for (i = 0; i < NMBNEU; i++) | 
| 496 |  | printf(",%g", colval(bramp[i][1],j)); | 
| 497 |  | printf(");\n"); | 
| 331 | – | printf("%c = %ci(1);\n", cchar[j], cchar[j]); | 
| 498 |  | printf("%cfi(n) = if(n-%g, %d, if(%cxa(n+1)-%c, n, %cfi(n+1)));\n", | 
| 499 |  | cchar[j], NMBNEU-1.5, NMBNEU-1, cchar[j], | 
| 500 |  | cchar[j], cchar[j]); | 
| 501 |  | printf("%cndx = %cfi(1);\n", cchar[j], cchar[j]); | 
| 502 | < | printf("%cn = ((%cxa(%cndx+1)-%c)*%cya(%cndx) + ", | 
| 503 | < | cchar[j], cchar[j], cchar[j], | 
| 504 | < | cchar[j], cchar[j], cchar[j]); | 
| 502 | > | printf("%c%c = ((%cxa(%cndx+1)-%c)*%cya(%cndx) + ", | 
| 503 | > | cchar[j], scanning?'n':'o', cchar[j], | 
| 504 | > | cchar[j], cchar[j], cchar[j], cchar[j]); | 
| 505 |  | printf("(%c-%cxa(%cndx))*%cya(%cndx+1)) /\n", | 
| 506 |  | cchar[j], cchar[j], cchar[j], | 
| 507 |  | cchar[j], cchar[j]); | 
| 509 |  | cchar[j], cchar[j], cchar[j], cchar[j]); | 
| 510 |  | } | 
| 511 |  | /* print color mapping */ | 
| 512 | < | printf("ro = %g*rn + %g*gn + %g*bn ;\n", | 
| 513 | < | solmat[0][0], solmat[0][1], solmat[0][2]); | 
| 514 | < | printf("go = %g*rn + %g*gn + %g*bn ;\n", | 
| 515 | < | solmat[1][0], solmat[1][1], solmat[1][2]); | 
| 516 | < | printf("bo = %g*rn + %g*gn + %g*bn ;\n", | 
| 517 | < | solmat[2][0], solmat[2][1], solmat[2][2]); | 
| 512 | > | if (scanning) { | 
| 513 | > | printf("r = ri(1); g = gi(1); b = bi(1);\n"); | 
| 514 | > | printf("ro = %g*rn + %g*gn + %g*bn ;\n", | 
| 515 | > | solmat[0][0], solmat[0][1], solmat[0][2]); | 
| 516 | > | printf("go = %g*rn + %g*gn + %g*bn ;\n", | 
| 517 | > | solmat[1][0], solmat[1][1], solmat[1][2]); | 
| 518 | > | printf("bo = %g*rn + %g*gn + %g*bn ;\n", | 
| 519 | > | solmat[2][0], solmat[2][1], solmat[2][2]); | 
| 520 | > | } else { | 
| 521 | > | printf("r1 = ri(1); g1 = gi(1); b1 = bi(1);\n"); | 
| 522 | > | printf("r = %g*r1 + %g*g1 + %g*b1 ;\n", | 
| 523 | > | solmat[0][0], solmat[0][1], solmat[0][2]); | 
| 524 | > | printf("g = %g*r1 + %g*g1 + %g*b1 ;\n", | 
| 525 | > | solmat[1][0], solmat[1][1], solmat[1][2]); | 
| 526 | > | printf("b = %g*r1 + %g*g1 + %g*b1 ;\n", | 
| 527 | > | solmat[2][0], solmat[2][1], solmat[2][2]); | 
| 528 | > | } | 
| 529 |  | } | 
| 530 |  |  | 
| 531 |  |  | 
| 539 |  | double  colv[3], rowv[3]; | 
| 540 |  | register int    i, j, k; | 
| 541 |  |  | 
| 542 | < | if (n < 3 | n > NMBMOD) { | 
| 543 | < | fprintf(stderr, "%s: inconsistent code!\n", progname); | 
| 542 | > | if (n < 3) { | 
| 543 | > | fprintf(stderr, "%s: too few colors to match!\n", progname); | 
| 544 |  | exit(1); | 
| 545 |  | } | 
| 546 |  | if (n == 3) | 
| 589 |  | } | 
| 590 |  |  | 
| 591 |  |  | 
| 592 | + | cwarp()                         /* compute color warp map */ | 
| 593 | + | { | 
| 594 | + | register int    i; | 
| 595 | + |  | 
| 596 | + | if ((wcor = new3dw(W3EXACT)) == NULL) | 
| 597 | + | goto memerr; | 
| 598 | + | for (i = 0; i < 24; i++) | 
| 599 | + | if (!add3dpt(wcor, inpRGB[i], mbRGB[i])) | 
| 600 | + | goto memerr; | 
| 601 | + | return; | 
| 602 | + | memerr: | 
| 603 | + | perror(progname); | 
| 604 | + | exit(1); | 
| 605 | + | } | 
| 606 | + |  | 
| 607 | + |  | 
| 608 | + | int | 
| 609 |  | cvtcolor(cout, cin)             /* convert color according to our mapping */ | 
| 610 |  | COLOR   cout, cin; | 
| 611 |  | { | 
| 612 | < | double  r, g, b; | 
| 612 | > | COLOR   ctmp; | 
| 613 | > | int     clipped; | 
| 614 |  |  | 
| 615 | < | bresp(cout, cin); | 
| 616 | < | r = colval(cout,0)*solmat[0][0] + colval(cout,1)*solmat[0][1] | 
| 617 | < | + colval(cout,2)*solmat[0][2]; | 
| 618 | < | if (r < 0) r = 0; | 
| 619 | < | g = colval(cout,0)*solmat[1][0] + colval(cout,1)*solmat[1][1] | 
| 620 | < | + colval(cout,2)*solmat[1][2]; | 
| 621 | < | if (g < 0) g = 0; | 
| 622 | < | b = colval(cout,0)*solmat[2][0] + colval(cout,1)*solmat[2][1] | 
| 623 | < | + colval(cout,2)*solmat[2][2]; | 
| 624 | < | if (b < 0) b = 0; | 
| 625 | < | setcolor(cout, r, g, b); | 
| 615 | > | if (wcor != NULL) { | 
| 616 | > | clipped = warp3d(cout, cin, wcor); | 
| 617 | > | clipped |= clipgamut(cout,bright(cout),CGAMUT,colmin,colmax); | 
| 618 | > | } else if (scanning) { | 
| 619 | > | bresp(ctmp, cin); | 
| 620 | > | clipped = cresp(cout, ctmp); | 
| 621 | > | } else { | 
| 622 | > | clipped = cresp(ctmp, cin); | 
| 623 | > | bresp(cout, ctmp); | 
| 624 | > | } | 
| 625 | > | return(clipped); | 
| 626 |  | } | 
| 627 |  |  | 
| 628 |  |  | 
| 629 | < | putdebug()                      /* put out debugging picture */ | 
| 629 | > | int | 
| 630 | > | cresp(cout, cin)                /* transform color according to matrix */ | 
| 631 | > | COLOR   cout, cin; | 
| 632 |  | { | 
| 633 | + | colortrans(cout, solmat, cin); | 
| 634 | + | return(clipgamut(cout, bright(cout), CGAMUT, colmin, colmax)); | 
| 635 | + | } | 
| 636 | + |  | 
| 637 | + |  | 
| 638 | + | xyY2RGB(rgbout, xyYin)          /* convert xyY to RGB */ | 
| 639 | + | COLOR   rgbout; | 
| 640 | + | register float  xyYin[3]; | 
| 641 | + | { | 
| 642 | + | COLOR   ctmp; | 
| 643 | + | double  d; | 
| 644 | + |  | 
| 645 | + | d = xyYin[2] / xyYin[1]; | 
| 646 | + | ctmp[0] = xyYin[0] * d; | 
| 647 | + | ctmp[1] = xyYin[2]; | 
| 648 | + | ctmp[2] = (1. - xyYin[0] - xyYin[1]) * d; | 
| 649 | + | /* allow negative values */ | 
| 650 | + | colortrans(rgbout, xyz2rgbmat, ctmp); | 
| 651 | + | } | 
| 652 | + |  | 
| 653 | + |  | 
| 654 | + | picdebug()                      /* put out debugging picture */ | 
| 655 | + | { | 
| 656 | + | static COLOR    blkcol = BLKCOLOR; | 
| 657 |  | COLOR   *scan; | 
| 658 | < | int     y; | 
| 659 | < | register int    x, i; | 
| 658 | > | int     y, i; | 
| 659 | > | register int    x, rg; | 
| 660 |  |  | 
| 440 | – | if (debugfp == NULL) | 
| 441 | – | return; | 
| 661 |  | if (fseek(stdin, 0L, 0) == EOF) { | 
| 662 |  | fprintf(stderr, "%s: cannot seek on input picture\n", progname); | 
| 663 |  | exit(1); | 
| 671 |  | exit(1); | 
| 672 |  | } | 
| 673 |  | /* finish debug header */ | 
| 674 | + | fputformat(COLRFMT, debugfp); | 
| 675 |  | putc('\n', debugfp); | 
| 676 |  | fprtresolu(xmax, ymax, debugfp); | 
| 677 | + | /* write debug picture */ | 
| 678 |  | for (y = ymax-1; y >= 0; y--) { | 
| 679 |  | if (freadscan(scan, xmax, stdin) < 0) { | 
| 680 |  | fprintf(stderr, "%s: error rereading input picture\n", | 
| 682 |  | exit(1); | 
| 683 |  | } | 
| 684 |  | for (x = 0; x < xmax; x++) { | 
| 685 | < | i = chartndx(x, y); | 
| 686 | < | if (i < 0) | 
| 685 | > | rg = chartndx(x, y, &i); | 
| 686 | > | if (rg == RG_CENT) { | 
| 687 | > | if (!(1L<<i & gmtflags) || (x+y)&07) { | 
| 688 | > | copycolor(scan[x], mbRGB[i]); | 
| 689 | > | clipgamut(scan[x], bright(scan[x]), | 
| 690 | > | CGAMUT, colmin, colmax); | 
| 691 | > | } else | 
| 692 | > | copycolor(scan[x], blkcol); | 
| 693 | > | } else if (rg == RG_CORR) | 
| 694 |  | cvtcolor(scan[x], scan[x]); | 
| 695 | < | else | 
| 696 | < | copycolor(scan[x], mbRGB[i]); | 
| 695 | > | else if (rg != RG_ORIG) | 
| 696 | > | copycolor(scan[x], blkcol); | 
| 697 |  | } | 
| 698 |  | if (fwritescan(scan, xmax, debugfp) < 0) { | 
| 699 |  | fprintf(stderr, "%s: error writing debugging picture\n", | 
| 701 |  | exit(1); | 
| 702 |  | } | 
| 703 |  | } | 
| 704 | < | free((char *)scan); | 
| 704 | > | /* clean up */ | 
| 705 | > | fclose(debugfp); | 
| 706 | > | free((void *)scan); | 
| 707 | > | } | 
| 708 | > |  | 
| 709 | > |  | 
| 710 | > | clrdebug()                      /* put out debug picture from color input */ | 
| 711 | > | { | 
| 712 | > | static COLR     blkclr = BLKCOLR; | 
| 713 | > | COLR    mbclr[24], cvclr[24], orclr[24]; | 
| 714 | > | COLR    *scan; | 
| 715 | > | COLOR   ctmp, ct2; | 
| 716 | > | int     y, i; | 
| 717 | > | register int    x, rg; | 
| 718 | > | /* convert colors */ | 
| 719 | > | for (i = 0; i < 24; i++) { | 
| 720 | > | copycolor(ctmp, mbRGB[i]); | 
| 721 | > | clipgamut(ctmp, bright(ctmp), CGAMUT, cblack, cwhite); | 
| 722 | > | setcolr(mbclr[i], colval(ctmp,RED), | 
| 723 | > | colval(ctmp,GRN), colval(ctmp,BLU)); | 
| 724 | > | if (inpflags & 1L<<i) { | 
| 725 | > | copycolor(ctmp, inpRGB[i]); | 
| 726 | > | clipgamut(ctmp, bright(ctmp), CGAMUT, cblack, cwhite); | 
| 727 | > | setcolr(orclr[i], colval(ctmp,RED), | 
| 728 | > | colval(ctmp,GRN), colval(ctmp,BLU)); | 
| 729 | > | if (rawmap) | 
| 730 | > | copycolr(cvclr[i], mbclr[i]); | 
| 731 | > | else { | 
| 732 | > | bresp(ctmp, inpRGB[i]); | 
| 733 | > | colortrans(ct2, solmat, ctmp); | 
| 734 | > | clipgamut(ct2, bright(ct2), CGAMUT, | 
| 735 | > | cblack, cwhite); | 
| 736 | > | setcolr(cvclr[i], colval(ct2,RED), | 
| 737 | > | colval(ct2,GRN), | 
| 738 | > | colval(ct2,BLU)); | 
| 739 | > | } | 
| 740 | > | } | 
| 741 | > | } | 
| 742 | > | /* allocate scanline */ | 
| 743 | > | scan = (COLR *)malloc(xmax*sizeof(COLR)); | 
| 744 | > | if (scan == NULL) { | 
| 745 | > | perror(progname); | 
| 746 | > | exit(1); | 
| 747 | > | } | 
| 748 | > | /* finish debug header */ | 
| 749 | > | fputformat(COLRFMT, debugfp); | 
| 750 | > | putc('\n', debugfp); | 
| 751 | > | fprtresolu(xmax, ymax, debugfp); | 
| 752 | > | /* write debug picture */ | 
| 753 | > | for (y = ymax-1; y >= 0; y--) { | 
| 754 | > | for (x = 0; x < xmax; x++) { | 
| 755 | > | rg = chartndx(x, y, &i); | 
| 756 | > | if (rg == RG_CENT) { | 
| 757 | > | if (!(1L<<i & gmtflags) || (x+y)&07) | 
| 758 | > | copycolr(scan[x], mbclr[i]); | 
| 759 | > | else | 
| 760 | > | copycolr(scan[x], blkclr); | 
| 761 | > | } else if (rg == RG_BORD || !(1L<<i & inpflags)) | 
| 762 | > | copycolr(scan[x], blkclr); | 
| 763 | > | else if (rg == RG_ORIG) | 
| 764 | > | copycolr(scan[x], orclr[i]); | 
| 765 | > | else /* rg == RG_CORR */ | 
| 766 | > | copycolr(scan[x], cvclr[i]); | 
| 767 | > | } | 
| 768 | > | if (fwritecolrs(scan, xmax, debugfp) < 0) { | 
| 769 | > | fprintf(stderr, "%s: error writing debugging picture\n", | 
| 770 | > | progname); | 
| 771 | > | exit(1); | 
| 772 | > | } | 
| 773 | > | } | 
| 774 | > | /* clean up */ | 
| 775 | > | fclose(debugfp); | 
| 776 | > | free((void *)scan); | 
| 777 | > | } | 
| 778 | > |  | 
| 779 | > |  | 
| 780 | > | getpos(name, bnds, fp)          /* get boundary position */ | 
| 781 | > | char    *name; | 
| 782 | > | int     bnds[2]; | 
| 783 | > | FILE    *fp; | 
| 784 | > | { | 
| 785 | > | char    buf[64]; | 
| 786 | > |  | 
| 787 | > | fprintf(stderr, "\tSelect corner: %s\n", name); | 
| 788 | > | if (fgets(buf, sizeof(buf), fp) == NULL || | 
| 789 | > | sscanf(buf, "%d %d", &bnds[0], &bnds[1]) != 2) { | 
| 790 | > | fprintf(stderr, "%s: read error from display process\n", | 
| 791 | > | progname); | 
| 792 | > | exit(1); | 
| 793 | > | } | 
| 794 | > | } | 
| 795 | > |  | 
| 796 | > |  | 
| 797 | > | pickchartpos(pfn)               /* display picture and pick chart location */ | 
| 798 | > | char    *pfn; | 
| 799 | > | { | 
| 800 | > | char    combuf[512]; | 
| 801 | > | FILE    *pfp; | 
| 802 | > |  | 
| 803 | > | sprintf(combuf, DISPCOM, pfn); | 
| 804 | > | if ((pfp = popen(combuf, "r")) == NULL) { | 
| 805 | > | perror(combuf); | 
| 806 | > | exit(1); | 
| 807 | > | } | 
| 808 | > | fputs("Use middle mouse button to select chart corners:\n", stderr); | 
| 809 | > | getpos("upper left (dark skin)", bounds[0], pfp); | 
| 810 | > | getpos("upper right (bluish green)", bounds[1], pfp); | 
| 811 | > | getpos("lower left (white)", bounds[2], pfp); | 
| 812 | > | getpos("lower right (black)", bounds[3], pfp); | 
| 813 | > | fputs("Got it -- quit display program.\n", stderr); | 
| 814 | > | pclose(pfp); | 
| 815 |  | } |