int main(int argc, char *argv[]) { int fa,nfa; /* argument we're looking at */ char prof_name[MAXNAMEL+1] = { '\000' }; /* ICC profile name, "" if none */ char in_name[MAXNAMEL+1]; /* TIFF input file */ char *xl = NULL, out_name[MAXNAMEL+4+1] = "locus.ts"; /* locus output file */ int verb = 0; int dovrml = 0; int doaxes = 1; int usevec = 0; double vec[3]; int rv = 0; icc *icco = NULL; xicc *xicco = NULL; icxViewCond vc; /* Viewing Condition for CIECAM */ int vc_e = -1; /* Enumerated viewing condition */ int vc_s = -1; /* Surround override */ double vc_wXYZ[3] = {-1.0, -1.0, -1.0}; /* Adapted white override in XYZ */ double vc_wxy[2] = {-1.0, -1.0}; /* Adapted white override in x,y */ double vc_a = -1.0; /* Adapted luminance */ double vc_b = -1.0; /* Background % overid */ double vc_f = -1.0; /* Flare % overid */ double vc_fXYZ[3] = {-1.0, -1.0, -1.0}; /* Flare color override in XYZ */ double vc_fxy[2] = {-1.0, -1.0}; /* Flare color override in x,y */ icxLuBase *luo = NULL; /* Generic lookup object */ icColorSpaceSignature ins = icSigLabData, outs; /* Type of input and output spaces */ int inn, outn; /* Number of components */ icmLuAlgType alg; /* Type of lookup algorithm */ icmLookupFunc func = icmFwd; /* Must be */ icRenderingIntent intent = -1; /* Default */ icColorSpaceSignature pcsor = icSigLabData; /* Default */ icmLookupOrder order = icmLuOrdNorm; /* Default */ TIFF *rh = NULL; int x, y, width, height; /* Size of image */ uint16 samplesperpixel, bitspersample; uint16 pconfig, photometric, pmtc; uint16 resunits; float resx, resy; tdata_t *inbuf; void (*cvt)(double *out, double *in); /* TIFF conversion function, NULL if none */ icColorSpaceSignature tcs; /* TIFF colorspace */ uint16 extrasamples; /* Extra "alpha" samples */ uint16 *extrainfo; /* Info about extra samples */ int sign_mask; /* Handling of encoding sign */ int i, j; int nipoints = 0; /* Number of raster sample points */ co *inp = NULL; /* Input point values */ double tdel = 0.0; /* Total delta along locus */ rspl *rr = NULL; int nopoints = 0; /* Number of raster sample points */ co *outp = NULL; error_program = argv[0]; if (argc < 2) usage(); /* Process the arguments */ for(fa = 1;fa < argc;fa++) { nfa = fa; /* skip to nfa if next argument is used */ if (argv[fa][0] == '-') { /* Look for any flags */ char *na = NULL; /* next argument after flag, null if none */ if (argv[fa][2] != '\000') na = &argv[fa][2]; /* next is directly after flag */ else { if ((fa+1) < argc) { if (argv[fa+1][0] != '-') { nfa = fa + 1; na = argv[nfa]; /* next is seperate non-flag argument */ } } } if (argv[fa][1] == '?') usage(); /* Verbosity */ else if (argv[fa][1] == 'v') { verb = 1; } /* Intent */ else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') { fa = nfa; if (na == NULL) usage(); switch (na[0]) { case 'd': intent = icmDefaultIntent; break; case 'a': intent = icAbsoluteColorimetric; break; case 'p': intent = icPerceptual; break; case 'r': intent = icRelativeColorimetric; break; case 's': intent = icSaturation; break; /* Argyll special intents to check spaces underlying */ /* icxPerceptualAppearance & icxSaturationAppearance */ case 'P': intent = icmAbsolutePerceptual; break; case 'S': intent = icmAbsoluteSaturation; break; default: usage(); } } /* Search order */ else if (argv[fa][1] == 'o') { fa = nfa; if (na == NULL) usage(); switch (na[0]) { case 'n': case 'N': order = icmLuOrdNorm; break; case 'r': case 'R': order = icmLuOrdRev; break; default: usage(); } } /* PCS override */ else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') { fa = nfa; if (na == NULL) usage(); switch (na[0]) { case 'l': pcsor = icSigLabData; break; case 'j': pcsor = icxSigJabData; break; default: usage(); } } /* Viewing conditions */ else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') { fa = nfa; if (na == NULL) usage(); /* Switch to Jab automatically */ pcsor = icxSigJabData; /* Set the viewing conditions */ if (na[1] != ':') { if ((vc_e = xicc_enum_viewcond(NULL, NULL, -2, na, 1, NULL)) == -999) usage(); } else if (na[0] == 's' || na[0] == 'S') { if (na[1] != ':') usage(); if (na[2] == 'a' || na[2] == 'A') { vc_s = vc_average; } else if (na[2] == 'm' || na[2] == 'M') { vc_s = vc_dim; } else if (na[2] == 'd' || na[2] == 'D') { vc_s = vc_dark; } else if (na[2] == 'c' || na[2] == 'C') { vc_s = vc_cut_sheet; } else usage(); } else if (na[0] == 'w' || na[0] == 'W') { double x, y, z; if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) { vc_wXYZ[0] = x; vc_wXYZ[1] = y; vc_wXYZ[2] = z; } else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) { vc_wxy[0] = x; vc_wxy[1] = y; } else usage(); } else if (na[0] == 'a' || na[0] == 'A') { if (na[1] != ':') usage(); vc_a = atof(na+2); } else if (na[0] == 'b' || na[0] == 'B') { if (na[1] != ':') usage(); vc_b = atof(na+2); } else if (na[0] == 'f' || na[0] == 'F') { double x, y, z; if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) { vc_fXYZ[0] = x; vc_fXYZ[1] = y; vc_fXYZ[2] = z; } else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) { vc_fxy[0] = x; vc_fxy[1] = y; } else if (sscanf(na+1,":%lf",&x) == 1) { vc_f = x; } else usage(); } else usage(); } /* VRML output */ else if (argv[fa][1] == 'w' || argv[fa][1] == 'W') { dovrml = 1; } /* No axis output */ else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') { doaxes = 0; } /* Vector direction for span */ else if (argv[fa][1] == 'V') { usevec = 1; if (na == NULL) usage(); fa = nfa; if (sscanf(na, " %lf , %lf , %lf ",&vec[0], &vec[1], &vec[2]) != 3) usage(); } /* Output file name */ else if (argv[fa][1] == 'O') { fa = nfa; if (na == NULL) usage(); strncpy(out_name,na,MAXNAMEL); out_name[MAXNAMEL] = '\000'; } else usage(); } else break; } if (fa >= argc || argv[fa][0] == '-') usage(); if (fa < (argc-1)) strncpy(prof_name,argv[fa++],MAXNAMEL); prof_name[MAXNAMEL] = '\000'; if (fa >= argc || argv[fa][0] == '-') usage(); strncpy(in_name,argv[fa],MAXNAMEL); in_name[MAXNAMEL] = '\000'; if ((xl = strrchr(out_name, '.')) == NULL) /* Figure where extention is */ xl = out_name + strlen(out_name); if (verb) { printf("Profile = '%s'\n",prof_name); printf("Input TIFF = '%s'\n",in_name); printf("Output file = '%s'\n",out_name); } if (intent == -1) { if (pcsor == icxSigJabData) intent = icRelativeColorimetric; /* Default to icxAppearance */ else intent = icAbsoluteColorimetric; /* Default to icAbsoluteColorimetric */ } /* - - - - - - - - - - - - - - - - */ /* If we were provided an ICC profile to use */ if (prof_name[0] != '\000') { /* Open up the profile or TIFF embedded profile for reading */ if ((icco = read_embedded_icc(prof_name)) == NULL) error ("Can't open profile in file '%s'",prof_name); if (verb) { icmFile *op; if ((op = new_icmFileStd_fp(stdout)) == NULL) error ("Can't open stdout"); icco->header->dump(icco->header, op, 1); op->del(op); } /* Check that the profile is appropriate */ if (icco->header->deviceClass != icSigInputClass && icco->header->deviceClass != icSigDisplayClass && icco->header->deviceClass != icSigOutputClass && icco->header->deviceClass != icSigColorSpaceClass) error("Profile type isn't device or colorspace"); /* Wrap with an expanded icc */ if ((xicco = new_xicc(icco)) == NULL) error ("Creation of xicc failed"); /* Setup the default viewing conditions */ if (xicc_enum_viewcond(xicco, &vc, -1, NULL, 0, NULL) == -999) error ("%d, %s",xicco->errc, xicco->err); if (vc_e != -1) if (xicc_enum_viewcond(xicco, &vc, vc_e, NULL, 0, NULL) == -999) error ("%d, %s",xicco->errc, xicco->err); if (vc_s >= 0) vc.Ev = vc_s; if (vc_wXYZ[1] > 0.0) { /* Normalise it to current media white */ vc.Wxyz[0] = vc_wXYZ[0]/vc_wXYZ[1] * vc.Wxyz[1]; vc.Wxyz[2] = vc_wXYZ[2]/vc_wXYZ[1] * vc.Wxyz[1]; } if (vc_wxy[0] >= 0.0) { double x = vc_wxy[0]; double y = vc_wxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */ double z = 1.0 - x - y; vc.Wxyz[0] = x/y * vc.Wxyz[1]; vc.Wxyz[2] = z/y * vc.Wxyz[1]; } if (vc_a >= 0.0) vc.La = vc_a; if (vc_b >= 0.0) vc.Yb = vc_b/100.0; if (vc_f >= 0.0) vc.Yf = vc_f/100.0; if (vc_fXYZ[1] > 0.0) { /* Normalise it to current media white */ vc.Fxyz[0] = vc_fXYZ[0]/vc_fXYZ[1] * vc.Fxyz[1]; vc.Fxyz[2] = vc_fXYZ[2]/vc_fXYZ[1] * vc.Fxyz[1]; } if (vc_fxy[0] >= 0.0) { double x = vc_fxy[0]; double y = vc_fxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */ double z = 1.0 - x - y; vc.Fxyz[0] = x/y * vc.Fxyz[1]; vc.Fxyz[2] = z/y * vc.Fxyz[1]; } /* Get a expanded color conversion object */ if ((luo = xicco->get_luobj(xicco, ICX_CLIP_NEAREST , func, intent, pcsor, order, &vc, NULL)) == NULL) error ("%d, %s",xicco->errc, xicco->err); luo->spaces(luo, &ins, &inn, &outs, &outn, &alg, NULL, NULL, NULL); } /* Establish the PCS range if we are filtering */ { double pcsmin[3], pcsmax[3]; /* PCS range for filter stats array */ if (luo) { gamut *csgam; if ((csgam = luo->get_gamut(luo, 20.0)) == NULL) error("Getting the gamut of the source colorspace failed"); csgam->getrange(csgam, pcsmin, pcsmax); csgam->del(csgam); } else { pcsmin[0] = 0.0; pcsmax[0] = 100.0; pcsmin[1] = -128.0; pcsmax[1] = 128.0; pcsmin[2] = -128.0; pcsmax[2] = 128.0; } if (verb) printf("PCS range = %f..%f, %f..%f. %f..%f\n\n", pcsmin[0], pcsmax[0], pcsmin[1], pcsmax[1], pcsmin[2], pcsmax[2]); /* Allocate and initialize the filter */ set_fminmax(pcsmin, pcsmax); } /* - - - - - - - - - - - - - - - */ /* Open up input tiff file ready for reading */ /* Got arguments, so setup to process the file */ if ((rh = TIFFOpen(in_name, "r")) == NULL) error("error opening read file '%s'",in_name); TIFFGetField(rh, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField(rh, TIFFTAG_IMAGELENGTH, &height); TIFFGetField(rh, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel); TIFFGetField(rh, TIFFTAG_BITSPERSAMPLE, &bitspersample); if (bitspersample != 8 && bitspersample != 16) error("TIFF Input file must be 8 bit/channel"); TIFFGetFieldDefaulted(rh, TIFFTAG_EXTRASAMPLES, &extrasamples, &extrainfo); TIFFGetField(rh, TIFFTAG_PHOTOMETRIC, &photometric); if (inn != (samplesperpixel-extrasamples)) error ("TIFF Input file has %d input chanels mismatched to colorspace '%s'", samplesperpixel, icm2str(icmColorSpaceSignature, ins)); if ((tcs = TiffPhotometric2ColorSpaceSignature(&cvt, &sign_mask, photometric, bitspersample, samplesperpixel, extrasamples)) == 0) error("Can't handle TIFF file photometric %s", Photometric2str(photometric)); if (tcs != ins) { if (luo != NULL) error("TIFF photometric '%s' doesn't match ICC input colorspace '%s' !", Photometric2str(photometric), icm2str(icmColorSpaceSignature,ins)); else error("No profile provided and TIFF photometric '%s' isn't Lab !", Photometric2str(photometric)); } TIFFGetField(rh, TIFFTAG_PLANARCONFIG, &pconfig); if (pconfig != PLANARCONFIG_CONTIG) error ("TIFF Input file must be planar"); TIFFGetField(rh, TIFFTAG_RESOLUTIONUNIT, &resunits); TIFFGetField(rh, TIFFTAG_XRESOLUTION, &resx); TIFFGetField(rh, TIFFTAG_YRESOLUTION, &resy); if (verb) { printf("Input TIFF file '%s'\n",in_name); printf("TIFF file colorspace is %s\n",icm2str(icmColorSpaceSignature,tcs)); printf("TIFF file photometric is %s\n",Photometric2str(photometric)); printf("\n"); } /* - - - - - - - - - - - - - - - */ /* Process colors to translate */ /* (Should fix this to process a group of lines at a time ?) */ nipoints = width * height; // if ((inp = malloc(sizeof(co) * nipoints)) == NULL) // error("Unable to allocate co array"); inbuf = _TIFFmalloc(TIFFScanlineSize(rh)); for (i = y = 0; y < height; y++) { /* Read in the next line */ if (TIFFReadScanline(rh, inbuf, y, 0) < 0) error ("Failed to read TIFF line %d",y); /* Do floating point conversion */ for (x = 0; x < width; x++) { int e; double in[MAX_CHAN], out[MAX_CHAN]; if (bitspersample == 8) { for (e = 0; e < samplesperpixel; e++) { int v = ((unsigned char *)inbuf)[x * samplesperpixel + e]; if (sign_mask & (1 << i)) /* Treat input as signed */ v = (v & 0x80) ? v - 0x80 : v + 0x80; in[e] = v/255.0; } } else { for (e = 0; e < samplesperpixel; e++) { int v = ((unsigned short *)inbuf)[x * samplesperpixel + e]; if (sign_mask & (1 << i)) /* Treat input as signed */ v = (v & 0x8000) ? v - 0x8000 : v + 0x8000; in[e] = v/65535.0; } } if (cvt != NULL) { /* Undo TIFF encoding */ cvt(in, in); } if (luo != NULL) { if ((rv = luo->lookup(luo, out, in)) > 1) error ("%d, %s",icco->errc,icco->err); if (outs == icSigXYZData) /* Convert to Lab */ icmXYZ2Lab(&icco->header->illuminant, out, out); } else { for (e = 0; e < samplesperpixel; e++) out[e] = in[e]; } //printf("~1 %f %f %f -> %f %f %f\n", in[0], in[1], in[2], out[0], out[1], out[2]); add_fpixel(out); #ifdef NEVER /* Store PCS value in array */ inp[i].v[0] = out[0]; inp[i].v[1] = out[1]; inp[i].v[2] = out[2]; i++; #endif } } _TIFFfree(inbuf); TIFFClose(rh); /* Close Input file */ /* Done with lookup object */ if (luo != NULL) { luo->del(luo); xicco->del(xicco); /* Expansion wrapper */ icco->del(icco); /* Icc */ } nipoints = flush_filter(verb, 80.0); if ((inp = malloc(sizeof(co) * nipoints)) == NULL) error("Unable to allocate co array"); get_filter(inp); printf("~1 There are %d points\n",nipoints); //for (i = 0; i < nipoints; i++) //printf("~1 point %d = %f %f %f\n", i, inp[i].v[0], inp[i].v[1], inp[i].v[2]); del_filter(); /* Create the locus */ { double s0[3], s1[3]; double t0[3], t1[3]; double mm[3][4]; double im[3][4]; int gres[MXDI] = { 256 } ; if (usevec) { double max = -1e6; double min = 1e6; double dist; icmScale3(vec, vec, 1.0/icmNorm3(vec)); /* Locate the two furthest distant points measured along the vector */ for (i = 0; i < nipoints; i++) { double tt; tt = icmDot3(vec, inp[i].v); if (tt > max) { max = tt; icmAry2Ary(s1, inp[i].v); } if (tt < min) { min = tt; icmAry2Ary(s0, inp[i].v); } } dist = icmNorm33sq(s0, s1); printf("~1 most distant in vector %f %f %f = %f %f %f -> %f %f %f dist %f\n", vec[0], vec[1], vec[2], s0[0], s0[1], s0[2], s1[0], s1[1], s1[2], sqrt(dist)); t0[0] = 0.0; t0[1] = 0.0; t0[2] = 0.0; t1[0] = sqrt(dist); t1[1] = 0.0; t1[2] = 0.0; } else { double dist = 0.0; /* Locate the two furthest distant points (brute force) */ for (i = 0; i < (nipoints-1); i++) { for (j = i+1; j < nipoints; j++) { double tt; if ((tt = icmNorm33sq(inp[i].v, inp[j].v)) > dist) { dist = tt; icmAry2Ary(s0, inp[i].v); icmAry2Ary(s1, inp[j].v); } } } printf("~1 most distant = %f %f %f -> %f %f %f dist %f\n", s0[0], s0[1], s0[2], s1[0], s1[1], s1[2], sqrt(dist)); t0[0] = 0.0; t0[1] = 0.0; t0[2] = 0.0; t1[0] = sqrt(dist); t1[1] = 0.0; t1[2] = 0.0; } /* Transform our direction vector to the L* axis, and create inverse too */ icmVecRotMat(mm, s1, s0, t1, t0); icmVecRotMat(im, t1, t0, s1, s0); /* Setup for rspl to create smoothed locus */ for (i = 0; i < nipoints; i++) { icmMul3By3x4(inp[i].v, mm, inp[i].v); inp[i].p[0] = inp[i].v[0]; inp[i].v[0] = inp[i].v[1]; inp[i].v[1] = inp[i].v[2]; //printf("~1 point %d = %f -> %f %f\n", i, inp[i].p[0], inp[i].v[0], inp[i].v[1]); } /* Create rspl */ if ((rr = new_rspl(RSPL_NOFLAGS, 1, 2)) == NULL) error("Creating rspl failed"); rr->fit_rspl(rr, RSPL_NOFLAGS,inp, nipoints, NULL, NULL, gres, NULL, NULL, 5.0, NULL, NULL); #ifdef DEBUG_PLOT { #define XRES 100 double xx[XRES]; double y1[XRES]; double y2[XRES]; for (i = 0; i < XRES; i++) { co pp; double x; x = i/(double)(XRES-1); xx[i] = x * (t1[0] - t0[0]); pp.p[0] = xx[i]; rr->interp(rr, &pp); y1[i] = pp.v[0]; y2[i] = pp.v[1]; } do_plot(xx,y1,y2,NULL,XRES); } #endif /* DEBUG_PLOT */ free(inp); nopoints = t1[0] / DE_SPACE; if (nopoints < 2) nopoints = 2; /* Create the output points */ if ((outp = malloc(sizeof(co) * nopoints)) == NULL) error("Unable to allocate co array"); /* Setup initial division of locus */ for (i = 0; i < nopoints; i++) { double xx; xx = i/(double)(nopoints-1); xx *= (t1[0] - t0[0]); outp[i].p[0] = xx; //printf("~1 div %d = %f\n",i,outp[i].p[0]); } for (i = 0; i < (nopoints-1); i++) { outp[i].p[1] = outp[i+1].p[0] - outp[i].p[0]; //printf("~1 del div %d = %f\n",i,outp[i].p[1]); } /* Itterate until the delta between samples is even */ for (j = 0; j < 10; j++) { double alen, minl, maxl; double tdiv; alen = 0.0; minl = 1e38; maxl = -1.0; for (i = 0; i < nopoints; i++) { rr->interp(rr, &outp[i]); outp[i].v[2] = outp[i].v[1]; outp[i].v[1] = outp[i].v[0]; outp[i].v[0] = outp[i].p[0]; icmMul3By3x4(outp[i].v, im, outp[i].v); //printf("~1 locus pnt %d = %f %f %f\n", i,outp[i].v[0],outp[i].v[1],outp[i].v[1]); if (i > 0) { double tt[3], len; icmSub3(tt, outp[i].v, outp[i-1].v); len = icmNorm3(tt); outp[i-1].p[2] = len; if (len > maxl) maxl = len; if (len < minl) minl = len; alen += len; } } alen /= (nopoints-1.0); printf("~1 itter %d, alen = %f, minl = %f, maxl = %f\n",j,alen,minl,maxl); /* Adjust spacing */ tdiv = 0.0; for (i = 0; i < (nopoints-1); i++) { outp[i].p[1] *= pow(alen/outp[i].p[2], 1.0); tdiv += outp[i].p[1]; } //printf("~1 tdiv = %f\n",tdiv); for (i = 0; i < (nopoints-1); i++) { outp[i].p[1] *= (t1[0] - t0[0])/tdiv; //printf("~1 del div %d = %f\n",i,outp[i].p[1]); } tdiv = 0.0; for (i = 0; i < (nopoints-1); i++) { tdiv += outp[i].p[1]; } //printf("~1 tdiv now = %f\n",tdiv); for (i = 1; i < nopoints; i++) { outp[i].p[0] = outp[i-1].p[0] + outp[i-1].p[1]; //printf("~1 div %d = %f\n",i,outp[i].p[0]); } } /* Write the CGATS file */ { time_t clk = time(0); struct tm *tsp = localtime(&clk); char *atm = asctime(tsp); /* Ascii time */ cgats *pp; pp = new_cgats(); /* Create a CGATS structure */ pp->add_other(pp, "TS"); /* Test Set */ pp->add_table(pp, tt_other, 0); /* Add the first table for target points */ pp->add_kword(pp, 0, "DESCRIPTOR", "Argyll Test Point set",NULL); pp->add_kword(pp, 0, "ORIGINATOR", "Argyll tiffgmts", NULL); atm[strlen(atm)-1] = '\000'; /* Remove \n from end */ pp->add_kword(pp, 0, "CREATED",atm, NULL); pp->add_field(pp, 0, "SAMPLE_ID", cs_t); pp->add_field(pp, 0, "LAB_L", r_t); pp->add_field(pp, 0, "LAB_A", r_t); pp->add_field(pp, 0, "LAB_B", r_t); for (i = 0; i < nopoints; i++) { char buf[100]; cgats_set_elem ary[1 + 3]; sprintf(buf,"%d",i+1); ary[0].c = buf; ary[1 + 0].d = outp[i].v[0]; ary[1 + 1].d = outp[i].v[1]; ary[1 + 2].d = outp[i].v[2]; pp->add_setarr(pp, 0, ary); } if (pp->write_name(pp, out_name)) error("Write error : %s",pp->err); } /* Create the VRML file */ if (dovrml) { vrml *vv; strcpy(xl,".wrl"); printf("Output vrml file '%s'\n",out_name); if ((vv = new_vrml(out_name, doaxes)) == NULL) error ("Creating VRML object failed"); #ifdef NEVER vv->start_line_set(vv); for (i = 0; i < nopoints; i++) { vv->add_vertex(vv, outp[i].v); } vv->make_lines(vv, nopoints); #else for (i = 1; i < nopoints; i++) { vv->add_cone(vv, outp[i-1].v, outp[i].v, NULL, 0.5); } #endif vv->del(vv); } free(outp); } rr->del(rr); return 0; }
/* Might be easier to close it and re-open it ? */ static icxLuBase * set_icxLuMatrix( xicc *xicp, icmLuBase *plu, /* Pointer to Lu we are expanding (ours) */ int flags, /* white/black point flags */ int nodp, /* Number of points */ cow *ipoints, /* Array of input points in XYZ space */ double dispLuminance, /* > 0.0 if display luminance value and is known */ double wpscale, /* > 0.0 if input white point is to be scaled */ int quality /* Quality metric, 0..3 */ ) { icxLuMatrix *p; /* Object being created */ icc *icco = xicp->pp; /* Underlying icc object */ icmLuMatrix *pmlu = (icmLuMatrix *)plu; /* icc matrix lookup object */ int luflags = 0; /* icxLuMatrix alloc clip, merge flags */ int isLinear = 0; /* NZ if pure linear, gamma = 1.0 */ int isGamma = 0; /* NZ if gamma rather than shaper */ int isShTRC = 0; /* NZ if shared TRCs */ int inputChan = 3; /* Must be RGB like */ int outputChan = 3; /* Must be the PCS */ icmHeader *h = icco->header; /* Pointer to icc header */ int rsplflags = 0; /* Flags for scattered data rspl */ int e, f, i, j; int maxits = 200; /* Optimisation stop params */ double stopon = 0.01; /* Absolute delta E change to stop on */ cow *points; /* Copy of ipoints */ mxopt os; /* Optimisation information */ double rerr; #ifdef DEBUG_PLOT #define XRES 100 double xx[XRES]; double y1[XRES]; #endif /* DEBUG_PLOT */ if (flags & ICX_VERBOSE) rsplflags |= RSPL_VERBOSE; luflags = flags; /* Transfer straight though ? */ /* Check out some things about the profile */ { icmCurve *wor, *wog, *wob; wor = pmlu->redCurve; wog = pmlu->greenCurve; wob = pmlu->blueCurve; if (wor == wog) { if (wog != wob) { xicp->errc = 1; sprintf(xicp->err,"icx_set_matrix: TRC sharing is inconsistent"); return NULL; } isShTRC = 1; } if (wor->flag != wog->flag || wog->flag != wob->flag) { xicp->errc = 1; sprintf(xicp->err,"icx_set_matrix: TRC type is inconsistent"); return NULL; } if (wor->flag == icmCurveGamma) { isGamma = 1; } if (flags & ICX_NO_IN_SHP_LUTS) { isLinear = 1; } } /* Do basic icxLu creation and initialisation */ if ((p = alloc_icxLuMatrix(xicp, plu, 0, luflags)) == NULL) return NULL; p->func = icmFwd; /* Assumed by caller */ /* Get the effective spaces of underlying icm, and set icx the same */ plu->spaces(plu, &p->ins, NULL, &p->outs, NULL, NULL, &p->intent, NULL, &p->pcs, NULL); /* For set_icx the effective pcs has to be the same as the native pcs */ /* Sanity check for matrix */ if (p->pcs != icSigXYZData) { p->pp->errc = 1; sprintf(p->pp->err,"Can't create matrix profile with PCS of Lab !"); p->del((icxLuBase *)p); return NULL; } /* In general the native and effective ranges of the icx will be the same as the */ /* underlying icm lookup object. */ p->plu->get_lutranges(p->plu, p->ninmin, p->ninmax, p->noutmin, p->noutmax); p->plu->get_ranges(p->plu, p->inmin, p->inmax, p->outmin, p->outmax); /* If we have a Jab PCS override, reflect this in the effective icx range. */ /* Note that the ab ranges are nominal. They will exceed this range */ /* for colors representable in L*a*b* PCS */ if (p->ins == icxSigJabData) { p->inmin[0] = 0.0; p->inmax[0] = 100.0; p->inmin[1] = -128.0; p->inmax[1] = 128.0; p->inmin[2] = -128.0; p->inmax[2] = 128.0; } else if (p->outs == icxSigJabData) { p->outmin[0] = 0.0; p->outmax[0] = 100.0; p->outmin[1] = -128.0; p->outmax[1] = 128.0; p->outmin[2] = -128.0; p->outmax[2] = 128.0; } /* ------------------------------- */ /* Allocate the array passed to fit_rspl() */ if ((points = (cow *)malloc(sizeof(cow) * nodp)) == NULL) { p->pp->errc = 2; sprintf(p->pp->err,"Allocation of scattered coordinate array failed"); p->del((icxLuBase *)p); return NULL; } /* Setup points ready for optimisation */ for (i = 0; i < nodp; i++) { for (e = 0; e < inputChan; e++) points[i].p[e] = ipoints[i].p[e]; for (f = 0; f < outputChan; f++) points[i].v[f] = ipoints[i].v[f]; points[i].w = ipoints[i].w; /* Make sure its Lab for delta E calculation */ icmXYZ2Lab(&icmD50, points[i].v, points[i].v); } /* Setup for optimising run */ if (flags & ICX_VERBOSE) os.verb = 1; else os.verb = 0; os.points = points; os.nodp = nodp; os.isShTRC = 0; /* Set quality/effort factors */ if (quality >= 3) { /* Ultra high */ os.norders = 20; maxits = 5000; stopon = 0.000001; } else if (quality == 2) { /* High */ os.norders = 15; maxits = 2000; stopon = 0.00001; } else if (quality == 1) { /* Medium */ os.norders = 10; maxits = 1000; stopon = 0.0001; } else { /* Low */ os.norders = 5; maxits = 500; stopon = 0.001; } if (os.norders > MXNORDERS) os.norders = MXNORDERS; /* Set initial optimisation values */ os.v[0] = 0.4; os.v[1] = 0.4; os.v[2] = 0.2; /* Matrix */ os.v[3] = 0.2; os.v[4] = 0.8; os.v[5] = 0.1; os.v[6] = 0.02; os.v[7] = 0.15; os.v[8] = 1.3; if (isLinear) { /* Just gamma curve */ os.isLinear = 1; os.isGamma = 1; os.optdim = 9; os.v[9] = os.v[10] = os.v[11] = 1.0; /* Linear */ } else if (isGamma) { /* Just gamma curve */ os.isLinear = 0; os.isGamma = 1; os.optdim = 12; os.v[9] = os.v[10] = os.v[11] = 2.4; /* Gamma */ } else { /* Creating input curves */ os.isLinear = 0; os.isGamma = 0; os.optdim = 12 + 3 * os.norders; os.v[9] = os.v[10] = os.v[11] = 0.0; /* Offset */ os.v[12] = os.v[13] = os.v[14] = 2.0; /* 0th Harmonic */ for (i = 15; i < os.optdim; i++) os.v[i] = 0.0; /* Higher orders */ } /* Set search area to starting values */ for (j = 0; j < os.optdim; j++) os.sa[j] = 0.2; /* Matrix, Gamma, Offsets, harmonics */ if (isShTRC) { /* Adjust things for shared */ os.isShTRC = 1; if (os.optdim > 9) { /* Pack red paramenters down */ for (i = 9; i < os.optdim; i++) { os.v[i] = os.v[(i - 9) * 3 + 9]; os.sa[i] = os.sa[(i - 9) * 3 + 9]; } os.optdim = ((os.optdim - 9)/3) + 9; } } if (os.verb) { if (os.isLinear) printf("Creating matrix...\n"); else printf("Creating matrix and curves...\n"); } if (powell(&rerr, os.optdim, os.v, os.sa, stopon, maxits, mxoptfunc, (void *)&os, mxprogfunc, (void *)&os) != 0) warning("Powell failed to converge, residual error = %f",rerr); #ifdef NEVER printf("Matrix = %f %f %f\n",os.v[0], os.v[1], os.v[2]); printf(" %f %f %f\n",os.v[3], os.v[4], os.v[5]); printf(" %f %f %f\n",os.v[6], os.v[7], os.v[8]); if (!isLinear) { /* Creating input curves */ if (isGamma) { /* Creating input curves */ if (isShTRC) printf("Gamma = %f\n",os.v[9]); else printf("Gamma = %f %f %f\n",os.v[9], os.v[10], os.v[11]); } else { /* Creating input curves */ if (isShTRC) printf("Offset = %f\n",os.v[9]); else printf("Offset = %f %f %f\n",os.v[9], os.v[10], os.v[11]); for (j = 0; j < os.norders; j++) { if (isShTRC) printf("%d harmonics = %f\n",j, os.v[10 + j]); else printf("%d harmonics = %f %f %f\n",j, os.v[12 + j * 3], os.v[13 + j * 3], os.v[14 + j * 3]); } } } #endif /* NEVER */ /* Deal with white/black points */ if (flags & (ICX_SET_WHITE | ICX_SET_BLACK)) { double wp[3]; /* Absolute White point in XYZ */ double bp[3]; /* Absolute Black point in XYZ */ if (flags & ICX_VERBOSE) printf("Find white & black points\n"); icmXYZ2Ary(wp, icmD50); /* Set a default value - D50 */ icmXYZ2Ary(bp, icmBlack); /* Set a default value - absolute black */ /* Figure out the device values for white */ if (h->deviceClass == icSigInputClass) { double dwhite[MXDI], dblack[MXDI]; /* Device white and black values */ double Lmax = -1e60; double Lmin = 1e60; /* We assume that the input target is well behaved, */ /* and that it includes a white and black point patch, */ /* and that they have the extreme L values */ /* NOTE that this may not be the best approach ! It may be better to average the chromaticity of all the neutral seeming patches, since the whitest patch may have (for instance) a blue tint. */ /* Discover the white and black points */ for (i = 0; i < nodp; i++) { if (points[i].v[0] > Lmax) { Lmax = wp[0] = points[i].v[0]; wp[1] = points[i].v[1]; wp[2] = points[i].v[2]; dwhite[0] = points[i].p[0]; dwhite[1] = points[i].p[1]; dwhite[2] = points[i].p[2]; } if (points[i].v[0] < Lmin) { Lmin = bp[0] = points[i].v[0]; bp[1] = points[i].v[1]; bp[2] = points[i].v[2]; dblack[0] = points[i].p[0]; dblack[1] = points[i].p[1]; dblack[2] = points[i].p[2]; } } /* Lookup device white/black values in model */ mxmfunc(&os, os.v, wp, dwhite); mxmfunc(&os, os.v, bp, dblack); /* If we were given an input white point scale factor, apply it */ if (wpscale >= 0.0) { wp[0] *= wpscale; wp[1] *= wpscale; wp[2] *= wpscale; } } else { /* Assume Monitor class */ switch(h->colorSpace) { case icSigCmyData: { double cmy[3]; /* Lookup white value */ for (e = 0; e < inputChan; e++) cmy[e] = 0.0; mxmfunc(&os, os.v, wp, cmy); if (flags & ICX_VERBOSE) printf("Initial white point = %f %f %f\n",wp[0],wp[1],wp[2]); /* Lookup black value */ for (e = 0; e < inputChan; e++) cmy[e] = 1.0; mxmfunc(&os, os.v, bp, cmy); if (flags & ICX_VERBOSE) printf("Initial black point = %f %f %f\n",bp[0],bp[1],bp[2]); break; } case icSigRgbData: { double rgb[3]; /* Lookup white value */ for (e = 0; e < inputChan; e++) rgb[e] = 1.0; mxmfunc(&os, os.v, wp, rgb); if (flags & ICX_VERBOSE) printf("Initial white point = %f %f %f\n",wp[0],wp[1],wp[2]); /* Lookup black value */ for (e = 0; e < inputChan; e++) rgb[e] = 0.0; mxmfunc(&os, os.v, bp, rgb); if (flags & ICX_VERBOSE) printf("Initial black point = %f %f %f\n",bp[0],bp[1],bp[2]); break; } default: { xicp->errc = 1; sprintf(xicp->err,"set_icxLuMatrix: can't handle color space %s", icm2str(icmColorSpaceSignature, h->colorSpace)); p->del((icxLuBase *)p); return NULL; break; } } } /* If this is a display, adjust the white point to be */ /* exactly Y = 1.0, and compensate the matrix, dispLuminance */ /* and black point accordingly. */ if (h->deviceClass == icSigDisplayClass) { double scale = 1.0/wp[1]; int i; for (i = 0; i < 9; i++) { os.v[i] *= scale; } dispLuminance *= wp[1]; for (i = 0; i < 3; i++) { wp[i] *= scale; bp[i] *= scale; } } /* Absolute luminance tag */ if (flags & ICX_WRITE_WBL && h->deviceClass == icSigDisplayClass && dispLuminance > 0.0) { icmXYZArray *wo; if ((wo = (icmXYZArray *)icco->read_tag( icco, icSigLuminanceTag)) == NULL) { xicp->errc = 1; sprintf(xicp->err,"icx_set_luminance: couldn't find luminance tag"); p->del((icxLuBase *)p); return NULL; } if (wo->ttype != icSigXYZArrayType) { xicp->errc = 1; sprintf(xicp->err,"luminance: tag has wrong type"); p->del((icxLuBase *)p); return NULL; } wo->size = 1; wo->allocate((icmBase *)wo); /* Allocate space */ wo->data[0].X = 0.0; wo->data[0].Y = dispLuminance; wo->data[0].Z = 0.0; if (flags & ICX_VERBOSE) printf("Display Luminance = %f\n", wo->data[0].Y); } /* Write white and black tags */ if ((flags & ICX_WRITE_WBL) && (flags & ICX_SET_WHITE)) { /* White Point Tag: */ icmXYZArray *wo; if ((wo = (icmXYZArray *)icco->read_tag( icco, icSigMediaWhitePointTag)) == NULL) { xicp->errc = 1; sprintf(xicp->err,"icx_set_white_black: couldn't find white tag"); p->del((icxLuBase *)p); return NULL; } if (wo->ttype != icSigXYZArrayType) { xicp->errc = 1; sprintf(xicp->err,"icx_set_white_black: white tag has wrong type"); p->del((icxLuBase *)p); return NULL; } wo->size = 1; wo->allocate((icmBase *)wo); /* Allocate space */ wo->data[0].X = wp[0]; wo->data[0].Y = wp[1]; wo->data[0].Z = wp[2]; if (flags & ICX_VERBOSE) printf("White point XYZ = %f %f %f\n",wp[0],wp[1],wp[2]); } if ((flags & ICX_WRITE_WBL) && (flags & ICX_SET_BLACK)) { /* Black Point Tag: */ icmXYZArray *wo; if ((wo = (icmXYZArray *)icco->read_tag( icco, icSigMediaBlackPointTag)) == NULL) { xicp->errc = 1; sprintf(xicp->err,"icx_set_white_black: couldn't find black tag"); p->del((icxLuBase *)p); return NULL; } if (wo->ttype != icSigXYZArrayType) { xicp->errc = 1; sprintf(xicp->err,"icx_set_white_black: black tag has wrong type"); p->del((icxLuBase *)p); return NULL; } wo->size = 1; wo->allocate((icmBase *)wo); /* Allocate space */ wo->data[0].X = bp[0]; wo->data[0].Y = bp[1]; wo->data[0].Z = bp[2]; if (flags & ICX_VERBOSE) printf("Black point XYZ = %f %f %f\n",bp[0],bp[1],bp[2]); } /* Fix matrix to be relative to D50 white point, rather than absolute */ if (flags & ICX_SET_WHITE) { icmXYZNumber swp; double mat[3][3]; if (flags & ICX_VERBOSE) printf("Fixup matrix for white point\n"); icmAry2XYZ(swp, wp); /* Transfer from parameter to matrix */ mat[0][0] = os.v[0]; mat[0][1] = os.v[1]; mat[0][2] = os.v[2]; mat[1][0] = os.v[3]; mat[1][1] = os.v[4]; mat[1][2] = os.v[5]; mat[2][0] = os.v[6]; mat[2][1] = os.v[7]; mat[2][2] = os.v[8]; /* Adapt matrix */ icmChromAdaptMatrix(ICM_CAM_MULMATRIX | ICM_CAM_BRADFORD, icmD50, swp, mat); /* Transfer back to parameters */ os.v[0] = mat[0][0]; os.v[1] = mat[0][1]; os.v[2] = mat[0][2]; os.v[3] = mat[1][0]; os.v[4] = mat[1][1]; os.v[5] = mat[1][2]; os.v[6] = mat[2][0]; os.v[7] = mat[2][1]; os.v[8] = mat[2][2]; if (flags & ICX_VERBOSE) { printf("After white point adjust:\n"); printf("Matrix = %f %f %f\n",os.v[0], os.v[1], os.v[2]); printf(" %f %f %f\n",os.v[3], os.v[4], os.v[5]); printf(" %f %f %f\n",os.v[6], os.v[7], os.v[8]); } } } if (flags & ICX_VERBOSE) printf("Done gamma/shaper and matrix creation\n"); /* Write the gamma/shaper and matrix to the icc memory structures */ if (!isGamma) { /* Creating input curves */ unsigned int ui; icmCurve *wor, *wog, *wob; wor = pmlu->redCurve; wog = pmlu->greenCurve; wob = pmlu->blueCurve; for (ui = 0; ui < wor->size; ui++) { double in, rgb[3]; for (j = 0; j < 3; j++) { in = (double)ui / (wor->size - 1.0); mxmfunc1(&os, j, os.v, &rgb[j], &in); } wor->data[ui] = rgb[0]; /* Curve values 0.0 - 1.0 */ if (!isShTRC) { wog->data[ui] = rgb[1]; wob->data[ui] = rgb[2]; } } #ifdef DEBUG_PLOT /* Display the result fit */ for (j = 0; j < 3; j++) { for (i = 0; i < XRES; i++) { double x, y; xx[i] = x = i/(double)(XRES-1); mxmfunc1(&os, j, os.v, &y, &x); y1[i] = y; } do_plot(xx,y1,NULL,NULL,XRES); } #endif /* DEBUG_PLOT */ } else { /* Gamma */ icmCurve *wor, *wog, *wob; wor = pmlu->redCurve; wog = pmlu->greenCurve; wob = pmlu->blueCurve; wor->data[0] = os.v[9]; /* Gamma values */ if (!isShTRC) { wog->data[0] = os.v[10]; wob->data[0] = os.v[11]; } } /* Matrix values */ { icmXYZArray *wor, *wog, *wob; wor = pmlu->redColrnt; wog = pmlu->greenColrnt; wob = pmlu->blueColrnt; wor->data[0].X = os.v[0]; wor->data[0].Y = os.v[3]; wor->data[0].Z = os.v[6]; wog->data[0].X = os.v[1]; wog->data[0].Y = os.v[4]; wog->data[0].Z = os.v[7]; wob->data[0].X = os.v[2]; wob->data[0].Y = os.v[5]; wob->data[0].Z = os.v[8]; /* Load into pmlu matrix and inverse ??? */ } /* Free the coordinate lists */ free(points); if (flags & ICX_VERBOSE) printf("Profile done\n"); return (icxLuBase *)p; }
int main(int argc, char *argv[]) { int fa,nfa; /* argument we're looking at */ char in_name[100]; /* Raster file name */ char out_name[100]; /* Raster file name */ int slow = 0; int check = 0; int i, rv = 0; TIFF *rh = NULL, *wh = NULL; int x, y, width, height; /* Size of image */ uint16 samplesperpixel, bitspersample; int no_pmtc; /* Number of input photometrics */ uint16 photometric, pmtc[10]; /* Photometrics of input file, and input profile */ uint16 pconfig; /* Planar configuration */ uint16 resunits; float resx, resy; tdata_t *inbuf, *outbuf, *checkbuf; /* IMDI */ imdi *s = NULL; sucntx su; /* Setup context */ unsigned char *inp[MAX_CHAN]; unsigned char *outp[MAX_CHAN]; int clutres = 33; /* Error check */ int mxerr = 0; double avgerr = 0.0; double avgcount = 0.0; if (argc < 2) usage(); su.verb = 0; su.icombine = 0; su.ocombine = 0; su.link = 0; su.in.intent = icmDefaultIntent; su.out.intent = icmDefaultIntent; /* Process the arguments */ for(fa = 1;fa < argc;fa++) { nfa = fa; /* skip to nfa if next argument is used */ if (argv[fa][0] == '-') { /* Look for any flags */ char *na = NULL; /* next argument after flag, null if none */ if (argv[fa][2] != '\000') na = &argv[fa][2]; /* next is directly after flag */ else { if ((fa+1) < argc) { if (argv[fa+1][0] != '-') { nfa = fa + 1; na = argv[nfa]; /* next is seperate non-flag argument */ } } } if (argv[fa][1] == '?') usage(); /* Slow, Precise */ else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') { slow = 1; } /* Combine per channel curves */ else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') { su.icombine = 1; su.ocombine = 1; } /* Check curves */ else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') { check = 1; } /* Link profiles */ else if (argv[fa][1] == 'l' || argv[fa][1] == 'L') { su.link = 1; } /* Input profile Intent */ else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') { fa = nfa; if (na == NULL) usage(); switch (na[0]) { case 'p': case 'P': su.in.intent = icPerceptual; break; case 'r': case 'R': su.in.intent = icRelativeColorimetric; break; case 's': case 'S': su.in.intent = icSaturation; break; case 'a': case 'A': su.in.intent = icAbsoluteColorimetric; break; default: usage(); } } /* Output profile Intent */ else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') { fa = nfa; if (na == NULL) usage(); switch (na[0]) { case 'p': case 'P': su.out.intent = icPerceptual; break; case 'r': case 'R': su.out.intent = icRelativeColorimetric; break; case 's': case 'S': su.out.intent = icSaturation; break; case 'a': case 'A': su.out.intent = icAbsoluteColorimetric; break; default: usage(); } } /* Verbosity */ else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') { su.verb = 1; } else usage(); } else break; } if (su.link) { if (fa >= argc || argv[fa][0] == '-') usage(); strcpy(su.in.name,argv[fa++]); if (fa >= argc || argv[fa][0] == '-') usage(); strcpy(su.out.name,argv[fa++]); } else { if (fa >= argc || argv[fa][0] == '-') usage(); strcpy(su.dev.name,argv[fa++]); } if (fa >= argc || argv[fa][0] == '-') usage(); strcpy(in_name,argv[fa++]); if (fa >= argc || argv[fa][0] == '-') usage(); strcpy(out_name,argv[fa++]); /* - - - - - - - - - - - - - - - - */ if (su.link) { icColorSpaceSignature natpcs; /* Open up the input device profile for reading */ if ((su.in.fp = new_icmFileStd_name(su.in.name,"r")) == NULL) error ("Can't open file '%s'",su.in.name); if ((su.in.c = new_icc()) == NULL) error ("Creation of Input profile ICC object failed"); /* Read header etc. */ if ((rv = su.in.c->read(su.in.c,su.in.fp,0)) != 0) error ("%d, %s on file '%s'",rv,su.in.c->err,su.in.name); su.in.h = su.in.c->header; /* Check that it is a suitable device input icc */ if (su.in.h->deviceClass != icSigInputClass && su.in.h->deviceClass != icSigDisplayClass && su.in.h->deviceClass != icSigOutputClass && su.in.h->deviceClass != icSigColorSpaceClass) /* For sRGB etc. */ error("Input profile isn't a device profile"); /* Get a conversion object */ if ((su.in.luo = su.in.c->get_luobj(su.in.c, icmFwd, su.in.intent, icSigLabData, icmLuOrdNorm)) == NULL) error ("%d, %s for profile '%s'",su.in.c->errc, su.in.c->err, su.in.name); /* Get details of conversion (Arguments may be NULL if info not needed) */ su.in.luo->spaces(su.in.luo, &su.ins, &su.id, NULL, NULL, &su.in.alg, NULL, NULL, NULL); /* Get native PCS space */ su.in.luo->lutspaces(su.in.luo, NULL, NULL, NULL, NULL, &natpcs); if (natpcs == icSigXYZData) { su.icombine = 1; /* XYZ is to non-linear to be a benefit */ } /* Open up the output device profile for reading */ if ((su.out.fp = new_icmFileStd_name(su.out.name,"r")) == NULL) error ("Can't open file '%s'",su.out.name); if ((su.out.c = new_icc()) == NULL) error ("Creation of Output profile ICC object failed"); /* Read header etc. */ if ((rv = su.out.c->read(su.out.c,su.out.fp,0)) != 0) error ("%d, %s on file '%s'",rv,su.out.c->err,su.out.name); su.out.h = su.out.c->header; /* Check that it is a suitable device output icc */ if (su.out.h->deviceClass != icSigInputClass && su.out.h->deviceClass != icSigDisplayClass && su.out.h->deviceClass != icSigOutputClass && su.out.h->deviceClass != icSigColorSpaceClass) /* For sRGB etc. */ error("Output profile isn't a device profile"); /* Get a conversion object */ if ((su.out.luo = su.out.c->get_luobj(su.out.c, icmBwd, su.out.intent, icSigLabData, icmLuOrdNorm)) == NULL) error ("%d, %s for profile '%s'",su.out.c->errc, su.out.c->err, su.out.name); /* Get details of conversion (Arguments may be NULL if info not needed) */ su.out.luo->spaces(su.out.luo, NULL, NULL, &su.outs, &su.od, &su.out.alg, NULL, NULL, NULL); /* Get native PCS space */ su.out.luo->lutspaces(su.out.luo, NULL, NULL, NULL, NULL, &natpcs); if (natpcs == icSigXYZData) { su.ocombine = 1; /* XYZ is to non-linear to be a benefit */ } /* See discussion in imdi/imdi_gen.c for ideal numbers */ /* Use "high quality" resolution numbers */ switch (su.id) { case 0: error ("Illegal number of input chanels"); case 1: clutres = 256; break; case 2: clutres = 256; break; case 3: clutres = 33; break; case 4: clutres = 18; break; case 5: clutres = 16; break; case 6: clutres = 9; break; case 7: clutres = 7; break; case 8: clutres = 6; break; deault: /* > 8 chan */ clutres = 3; break; } } else { icmLut *lut; /* ICC LUT table */ icmLuLut *luluo; /* LUT lookup object */ /* Open up the device link profile for reading */ if ((su.dev.fp = new_icmFileStd_name(su.dev.name,"r")) == NULL) error ("Can't open file '%s'",su.dev.name); if ((su.dev.c = new_icc()) == NULL) error ("Creation of ICC object failed"); if ((rv = su.dev.c->read(su.dev.c, su.dev.fp, 0)) != 0) error ("%d, %s",rv,su.dev.c->err); su.dev.h = su.dev.c->header; if (su.verb) { icmFile *op; if ((op = new_icmFileStd_fp(stdout)) == NULL) error ("Can't open stdout"); su.dev.h->dump(su.dev.h, op, 1); op->del(op); } /* Check that the profile is appropriate */ if (su.dev.h->deviceClass != icSigLinkClass) error("Profile isn't a device link profile"); /* Get a conversion object */ if ((su.dev.luo = su.dev.c->get_luobj(su.dev.c, icmFwd, icmDefaultIntent, icmSigDefaultData, icmLuOrdNorm)) == NULL) error ("%d, %s",su.dev.c->errc, su.dev.c->err); /* Get details of conversion (Arguments may be NULL if info not needed) */ su.dev.luo->spaces(su.dev.luo, &su.ins, &su.id, &su.outs, &su.od, &su.dev.alg, NULL, NULL, NULL); if (su.dev.alg != icmLutType) error ("DeviceLink profile doesn't have Lut !"); luluo = (icmLuLut *)su.dev.luo; /* Safe to coerce */ luluo->get_info(luluo, &lut, NULL, NULL, NULL); /* Get some details */ clutres = lut->clutPoints; /* Desired table resolution */ } /* - - - - - - - - - - - - - - - */ /* Open up input tiff file ready for reading */ /* Got arguments, so setup to process the file */ if ((rh = TIFFOpen(in_name, "r")) == NULL) error("error opening read file '%s'",in_name); TIFFGetField(rh, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField(rh, TIFFTAG_IMAGELENGTH, &height); TIFFGetField(rh, TIFFTAG_BITSPERSAMPLE, &bitspersample); if (bitspersample != 8 && bitspersample != 16) { error("TIFF Input file must be 8 or 16 bit/channel"); } TIFFGetField(rh, TIFFTAG_PHOTOMETRIC, &photometric); if ((no_pmtc = ColorSpaceSignature2TiffPhotometric(pmtc, su.ins)) == 0) error("ICC input colorspace '%s' can't be handled by a TIFF file!", icm2str(icmColorSpaceSignature, su.ins)); for (i = 0; i < no_pmtc; i++) { if (pmtc[i] == photometric) break; /* Matches */ } if (i >= no_pmtc) { switch (no_pmtc) { case 1: error("ICC input colorspace '%s' doesn't match TIFF photometric '%s'!", icm2str(icmColorSpaceSignature, su.ins), Photometric2str(pmtc[0])); case 2: error("ICC input colorspace '%s' doesn't match TIFF photometric '%s' or '%s'!", icm2str(icmColorSpaceSignature, su.ins), Photometric2str(pmtc[0]), Photometric2str(pmtc[1])); default: error("ICC input colorspace '%s' doesn't match TIFF photometric '%s', '%s' or '%s'!", icm2str(icmColorSpaceSignature, su.ins), Photometric2str(pmtc[0]), Photometric2str(pmtc[1]), Photometric2str(pmtc[2])); } } TIFFGetField(rh, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel); if (su.id != samplesperpixel) error ("TIFF Input file has %d input channels mismatched to colorspace '%s'", samplesperpixel, icm2str(icmColorSpaceSignature, su.ins)); TIFFGetField(rh, TIFFTAG_PLANARCONFIG, &pconfig); if (pconfig != PLANARCONFIG_CONTIG) error ("TIFF Input file must be planar"); TIFFGetField(rh, TIFFTAG_RESOLUTIONUNIT, &resunits); TIFFGetField(rh, TIFFTAG_XRESOLUTION, &resx); TIFFGetField(rh, TIFFTAG_YRESOLUTION, &resy); /* - - - - - - - - - - - - - - - */ if ((wh = TIFFOpen(out_name, "w")) == NULL) error("Can\'t create TIFF file '%s'!",out_name); TIFFSetField(wh, TIFFTAG_IMAGEWIDTH, width); TIFFSetField(wh, TIFFTAG_IMAGELENGTH, height); TIFFSetField(wh, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); TIFFSetField(wh, TIFFTAG_SAMPLESPERPIXEL, su.od); TIFFSetField(wh, TIFFTAG_BITSPERSAMPLE, bitspersample); TIFFSetField(wh, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); if ((no_pmtc = ColorSpaceSignature2TiffPhotometric(pmtc, su.outs)) == 0) error("TIFF file can't handle output colorspace '%s'!", icm2str(icmColorSpaceSignature, su.outs)); TIFFSetField(wh, TIFFTAG_PHOTOMETRIC, pmtc[0]); /* Use first returned */ if (pmtc[0] == PHOTOMETRIC_SEPARATED) { int iset; int inlen; char *inames; iset = ColorSpaceSignature2TiffInkset(su.outs, &inlen, &inames); if (iset != 0xffff && inlen > 0 && inames != NULL) { TIFFSetField(wh, TIFFTAG_INKSET, iset); if (inames != NULL) { TIFFSetField(wh, TIFFTAG_INKNAMES, inlen, inames); } } } TIFFSetField(wh, TIFFTAG_COMPRESSION, COMPRESSION_NONE); if (resunits) { TIFFSetField(wh, TIFFTAG_RESOLUTIONUNIT, resunits); TIFFSetField(wh, TIFFTAG_XRESOLUTION, resx); TIFFSetField(wh, TIFFTAG_YRESOLUTION, resy); } TIFFSetField(wh, TIFFTAG_IMAGEDESCRIPTION, "Color corrected by Argyll"); /* - - - - - - - - - - - - - - - */ /* Setup the imdi */ if (!slow) { s = new_imdi( su.id, /* Number of input dimensions */ su.od, /* Number of output dimensions */ /* Input pixel representation */ bitspersample == 8 ? pixint8 : pixint16, /* Output pixel representation */ 0x0, /* Treat every channel as unsigned */ bitspersample == 8 ? pixint8 : pixint16, 0x0, /* Treat every channel as unsigned */ clutres, /* Desired table resolution */ input_curve, /* Callback functions */ md_table, output_curve, (void *)&su /* Context to callbacks */ ); if (s == NULL) error("new_imdi failed"); } /* - - - - - - - - - - - - - - - */ /* Process colors to translate */ /* (Should fix this to process a group of lines at a time ?) */ inbuf = _TIFFmalloc(TIFFScanlineSize(rh)); outbuf = _TIFFmalloc(TIFFScanlineSize(wh)); if (check) checkbuf = _TIFFmalloc(TIFFScanlineSize(wh)); inp[0] = (unsigned char *)inbuf; outp[0] = (unsigned char *)outbuf; if (!slow) { /* Fast */ for (y = 0; y < height; y++) { /* Read in the next line */ if (TIFFReadScanline(rh, inbuf, y, 0) < 0) error ("Failed to read TIFF line %d",y); /* Do fast conversion */ s->interp(s, (void **)outp, (void **)inp, width); if (check) { /* Do floating point conversion */ for (x = 0; x < width; x++) { int i; double in[MAX_CHAN], out[MAX_CHAN]; if (bitspersample == 8) for (i = 0; i < su.id; i++) in[i] = ((unsigned char *)inbuf)[x * su.id + i]/255.0; else for (i = 0; i < su.id; i++) in[i] = ((unsigned short *)inbuf)[x * su.id + i]/65535.0; if (su.link) { if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1) error ("%d, %s",su.dev.c->errc,su.dev.c->err); if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1) error ("%d, %s",su.dev.c->errc,su.dev.c->err); } else { if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1) error ("%d, %s",su.dev.c->errc,su.dev.c->err); } if (bitspersample == 8) for (i = 0; i < su.od; i++) ((unsigned char *)checkbuf)[x * su.od + i] = (int)(out[i] * 255.0 + 0.5); else for (i = 0; i < su.od; i++) ((unsigned short *)checkbuf)[x * su.od + i] = (int)(out[i] * 65535.0 + 0.5); } /* Compute the errors */ for (x = 0; x < (width * su.od); x++) { int err; if (bitspersample == 8) err = ((unsigned char *)outbuf)[x] - ((unsigned char *)checkbuf)[x]; else err = ((unsigned short *)outbuf)[x] - ((unsigned short *)checkbuf)[x]; if (err < 0) err = -err; if (err > mxerr) mxerr = err; avgerr += (double)err; avgcount++; } } if (TIFFWriteScanline(wh, outbuf, y, 0) < 0) error ("Failed to write TIFF line %d",y); } } else { /* Slow but precise */ if (bitspersample == 8) { for (y = 0; y < height; y++) { /* Read in the next line */ if (TIFFReadScanline(rh, inbuf, y, 0) < 0) error ("Failed to read TIFF line %d",y); /* Do floating point conversion */ for (x = 0; x < width; x++) { int i; double in[MAX_CHAN], out[MAX_CHAN]; for (i = 0; i < su.id; i++) { in[i] = ((unsigned char *)inbuf)[x * su.id + i]/255.0; } if (su.link) { if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1) error ("%d, %s",su.dev.c->errc,su.dev.c->err); if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1) error ("%d, %s",su.dev.c->errc,su.dev.c->err); } else { if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1) error ("%d, %s",su.dev.c->errc,su.dev.c->err); } for (i = 0; i < su.od; i++) { double outi = out[i]; if (outi < 0.0) /* Protect against sillies */ outi = 0.0; else if (outi > 1.0) outi = 1.0; ((unsigned char *)outbuf)[x * su.od + i] = (int)(outi * 255.0 + 0.5); } } if (TIFFWriteScanline(wh, outbuf, y, 0) < 0) error ("Failed to write TIFF line %d",y); } } else if (bitspersample == 16) { for (y = 0; y < height; y++) { /* Read in the next line */ if (TIFFReadScanline(rh, inbuf, y, 0) < 0) error ("Failed to read TIFF line %d",y); /* Do floating point conversion */ for (x = 0; x < width; x++) { int i; double in[MAX_CHAN], out[MAX_CHAN]; for (i = 0; i < su.id; i++) { in[i] = ((unsigned short *)inbuf)[x * su.id + i]/65535.0; } if (su.link) { if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1) error ("%d, %s",su.dev.c->errc,su.dev.c->err); if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1) error ("%d, %s",su.dev.c->errc,su.dev.c->err); } else { if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1) error ("%d, %s",su.dev.c->errc,su.dev.c->err); } for (i = 0; i < su.od; i++) { double outi = out[i]; if (outi < 0.0) /* Protect against sillies */ outi = 0.0; else if (outi > 1.0) outi = 1.0; ((unsigned short *)outbuf)[x * su.od + i] = (int)(outi * 65535.0 + 0.5); } } if (TIFFWriteScanline(wh, outbuf, y, 0) < 0) error ("Failed to write TIFF line %d",y); } } } if (check) { printf("Worst error = %d bits, average error = %f bits\n", mxerr, avgerr/avgcount); if (bitspersample == 8) printf("Worst error = %f%%, average error = %f%%\n", mxerr/2.55, avgerr/(2.55 * avgcount)); else printf("Worst error = %f%%, average error = %f%%\n", mxerr/655.35, avgerr/(655.35 * avgcount)); } /* Done with lookup object */ if (s != NULL) s->done(s); if (su.link) { su.in.luo->del(su.in.luo); su.in.c->del(su.in.c); su.in.fp->del(su.in.fp); su.out.luo->del(su.out.luo); su.out.c->del(su.out.c); su.out.fp->del(su.out.fp); } else { su.dev.luo->del(su.dev.luo); su.dev.c->del(su.dev.c); su.dev.fp->del(su.dev.fp); } _TIFFfree(inbuf); _TIFFfree(outbuf); if (check) _TIFFfree(checkbuf); TIFFClose(rh); /* Close Input file */ TIFFClose(wh); /* Close Output file */ return 0; }
int main(int argc, char *argv[]) { int fa,nfa; /* argument we're looking at */ char prof_name[100]; mpp *mppo; int verb = 0; int test = 0; /* special test code */ int dogam = 0; /* Create gamut */ int dowrl = 0; /* Create VRML gamut */ int doaxes = 1; /* Create VRML axes */ double trans = 0.0; /* Transparency */ double gamres = 0.0; /* Gamut resolution */ int repYxy = 0; /* Report Yxy */ int repSpec = 0; /* Report Spectral */ int bwd = 0; /* Do reverse lookup */ double dlimit; /* Device ink limit */ double limit = -1.0; /* Used ink limit */ int spec = 0; /* Use spectral data flag */ int spec_n; /* Number of spectral bands, 0 if not valid */ double spec_wl_short; /* First reading wavelength in nm (shortest) */ double spec_wl_long; /* Last reading wavelength in nm (longest) */ int fwacomp = 0; /* FWA compensation */ icxIllumeType illum = icxIT_default; /* Spectral defaults */ xspect cust_illum; /* Custom illumination spectrum */ icxObserverType observ = icxOT_default; char buf[200]; double in[MAX_CHAN], out[MAX_CHAN]; int rv = 0; inkmask imask; /* Device Ink mask */ char *ident = NULL; /* Device colorspec description */ icColorSpaceSignature pcss; /* Type of PCS space */ int devn, pcsn; /* Number of components */ icColorSpaceSignature pcsor = icSigLabData; /* Default */ error_program = argv[0]; if (argc < 2) usage(); /* Process the arguments */ for(fa = 1;fa < argc;fa++) { nfa = fa; /* skip to nfa if next argument is used */ if (argv[fa][0] == '-') { /* Look for any flags */ char *na = NULL; /* next argument after flag, null if none */ if (argv[fa][2] != '\000') na = &argv[fa][2]; /* next is directly after flag */ else { if ((fa+1) < argc) { if (argv[fa+1][0] != '-') { nfa = fa + 1; na = argv[nfa]; /* next is seperate non-flag argument */ } } } if (argv[fa][1] == '?') usage(); /* Verbosity */ else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') { verb = 1; } /* function */ else if (argv[fa][1] == 'f' || argv[fa][1] == 'F') { fa = nfa; if (na == NULL) usage(); switch (na[0]) { case 'f': case 'F': bwd = 0; break; case 'b': case 'B': bwd = 1; break; default: usage(); } } /* PCS override */ else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') { fa = nfa; if (na == NULL) usage(); switch (na[0]) { case 'x': case 'X': pcsor = icSigXYZData; repYxy = repSpec = 0; break; case 'l': case 'L': pcsor = icSigLabData; repYxy = repSpec = 0; break; case 'y': case 'Y': pcsor = icSigXYZData; repYxy = 1; repSpec = 0; break; case 's': case 'S': pcsor = icSigXYZData; repYxy = 0; repSpec = 1; spec = 1; break; default: usage(); } } /* Ink Limit */ else if (argv[fa][1] == 'l' || argv[fa][1] == 'L') { fa = nfa; if (na == NULL) usage(); limit = atof(na); } /* Spectral Illuminant type */ else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') { fa = nfa; if (na == NULL) usage(); if (strcmp(na, "A") == 0) { spec = 1; illum = icxIT_A; } else if (strcmp(na, "C") == 0) { spec = 1; illum = icxIT_C; } else if (strcmp(na, "D50") == 0) { spec = 1; illum = icxIT_D50; } else if (strcmp(na, "D65") == 0) { spec = 1; illum = icxIT_D65; } else if (strcmp(na, "F5") == 0) { spec = 1; illum = icxIT_F5; } else if (strcmp(na, "F8") == 0) { spec = 1; illum = icxIT_F8; } else if (strcmp(na, "F10") == 0) { spec = 1; illum = icxIT_F10; } else { /* Assume it's a filename */ spec = 1; illum = icxIT_custom; if (read_xspect(&cust_illum, na) != 0) usage(); } } /* Spectral Observer type */ else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') { fa = nfa; if (na == NULL) usage(); if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */ spec = 1; observ = icxOT_CIE_1931_2; } else if (strcmp(na, "1964_10") == 0) { /* Classic 10 degree */ spec = 1; observ = icxOT_CIE_1964_10; } else if (strcmp(na, "1955_2") == 0) { /* Stiles and Burch 1955 2 degree */ spec = 1; observ = icxOT_Stiles_Burch_2; } else if (strcmp(na, "1978_2") == 0) { /* Judd and Voss 1978 2 degree */ spec = 1; observ = icxOT_Judd_Voss_2; } else if (strcmp(na, "shaw") == 0) { /* Shaw and Fairchilds 1997 2 degree */ spec = 1; observ = icxOT_Shaw_Fairchild_2; } else usage(); } /* Fluerescent Whitner compensation */ else if (argv[fa][1] == 'u' || argv[fa][1] == 'U') fwacomp = 1; /* Gamut plot */ else if (argv[fa][1] == 'g' || argv[fa][1] == 'G') dogam = 1; /* VRML plot */ else if (argv[fa][1] == 'w' || argv[fa][1] == 'W') { dogam = 1; dowrl = 1; } /* No VRML axes */ else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') { doaxes = 0; } /* Transparency */ else if (argv[fa][1] == 'a' || argv[fa][1] == 'A') { fa = nfa; if (na == NULL) usage(); trans = atof(na); } /* Surface Detail */ else if (argv[fa][1] == 'd' || argv[fa][1] == 'D') { fa = nfa; if (na == NULL) usage(); gamres = atof(na); dogam = 1; } /* Test code */ else if (argv[fa][1] == 't' || argv[fa][1] == 'T') { fa = nfa; if (na == NULL) usage(); test = atoi(na); } else usage(); } else break; } if (fa >= argc || argv[fa][0] == '-') usage(); strcpy(prof_name,argv[fa]); if ((mppo = new_mpp()) == NULL) error ("Creation of MPP object failed"); if ((rv = mppo->read_mpp(mppo,prof_name)) != 0) error ("%d, %s",rv,mppo->err); mppo->get_info(mppo, &imask, &devn, &dlimit, &spec_n, &spec_wl_short, &spec_wl_long, NULL); ident = icx_inkmask2char(imask, 1); if (verb) { printf("MPP profile with %d colorants, type %s, TAC %f\n",devn,ident, dlimit); } if (limit <= 0.0 || dlimit < limit) limit = dlimit; pcss = pcsor; pcsn = 3; if (spec && spec_n == 0) { error("Spectral profile needed for spectral result, custom illuminant, observer or FWA"); } /* Select CIE return value details */ if ((rv = mppo->set_ilob(mppo, illum, &cust_illum, observ, NULL, pcss, fwacomp)) != 0) { if (rv == 1) error("Spectral profile needed for custom illuminant, observer or FWA"); error("Error setting illuminant, observer, or FWA"); } if (test != 0) { printf("!!!!! Running special test code no %d !!!!!\n",test); if (test == 1) { double **dv, **rdv; dv = dmatrix(0, pcsn-1, 0, devn-1); rdv = dmatrix(0, pcsn-1, 0, devn-1); printf("Checking partial derivative at each input value\n"); /* Process colors to translate */ for (;;) { int i,j; char *bp, *nbp; double tout[MAX_CHAN]; /* Read in the next line */ if (fgets(buf, 200, stdin) == NULL) break; if (buf[0] == '#') { fprintf(stdout,"%s\n",buf); continue; } /* For each input number */ for (bp = buf-1, nbp = buf, i = 0; i < MAX_CHAN; i++) { bp = nbp; in[i] = strtod(bp, &nbp); if (nbp == bp) break; /* Failed */ } if (i == 0) break; /* Do conversion */ mppo->lookup(mppo, out, in); mppo->dlookup(mppo, out, dv, in); for (j = 0; j < devn; j++) { double del = 1e-9; if (in[j] > 0.5) del = -del; in[j] += del; mppo->lookup(mppo, tout, in); in[j] -= del; for (i = 0; i < pcsn; i++) { rdv[i][j] = (tout[i] - out[i])/del; } } /* Output the results */ for (j = 0; j < devn; j++) { if (j > 0) fprintf(stdout," %f",in[j]); else fprintf(stdout,"%f",in[j]); } printf(" [%s] -> ", ident); for (j = 0; j < pcsn; j++) { if (j > 0) fprintf(stdout," %f",out[j]); else fprintf(stdout,"%f",out[j]); } printf(" [%s]\n", icm2str(icmColorSpaceSignature, pcss)); /* Print the derivatives */ for (i = 0; i < pcsn; i++) { printf("Output chan %d: ",i); for (j = 0; j < devn; j++) { if (j < (devn-1)) fprintf(stdout,"%f ref %f, ",dv[i][j], rdv[i][j]); else fprintf(stdout,"%f ref %f\n",dv[i][j], rdv[i][j]); } } } free_dmatrix(dv, 0, pcsn-1, 0, devn-1); free_dmatrix(rdv, 0, pcsn-1, 0, devn-1); } else if (test == 2) { char *xl, gam_name[100]; strcpy(gam_name, prof_name); if ((xl = strrchr(gam_name, '.')) == NULL) /* Figure where extention is */ xl = gam_name + strlen(gam_name); strcpy(xl,".wrl"); diag_gamut(mppo, gamres, doaxes, trans, gam_name); } else { printf("Unknown test!\n"); } } else if (dogam) { gamut *gam; char *xl, gam_name[100]; int docusps = 1; if ((gam = mppo->get_gamut(mppo, gamres)) == NULL) error("get_gamut failed\n"); strcpy(gam_name, prof_name); if ((xl = strrchr(gam_name, '.')) == NULL) /* Figure where extention is */ xl = gam_name + strlen(gam_name); strcpy(xl,".gam"); if (gam->write_gam(gam,gam_name)) error ("write gamut failed on '%s'",gam_name); if (dowrl) { strcpy(xl,".wrl"); if (gam->write_vrml(gam,gam_name, doaxes, docusps)) error ("write vrml failed on '%s'",gam_name); } gam->del(gam); } else { /* Normal color lookup */ if (repYxy) { /* report Yxy rather than XYZ */ if (pcss == icSigXYZData) pcss = icSigYxyData; } /* Process colors to translate */ for (;;) { int i,j; char *bp, *nbp; /* Read in the next line */ if (fgets(buf, 200, stdin) == NULL) break; if (buf[0] == '#') { fprintf(stdout,"%s\n",buf); continue; } /* For each input number */ for (bp = buf-1, nbp = buf, i = 0; i < MAX_CHAN; i++) { bp = nbp; in[i] = strtod(bp, &nbp); if (nbp == bp) break; /* Failed */ } if (i == 0) break; if (!bwd) { if (repSpec) { xspect ospec; /* Do lookup of spectrum */ mppo->lookup_spec(mppo, &ospec, in); /* Output the results */ for (j = 0; j < devn; j++) { if (j > 0) fprintf(stdout," %f",in[j]); else fprintf(stdout,"%f",in[j]); } printf(" [%s] -> ", ident); for (j = 0; j < spec_n; j++) { if (j > 0) fprintf(stdout," %f",ospec.spec[j]); else fprintf(stdout,"%f",ospec.spec[j]); } printf(" [%3.0f .. %3.0f nm]\n", spec_wl_short, spec_wl_long); } else { /* Do conversion */ mppo->lookup(mppo, out, in); /* Output the results */ for (j = 0; j < devn; j++) { if (j > 0) fprintf(stdout," %f",in[j]); else fprintf(stdout,"%f",in[j]); } printf(" [%s] -> ", ident); if (repYxy && pcss == icSigYxyData) { double X = out[0]; double Y = out[1]; double Z = out[2]; double sum = X + Y + Z; if (sum < 1e-6) { out[0] = out[1] = out[2] = 0.0; } else { out[0] = Y; out[1] = X/sum; out[2] = Y/sum; } } for (j = 0; j < pcsn; j++) { if (j > 0) fprintf(stdout," %f",out[j]); else fprintf(stdout,"%f",out[j]); } printf(" [%s]\n", icm2str(icmColorSpaceSignature, pcss)); } } else { /* Do a reverse lookup */ if (repYxy && pcss == icSigYxyData) { double Y = in[0]; double x = in[1]; double y = in[2]; double z = 1.0 - x - y; double sum; if (y < 1e-6) { in[0] = in[1] = in[2] = 0.0; } else { sum = Y/y; in[0] = x * sum; in[1] = Y; in[2] = z * sum; } } /* Do conversion */ mpp_rev(mppo, limit, out, in); /* Output the results */ for (j = 0; j < pcsn; j++) { if (j > 0) fprintf(stdout," %f",in[j]); else fprintf(stdout,"%f",in[j]); } printf(" [%s] -> ", icm2str(icmColorSpaceSignature, pcss)); for (j = 0; j < devn; j++) { if (j > 0) fprintf(stdout," %f",out[j]); else fprintf(stdout,"%f",out[j]); } printf(" [%s]\n", ident); } } } free(ident); mppo->del(mppo); return 0; }