From c07d0c2d2f6f7b0eb6e92cc6204bf05037957e82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 15:43:52 +0200 Subject: Imported Upstream version 1.6.3 --- spectro/fakeread.c | 362 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 323 insertions(+), 39 deletions(-) (limited to 'spectro/fakeread.c') diff --git a/spectro/fakeread.c b/spectro/fakeread.c index df77930..ce06e93 100644 --- a/spectro/fakeread.c +++ b/spectro/fakeread.c @@ -1,4 +1,4 @@ -/* + /* * Argyll Color Correction System * Fake print target chart reader - use ICC or MPP profile rather than instrument * @@ -15,6 +15,10 @@ /* * TTBD: + This utility should really be merged with cctiff ? + ie. Add cgats i/o to cctiff + Add other processing steps such as TV, BT.1886, random to cctiff. + Do we need to deterct & mark display values normalized to Y = 100 ?? */ @@ -46,21 +50,36 @@ void usage(char *diag, ...) { va_end(args); fprintf(stderr,"\n"); } - fprintf(stderr,"usage: fakeread [-v] [-s] [separation.icm] profile.[%s|mpp|ti3] outfile\n",ICC_FILE_EXT_ND); - fprintf(stderr," -v Verbose mode\n"); - fprintf(stderr," -s Lookup MPP spectral values\n"); - fprintf(stderr," -p Use separation profile\n"); + /* Keep options in order of processing */ + fprintf(stderr,"usage: fakeread [-options] profile.[%s|mpp|ti3] outfile\n",ICC_FILE_EXT_ND); + fprintf(stderr," -v [n] Verbose mode [level]\n"); + fprintf(stderr," -e flag Video encode device input to sepration as:\n"); + fprintf(stderr," n normal 0..1 full range RGB levels (default)\n"); + fprintf(stderr," t (16-235)/255 \"TV\" RGB levels\n"); + fprintf(stderr," 6 Rec601 YCbCr SD (16-235,240)/255 \"TV\" levels\n"); + fprintf(stderr," 7 Rec709 1125/60Hz YCbCr HD (16-235,240)/255 \"TV\" levels\n"); + fprintf(stderr," 5 Rec709 1250/50Hz YCbCr HD (16-235,240)/255 \"TV\" levels\n"); + fprintf(stderr," 2 Rec2020 YCbCr UHD (16-235,240)/255 \"TV\" levels\n"); + fprintf(stderr," C Rec2020 Constant Luminance YCbCr UHD (16-235,240)/255 \"TV\" levels\n"); + fprintf(stderr," -p separation.%s Use device link separation profile on input\n",ICC_FILE_EXT_ND); + fprintf(stderr," -E flag Video decode separation device output. See -e above\n"); + fprintf(stderr," -k file.cal Apply calibration (include in .ti3 output)\n"); + fprintf(stderr," -i file.cal Include calibration in .ti3 output, but don't apply it\n"); + fprintf(stderr," -K file.cal Apply inverse calibration\n"); + + fprintf(stderr," -r level Add average random deviation of %% to device values (after sep. & cal.)\n"); + fprintf(stderr," -0 pow Apply power to device chanel 0-9\n"); + fprintf(stderr," -b output.%s Apply BT.1886-like mapping with effective gamma 2.2\n",ICC_FILE_EXT_ND); + fprintf(stderr," -b g.g:output.%s Apply BT.1886-like mapping with effective gamma g.g\n",ICC_FILE_EXT_ND); + fprintf(stderr," -B output.%s Apply BT.1886 mapping with technical gamma 2.4\n",ICC_FILE_EXT_ND); + fprintf(stderr," -B g.g:output.%s Apply BT.1886 mapping with technical gamma g.g\n",ICC_FILE_EXT_ND); + fprintf(stderr," -I intent r = relative colorimetric, a = absolute (default)\n"); + fprintf(stderr," -A L,a,b Scale black point to target Lab value\n"); fprintf(stderr," -l Output Lab rather than XYZ\n"); - fprintf(stderr," -k file.cal Apply calibration (after sep.) and include in .ti3\n"); - fprintf(stderr," -i file.cal Include calibration in .ti3 (but don't apply it)\n"); - fprintf(stderr," -r level Add average random deviation of %% to input device values (after sep. & cal.)\n"); - fprintf(stderr," -0 pow Apply power to input device chanel 0-9 (after sep. cal. & rand)\n"); + fprintf(stderr," -s Lookup MPP spectral values\n"); fprintf(stderr," -R level Add average random deviation of %% to output PCS values\n"); fprintf(stderr," -u Make random deviations have uniform distributions rather than normal\n"); fprintf(stderr," -S seed Set random seed\n"); - fprintf(stderr," -b L,a,b Scale black point to target Lab value\n"); - fprintf(stderr," -I intent r = relative colorimetric, a = absolute (default)\n"); - fprintf(stderr," [separation.%s] Device link separation profile\n",ICC_FILE_EXT_ND); fprintf(stderr," profile.[%s|mpp|ti3] ICC, MPP profile or TI3 to use\n",ICC_FILE_EXT_ND); fprintf(stderr," outfile Base name for input[ti1]/output[ti3] file\n"); exit(1); @@ -69,9 +88,14 @@ void usage(char *diag, ...) { int main(int argc, char *argv[]) { int j; - int fa,nfa; /* current argument we're looking at */ + int fa, nfa, mfa; /* current argument we're looking at */ int verb = 0; /* Verbose flag */ int dosep = 0; /* Use separation before profile */ + int bt1886 = 0; /* 1 to apply BT.1886 black point & effective gamma to input */ + /* 2 to apply BT.1886 black point & technical gamma to input */ + double egamma = 2.2; /* effective BT.1886 style gamma to ain for */ + double tgamma = 2.4; /* technical BT.1886 style gamma to ain for */ + bt1886_info bt; /* BT.1886 adjustment info */ int dolab = 0; /* Output Lab rather than XYZ */ int gfudge = 0; /* Do grey fudge, 1 = W->RGB, 2 = K->xxxK */ double chpow[10] = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; @@ -80,12 +104,13 @@ int main(int argc, char *argv[]) int unidist = 0; /* Use uniform distribution of errors */ unsigned int seed = time(NULL); /* Random seed value */ double tbp[3] = { -1.0, 0.0, 0.0 }; /* Target black point */ - int applycal = 0; /* NZ to apply calibration */ + int applycal = 0; /* 1 to apply calibration, 2 to apply inverse calibration */ static char sepname[MAXNAMEL+1] = { 0 }; /* ICC separation profile */ static char calname[MAXNAMEL+1] = { 0 }; /* device calibration */ static char profname[MAXNAMEL+1] = { 0 }; /* ICC or MPP Profile name */ static char inname[MAXNAMEL+1] = { 0 }; /* Input cgats file base name */ static char outname[MAXNAMEL+1] = { 0 }; /* Output cgats file base name */ + static char oprofname[MAXNAMEL+1] = { 0 }; /* BT.1886 output profile name */ cgats *icg; /* input cgats structure */ cgats *ocg; /* output cgats structure */ int nmask = 0; /* Test chart device colorant mask */ @@ -96,7 +121,11 @@ int main(int argc, char *argv[]) int fi; /* Colorspace index */ int inti3 = 0; /* Input is a renamed .ti3 file rather than .ti1 */ - /* ICC separation device link profile */ + /* TV encode/decode of separation/calibration device link */ + int in_tvenc = 0; /* 1 to use RGB Video Level encoding, 2 = Rec601, etc. */ + int out_tvenc = 0; /* 1 to use RGB Video Level encoding, 2 = Rec601, etc. */ + + /* ICC separation/calibration device link profile */ icmFile *sep_fp = NULL; /* Color profile file */ icc *sep_icco = NULL; /* Profile object */ icmLuBase *sep_luo = NULL; /* Conversion object */ @@ -114,6 +143,7 @@ int main(int argc, char *argv[]) icmFile *icc_fp = NULL; /* Color profile file */ icc *icc_icco = NULL; /* Profile object */ icmLuBase *icc_luo = NULL; /* Conversion object */ + icmLuAlgType alg; /* Algorithm */ /* MPP profile based */ mpp *mlu = NULL; /* Conversion object */ @@ -146,6 +176,7 @@ int main(int argc, char *argv[]) usage("Too few arguments"); /* Process the arguments */ + mfa = 2; /* Minimum final 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 */ @@ -155,7 +186,7 @@ int main(int argc, char *argv[]) na = &argv[fa][2]; /* next is directly after flag */ else { - if ((fa+1) < argc) + if ((fa+1+mfa) < argc) { if (argv[fa+1][0] != '-') { @@ -169,17 +200,93 @@ int main(int argc, char *argv[]) usage("Usage requested"); /* Verbose */ - else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') + else if (argv[fa][1] == 'v') { verb = 1; + if (na != NULL && na[0] >= '0' && na[0] <= '9') { + verb = atoi(na); + fa = nfa; + } + } + /* Spectral MPP lookup */ else if (argv[fa][1] == 's') dospec = 1; + /* Video RGB encoding */ + else if (argv[fa][1] == 'e' + || argv[fa][1] == 'E') { + int enc; + if (na == NULL) usage("Video encodong flag (-e/E) needs an argument"); + switch (na[0]) { + case 'n': /* Normal */ + enc = 0; + break; + case 't': /* TV 16 .. 235 */ + enc = 1; + break; + case '6': /* Rec601 YCbCr */ + enc = 2; + break; + case '7': /* Rec709 1150/60/2:1 YCbCr */ + enc = 3; + break; + case '5': /* Rec709 1250/50/2:1 YCbCr (HD) */ + enc = 4; + break; + case '2': /* Rec2020 Non-constant Luminance YCbCr (UHD) */ + enc = 5; + break; + case 'C': /* Rec2020 Constant Luminance YCbCr (UHD) */ + enc = 6; + break; + default: + usage("Video encoding (-E) argument not recognised"); + } + if (argv[fa][1] == 'e') + in_tvenc = enc; + else + out_tvenc = enc; + fa = nfa; + } + /* Separation */ - else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') + else if (argv[fa][1] == 'p') { dosep = 1; + fa = nfa; + if (na == NULL) usage("Expected an argument to -p"); + strncpy(sepname,na,MAXNAMEL); sepname[MAXNAMEL] = '\000'; + } + + /* BT.1886 modifier */ + else if (argv[fa][1] == 'b' + || argv[fa][1] == 'B') { + char *cp; + + bt1886 = 1; + if (argv[fa][1] == 'B') + bt1886 = 2; + + if (na == NULL) usage("BT.1886 flag (-%c) needs an argument",argv[fa][1]); + fa = nfa; + + if ((cp = strchr(na, ':')) != NULL) { + double gamma = 0.0; + *cp = '\000'; + cp++; + gamma = atof(na); + if (gamma < 0.01 || gamma > 10.0) usage("BT.1886 gamma is out of range"); + if (bt1886 == 1) + egamma = gamma; + else + tgamma = gamma; + } else { + cp = na; + } + strncpy(oprofname,cp,MAXNAMEL); oprofname[MAXNAMEL] = '\000'; + } + /* Lab */ else if (argv[fa][1] == 'l' || argv[fa][1] == 'L') dolab = 1; @@ -197,8 +304,12 @@ int main(int argc, char *argv[]) } /* calibration to device values */ - else if (argv[fa][1] == 'k' || argv[fa][1] == 'i') { - if (argv[fa][1] == 'k') + else if (argv[fa][1] == 'k' + || argv[fa][1] == 'K' + || argv[fa][1] == 'i') { + if (argv[fa][1] == 'K') + applycal = 2; + else if (argv[fa][1] == 'k') applycal = 1; else applycal = 0; @@ -233,8 +344,8 @@ int main(int argc, char *argv[]) } /* Black point scale */ - else if (argv[fa][1] == 'b' || argv[fa][1] == 'B') { - if (na == NULL) usage("Expect argument to -b"); + else if (argv[fa][1] == 'A') { + if (na == NULL) usage("Expect argument to -A"); fa = nfa; if (sscanf(na, " %lf , %lf , %lf ",&tbp[0], &tbp[1], &tbp[2]) != 3) usage("Couldn't parse argument to -b"); @@ -266,12 +377,6 @@ int main(int argc, char *argv[]) break; } - /* Get the file name argument */ - if (dosep) { - if (fa >= argc || argv[fa][0] == '-') usage("Missing separation profile filename argument"); - strncpy(sepname,argv[fa++],MAXNAMEL); sepname[MAXNAMEL] = '\000'; - } - if (fa >= argc || argv[fa][0] == '-') usage("Missing profile filename argument"); strncpy(profname,argv[fa++],MAXNAMEL); profname[MAXNAMEL] = '\000'; @@ -302,6 +407,13 @@ int main(int argc, char *argv[]) sep_luo->spaces(sep_luo, &sep_ins, &sep_inn, &sep_outs, NULL, NULL, NULL, NULL, NULL, NULL); sep_nmask = icx_icc_to_colorant_comb(sep_ins, sep_icco->header->deviceClass); } + if (in_tvenc && sep_ins != icSigRgbData) + error("Can only use TV encoding on sepration RGB input"); + if (out_tvenc && sep_outs != icSigRgbData) + error("Can only use TV decoding on sepration RGB output"); + } else { + if (in_tvenc || out_tvenc) + warning("TV encoding ignored because there is no sepration file"); } /* Deal with calibration */ @@ -310,11 +422,14 @@ int main(int argc, char *argv[]) error("new_xcal failed"); if ((cal->read(cal, calname)) != 0) error("%s",cal->err); - if (verb) - if (applycal) + if (verb) { + if (applycal == 1) printf("Applying calibration curves from '%s'\n",calname); + else if (applycal == 2) + printf("Applying inverse calibration curves from '%s'\n",calname); else printf("Embedding calibration curves from '%s' in output\n",calname); + } } /* Deal with ICC profile */ @@ -345,7 +460,8 @@ int main(int argc, char *argv[]) } /* Get details of conversion */ - icc_luo->spaces(icc_luo, &ins, &inn, &outs, &outn, NULL, NULL, NULL, NULL, NULL); + icc_luo->spaces(icc_luo, &ins, &inn, &outs, &outn, &alg, NULL, NULL, NULL, NULL); + cnv_nmask = icx_icc_to_colorant_comb(ins, icc_icco->header->deviceClass); if (dospec) @@ -531,6 +647,105 @@ int main(int argc, char *argv[]) free(ti3_bident); } + /* Dealy with BT.1886 processing */ + if (bt1886) { + icmFile *ofp = NULL; /* Output Color profile file */ + icc *oicco = NULL; /* Output Profile object */ + icmLuBase *oluo = NULL; /* Output Conversion object */ + icmLuMatrix *lu; /* Input profile lookup */ + double bp[3], rgb[3]; + + if (icc_luo == NULL) + error ("Can only use BT.1886 with an ICC profile"); + + /* Check input profile is an RGB matrix profile */ + if (alg != icmMatrixFwdType + || ins != icSigRgbData) + error("BT.1886 mode only works with an RGB matrix input profile"); + + lu = (icmLuMatrix *)icc_luo; /* Safe to coerce - we have checked it's matrix. */ + + /* Open up output profile used for BT.1886 black point */ + if ((ofp = new_icmFileStd_name(oprofname,"r")) == NULL) + error ("Can't open file '%s'",oprofname); + + if ((oicco = new_icc()) == NULL) + error ("Creation of ICC object failed"); + + if (oicco->read(oicco,ofp,0)) + error ("Unable to read '%s'",oprofname); + + if ((oluo = oicco->get_luobj(oicco, icmFwd, icRelativeColorimetric, + icSigXYZData, icmLuOrdNorm)) == NULL) + error("get output ICC lookup object failed: %d, %s",oicco->errc,oicco->err); + + /* We're assuming that the input space has a perfect black point... */ + + /* Lookup the ouput black point in XYZ PCS. We're assuming monotonicity.. */ + bp[0] = bp[1] = bp[2] = 0.0; + oluo->lookup(oluo, bp, bp); + + /* Done with output profile */ + oluo->del(oluo); + oicco->del(oicco); + ofp->del(ofp); + + if (bt1886 == 1) { /* Using effective gamma */ + tgamma = xicc_tech_gamma(egamma, bp[1]); + if (verb) + printf("BT.1886: Technical gamma %f used to achieve effective gamma %f\n", + tgamma, egamma); + } else { + if (verb) + printf("BT.1886: Using technical gamma %f\n",tgamma); + } + + bt1886_setup(&bt, bp, tgamma); + + if (verb) { + printf("BT.1886: target out black rel XYZ = %f %f %f, Lab %f %f %f\n", + bp[0],bp[1],bp[2], bt.outL, bt.tab[0], bt.tab[1]); + printf("BT.1886: Y input offset = %f\n", bt.ingo); + printf("BT.1886: Y output scale = %f\n", bt.outsc); + } + + /* Check black point now produced by input profile with bt.1886 adjustment */ + rgb[0] = rgb[1] = rgb[2] = 0.0; + lu->fwd_curve(lu, rgb, rgb); + lu->fwd_matrix(lu, rgb, rgb); + bt1886_apply(&bt, lu, rgb, rgb); + if (verb) printf("BT.1886: check input black point rel. XYZ %f %f %f\n", rgb[0],rgb[1],rgb[2]); + if (verb > 1) { + int i, no = 21; + + /* Overral rendering curve from video in to output target */ + printf("BT.1886: overall rendering\n"); + for (i = 0; i < no; i++) { + double v = i/(no-1.0), vv; + double vi[3], vo[3], Lab[3]; + double loglog = 0.0; + + if (v <= 0.081) + vv = v/4.5; + else + vv = pow((0.099 + v)/1.099, 1.0/0.45); + + vi[0] = vv * 0.9642; /* To D50 XYZ */ + vi[1] = vv * 1.0000; + vi[2] = vv * 0.8249; + + bt1886_apply(&bt, lu, vo, vi); /* BT.1886 mapping */ + + icmXYZ2Lab(&icmD50, Lab, vo); + + if (v > 1e-9 && vo[1] > 1e-9 && fabs(v - 1.0) > 1e-9) + loglog = log(vo[1])/log(v); + + printf(" In %5.1f%% -> XYZ in %f -> bt.1886 %f, log/log %.3f, Lab %f %f %f \n",v * 100.0,vi[1],vo[1], loglog, Lab[0], Lab[1], Lab[2]); + } + } + } + /* Some sanity checking */ if (tbp[0] >= 0.0 && icc_luo == NULL) error("Black scaling only works with ICC profile"); @@ -642,6 +857,9 @@ int main(int argc, char *argv[]) if ((ti = icg->find_kword(icg, 0, "FULL_SPREAD_PATCHES")) >= 0) ocg->add_kword(ocg, 0, "FULL_SPREAD_PATCHES",icg->t[0].kdata[ti], NULL); + if ((ti = icg->find_kword(icg, 0, "DARK_REGION_EMPHASIS")) >= 0) + ocg->add_kword(ocg, 0, "DARK_REGION_EMPHASIS",icg->t[0].kdata[ti], NULL); + if (icc_luo == NULL) { /* If MPP profile, we know what the target instrument is */ ocg->add_kword(ocg, 0, "TARGET_INSTRUMENT", inst_name(itype) , NULL); } @@ -842,13 +1060,62 @@ int main(int argc, char *argv[]) } } - if (dosep) + if (dosep) { + if (in_tvenc != 0) { + if (in_tvenc == 1) { /* Video 16-235 range */ + icmRGB_2_VidRGB(dev, dev); + } else if (in_tvenc == 2) { /* Rec601 YCbCr */ + icmRec601_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } else if (in_tvenc == 3) { /* Rec709 YCbCr */ + icmRec709_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */ + icmRec709_50_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */ + icmRec2020_NCL_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */ + icmRec2020_CL_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } + } + if (sep_luo->lookup(sep_luo, sep, dev) > 1) error ("%d, %s",icc_icco->errc,icc_icco->err); + if (out_tvenc != 0) { + if (out_tvenc == 1) { /* Video 16-235 range */ + icmVidRGB_2_RGB(sep, sep); + } else if (out_tvenc == 2) { /* Rec601 YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec601_YPbPr_2_RGBd(sep, sep); + } else if (out_tvenc == 3) { /* Rec709 1150/60/2:1 YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec709_YPbPr_2_RGBd(sep, sep); + } else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec709_50_YPbPr_2_RGBd(sep, sep); + } else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec2020_NCL_YPbPr_2_RGBd(sep, sep); + } else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec2020_CL_YPbPr_2_RGBd(sep, sep); + } + } + } + /* Do calibration */ - if (applycal && cal != NULL) - cal->interp(cal, sep, sep); + if (applycal && cal != NULL) { + if (applycal == 1) + cal->interp(cal, sep, sep); + else if (applycal == 2) { + if (cal->inv_interp(cal, sep, sep)) + warning("Inverse calibration of patch %d failed",i+1); + } + } /* Add randomness and non-linearity to device values. */ /* rdlevel = avg. dev. */ @@ -875,8 +1142,21 @@ int main(int argc, char *argv[]) /* Do color conversion */ if (icc_luo != NULL) { - if (icc_luo->lookup(icc_luo, PCS, sep) > 1) - error ("%d, %s",icc_icco->errc,icc_icco->err); + if (bt1886) { + icmLuMatrix *lu = (icmLuMatrix *)icc_luo; /* Safe to coerce */ +//printf("Matrix dev in: %s\n",icmPdv(inn, sep)); + lu->fwd_curve(lu, sep, sep); +//printf("Matrix after in curve: %s\n",icmPdv(inn, sep)); + lu->fwd_matrix(lu, PCS, sep); +//printf("Matrix after matrix XYZ %s Lab %s\n",icmPdv(3, PCS), icmPLab(PCS)); + bt1886_apply(&bt, lu, PCS, PCS); +//printf("Matrix after bt1186 XYZ %s Lab %s\n",icmPdv(3, PCS), icmPLab(PCS)); + lu->fwd_abs(lu, PCS, PCS); +//printf("Matrix after abs %s\n",icmPdv(3, PCS)); + } else { + if (icc_luo->lookup(icc_luo, PCS, sep) > 1) + error ("%d, %s",icc_icco->errc,icc_icco->err); + } if (tbp[0] >= 0) { /* Doing black point scaling */ @@ -913,7 +1193,7 @@ int main(int argc, char *argv[]) } } /* Copy best value over */ - if (!dosep) /* Doesn't make sense for separation */ + if (!dosep) /* Doesn't make sense for separation ??? */ for (j = 0; j < nchan; j++) { dev[j] = *((double *)ti3->t[0].fdata[bix][ti3_chix[j]]) / 100.0; } @@ -949,8 +1229,12 @@ int main(int argc, char *argv[]) /* Add randomness. rplevel is avg. dev. */ /* Note PCS is 0..100 XYZ or Lab at this point */ + /* Adding uniform error to XYZ produces unreasonable */ + /* bumpiness near black, so scale it by Y */ if (rplevel > 0.0) { - double opcs[3]; + double opcs[3], ll = 1.0; + if (!dolab) + ll = 0.01 * PCS[1]; for (j = 0; j < 3; j++) { double dv = PCS[j]; double rr; @@ -959,7 +1243,7 @@ int main(int argc, char *argv[]) rr = 100.0 * d_rand(-2.0 * rplevel, 2.0 * rplevel); else rr = 100.0 * 1.2533 * rplevel * norm_rand(); - dv += rr; + dv += ll * rr; /* Don't let L*, X, Y or Z go negative */ if ((!dolab || j == 0) && dv < 0.0) -- cgit v1.2.3