summaryrefslogtreecommitdiff
path: root/spectro/fakeread.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2015-05-01 16:24:15 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2015-05-01 16:24:15 +0200
commita30ba67504ffd12c4db499adbb5ce47a7d1f6036 (patch)
tree9ae1a7e3849dda6bbb5c578232f6f2fa5b2e7e7e /spectro/fakeread.c
parent89e99e8a827859729729dfc92d74be4a8f96f1a4 (diff)
parent094535c010320967639e8e86f974d878e80baa72 (diff)
New release 1.7.0
Diffstat (limited to 'spectro/fakeread.c')
-rw-r--r--spectro/fakeread.c845
1 files changed, 503 insertions, 342 deletions
diff --git a/spectro/fakeread.c b/spectro/fakeread.c
index ce06e93..892f548 100644
--- a/spectro/fakeread.c
+++ b/spectro/fakeread.c
@@ -19,7 +19,10 @@
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 ??
+ If a display profile has the icSigLuminanceTag, the value
+ should be used to set the .ti3 LUMINANCE_XYZ_CDM2 value,
+ and set NORMALIZED_TO_Y_100 to YES.
+
*/
@@ -36,7 +39,9 @@
#include "numlib.h"
#include "cgats.h"
#include "xicc.h"
+#include "bt1886.h"
#include "icc.h"
+#include "ui.h"
void usage(char *diag, ...) {
fprintf(stderr,"Fake test chart reader - lookup values in ICC/MPP profile, Version %s\n",
@@ -63,16 +68,17 @@ void usage(char *diag, ...) {
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," -Z nbits Quantize test values to fit in nbits\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 <level>%% 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," -B display.%s Use BT.1886 source EOTF with technical gamma 2.4\n",ICC_FILE_EXT_ND);
+ fprintf(stderr," -b g.g:display.%s Use BT.1886-like source EOTF with effective gamma g.g\n",ICC_FILE_EXT_ND);
+ fprintf(stderr," -b p.p:g.g:display.%s Use effective gamma g.g source EOTF with p.p prop. output black point offset\n",ICC_FILE_EXT_ND);
+ fprintf(stderr," -g g.g:display.%s Use effective gamma g.g source EOTF with all output black point offset\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");
@@ -80,6 +86,7 @@ void usage(char *diag, ...) {
fprintf(stderr," -R level Add average random deviation of <level>%% 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," -U Reverse convert PCS to device, output_r.ti3\n");
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);
@@ -90,12 +97,15 @@ int main(int argc, char *argv[])
int j;
int fa, nfa, mfa; /* current argument we're looking at */
int verb = 0; /* Verbose flag */
+ int revlookup = 0; /* Do PCS to device space conversion */
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 outoprop = 0.0; /* Proportion of black output offset, 0.0 .. 1.0. 0.0 == BT.1886 */
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 islab = 0; /* Input has Lab rather than XYZ */
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 };
@@ -109,8 +119,8 @@ int main(int argc, char *argv[])
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 */
+ static char outname[MAXNAMEL+3] = { 0 }; /* Output cgats file base name */
+ static char odispname[MAXNAMEL+1] = { 0 }; /* BT.1886 display profile name */
cgats *icg; /* input cgats structure */
cgats *ocg; /* output cgats structure */
int nmask = 0; /* Test chart device colorant mask */
@@ -119,11 +129,12 @@ int main(int argc, char *argv[])
int si; /* Sample id index */
int ti; /* Temp index */
int fi; /* Colorspace index */
- int inti3 = 0; /* Input is a renamed .ti3 file rather than .ti1 */
+ int inti3 = 0; /* Input is a .ti3 format rather than .ti1 */
/* 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. */
+ int qbits = 0; /* Quantization bits, 0 = not set */
/* ICC separation/calibration device link profile */
icmFile *sep_fp = NULL; /* Color profile file */
@@ -154,12 +165,12 @@ int main(int argc, char *argv[])
double spec_wl_long; /* Last reading wavelength in nm (longest) */
/* TI3 based fake read */
- cgats *ti3 = NULL; /* input cgats structure */
- int ti3_npat = 0; /* Number of patches in reference file */
+ cgats *ti3 = NULL; /* input cgats structure */
+ int ti3_npat = 0; /* Number of patches in reference file */
int ti3_chix[ICX_MXINKS]; /* Device chanel indexes */
- int ti3_pcsix[3]; /* Device chanel indexes */
+ int ti3_pcsix[3]; /* PCS chanel indexes */
int ti3_spi[XSPECT_MAX_BANDS]; /* CGATS indexes for each wavelength */
- int ti3_isLab = 0; /* Flag indicating PCS for TI3 file */
+ int ti3_isLab = 0; /* Flag indicating PCS for TI3 file */
int rv = 0;
int inn, outn; /* Number of channels for conversion input, output */
@@ -245,9 +256,21 @@ int main(int argc, char *argv[])
}
if (argv[fa][1] == 'e')
in_tvenc = enc;
- else
+ else {
out_tvenc = enc;
+ if (qbits == 0)
+ qbits = 8;
+ }
+ fa = nfa;
+ }
+
+ /* Specify quantization bits */
+ else if (argv[fa][1] == 'Z') {
fa = nfa;
+ if (na == NULL) usage("Expected argument to -Z");
+ qbits = atoi(na);
+ if (qbits < 1 || qbits > 32)
+ usage("Argument to -Q must be between 1 and 32");
}
/* Separation */
@@ -259,40 +282,57 @@ int main(int argc, char *argv[])
strncpy(sepname,na,MAXNAMEL); sepname[MAXNAMEL] = '\000';
}
- /* BT.1886 modifier */
+ /* Gamma curve modifier */
else if (argv[fa][1] == 'b'
- || argv[fa][1] == 'B') {
+ || argv[fa][1] == 'B'
+ || argv[fa][1] == 'g'
+ || argv[fa][1] == 'G') {
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 (na == NULL) usage("Gamma curve flag (-%c) needs an argument",argv[fa][1]);
- if ((cp = strchr(na, ':')) != NULL) {
- double gamma = 0.0;
+ bt1886 = 1; /* Effective */
+ if (argv[fa][1] == 'B' || argv[fa][1] == 'G')
+ bt1886 = 2; /* Technical */
+
+ if (argv[fa][1] == 'g' || argv[fa][1] == 'G')
+ outoprop = 1.0;
+
+ /* Grab the filename */
+ if ((cp = strrchr(na, ':')) != NULL) {
+ double gamma, opr;
+
+ strncpy(odispname,cp+1,MAXNAMEL); odispname[MAXNAMEL] = '\000';
*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;
+
+ if (sscanf(na,"%lf:%lf",&opr, &gamma) == 2) {
+ outoprop = opr;
+ if (bt1886 == 1)
+ egamma = gamma;
+ else
+ tgamma = gamma;
+
+ } else if (sscanf(na,"%lf",&gamma) == 1) {
+ if (bt1886 == 1)
+ egamma = gamma;
+ else
+ tgamma = gamma;
+ } else {
+ usage("Gamma curve (-%c) arguments not recognised",argv[fa][1]);
+ }
+
+ } else { /* No outoprop or gamma, just filanem */
+ strncpy(odispname,na,MAXNAMEL); odispname[MAXNAMEL] = '\000';
}
- strncpy(oprofname,cp,MAXNAMEL); oprofname[MAXNAMEL] = '\000';
+ fa = nfa; /* Used argument */
}
/* Lab */
- else if (argv[fa][1] == 'l' || argv[fa][1] == 'L')
+ else if (argv[fa][1] == 'l')
dolab = 1;
/* Uniform distrivuted errors */
- else if (argv[fa][1] == 'u' || argv[fa][1] == 'U')
+ else if (argv[fa][1] == 'u')
unidist = 1;
/* Random seed value */
@@ -370,6 +410,11 @@ int main(int argc, char *argv[])
}
}
+ /* Reverse lookup */
+ else if (argv[fa][1] == 'U') {
+ revlookup = 1;
+ }
+
else
usage("Unrecognised flag");
}
@@ -382,11 +427,80 @@ int main(int argc, char *argv[])
if (fa >= argc || argv[fa][0] == '-') usage("Missing basename argument");
strncpy(inname,argv[fa],MAXNAMEL-4); inname[MAXNAMEL-4] = '\000';
- strcat(inname,".ti1");
+ if (revlookup)
+ strcat(inname,".ti3");
+ else
+ strcat(inname,".ti1");
strncpy(outname,argv[fa],MAXNAMEL-4); outname[MAXNAMEL-4] = '\000';
+ if (revlookup)
+ strcat(outname,"_r");
strcat(outname,".ti3");
rand32(seed); /* Init seed */
+
+ if (revlookup && (
+ dosep
+ || calname[0] != '\000'
+ || dospec
+ || tbp[0] >= 0.0
+ || bt1886))
+ error("Can't do separation, apply calibration, do spectral, black point scale, bt.1886, with reverse lookup");
+
+ /* Deal with input CGATS files */
+ icg = new_cgats(); /* Create a CGATS structure */
+ icg->add_other(icg, "CTI1"); /* our special input type is Calibration Target Information 1 */
+ icg->add_other(icg, "CTI3"); /* also accept renamed .ti3 file */
+
+ if (icg->read_name(icg, inname))
+ error("CGATS file read error : %s",icg->err);
+
+ if (icg->ntables == 0 || icg->t[0].tt != tt_other || (icg->t[0].oi != 0 && icg->t[0].oi != 1))
+ error ("Input file isn't a CTI1 format file");
+ if (icg->t[0].oi == 1)
+ inti3 = 1; /* It's a renamed .ti3 file */
+ if (icg->ntables != 1 && icg->ntables != 2 && icg->ntables != 3)
+ error ("Input file doesn't contain one, two or three tables");
+
+ if ((npat = icg->t[0].nsets) <= 0)
+ error ("No sets of data");
+
+ /* Figure out the color space of the .ti1 */
+ if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0)
+ error ("Input file doesn't contain keyword COLOR_REP");
+
+ if (inti3) {
+ char *rbuf, *outc;
+
+ if ((rbuf = strdup(icg->t[0].kdata[fi])) == NULL)
+ error("Malloc failed");
+
+ /* Split COLOR_REP into device and PCS space */
+ if ((outc = strchr(rbuf, '_')) == NULL)
+ error("Input file '%s' COLOR_REP '%s' invalid", inname, icg->t[0].kdata[fi]);
+ *outc++ = '\000';
+
+ if ((nmask = icx_char2inkmask(rbuf)) == 0) {
+ error ("Input file '%s' keyword COLOR_REP has unknown device value '%s'",inname,rbuf);
+ }
+
+ if (strcmp(outc,"LAB") == 0)
+ islab = 1;
+ else if (strcmp(outc,"XYZ") == 0)
+ islab = 0;
+ else
+ error("Input .ti3 file PCS is neither LAB nor XYZ");
+ dolab = islab;
+
+ free(rbuf);
+ } else {
+ if ((nmask = icx_char2inkmask(icg->t[0].kdata[fi])) == 0)
+ error ("Input file '%s' keyword COLOR_REP has unknown value '%s'",inname, icg->t[0].kdata[fi]);
+ }
+
+ if (revlookup && !inti3) {
+ error("reverse lookup expects .ti3 file as input");
+ }
+
/* Deal with separation */
if (dosep) {
if ((sep_fp = new_icmFileStd_name(sepname,"r")) == NULL)
@@ -439,7 +553,7 @@ int main(int argc, char *argv[])
if ((icc_icco = new_icc()) == NULL)
error ("Creation of ICC object failed");
- /* Deal with ICC profile */
+ /* Read ICC profile */
if ((rv = icc_icco->read(icc_icco,icc_fp,0)) == 0) {
/* Embed any calibration in the output if it's present */
@@ -451,12 +565,22 @@ int main(int argc, char *argv[])
printf("Embedding calibration curves from ICC profile in output\n");
}
- /* Get a Device to PCS conversion object */
- if ((icc_luo = icc_icco->get_luobj(icc_icco, icmFwd, intent,
- dolab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) {
- if ((icc_luo = icc_icco->get_luobj(icc_icco, icmFwd, icmDefaultIntent,
- dolab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL)
- error ("%d, %s",icc_icco->errc, icc_icco->err);
+ if (revlookup) {
+ /* Get a PCS to Device conversion object */
+ if ((icc_luo = icc_icco->get_luobj(icc_icco, icmBwd, intent,
+ islab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) {
+ if ((icc_luo = icc_icco->get_luobj(icc_icco, icmBwd, icmDefaultIntent,
+ islab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",icc_icco->errc, icc_icco->err);
+ }
+ } else {
+ /* Get a Device to PCS conversion object */
+ if ((icc_luo = icc_icco->get_luobj(icc_icco, icmFwd, intent,
+ dolab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) {
+ if ((icc_luo = icc_icco->get_luobj(icc_icco, icmFwd, icmDefaultIntent,
+ dolab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",icc_icco->errc, icc_icco->err);
+ }
}
/* Get details of conversion */
@@ -504,6 +628,9 @@ int main(int argc, char *argv[])
/* If we don't have an ICC lookup object, look for an MPP */
if (icc_luo == NULL) {
+ if (revlookup)
+ error("Reverse lookup using MPP not supported");
+
if ((mlu = new_mpp()) == NULL)
error ("Creation of MPP object failed");
@@ -537,6 +664,9 @@ int main(int argc, char *argv[])
char *ti3_bident;
int ti3_nchan;
+ if (revlookup)
+ error("Reverse lookup using TI3 as conversion not supported");
+
ti3 = new_cgats(); /* Create a CGATS structure */
ti3->add_other(ti3, "CTI3");/* our special input type is Calibration Target Information 3 */
@@ -666,14 +796,14 @@ int main(int argc, char *argv[])
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 ((ofp = new_icmFileStd_name(odispname,"r")) == NULL)
+ error ("Can't open file '%s'",odispname);
if ((oicco = new_icc()) == NULL)
error ("Creation of ICC object failed");
if (oicco->read(oicco,ofp,0))
- error ("Unable to read '%s'",oprofname);
+ error ("Unable to read '%s'",odispname);
if ((oluo = oicco->get_luobj(oicco, icmFwd, icRelativeColorimetric,
icSigXYZData, icmLuOrdNorm)) == NULL)
@@ -690,30 +820,35 @@ int main(int argc, char *argv[])
oicco->del(oicco);
ofp->del(ofp);
+ bt1886_setup(&bt, &lu->pcswht, bp, outoprop,
+ bt1886 == 1 ? egamma : tgamma, bt1886 == 1 ? 1 : 0);
+
+ if (verb)
+ printf("Gamma Curve: Using ouput black offset proportion %f\n",outoprop);
+
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);
+ printf("Gamma Curve: Technical gamma %f used to achieve effective gamma %f\n",
+ bt.gamma, egamma);
} else {
if (verb)
- printf("BT.1886: Using technical gamma %f\n",tgamma);
+ printf("Gamma Curve: Using technical gamma %f\n",bt.gamma);
}
- bt1886_setup(&bt, bp, tgamma);
if (verb) {
- printf("BT.1886: target out black rel XYZ = %f %f %f, Lab %f %f %f\n",
+ printf("Gamma Curve: 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);
+ printf("Gamma Curve: Y input offset = %f\n", bt.ingo);
+ printf("Gamma Curve: Y output scale = %f\n", bt.outsc);
+ printf("Gamma Curve: Y output offset = %f\n", bt.outo);
}
/* 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);
+ bt1886_fwd_curve(&bt, rgb, rgb);
lu->fwd_matrix(lu, rgb, rgb);
- bt1886_apply(&bt, lu, rgb, rgb);
+ bt1886_wp_adjust(&bt, 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;
@@ -725,18 +860,12 @@ int main(int argc, char *argv[])
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 */
+ vi[0] = vi[1] = vi[2] = v;
+ bt1886_fwd_curve(&bt, vo, vi);
+ lu->fwd_matrix(lu, vo, vo);
+ bt1886_wp_adjust(&bt, vo, vo);
- icmXYZ2Lab(&icmD50, Lab, vo);
+ icmXYZ2Lab(&lu->pcswht, Lab, vo);
if (v > 1e-9 && vo[1] > 1e-9 && fabs(v - 1.0) > 1e-9)
loglog = log(vo[1])/log(v);
@@ -764,49 +893,6 @@ int main(int argc, char *argv[])
fflush(stdout);
}
- /* Deal with input CGATS files */
- icg = new_cgats(); /* Create a CGATS structure */
- icg->add_other(icg, "CTI1"); /* our special input type is Calibration Target Information 1 */
- icg->add_other(icg, "CTI3"); /* also accept renamed .ti3 file */
-
- if (icg->read_name(icg, inname))
- error("CGATS file read error : %s",icg->err);
-
- if (icg->ntables == 0 || icg->t[0].tt != tt_other || (icg->t[0].oi != 0 && icg->t[0].oi != 1))
- error ("Input file isn't a CTI1 format file");
- if (icg->t[0].oi == 1)
- inti3 = 1; /* It's a renamed .ti3 file */
- if (icg->ntables != 1 && icg->ntables != 2 && icg->ntables != 3)
- error ("Input file doesn't contain one, two or three tables");
-
- if ((npat = icg->t[0].nsets) <= 0)
- error ("No sets of data");
-
- /* Figure out the color space of the .ti1 */
- if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0)
- error ("Input file doesn't contain keyword COLOR_REP");
-
- if (inti3) {
- char *rbuf, *outc;
-
- if ((rbuf = strdup(icg->t[0].kdata[fi])) == NULL)
- error("Malloc failed");
-
- /* Split COLOR_REP into device and PCS space */
- if ((outc = strchr(rbuf, '_')) == NULL)
- error("Input file '%s' COLOR_REP '%s' invalid", inname, icg->t[0].kdata[fi]);
- *outc++ = '\000';
-
- if ((nmask = icx_char2inkmask(rbuf)) == 0) {
- error ("Input file '%s' keyword COLOR_REP has unknown device value '%s'",inname,rbuf);
- }
-
- free(rbuf);
- } else {
- if ((nmask = icx_char2inkmask(icg->t[0].kdata[fi])) == 0)
- error ("Input file '%s' keyword COLOR_REP has unknown value '%s'",inname, icg->t[0].kdata[fi]);
- }
-
/* Setup output cgats file */
ocg = new_cgats(); /* Create a CGATS structure */
ocg->add_other(ocg, "CTI3"); /* our special type is Calibration Target Information 3 */
@@ -873,11 +959,12 @@ int main(int argc, char *argv[])
{
int i, j, ii;
int chix[ICX_MXINKS]; /* Device chanel indexes */
+ int pcsix[3]; /* PCS chanel indexes (for revlookup) */
char *ident, *bident;
int nsetel = 0;
cgats_set_elem *setel; /* Array of set value elements */
- nchan = icx_noofinks(nmask);
+ nchan = icx_noofinks(nmask); /* No. device channels */
ident = icx_inkmask2char(nmask, 1);
bident = icx_inkmask2char(nmask, 0);
@@ -921,10 +1008,19 @@ int main(int argc, char *argv[])
gfudge = 1;
else if (nmask == ICX_K && ins == icSigCmykData)
gfudge = 2; /* Should allow for other colorant combo's that include black */
- else if (icx_colorant_comb_match_icc(nmask, ins) == 0) {
- error("ICC device space '%s' dosen't match TI1 '%s'",
- icm2str(icmColorSpaceSignature, ins),
- ident); // Should free().
+ else {
+ if (!revlookup) {
+ if (icx_colorant_comb_match_icc(nmask, ins) == 0)
+ error("ICC device space '%s' dosen't match TI1 '%s'",
+ icm2str(icmColorSpaceSignature, ins),
+ ident); // Should free().
+ } else {
+ if (icx_colorant_comb_match_icc(nmask, outs) == 0)
+ error("ICC device space '%s' dosen't match TI1 '%s'",
+ icm2str(icmColorSpaceSignature, ins),
+ ident); // Should free().
+
+ }
}
} else if (mlu != NULL) {
/* Check if mpp is compatible with .ti1 */
@@ -953,6 +1049,7 @@ int main(int argc, char *argv[])
nsetel += nchan; /* For device values */
nsetel += 3; /* For XYZ/Lab */
+ /* Locate device fields in source file, and add to output file */
for (j = 0; j < nchan; j++) {
int imask;
char fname[100];
@@ -970,6 +1067,19 @@ int main(int argc, char *argv[])
chix[j] = ii;
}
+ /* Find PCS fields if doing reverse lookup */
+ if (revlookup) {
+ for (j = 0; j < 3; j++) {
+ int ii;
+
+ if ((ii = icg->find_field(icg, 0, islab ? labfname[j] : xyzfname[j])) < 0)
+ error ("Input file doesn't contain field %s",islab ? labfname[j] : xyzfname[j]);
+ if (icg->t[0].ftype[ii] != r_t)
+ error ("Field %s is wrong type",islab ? labfname[j] : xyzfname[j]);
+ pcsix[j] = ii;
+ }
+ }
+
/* Add PCS fields */
for (j = 0; j < 3; j++) {
ocg->add_field(ocg, 0, dolab ? labfname[j] : xyzfname[j], r_t);
@@ -1009,261 +1119,312 @@ int main(int argc, char *argv[])
if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * nsetel)) == NULL)
error("Malloc failed!");
- /* Read all the test patches in, convert them, */
+ /* Read all the device test patches in, convert them to PCS, */
/* and write them out. */
- for (i = 0; i < npat; i++) {
- int k = 0;
- char *id;
- double odev[ICX_MXINKS], dev[ICX_MXINKS], sep[ICX_MXINKS], PCS[3];
- xspect out;
-
- id = ((char *)icg->t[0].fdata[i][si]);
- for (j = 0; j < nchan; j++) {
- double dv = *((double *)icg->t[0].fdata[i][chix[j]]) / 100.0;
- odev[j] = dev[j] = sep[j] = dv;
- }
-
- if (gfudge) {
- int nch;
-
- if (dosep) /* Figure number of channels into conversion */
- nch = sep_inn;
- else
- nch = inn;
-
- if (gfudge == 1) { /* Convert W -> RGB */
- double wval = dev[0];
- for (j = 0; j < nch; j++)
- dev[j] = sep[j] = wval;
-
- } else { /* Convert K->xxxK */
- int kch;
- int inmask;
- double kval = dev[0];
-
+ if (!revlookup) {
+ for (i = 0; i < npat; i++) {
+ int k = 0;
+ char *id;
+ double odev[ICX_MXINKS], dev[ICX_MXINKS], sep[ICX_MXINKS], PCS[3];
+ xspect out;
+ double qscale = (1 << qbits) - 1.0;
+
+ for (j = 0; j < nchan; j++) {
+ double dv = *((double *)icg->t[0].fdata[i][chix[j]]) / 100.0;
+ if (qbits > 0) {
+ double vr;
+ dv *= qscale;
+ vr = floor(dv + 0.5);
+ if ((vr - dv) == 0.5 && (((int)vr) & 1) != 0) /* Round to even */
+ vr -= 1.0;
+ dv = vr/qscale;
+ }
+ odev[j] = dev[j] = sep[j] = dv;
+ }
+
+ if (gfudge) {
+ int nch;
+
if (dosep) /* Figure number of channels into conversion */
- inmask = sep_nmask;
+ nch = sep_inn;
else
- inmask = cnv_nmask;
-
- if (inmask == 0)
- error("Input colorspace ambiguous - can't determine if it has black");
-
- if ((kch = icx_ink2index(inmask, ICX_BLACK)) == -1)
- error("Can't find black colorant for K fudge");
- for (j = 0; j < nch; j++) {
- if (j == kch)
- dev[j] = sep[j] = kval;
+ nch = inn;
+
+ if (gfudge == 1) { /* Convert W -> RGB */
+ double wval = dev[0];
+ for (j = 0; j < nch; j++)
+ dev[j] = sep[j] = wval;
+
+ } else { /* Convert K->xxxK */
+ int kch;
+ int inmask;
+ double kval = dev[0];
+
+ if (dosep) /* Figure number of channels into conversion */
+ inmask = sep_nmask;
else
- dev[j] = sep[j] = 0.0;
- }
- }
- }
-
- 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);
+ inmask = cnv_nmask;
+
+ if (inmask == 0)
+ error("Input colorspace ambiguous - can't determine if it has black");
+
+ if ((kch = icx_ink2index(inmask, ICX_BLACK)) == -1)
+ error("Can't find black colorant for K fudge");
+ for (j = 0; j < nch; j++) {
+ if (j == kch)
+ dev[j] = sep[j] = kval;
+ else
+ dev[j] = sep[j] = 0.0;
+ }
}
}
-
- 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);
+
+ 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);
+ }
}
- }
- }
-
- /* Do calibration */
- 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. */
- /* Note dev/sep is 0-1.0 at this stage */
- for (j = 0; j < inn; j++) {
- double dv = sep[j];
- if (rdlevel > 0.0) {
- double rr;
- if (unidist)
- rr = d_rand(-2.0 * rdlevel, 2.0 * rdlevel);
- else
- rr = 1.2533 * rdlevel * norm_rand();
- dv += rr;
- if (dv < 0.0)
- dv = 0.0;
- else if (dv > 1.0)
- dv = 1.0;
- }
- if (j < 10 && chpow[j] != 1.0) {
- dv = pow(dv, chpow[j]);
- }
- sep[j] = dv;
- }
-
- /* Do color conversion */
- if (icc_luo != NULL) {
- 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)
+
+ if (sep_luo->lookup(sep_luo, sep, dev) > 1)
error ("%d, %s",icc_icco->errc,icc_icco->err);
- }
-
- if (tbp[0] >= 0) { /* Doing black point scaling */
-
- for (j = 0; j < 3; j++)
- PCS[j] -= wp[j];
- icmMulBy3x3(PCS, bpt, PCS);
- for (j = 0; j < 3; j++)
- PCS[j] += wp[j];
+
+ 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);
+ }
+ }
}
- } else if (mlu != NULL) {
- mlu->lookup(mlu, PCS, sep);
- if (dospec && spec_n > 0) {
- mlu->lookup_spec(mlu, &out, sep);
+ /* Do calibration */
+ 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);
+ }
}
- } else if (ti3 != NULL) {
- int m;
- double bdif = 1e6;
- int bix = -1;
-
- /* Search for the closest device values in TI3 file */
- for (m = 0; m < ti3_npat; m++) {
- double dif;
-
- for (dif = 0.0, j = 0; j < nchan; j++) {
- double xx;
-
- xx = (*((double *)ti3->t[0].fdata[m][ti3_chix[j]]) / 100.0) - sep[j];
- dif += xx * xx;
+
+ /* Add randomness and non-linearity to device values. */
+ /* rdlevel = avg. dev. */
+ /* Note dev/sep is 0-1.0 at this stage */
+ for (j = 0; j < inn; j++) {
+ double dv = sep[j];
+ if (rdlevel > 0.0) {
+ double rr;
+ if (unidist)
+ rr = d_rand(-2.0 * rdlevel, 2.0 * rdlevel);
+ else
+ rr = 1.2533 * rdlevel * norm_rand();
+ dv += rr;
+ if (dv < 0.0)
+ dv = 0.0;
+ else if (dv > 1.0)
+ dv = 1.0;
}
- if (dif < bdif) {
- bdif = dif;
- bix = m;
+ if (j < 10 && chpow[j] != 1.0) {
+ dv = pow(dv, chpow[j]);
}
+ sep[j] = dv;
}
- /* Copy best value over */
- 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;
+
+ /* Do color conversion */
+ if (icc_luo != NULL) {
+ if (bt1886) {
+ icmLuMatrix *lu = (icmLuMatrix *)icc_luo; /* Safe to coerce */
+ //printf("Matrix dev in: %s\n",icmPdv(inn, sep));
+ bt1886_fwd_curve(&bt, sep, sep);
+ //printf("Matrix after bt1886 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_wp_adjust(&bt, PCS, PCS);
+ //printf("Matrix after bt1186 wp adj. 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 */
+
+ for (j = 0; j < 3; j++)
+ PCS[j] -= wp[j];
+ icmMulBy3x3(PCS, bpt, PCS);
+ for (j = 0; j < 3; j++)
+ PCS[j] += wp[j];
+ }
+
+ } else if (mlu != NULL) {
+ mlu->lookup(mlu, PCS, sep);
+ if (dospec && spec_n > 0) {
+ mlu->lookup_spec(mlu, &out, sep);
+ }
+ } else if (ti3 != NULL) {
+ int m;
+ double bdif = 1e6;
+ int bix = -1;
+
+ /* Search for the closest device values in TI3 file */
+ for (m = 0; m < ti3_npat; m++) {
+ double dif;
+
+ for (dif = 0.0, j = 0; j < nchan; j++) {
+ double xx;
+
+ xx = (*((double *)ti3->t[0].fdata[m][ti3_chix[j]]) / 100.0) - sep[j];
+ dif += xx * xx;
+ }
+ if (dif < bdif) {
+ bdif = dif;
+ bix = m;
+ }
+ }
+ /* Copy best value over */
+ 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;
+ }
+ for (j = 0; j < 3; j++) {
+ PCS[j] = *((double *)ti3->t[0].fdata[bix][ti3_pcsix[j]]);
+ }
+ if (ti3_isLab && !dolab) { /* Convert Lab to XYZ */
+ icmLab2XYZ(&icmD50, PCS, PCS);
+ } else if (!ti3_isLab && dolab) { /* Convert XYZ to Lab */
+ icmXYZ2Lab(&icmD50, PCS, PCS);
+ } else if (!ti3_isLab) { /* Convert XYZ100 to XYZ1 */
+ PCS[0] /= 100.0;
+ PCS[1] /= 100.0;
+ PCS[2] /= 100.0;
+ }
+ if (dospec && spec_n > 0) {
+ for (j = 0; j < spec_n; j++) {
+ out.spec[j] = *((double *)ti3->t[0].fdata[bix][ti3_spi[j]]);
+ }
}
- for (j = 0; j < 3; j++) {
- PCS[j] = *((double *)ti3->t[0].fdata[bix][ti3_pcsix[j]]);
}
- if (ti3_isLab && !dolab) { /* Convert Lab to XYZ */
- icmLab2XYZ(&icmD50, PCS, PCS);
- } else if (!ti3_isLab && dolab) { /* Convert XYZ to Lab */
- icmXYZ2Lab(&icmD50, PCS, PCS);
- } else if (!ti3_isLab) { /* Convert XYZ100 to XYZ1 */
- PCS[0] /= 100.0;
- PCS[1] /= 100.0;
- PCS[2] /= 100.0;
+
+ id = ((char *)icg->t[0].fdata[i][si]);
+ setel[k++].c = id;
+
+ for (j = 0; j < nchan; j++)
+ setel[k++].d = 100.0 * odev[j];
+
+ if (dolab == 0) {
+ PCS[0] *= 100.0;
+ PCS[1] *= 100.0;
+ PCS[2] *= 100.0;
}
+
+ /* 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], ll = 1.0;
+ if (!dolab)
+ ll = 0.01 * PCS[1];
+ for (j = 0; j < 3; j++) {
+ double dv = PCS[j];
+ double rr;
+ opcs[j] = dv;
+ if (unidist)
+ rr = 100.0 * d_rand(-2.0 * rplevel, 2.0 * rplevel);
+ else
+ rr = 100.0 * 1.2533 * rplevel * norm_rand();
+ dv += ll * rr;
+
+ /* Don't let L*, X, Y or Z go negative */
+ if ((!dolab || j == 0) && dv < 0.0)
+ dv = 0.0;
+ PCS[j] = dv;
+ }
+ //printf("~1 pcs %f %f %f -> %f %f %f\n", opcs[0], opcs[1], opcs[2], PCS[0], PCS[1], PCS[2]);
+ }
+
+ setel[k++].d = PCS[0];
+ setel[k++].d = PCS[1];
+ setel[k++].d = PCS[2];
+
if (dospec && spec_n > 0) {
for (j = 0; j < spec_n; j++) {
- out.spec[j] = *((double *)ti3->t[0].fdata[bix][ti3_spi[j]]);
+ setel[k++].d = 100.0 * out.spec[j];
}
}
+
+ ocg->add_setarr(ocg, 0, setel);
}
- setel[k++].c = id;
-
- for (j = 0; j < nchan; j++)
- setel[k++].d = 100.0 * odev[j];
-
- if (dolab == 0) {
- PCS[0] *= 100.0;
- PCS[1] *= 100.0;
- PCS[2] *= 100.0;
- }
+ /* Do reverse (PCS -> device) lookup */
+ } else {
- /* 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], ll = 1.0;
- if (!dolab)
- ll = 0.01 * PCS[1];
+ /* Read all the PCS test patches in, convert them to device values, */
+ /* and write them out. */
+ for (i = 0; i < npat; i++) {
+ int k = 0;
+ char *id;
+ double pcs[3];
+ double dev[ICX_MXINKS];
+
+ /* read PCS values */
for (j = 0; j < 3; j++) {
- double dv = PCS[j];
- double rr;
- opcs[j] = dv;
- if (unidist)
- rr = 100.0 * d_rand(-2.0 * rplevel, 2.0 * rplevel);
- else
- rr = 100.0 * 1.2533 * rplevel * norm_rand();
- dv += ll * rr;
-
- /* Don't let L*, X, Y or Z go negative */
- if ((!dolab || j == 0) && dv < 0.0)
- dv = 0.0;
- PCS[j] = dv;
+ double pv = *((double *)icg->t[0].fdata[i][pcsix[j]]);
+ if (!islab)
+ pv /= 100.0;
+ pcs[j] = pv;
}
-//printf("~1 pcs %f %f %f -> %f %f %f\n", opcs[0], opcs[1], opcs[2], PCS[0], PCS[1], PCS[2]);
- }
-
- setel[k++].d = PCS[0];
- setel[k++].d = PCS[1];
- setel[k++].d = PCS[2];
-
- if (dospec && spec_n > 0) {
- for (j = 0; j < spec_n; j++) {
- setel[k++].d = 100.0 * out.spec[j];
+
+ /* Do PCS to device color conversion */
+ if (icc_luo->lookup(icc_luo, dev, pcs) > 1)
+ error ("%d, %s",icc_icco->errc,icc_icco->err);
+
+ /* Write the values */
+ id = ((char *)icg->t[0].fdata[i][si]);
+ setel[k++].c = id;
+
+ for (j = 0; j < nchan; j++)
+ setel[k++].d = 100.0 * dev[j];
+
+ for (j = 0; j < 3; j++) {
+ double pv = pcs[j];
+ if (!islab)
+ pv *= 100.0;
+ setel[k++].d = pv;
}
+
+ ocg->add_setarr(ocg, 0, setel);
}
-
- ocg->add_setarr(ocg, 0, setel);
}
free(setel);