/* Do smoothness scaling check & return results */ static double do_stest( int verb, /* Verbosity */ int di, /* Dimensions */ int its, /* Number of function tests */ int res /* RSPL grid resolution */ ) { funcp fp; /* Function parameters */ DCOUNT(gc, MXDIDO, di, 1, 1, res-1); int it; double atse = 0.0; /* Make repeatable by setting random seed before a test set. */ rand32(0x12345678); for (it = 0; it < its; it++) { double tse; setup_func(&fp, di); /* New function */ DC_INIT(gc) tse = 0.0; for (; !DC_DONE(gc);) { double g[MXDI]; int e, k; double y1, y2, y3; double del; for (e = 0; e < di; e++) g[e] = gc[e]/(res-1.0); y2 = lookup_func(&fp, g); del = 1.0/(res-1.0); for (k = 0 ; k < di; k++) { double err; g[k] -= del; y1 = lookup_func(&fp, g); g[k] += 2.0 * del; y3 = lookup_func(&fp, g); g[k] -= del; err = 0.5 * (y3 + y1) - y2; tse += err * err; } DC_INC(gc); } /* Apply adjustments and corrections */ tse *= pow((res-1.0), 4.0); /* Aprox. geometric resolution factor */ tse /= pow((res-2.0),(double)di); /* Average squared non-smoothness */ if (verb) printf("smf for it %d = %f\n",it,tse); atse += tse; } return atse/(double)its; }
/* device space "fold-over" */ static void diag_gamut( icxLuBase *p, /* Lookup object */ double detail, /* Gamut resolution detail */ int doaxes, /* Do Lab axes */ double tlimit, /* Total ink limit */ double klimit, /* K ink limit */ char *outname /* Output VRML file */ ) { int i, j; FILE *wrl; struct { double x, y, z; double wx, wy, wz; double r, g, b; } axes[5] = { { 0, 0, 50-GAMUT_LCENT, 2, 2, 100, .7, .7, .7 }, /* L axis */ { 50, 0, 0-GAMUT_LCENT, 100, 2, 2, 1, 0, 0 }, /* +a (red) axis */ { 0, -50, 0-GAMUT_LCENT, 2, 100, 2, 0, 0, 1 }, /* -b (blue) axis */ { -50, 0, 0-GAMUT_LCENT, 100, 2, 2, 0, 1, 0 }, /* -a (green) axis */ { 0, 50, 0-GAMUT_LCENT, 2, 100, 2, 1, 1, 0 }, /* +b (yellow) axis */ }; int vix; /* Vertex index */ DCOUNT(coa, MXDI, p->inputChan, 0, 0, 2); double col[1 << MXDI][3]; /* Color asigned to each major vertex */ int res; if (tlimit < 0.0) tlimit = p->inputChan; if (klimit < 0.0) klimit = 1.0; /* Asign some colors to the combination nodes */ for (i = 0; i < (1 << p->inputChan); i++) { int a, b, c, j; double h; j = (i ^ 0x5a5a5a5a) % (1 << p->inputChan); h = (double)j/((1 << p->inputChan)-1); /* Make fully saturated with chosen hue */ if (h < 1.0/3.0) { a = 0; b = 1; c = 2; } else if (h < 2.0/3.0) { a = 1; b = 2; c = 0; h -= 1.0/3.0; } else { a = 2; b = 0; c = 1; h -= 2.0/3.0; } h *= 3.0; col[i][a] = (1.0 - h); col[i][b] = h; col[i][c] = d_rand(0.0, 1.0); } if (detail > 0.0) res = (int)(100.0/detail); /* Establish an appropriate sampling density */ else res = 4; if (res < 2) res = 2; if ((wrl = fopen(outname,"w")) == NULL) error("Error opening wrl output file '%s'",outname); /* Spit out a VRML 2 Object surface of gamut */ fprintf(wrl,"#VRML V2.0 utf8\n"); fprintf(wrl,"\n"); fprintf(wrl,"# Created by the Argyll CMS\n"); fprintf(wrl,"Transform {\n"); fprintf(wrl,"children [\n"); fprintf(wrl," NavigationInfo {\n"); fprintf(wrl," type \"EXAMINE\" # It's an object we examine\n"); fprintf(wrl," } # We'll add our own light\n"); fprintf(wrl,"\n"); fprintf(wrl," DirectionalLight {\n"); fprintf(wrl," direction 0 0 -1 # Light illuminating the scene\n"); fprintf(wrl," direction 0 -1 0 # Light illuminating the scene\n"); fprintf(wrl," }\n"); fprintf(wrl,"\n"); fprintf(wrl," Viewpoint {\n"); fprintf(wrl," position 0 0 340 # Position we view from\n"); fprintf(wrl," }\n"); fprintf(wrl,"\n"); if (doaxes != 0) { fprintf(wrl,"# Lab axes as boxes:\n"); for (i = 0; i < 5; i++) { fprintf(wrl,"Transform { translation %f %f %f\n", axes[i].x, axes[i].y, axes[i].z); fprintf(wrl,"\tchildren [\n"); fprintf(wrl,"\t\tShape{\n"); fprintf(wrl,"\t\t\tgeometry Box { size %f %f %f }\n", axes[i].wx, axes[i].wy, axes[i].wz); fprintf(wrl,"\t\t\tappearance Appearance { material Material "); fprintf(wrl,"{ diffuseColor %f %f %f} }\n", axes[i].r, axes[i].g, axes[i].b); fprintf(wrl,"\t\t}\n"); fprintf(wrl,"\t]\n"); fprintf(wrl,"}\n"); } fprintf(wrl,"\n"); } fprintf(wrl," Transform {\n"); fprintf(wrl," translation 0 0 0\n"); fprintf(wrl," children [\n"); fprintf(wrl," Shape { \n"); fprintf(wrl," geometry IndexedFaceSet {\n"); fprintf(wrl," solid FALSE\n"); /* Don't back face cull */ fprintf(wrl," convex TRUE\n"); fprintf(wrl,"\n"); fprintf(wrl," coord Coordinate { \n"); fprintf(wrl," point [ # Verticy coordinates\n"); /* Itterate over all the faces in the device space */ /* generating the vertx positions. */ DC_INIT(coa); vix = 0; while(!DC_DONE(coa)) { int e, m1, m2; double in[MXDI]; double inl[MXDI]; double out[3]; double sum; /* Scan only device surface */ for (m1 = 0; m1 < p->inputChan; m1++) { if (coa[m1] != 0) continue; for (m2 = m1 + 1; m2 < p->inputChan; m2++) { int x, y; if (coa[m2] != 0) continue; for (e = 0; e < p->inputChan; e++) in[e] = (double)coa[e]; /* Base value */ /* Scan over 2D device space face */ for (x = 0; x < res; x++) { /* step over surface */ in[m1] = x/(res - 1.0); for (y = 0; y < res; y++) { in[m2] = y/(res - 1.0); for (sum = 0.0, e = 0; e < p->inputChan; e++) { sum += inl[e] = in[e]; } if (sum >= tlimit) { for (e = 0; e < p->inputChan; e++) inl[e] *= tlimit/sum; } if (p->inputChan >= 3 && inl[3] >= klimit) inl[3] = klimit; p->lookup(p, out, inl); fprintf(wrl,"%f %f %f,\n",out[1], out[2], out[0]-50.0); vix++; } } } } /* Increment index within block */ DC_INC(coa); } fprintf(wrl," ]\n"); fprintf(wrl," }\n"); fprintf(wrl,"\n"); fprintf(wrl," coordIndex [ # Indexes of poligon Verticies \n"); /* Itterate over all the faces in the device space */ /* generating the quadrilateral indexes. */ DC_INIT(coa); vix = 0; while(!DC_DONE(coa)) { int e, m1, m2; double in[MXDI]; /* Scan only device surface */ for (m1 = 0; m1 < p->inputChan; m1++) { if (coa[m1] != 0) continue; for (m2 = m1 + 1; m2 < p->inputChan; m2++) { int x, y; if (coa[m2] != 0) continue; for (e = 0; e < p->inputChan; e++) in[e] = (double)coa[e]; /* Base value */ /* Scan over 2D device space face */ /* Only output quads under the total ink limit */ /* Scan over 2D device space face */ for (x = 0; x < res; x++) { /* step over surface */ for (y = 0; y < res; y++) { if (x < (res-1) && y < (res-1)) { fprintf(wrl,"%d, %d, %d, %d, -1\n", vix, vix + 1, vix + 1 + res, vix + res); } vix++; } } } } /* Increment index within block */ DC_INC(coa); } fprintf(wrl," ]\n"); fprintf(wrl,"\n"); fprintf(wrl," colorPerVertex TRUE\n"); fprintf(wrl," color Color {\n"); fprintf(wrl," color [ # RGB colors of each vertex\n"); /* Itterate over all the faces in the device space */ /* generating the vertx colors. */ DC_INIT(coa); vix = 0; while(!DC_DONE(coa)) { int e, m1, m2; double in[MXDI]; /* Scan only device surface */ for (m1 = 0; m1 < p->inputChan; m1++) { if (coa[m1] != 0) continue; for (m2 = m1 + 1; m2 < p->inputChan; m2++) { int x, y; if (coa[m2] != 0) continue; for (e = 0; e < p->inputChan; e++) in[e] = (double)coa[e]; /* Base value */ /* Scan over 2D device space face */ for (x = 0; x < res; x++) { /* step over surface */ double xb = x/(res - 1.0); for (y = 0; y < res; y++) { int v0, v1, v2, v3; double yb = y/(res - 1.0); double rgb[3]; for (v0 = 0, e = 0; e < p->inputChan; e++) v0 |= coa[e] ? (1 << e) : 0; /* Binary index */ v1 = v0 | (1 << m2); /* Y offset */ v2 = v0 | (1 << m2) | (1 << m1); /* X+Y offset */ v3 = v0 | (1 << m1); /* Y offset */ /* Linear interp between the main verticies */ for (j = 0; j < 3; j++) { rgb[j] = (1.0 - yb) * (1.0 - xb) * col[v0][j] + yb * (1.0 - xb) * col[v1][j] + (1.0 - yb) * xb * col[v3][j] + yb * xb * col[v2][j]; } fprintf(wrl,"%f %f %f,\n",rgb[1], rgb[2], rgb[0]); vix++; } } } } /* Increment index within block */ DC_INC(coa); } fprintf(wrl," ] \n"); fprintf(wrl," }\n"); fprintf(wrl," }\n"); fprintf(wrl," appearance Appearance { \n"); fprintf(wrl," material Material {\n"); fprintf(wrl," transparency 0.0\n"); fprintf(wrl," ambientIntensity 0.3\n"); fprintf(wrl," shininess 0.5\n"); fprintf(wrl," }\n"); fprintf(wrl," }\n"); fprintf(wrl," } # end Shape\n"); fprintf(wrl," ]\n"); fprintf(wrl," }\n"); fprintf(wrl,"\n"); fprintf(wrl," ] # end of children for world\n"); fprintf(wrl,"}\n"); if (fclose(wrl) != 0) error("Error closing output file '%s'",outname); }
/* Return NULL on error, check errc+err for reason */ static gamut *icxLuMatrixGamut( icxLuBase *plu, /* this */ double detail /* gamut detail level, 0.0 = def */ ) { xicc *p = plu->pp; /* parent xicc */ icxLuMatrix *lumat = (icxLuMatrix *)plu; /* Lookup xMatrix type object */ icColorSpaceSignature pcs; icmLookupFunc func; double white[3], black[3], kblack[3]; gamut *gam; int res; /* Sample point resolution */ int i, e; if (detail == 0.0) detail = 10.0; /* get some details */ plu->spaces(plu, NULL, NULL, NULL, NULL, NULL, NULL, &func, &pcs); if (func != icmFwd && func != icmBwd) { p->errc = 1; sprintf(p->err,"Creating Gamut surface for anything other than Device <-> PCS is not supported."); return NULL; } if (pcs != icSigLabData && pcs != icxSigJabData) { p->errc = 1; sprintf(p->err,"Creating Gamut surface PCS of other than Lab or Jab is not supported."); return NULL; } gam = new_gamut(detail, pcs == icxSigJabData, 0); /* Explore the gamut by itterating through */ /* it with sample points in device space. */ res = (int)(600.0/detail); /* Establish an appropriate sampling density */ if (res < 40) res = 40; /* Since matrix profiles can't be non-monotonic, */ /* just itterate through the surface colors. */ for (i = 0; i < 3; i++) { int co[3]; int ep[3]; int co_e = 0; for (e = 0; e < 3; e++) { co[e] = 0; ep[e] = res; } ep[i] = 2; while (co_e < 3) { double in[3]; double out[3]; for (e = 0; e < 3; e++) /* Convert count to input value */ in[e] = co[e]/(ep[e]-1.0); /* Always use the device->PCS conversion */ if (lumat->fwd_lookup((icxLuBase *)lumat, out, in) > 1) error ("%d, %s",p->errc,p->err); gam->expand(gam, out); /* Increment the counter */ for (co_e = 0; co_e < 3; co_e++) { co[co_e]++; if (co[co_e] < ep[co_e]) break; /* No carry */ co[co_e] = 0; } } } #ifdef NEVER /* Try it twice */ for (i = 0; i < 3; i++) { int co[3]; int ep[3]; int co_e = 0; for (e = 0; e < 3; e++) { co[e] = 0; ep[e] = res; } ep[i] = 2; while (co_e < 3) { double in[3]; double out[3]; for (e = 0; e < 3; e++) /* Convert count to input value */ in[e] = co[e]/(ep[e]-1.0); /* Always use the device->PCS conversion */ if (lumat->fwd_lookup((icxLuBase *)lumat, out, in) > 1) error ("%d, %s",p->errc,p->err); gam->expand(gam, out); /* Increment the counter */ for (co_e = 0; co_e < 3; co_e++) { co[co_e]++; if (co[co_e] < ep[co_e]) break; /* No carry */ co[co_e] = 0; } } } #endif #ifdef NEVER // (doesn't seem to make much difference) /* run along the primary ridges in more detail too */ /* just itterate through the surface colors. */ for (i = 0; i < 3; i++) { int j; double in[3]; double out[3]; res *= 4; for (j = 0; j < res; j++) { double vv = i/(res-1.0); in[0] = in[1] = in[2] = vv; in[i] = 0.0; if (lumat->fwd_lookup((icxLuBase *)lumat, out, in) > 1) error ("%d, %s",p->errc,p->err); gam->expand(gam, out); in[0] = in[1] = in[2] = 0.0; in[i] = vv; if (lumat->fwd_lookup((icxLuBase *)lumat, out, in) > 1) error ("%d, %s",p->errc,p->err); gam->expand(gam, out); } } #endif /* Put the white and black points in the gamut */ plu->efv_wh_bk_points(plu, white, black, kblack); gam->setwb(gam, white, black, kblack); /* set the cusp points by itterating through the 0 & 100% colorant combinations */ { DCOUNT(co, 3, 3, 0, 0, 2); gam->setcusps(gam, 0, NULL); DC_INIT(co); while(!DC_DONE(co)) { int e; double in[3]; double out[3]; if (!(co[0] == 0 && co[1] == 0 && co[2] == 0) && !(co[0] == 1 && co[1] == 1 && co[2] == 1)) { /* Skip white and black */ for (e = 0; e < 3; e++) in[e] = (double)co[e]; /* Always use the device->PCS conversion */ if (lumat->fwd_lookup((icxLuBase *)lumat, out, in) > 1) error ("%d, %s",p->errc,p->err); gam->setcusps(gam, 3, out); } DC_INC(co); } gam->setcusps(gam, 2, NULL); } #ifdef NEVER /* Not sure if this is a good idea ?? */ gam->getwb(gam, NULL, NULL, white, black); /* Get the actual gamut white and black points */ gam->setwb(gam, white, black); /* Put it back as colorspace one */ #endif return gam; }
/* Do a reverse lookup on the mpp */ static void mpp_rev( mpp *mppo, double limit, /* Ink limit */ double *out, /* Device value */ double *in /* Lab target */ ) { int i, j; inkmask imask; /* Device Ink mask */ int inn; revlus rs; /* Reverse lookup structure */ double sr[MAX_CHAN]; /* Search radius */ double tt; /* !!! This needs to be cached elsewhere !!!! */ static saent *start = NULL; /* Start array */ static int nisay = 0; /* Number in start array */ mppo->get_info(mppo, &imask, &inn, NULL, NULL, NULL, NULL, NULL); rs.di = inn; /* Number of device channels */ rs.Lab[0] = in[0]; /* Target PCS value */ rs.Lab[1] = in[1]; rs.Lab[2] = in[2]; rs.dev2lab = mppo->lookup; /* Dev->PCS Lookup function and context */ rs.d2lcntx = (void *)mppo; rs.ilimit = limit; /* Total ink limit */ { double Labw[3]; /* Lab value of white */ double Lab[MAX_CHAN][3]; /* Lab value of device primaries */ double min, max; /* Lookup the L value of all the device primaries */ for (j = 0; j < inn; j++) out[j] = 0.0; mppo->lookup(mppo, Labw, out); for (i = 0; i < inn; i++) { double tt; double de; out[i] = 1.0; mppo->lookup(mppo, Lab[i], out); /* Use DE measure heavily weighted towards L only */ tt = L_WEIGHT * (Labw[0] - Lab[i][0]); de = tt * tt; tt = 0.4 * (Labw[1] - Lab[i][1]); de += tt * tt; tt = 0.2 * (Labw[2] - Lab[i][2]); de += tt * tt; rs.oweight[i] = sqrt(de); out[i] = 0.0; } /* Normalise weights from 0 .. 1.0 */ min = 1e6, max = 0.0; for (j = 0; j < inn; j++) { if (rs.oweight[j] < min) min = rs.oweight[j]; if (rs.oweight[j] > max) max = rs.oweight[j]; } for (j = 0; j < inn; j++) rs.oweight[j] = (rs.oweight[j] - min)/(max - min); { for (j = 0; j < inn; j++) rs.sord[j] = j; for (i = 0; i < (inn-1); i++) { for (j = i+1; j < inn; j++) { if (rs.oweight[rs.sord[i]] > rs.oweight[rs.sord[j]]) { int xx; xx = rs.sord[i]; rs.sord[i] = rs.sord[j]; rs.sord[j] = xx; } } } } for (j = 0; j < inn; j++) printf("~1 oweight[%d] = %f\n",j,rs.oweight[j]); for (j = 0; j < inn; j++) printf("~1 sorted oweight[%d] = %f\n",j,rs.oweight[rs.sord[j]]); } /* Initialise the start point array */ if (start == NULL) { int mxstart; int steps = 4; DCOUNT(dix, MAX_CHAN, inn, 0, 0, steps); printf("~1 initing start point array\n"); for (mxstart = 1, j = 0; j < inn; j++) /* Compute maximum entries */ mxstart *= steps; printf("~1 mxstart = %d\n",mxstart); if ((start = malloc(sizeof(saent) * mxstart)) == NULL) error("mpp_rev malloc of start array failed\n"); nisay = 0; DC_INIT(dix); while(!DC_DONE(dix)) { double sum = 0.0; /* Figure device values */ for (j = 0; j < inn; j++) { sum += start[nisay].dev[j] = dix[j]/(steps-1.0); } if (sum <= limit) { /* Within ink limit */ double oerr; double tot; /* Compute Lab */ mppo->lookup(mppo, start[nisay].Lab, start[nisay].dev); /* Compute order error */ /* Skip first 3 colorants */ oerr = tot = 0.0; for (j = 3; j < inn; j++) { int ix = rs.sord[j]; /* Sorted order index */ double vv = start[nisay].dev[ix]; double we = (double)j - 2.0; /* Increasing weight */ if (vv > 0.0001) { oerr += tot + we * vv; } tot += we; } oerr /= tot; start[nisay].oerr = oerr; nisay++; } DC_INC(dix); } printf("~1 start point array done, %d out of %d valid\n",nisay,mxstart); } /* Search the start array for closest matching point */ { double bde = 1e38; int bix = 0; for (i = 0; i < nisay; i++) { double de; /* Compute delta E */ for (de = 0.0, j = 0; j < 3; j++) { double tt = rs.Lab[j] - start[i].Lab[j]; de += tt * tt; } de += 0.0 * start[i].oerr; if (de < bde) { bde = de; bix = i; } } printf("Start point at index %d, bde = %f, dev = ",bix,bde); for (j = 0; j < inn; j++) { printf("%f",start[bix].dev[j]); if (j < (inn-1)) printf(" "); } printf("\n"); for (j = 0; j < inn; j++) { out[j] = start[bix].dev[j]; sr[j] = 0.1; } } #ifdef NEVER out[0] = 0.0; out[1] = 0.0; out[2] = 0.45; out[3] = 0.0; out[4] = 0.0; out[5] = 0.0; out[6] = 0.6; out[7] = 1.0; #endif #ifdef NEVER out[0] = 1.0; out[1] = 0.0; out[2] = 0.0; out[3] = 0.0; out[4] = 0.0; out[5] = 0.0; out[6] = 0.0; out[7] = 0.0; #endif #ifdef NEVER rs.pass = 0; if (powell(&tt, inn, out, sr, 0.001, 5000, revoptfunc, (void *)&rs) != 0) { error("Powell failed inside mpp_rev()"); } printf("\n\n\n\n\n\n#############################################\n"); printf("~1 after first pass got "); for (j = 0; j < inn; j++) { printf("%f",out[j]); if (j < (inn-1)) printf(" "); } printf("\n"); printf("#############################################\n\n\n\n\n\n\n\n"); #endif #ifndef NEVER out[0] = 0.0; out[1] = 0.0; out[2] = 0.0; out[3] = 0.0; out[4] = 0.0; out[5] = 1.0; out[6] = 0.0; out[7] = 0.0; #endif #ifndef NEVER rs.pass = 1; if (powell(&tt, inn, out, sr, 0.00001, 5000, revoptfunc, (void *)&rs, NULL, NULL) != 0) { error("Powell failed inside mpp_rev()"); } #endif snap2gamut(&rs, out); }