1628 lines
48 KiB
C
1628 lines
48 KiB
C
|
|
/*
|
|
* xicc lookup/test utility
|
|
*
|
|
* This program is the analog of icclu, but allows reverse lookup
|
|
* of transforms by making use of xicc interpolation code, + other
|
|
* more advanced features.
|
|
* (Based on the old xfmlu.c)
|
|
*
|
|
* Author: Graeme W. Gill
|
|
* Date: 8/7/00
|
|
* Version: 1.00
|
|
*
|
|
* Copyright 1999, 2000 Graeme W. Gill
|
|
* All rights reserved.
|
|
* This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
|
|
* see the License.txt file for licencing details.
|
|
*/
|
|
|
|
/* TTBD:
|
|
|
|
Add HSV as alternative to RGB ?
|
|
|
|
Can -ff and -fif be made to work with device link files ?
|
|
|
|
NOTE that we aren't allowing for device link with show TAC and
|
|
separate/different cal curves for in and out space etc. ....
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "copyright.h"
|
|
#include "aconfig.h"
|
|
#include "numlib.h"
|
|
#include "plot.h"
|
|
#include "conv.h"
|
|
#include "xicc.h"
|
|
#include "ui.h"
|
|
|
|
#undef SPTEST /* [und] Test (flawed) rspl gamut surface code */
|
|
|
|
#define USE_NEARCLIP /* [def] Our usual expectation */
|
|
#define USE_FASTNNSETP /* [def] Make it more responsive, but not same alg. */
|
|
/* NOTE camclip & merglut are runtime options! */
|
|
#define XRES 128 /* [128] Plotting resolution */
|
|
|
|
#ifndef USE_NEARCLIP
|
|
# pragma message("!!!!!!!!!!!! USE_NEARCLIP turned off !!!!!!!!!")
|
|
#endif
|
|
|
|
#ifndef USE_FASTNNSETP
|
|
# pragma message("!!!!!!!!!!!! USE_FASTNNSETP turned off !!!!!!!!!")
|
|
#endif
|
|
|
|
/* ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
|
upper . . . .. .. .. .
|
|
lower .. ... . ... .. . ...
|
|
*/
|
|
|
|
void usage(char *diag) {
|
|
int i;
|
|
fprintf(stderr,"Lookup ICC or CAL colors, Version %s\n",ARGYLL_VERSION_STR);
|
|
fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
|
|
fprintf(stderr,"usage: xicclu [-options] profile_or_cal\n");
|
|
if (diag != NULL)
|
|
fprintf(stderr,"Diagnostic: %s\n",diag);
|
|
fprintf(stderr," -v level Verbosity level 0 - 2 (default = 1)\n");
|
|
fprintf(stderr," -g Plot slice instead of looking colors up. (Default white to black)\n");
|
|
fprintf(stderr," -G s:L:a:b Override plot slice start with Lab or Jab co-ordinate \n");
|
|
fprintf(stderr," -G e:L:a:b Override plot slice end with Lab or Jab co-ordinate \n");
|
|
fprintf(stderr," -V Lookup or plot the 'vcgt' calibration tag from profile\n");
|
|
fprintf(stderr," -f function f = forward, b = backwards, g = gamut, p = preview\n");
|
|
fprintf(stderr," if = inverted forward, ib = inverted backwards\n");
|
|
fprintf(stderr," -i intent a = absolute, r = relative colorimetric\n");
|
|
fprintf(stderr," p = perceptual, s = saturation, A = disp. abs. measurements\n");
|
|
// fprintf(stderr," P = absolute perceptual, S = absolute saturation\n");
|
|
fprintf(stderr," E = appearance, pE = percept. appce., sE = sat. appce.\n");
|
|
fprintf(stderr," -o order n = normal (priority: lut > matrix > monochrome)\n");
|
|
fprintf(stderr," r = reverse (priority: monochrome > matrix > lut)\n");
|
|
fprintf(stderr," -p oride x = XYZ_PCS, X = XYZ * 100, l = Lab_PCS, L = LCh, y = Yxy, u = Lu'v'\n");
|
|
fprintf(stderr," j = %s Appearance Jab, J = %s Appearance JCh\n",icxcam_description(cam_default),icxcam_description(cam_default));
|
|
fprintf(stderr," -s scale Scale device range 0.0 - scale rather than 0.0 - 1.0\n");
|
|
fprintf(stderr," -e flag Video encode device input as:\n");
|
|
fprintf(stderr," -E flag Video decode device output 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," -k [zhxrlv] Black value target: z = zero K,\n");
|
|
fprintf(stderr," h = 0.5 K, x = max K, r = ramp K (def.)\n");
|
|
fprintf(stderr," l = extra PCS input is portion of K locus\n");
|
|
fprintf(stderr," v = extra PCS input is K target value\n");
|
|
fprintf(stderr," -k p stle stpo enpo enle shape\n");
|
|
fprintf(stderr," stle: K level at White 0.0 - 1.0\n");
|
|
fprintf(stderr," stpo: start point of transition Wh 0.0 - Bk 1.0\n");
|
|
fprintf(stderr," enpo: End point of transition Wh 0.0 - Bk 1.0\n");
|
|
fprintf(stderr," enle: K level at Black 0.0 - 1.0\n");
|
|
fprintf(stderr," shape: 1.0 = straight, 0.0-1.0 concave, 1.0-2.0 convex\n");
|
|
fprintf(stderr," -k q stle0 stpo0 enpo0 enle0 shape0 stle2 stpo2 enpo2 enle2 shape2\n");
|
|
fprintf(stderr," Transfer extra PCS input to dual curve limits\n");
|
|
fprintf(stderr," -K parameters Same as -k, but target is K locus rather than K value itself\n");
|
|
fprintf(stderr," -l tlimit set total ink limit, 0 - 400%% (estimate by default)\n");
|
|
fprintf(stderr," -L klimit set black ink limit, 0 - 100%% (estimate by default)\n");
|
|
fprintf(stderr," -Y c:file.inkvol Provide or overide calibration total ink curves\n");
|
|
fprintf(stderr," -T Show total ink limit of device values\n");
|
|
fprintf(stderr," -a show actual target values if clipped\n");
|
|
fprintf(stderr," -u warn if output PCS is outside the spectrum locus\n");
|
|
fprintf(stderr," -m merge output processing into clut\n");
|
|
fprintf(stderr," -b use CAM Jab for clipping\n");
|
|
// fprintf(stderr," -S Use internal optimised separation for inverse 4d [NOT IMPLEMENTED]\n");
|
|
|
|
#ifdef SPTEST
|
|
fprintf(stderr," -w special gamut surface test PCS space\n");
|
|
fprintf(stderr," -W special gamut surface test, PCS' space\n");
|
|
#endif
|
|
fprintf(stderr," -c viewcond set viewing conditions for CIECAM97s,\n");
|
|
fprintf(stderr," either an enumerated choice, or a parameter:value changes\n");
|
|
for (i = 0; ; i++) {
|
|
icxViewCond vc;
|
|
if (xicc_enum_viewcond(NULL, &vc, i, NULL, 1, NULL) == -999)
|
|
break;
|
|
|
|
fprintf(stderr," %s\n",vc.desc);
|
|
}
|
|
|
|
fprintf(stderr," s:surround n = auto, a = average, m = dim, d = dark,\n");
|
|
fprintf(stderr," c = transparency (default average)\n");
|
|
fprintf(stderr," w:X:Y:Z Adapted white point as XYZ (default media white, Abs: D50)\n");
|
|
fprintf(stderr," w:x:y Adapted white point as x, y\n");
|
|
fprintf(stderr," a:adaptation Adaptation luminance in cd.m^2 (default 50.0)\n");
|
|
fprintf(stderr," b:background Background %% of image luminance (default 20)\n");
|
|
fprintf(stderr," l:imagewhite Image white in cd.m^2 if surround = auto (default 250)\n");
|
|
fprintf(stderr," f:flare Flare light %% of image luminance (default 0)\n");
|
|
fprintf(stderr," g:glare Flare light %% of ambient (default %d)\n",XICC_DEFAULT_GLARE);
|
|
fprintf(stderr," g:X:Y:Z Flare color as XYZ (default media white, Abs: D50)\n");
|
|
fprintf(stderr," g:x:y Flare color as x, y\n");
|
|
fprintf(stderr," h:hkscale Helmholtz-Kohlrausch effect scale factor (default 1.0)\n");
|
|
fprintf(stderr," m:mtaf Mid-tone partial adaptation factor (default 0.0)\n");
|
|
fprintf(stderr," m:X:Y:Z Mid-tone Adaptation white as XYZ (default D50)\n");
|
|
fprintf(stderr," m:x:y Mid-tone Adaptation white as x, y\n");
|
|
fprintf(stderr,"\n");
|
|
fprintf(stderr," The colors to be translated should be fed into standard in,\n");
|
|
fprintf(stderr," one input color per line, white space separated.\n");
|
|
fprintf(stderr," A line starting with a # will be ignored.\n");
|
|
fprintf(stderr," A line not starting with a number will terminate the program.\n");
|
|
fprintf(stderr," Use -v0 for just output colors.\n");
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef SPTEST
|
|
|
|
#pragma message("!!!!!!!!!!!! Experimental gamut boundary test !!!!!!!!!")
|
|
|
|
/* Output curve function */
|
|
void spoutf(void *cbntx, double *out, double *in) {
|
|
icxLuLut *clu = (icxLuLut *)cbntx;
|
|
|
|
clu->output(clu, out, in);
|
|
clu->out_abs(clu, out, out);
|
|
}
|
|
|
|
/* Inverse output curve function */
|
|
void spioutf(void *cbntx, double *out, double *in) {
|
|
icxLuLut *clu = (icxLuLut *)cbntx;
|
|
|
|
clu->inv_out_abs(clu, out, in);
|
|
clu->inv_output(clu, out, out);
|
|
}
|
|
|
|
|
|
#endif /* SPTEST */
|
|
|
|
int
|
|
main(int argc, char *argv[]) {
|
|
int fa, nfa, mfa; /* argument we're looking at */
|
|
static char prof_name[MAXNAMEL+1];
|
|
icmFile *fp = NULL;
|
|
icmErr err = { 0, { '\000'} };
|
|
icc *icco = NULL;
|
|
xicc *xicco = NULL;
|
|
xcal *cal = NULL; /* If .cal rather than .icm/.icc, not NULL */
|
|
int dovcgt = 0; /* Extract cal out of icc profile */
|
|
int doplot = 0; /* Do grey axis plot */
|
|
double pstart[3] = { -1000.0 }; /* Plot Lab/Jab PCS start point = white */
|
|
double pend[3] = { -1000.0 }; /* Plot Lab/Jab PCS end point = black */
|
|
icxInk ink; /* Ink parameters */
|
|
double tlimit = -1.0; /* Total ink limit */
|
|
double klimit = -1.0; /* Black ink limit */
|
|
double showtac = 0; /* Show device TAC */
|
|
static char tcalname[MAXNAMEL+1] = ""; /* .cal overide for icc */
|
|
xcal *tcal = NULL; /* TAC calibration override for icc */
|
|
int intsep = 0;
|
|
icxViewCond vc; /* Viewing Condition for CIECAM97s */
|
|
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 % overide */
|
|
double vc_l = -1.0; /* Scene luminance override */
|
|
double vc_f = -1.0; /* Flare % overide */
|
|
double vc_g = -1.0; /* Glare % overide */
|
|
double vc_gXYZ[3] = {-1.0, -1.0, -1.0}; /* Glare color override in XYZ */
|
|
double vc_gxy[2] = {-1.0, -1.0}; /* Glare color override in x,y */
|
|
double vc_hkscale = -1.0; /* HK scaling factor */
|
|
double vc_mtaf = -1.0; /* Mid tone partial adapation factor from Wxyz to Wxyz2 */
|
|
double vc_Wxyz2[3] = {-1.0, -1.0, -1.0}; /* Adapted white override in XYZ */
|
|
int verb = 1;
|
|
int actual = 0;
|
|
int slocwarn = 0;
|
|
int merge = 0;
|
|
int camclip = 0;
|
|
int repYxy = 0; /* Report Yxy */
|
|
int repYuv = 0; /* Report Yu'v' */
|
|
int repJCh = 0; /* Report JCh */
|
|
int repLCh = 0; /* Report LCh */
|
|
int repXYZ100 = 0; /* Scale XYZ by 10 */
|
|
double scale = 0.0; /* Device value scale factor */
|
|
int in_tvenc = 0; /* 1 to use RGB Video Level encoding, 2 = Rec601, 3 = Rec709 YCbCr */
|
|
int out_tvenc = 0; /* 1 to use RGB Video Level encoding, 2 = Rec601, 3 = Rec709 YCbCr */
|
|
int rv = 0;
|
|
char buf[200];
|
|
double uin[MAX_CHAN], in[MAX_CHAN], out[MAX_CHAN], uout[MAX_CHAN];
|
|
|
|
icxLuBase *luo = NULL, *aluo = NULL;
|
|
icColorSpaceSignature ins, outs; /* Type of input and output spaces */
|
|
int inn, outn; /* Number of components */
|
|
icmLuAlgType alg; /* Type of lookup algorithm */
|
|
|
|
/* Lookup parameters */
|
|
icmLookupFunc func = icmFwd; /* Default */
|
|
icRenderingIntent intent = icmDefaultIntent; /* Default */
|
|
int absmeas = 0; /* Show display absolute measurement in XYZ */
|
|
double dispLuminance = -1.0; /* Luminance value for absmeas */
|
|
icColorSpaceSignature pcsor = icmSigDefaultData; /* Default */
|
|
icmLookupOrder order = icmLuOrdNorm; /* Default */
|
|
int inking = 3; /* Default is ramp */
|
|
int locus = 0; /* Default is K value */
|
|
/* K curve params */
|
|
double Kstle = 0.0, Kstpo = 0.0, Kenle = 0.0, Kenpo = 0.0, Kshap = 0.0;
|
|
/* K curve params (max) */
|
|
double Kstle1 = 0.0, Kstpo1 = 0.0, Kenle1 = 0.0, Kenpo1 = 0.0, Kshap1 = 0.0;
|
|
int invert = 0;
|
|
|
|
xslpoly *chlp = NULL;
|
|
|
|
#ifdef SPTEST
|
|
int sptest = 0;
|
|
warning("xicc/xicclu.c !!!! special rspl gamut sest code is compiled in !!!!\n");
|
|
#endif
|
|
|
|
|
|
error_program = argv[0];
|
|
|
|
if (argc < 2)
|
|
usage("Too few arguments");
|
|
|
|
/* Process the arguments */
|
|
mfa = 1; /* 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 */
|
|
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+mfa) < argc) {
|
|
if (argv[fa+1][0] != '-') {
|
|
nfa = fa + 1;
|
|
na = argv[nfa]; /* next is seperate non-flag argument */
|
|
}
|
|
}
|
|
}
|
|
|
|
if (argv[fa][1] == '?')
|
|
usage("Requested usage");
|
|
|
|
/* Verbosity */
|
|
else if (argv[fa][1] == 'v') {
|
|
if (na == NULL) {
|
|
verb = 2;
|
|
} else {
|
|
fa = nfa;
|
|
if (na[0] == '0')
|
|
verb = 0;
|
|
else if (na[0] == '1')
|
|
verb = 1;
|
|
else if (na[0] == '2')
|
|
verb = 2;
|
|
else
|
|
usage("Illegal verbosity level");
|
|
}
|
|
}
|
|
|
|
/* Load ICC 'vcgt' tag as Cal */
|
|
else if (argv[fa][1] == 'V') {
|
|
dovcgt = 1;
|
|
}
|
|
|
|
/* Locus plot */
|
|
else if (argv[fa][1] == 'g') {
|
|
doplot = 1;
|
|
}
|
|
/* Plot start or end override */
|
|
else if (argv[fa][1] == 'G') {
|
|
if (na == NULL) usage("No parameter after flag -G");
|
|
fa = nfa;
|
|
if (na[0] == 's' || na[0] == 'S') {
|
|
if (sscanf(na+1,":%lf:%lf:%lf",&pstart[0],&pstart[1],&pstart[2]) != 3)
|
|
usage("Unrecognised parameters after -Gs");
|
|
} else if (na[0] == 'e' || na[0] == 'E') {
|
|
if (sscanf(na+1,":%lf:%lf:%lf",&pend[0],&pend[1],&pend[2]) != 3)
|
|
usage("Unrecognised parameters after -Ge");
|
|
} else
|
|
usage("Unrecognised parameters after -G");
|
|
}
|
|
/* Actual target values */
|
|
else if (argv[fa][1] == 'a') {
|
|
actual = 1;
|
|
}
|
|
/* Warn if output is outside the spectrum locus */
|
|
else if (argv[fa][1] == 'u') {
|
|
slocwarn = 1;
|
|
}
|
|
/* Merge output */
|
|
else if (argv[fa][1] == 'm') {
|
|
merge = 1;
|
|
}
|
|
/* Use CAM Jab for clipping on reverse lookup */
|
|
else if (argv[fa][1] == 'b') {
|
|
camclip = 1;
|
|
}
|
|
/* Use optimised internal separation */
|
|
else if (argv[fa][1] == 'S') {
|
|
intsep = 1;
|
|
}
|
|
/* Device scale */
|
|
else if (argv[fa][1] == 's') {
|
|
if (na == NULL) usage("No parameter after flag -s");
|
|
fa = nfa;
|
|
scale = atof(na);
|
|
if (scale <= 0.0) usage("Illegal scale value");
|
|
}
|
|
/* 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");
|
|
fa = nfa;
|
|
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;
|
|
}
|
|
|
|
/* function */
|
|
else if (argv[fa][1] == 'f') {
|
|
if (na == NULL) usage("No parameter after flag -f");
|
|
fa = nfa;
|
|
switch (na[0]) {
|
|
case 'f':
|
|
func = icmFwd;
|
|
break;
|
|
case 'b':
|
|
func = icmBwd;
|
|
break;
|
|
case 'g':
|
|
func = icmGamut;
|
|
break;
|
|
case 'p':
|
|
func = icmPreview;
|
|
break;
|
|
case 'i':
|
|
invert = 1;
|
|
if (na[1] == 'f')
|
|
func = icmFwd;
|
|
else if (na[1] == 'b')
|
|
func = icmBwd;
|
|
else
|
|
usage("Unknown parameter after flag -fi");
|
|
break;
|
|
default:
|
|
usage("Unknown parameter after flag -f");
|
|
}
|
|
}
|
|
|
|
/* Intent */
|
|
else if (argv[fa][1] == 'i') {
|
|
if (na == NULL) usage("No parameter after flag -i");
|
|
fa = nfa;
|
|
absmeas = 0;
|
|
switch (na[0]) {
|
|
case 'p':
|
|
if (na[1] == '\000')
|
|
intent = icPerceptual;
|
|
else if (na[1] == 'E')
|
|
intent = icxPerceptualAppearance;
|
|
else
|
|
usage("Unknown parameter after flag -ip");
|
|
break;
|
|
case 'r':
|
|
intent = icRelativeColorimetric;
|
|
break;
|
|
case 's':
|
|
if (na[1] == '\000')
|
|
intent = icSaturation;
|
|
else if (na[1] == 'E')
|
|
intent = icxSaturationAppearance;
|
|
else
|
|
usage("Unknown parameter after flag -is");
|
|
break;
|
|
case 'a':
|
|
intent = icAbsoluteColorimetric;
|
|
break;
|
|
case 'A':
|
|
intent = icAbsoluteColorimetric;
|
|
absmeas = 1;
|
|
break;
|
|
/* Argyll special intents to check spaces underlying */
|
|
/* icxPerceptualAppearance & icxSaturationAppearance */
|
|
case 'P':
|
|
intent = icmAbsolutePerceptual;
|
|
break;
|
|
case 'S':
|
|
intent = icmAbsoluteSaturation;
|
|
break;
|
|
|
|
case 'E':
|
|
intent = icxAppearance;
|
|
break;
|
|
|
|
default:
|
|
usage("Unknown parameter after flag -i");
|
|
}
|
|
}
|
|
|
|
/* PCS override */
|
|
else if (argv[fa][1] == 'p') {
|
|
if (na == NULL) usage("No parameter after flag -i");
|
|
fa = nfa;
|
|
switch (na[0]) {
|
|
case 'x':
|
|
/* (See also absmeas after options) */
|
|
pcsor = icSigXYZData;
|
|
repYxy = 0;
|
|
repYuv = 0;
|
|
repLCh = 0;
|
|
repJCh = 0;
|
|
repXYZ100 = 0;
|
|
break;
|
|
case 'X':
|
|
pcsor = icSigXYZData;
|
|
repYxy = 0;
|
|
repYuv = 0;
|
|
repLCh = 0;
|
|
repJCh = 0;
|
|
repXYZ100 = 1;
|
|
break;
|
|
case 'l':
|
|
pcsor = icSigLabData;
|
|
repYxy = 0;
|
|
repYuv = 0;
|
|
repLCh = 0;
|
|
repJCh = 0;
|
|
repXYZ100 = 0;
|
|
break;
|
|
case 'L':
|
|
pcsor = icSigLabData;
|
|
repYxy = 0;
|
|
repYuv = 0;
|
|
repLCh = 1;
|
|
repJCh = 0;
|
|
repXYZ100 = 0;
|
|
break;
|
|
case 'y':
|
|
case 'Y':
|
|
pcsor = icSigXYZData;
|
|
repYxy = 1;
|
|
repYuv = 0;
|
|
repLCh = 0;
|
|
repJCh = 0;
|
|
repXYZ100 = 0;
|
|
break;
|
|
case 'u':
|
|
pcsor = icSigXYZData;
|
|
repYxy = 0;
|
|
repYuv = 1;
|
|
repLCh = 0;
|
|
repJCh = 0;
|
|
repXYZ100 = 0;
|
|
break;
|
|
case 'j':
|
|
pcsor = icxSigJabData;
|
|
repYxy = 0;
|
|
repYuv = 0;
|
|
repLCh = 0;
|
|
repJCh = 0;
|
|
repXYZ100 = 0;
|
|
break;
|
|
case 'J':
|
|
pcsor = icxSigJabData;
|
|
repYxy = 0;
|
|
repYuv = 0;
|
|
repLCh = 0;
|
|
repJCh = 1;
|
|
repXYZ100 = 0;
|
|
break;
|
|
default:
|
|
usage("Unknown parameter after flag -p");
|
|
}
|
|
}
|
|
|
|
/* Search order */
|
|
else if (argv[fa][1] == 'o') {
|
|
if (na == NULL) usage("No parameter after flag -o");
|
|
fa = nfa;
|
|
switch (na[0]) {
|
|
case 'n':
|
|
order = icmLuOrdNorm;
|
|
break;
|
|
case 'r':
|
|
order = icmLuOrdRev;
|
|
break;
|
|
default:
|
|
usage("Unknown parameter after flag -o");
|
|
}
|
|
}
|
|
|
|
/* Inking rule */
|
|
else if (argv[fa][1] == 'k'
|
|
|| argv[fa][1] == 'K') {
|
|
if (na == NULL) usage("No parameter after flag -k");
|
|
fa = nfa;
|
|
if (argv[fa][1] == 'k')
|
|
locus = 0; /* K value target */
|
|
else
|
|
locus = 1; /* K locus target */
|
|
switch (na[0]) {
|
|
case 'z':
|
|
case 'Z':
|
|
inking = 0; /* Use minimum k */
|
|
break;
|
|
case 'h':
|
|
case 'H':
|
|
inking = 1; /* Use 0.5 k */
|
|
break;
|
|
case 'x':
|
|
case 'X':
|
|
inking = 2; /* Use maximum k */
|
|
break;
|
|
case 'r':
|
|
case 'R':
|
|
inking = 3; /* Use ramping K */
|
|
break;
|
|
case 'l':
|
|
case 'L':
|
|
inking = 4; /* Extra param is locus */
|
|
break;
|
|
case 'v':
|
|
case 'V':
|
|
inking = 5; /* Extra param is K target */
|
|
break;
|
|
case 'p':
|
|
case 'P':
|
|
case 'q':
|
|
case 'Q':
|
|
inking = 6; /* Use curve parameter */
|
|
|
|
++fa;
|
|
if (fa >= argc) usage("Inking rule (-kp) expects more parameters");
|
|
Kstle = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc) usage("Inking rule (-kp) expects more parameters");
|
|
Kstpo = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kp) expects more parameters");
|
|
Kenpo = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kp) expects more parameters");
|
|
Kenle = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kp) expects more parameters");
|
|
Kshap = atof(argv[fa]);
|
|
|
|
if (na[0] == 'q' || na[0] == 'Q') {
|
|
inking = 7; /* Use transfer to dual curve parameter */
|
|
|
|
++fa;
|
|
if (fa >= argc) usage("Inking rule (-kq) expects more parameters");
|
|
Kstle1 = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc) usage("Inking rule (-kq) expects more parameters");
|
|
Kstpo1 = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kq) expects more parameters");
|
|
Kenpo1 = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc) usage("Inking rule (-kq) expects more parameters");
|
|
Kenle1 = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kq) expects more parameters");
|
|
Kshap1 = atof(argv[fa]);
|
|
|
|
}
|
|
break;
|
|
default:
|
|
usage("Unknown parameter after flag -k");
|
|
}
|
|
}
|
|
|
|
else if (argv[fa][1] == 'l') {
|
|
if (na == NULL) usage("No parameter after flag -l");
|
|
fa = nfa;
|
|
tlimit = atoi(na)/100.0;
|
|
}
|
|
|
|
else if (argv[fa][1] == 'L') {
|
|
if (na == NULL) usage("No parameter after flag -L");
|
|
fa = nfa;
|
|
klimit = atoi(na)/100.0;
|
|
}
|
|
else if (argv[fa][1] == 'T') {
|
|
showtac = 1;
|
|
}
|
|
|
|
#ifdef SPTEST
|
|
else if (argv[fa][1] == 'w') {
|
|
sptest = 1;
|
|
}
|
|
else if (argv[fa][1] == 'W') {
|
|
sptest = 2;
|
|
}
|
|
#endif
|
|
/* Viewing conditions */
|
|
else if (argv[fa][1] == 'c') {
|
|
if (na == NULL) usage("No parameter after flag -c");
|
|
fa = nfa;
|
|
#ifdef NEVER
|
|
if (na[0] >= '0' && na[0] <= '9') {
|
|
vc_e = atoi(na);
|
|
} else
|
|
#endif
|
|
if (na[1] != ':') {
|
|
if ((vc_e = xicc_enum_viewcond(NULL, NULL, -2, na, 1, NULL)) == -999)
|
|
usage("Urecognised Enumerated Viewing conditions");
|
|
} else if (na[0] == 's' || na[0] == 'S') {
|
|
if (na[1] != ':')
|
|
usage("Unrecognised parameters after -cs");
|
|
if (na[2] == 'n' || na[2] == 'N') {
|
|
vc_s = vc_none; /* Automatic using Lv */
|
|
} else 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("Unrecognised parameters after -cs:");
|
|
} 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("Unrecognised parameters after -cw");
|
|
} else if (na[0] == 'a' || na[0] == 'A') {
|
|
if (na[1] != ':')
|
|
usage("Unrecognised parameters after -ca");
|
|
vc_a = atof(na+2);
|
|
} else if (na[0] == 'b' || na[0] == 'B') {
|
|
if (na[1] != ':')
|
|
usage("Unrecognised parameters after -cb");
|
|
vc_b = atof(na+2);
|
|
} else if (na[0] == 'l' || na[0] == 'L') {
|
|
if (na[1] != ':')
|
|
usage("Viewing conditions (-cl) missing ':'");
|
|
vc_l = atof(na+2);
|
|
} else if (na[0] == 'f' || na[0] == 'F') {
|
|
if (na[1] != ':')
|
|
usage("Viewing conditions (-cf) missing ':'");
|
|
vc_f = atof(na+2);
|
|
} else if (na[0] == 'g' || na[0] == 'G') {
|
|
double x, y, z;
|
|
if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) {
|
|
vc_gXYZ[0] = x; vc_gXYZ[1] = y; vc_gXYZ[2] = z;
|
|
} else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) {
|
|
vc_gxy[0] = x; vc_gxy[1] = y;
|
|
} else if (sscanf(na+1,":%lf",&x) == 1) {
|
|
vc_g = x;
|
|
} else
|
|
usage("Unrecognised parameters after -cg");
|
|
} else if (na[0] == 'h' || na[0] == 'H') {
|
|
if (na[1] != ':')
|
|
usage("Unrecognised parameters after -ch");
|
|
vc_hkscale = atof(na+2);
|
|
} else if (na[0] == 'm' || na[0] == 'M') {
|
|
double x, y, z;
|
|
if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) {
|
|
vc_Wxyz2[0] = x; vc_Wxyz2[1] = y; vc_Wxyz2[2] = z;
|
|
} else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) {
|
|
vc_Wxyz2[0] = x; vc_Wxyz2[1] = y; vc_Wxyz2[2] = -1;
|
|
} else if (sscanf(na+1,":%lf",&x) == 1) {
|
|
vc_mtaf = x;
|
|
} else
|
|
usage("Unrecognised parameters after -cm");
|
|
} else
|
|
usage("Unrecognised parameters after -c");
|
|
}
|
|
|
|
/* Extra flags */
|
|
else if (argv[fa][1] == 'Y') {
|
|
fa = nfa;
|
|
if (na == NULL)
|
|
usage("Parameter expected after -Y");
|
|
|
|
if (na[0] == 'c') {
|
|
if (na[1] != ':')
|
|
usage("-Y c:fname.cal syntax incorrect");
|
|
strncpy(tcalname,&na[2],MAXNAMEL); tcalname[MAXNAMEL] = '\000';
|
|
} else
|
|
{
|
|
usage("-Y function not recognised");
|
|
}
|
|
}
|
|
|
|
else
|
|
usage("Unknown flag");
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (fa >= argc || argv[fa][0] == '-') usage("Expecting profile file name");
|
|
strncpy(prof_name,argv[fa],MAXNAMEL); prof_name[MAXNAMEL] = '\000';
|
|
|
|
|
|
if (slocwarn) {
|
|
if ((chlp = chrom_locus_poligon(0, icxOT_CIE_1931_2, 0)) == NULL)
|
|
error("chrom_locus_poligon failed");
|
|
}
|
|
|
|
/* Force to XYZ PCS */
|
|
if (absmeas) {
|
|
pcsor = icSigXYZData;
|
|
repYxy = 0;
|
|
repYuv = 0;
|
|
repLCh = 0;
|
|
repJCh = 0;
|
|
repXYZ100 = 0;
|
|
}
|
|
|
|
/* Open up the profile for reading */
|
|
if ((fp = new_icmFileStd_name(&err, prof_name,"r")) == NULL)
|
|
error ("Can't open file '%s' (0x%x, '%s')",prof_name,err.c,err.m);
|
|
|
|
if ((icco = new_icc(&err)) == NULL)
|
|
error ("Creation of ICC object failed (0x%x, '%s')",err.c,err.m);
|
|
|
|
if (dovcgt) {
|
|
if ((rv = icco->read(icco,fp,0)) != 0)
|
|
error ("Can't load ICC file '%s'",prof_name);
|
|
|
|
if ((cal = new_xcal()) == NULL)
|
|
error("new_xcal failed");
|
|
|
|
if ((cal->read_icc(cal, icco)) != 0) {
|
|
error ("ICC file '%s' doesn't contain a 'vcgt' tag",prof_name);
|
|
}
|
|
|
|
icco->del(icco);
|
|
fp->del(fp);
|
|
fp = NULL;
|
|
|
|
/* Get details of conversion (Arguments may be NULL if info not needed) */
|
|
outs = ins = cal->colspace;
|
|
outn = inn = cal->devchan;
|
|
|
|
} else if ((rv = icco->read(icco,fp,0)) == 0) { /* ICC profile */
|
|
|
|
/* Read any user supplied cal curves */
|
|
if (tcalname[0] != '\000') {
|
|
xcal *xc;
|
|
|
|
if ((xc = new_xcal()) == NULL)
|
|
error("new_xcal failed");
|
|
if ((xc->read(xc, tcalname)) != 0)
|
|
error("%s",xc->e.m);
|
|
tcal = xc;
|
|
}
|
|
|
|
if (doplot) {
|
|
|
|
/* Force PCS to be Lab or Jab */
|
|
repJCh = 0;
|
|
repLCh = 0;
|
|
if (pcsor != icxSigJabData)
|
|
pcsor = icSigLabData;
|
|
|
|
if ((invert == 0 && func != icmBwd)
|
|
|| (invert != 0 && func != icmFwd))
|
|
error("Must use -fb or -fif for grey axis plot");
|
|
}
|
|
|
|
if (icco->header->cmmId == icmstr2tag("argl"))
|
|
icco->allowclutPoints256 = 1;
|
|
|
|
if (doplot) {
|
|
if (icco->header->deviceClass != icSigInputClass
|
|
&& icco->header->deviceClass != icSigDisplayClass
|
|
&& icco->header->deviceClass != icSigOutputClass)
|
|
error("Profile must be a device profile to plot neutral axis");
|
|
}
|
|
|
|
/* Lookup a TAC from the B2A */
|
|
if (verb > 1 && icco != NULL) {
|
|
icxCalCBctx ctx;
|
|
icColorSpaceSignature colorSpace;
|
|
double tac;
|
|
double chmax[MAX_CHAN];
|
|
|
|
if (icco->header->deviceClass == icSigLinkClass)
|
|
colorSpace = icco->header->pcs;
|
|
else
|
|
colorSpace = icco->header->colorSpace;
|
|
|
|
tac = icco->get_tac(icco, chmax, NULL, NULL);
|
|
|
|
if (tac <= -1.0)
|
|
printf("No B2A TAC\n");
|
|
else
|
|
printf("B2A TAC = %f, chan max = %s\n",tac,
|
|
icmPdv(icmCSSig2nchan(colorSpace), chmax));
|
|
|
|
/* Should use icxGetLimits() instead ?? */
|
|
|
|
/* Use any device calibration curves that are in the 'targ' .ti3 or given to us */
|
|
ctx.cal = tcal;
|
|
|
|
/* Or use any 'targ' .ti3 device calibration curves */
|
|
if (ctx.cal == NULL)
|
|
ctx.cal = xiccReadCalTag(icco);
|
|
|
|
if (ctx.cal != NULL
|
|
) {
|
|
tac = icco->get_tac(icco, chmax, xiccCalCallback_void, (void *)&ctx);
|
|
|
|
if (tac > 0.0) {
|
|
printf("B2A Final TAC = %f, chan max = %s\n",tac,
|
|
icmPdv(icmCSSig2nchan(colorSpace), chmax));
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/* Wrap with an expanded icc */
|
|
if ((xicco = new_xicc(icco)) == NULL)
|
|
error ("Creation of xicc failed");
|
|
|
|
/* Set or override calibration curves (Note that new_xicc() sets these */
|
|
/* from 'targ' .ti3 by default, but user can override here.) */
|
|
xicco->set_cal_curves(xicco, tcal);
|
|
/* Release our references to them */
|
|
if (tcal)
|
|
tcal->del(tcal);
|
|
|
|
/* Get the actual calibration curves being used by the xicc */
|
|
xicco->get_cal_curves(xicco, &tcal);
|
|
/* Take a references to them */
|
|
if (tcal)
|
|
tcal->ref(tcal);
|
|
|
|
/* Set the default ink limits if not set on command line */
|
|
icxDefaultLimits(xicco, &ink.tlimit, tlimit, &ink.klimit, klimit);
|
|
|
|
if (verb > 1) {
|
|
if (ink.tlimit >= 0.0)
|
|
printf("Final Total ink limit is %3.0f%%\n",100.0 * ink.tlimit);
|
|
if (ink.klimit >= 0.0)
|
|
printf("Final Black ink limit is %3.0f%%\n",100.0 * ink.klimit);
|
|
}
|
|
|
|
ink.KonlyLmin = 0; /* Use normal black as locus Lmin */
|
|
|
|
ink.c.Ksmth = ICXINKDEFSMTH; /* Default curve smoothing */
|
|
ink.c.Kskew = ICXINKDEFSKEW; /* default curve skew */
|
|
ink.x.Ksmth = ICXINKDEFSMTH;
|
|
ink.x.Kskew = ICXINKDEFSKEW;
|
|
|
|
if (inking == 0) { /* Use minimum */
|
|
ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
|
|
ink.c.Kstle = 0.0;
|
|
ink.c.Kstpo = 0.0;
|
|
ink.c.Kenpo = 1.0;
|
|
ink.c.Kenle = 0.0;
|
|
ink.c.Kshap = 1.0;
|
|
} else if (inking == 1) { /* Use 0.5 */
|
|
ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
|
|
ink.c.Kstle = 0.5;
|
|
ink.c.Kstpo = 0.0;
|
|
ink.c.Kenpo = 1.0;
|
|
ink.c.Kenle = 0.5;
|
|
ink.c.Kshap = 1.0;
|
|
} else if (inking == 2) { /* Use maximum */
|
|
ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
|
|
ink.c.Kstle = 1.0;
|
|
ink.c.Kstpo = 0.0;
|
|
ink.c.Kenpo = 1.0;
|
|
ink.c.Kenle = 1.0;
|
|
ink.c.Kshap = 1.0;
|
|
} else if (inking == 3) { /* Use ramp */
|
|
ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
|
|
ink.c.Kstle = 0.0;
|
|
ink.c.Kstpo = 0.0;
|
|
ink.c.Kenpo = 1.0;
|
|
ink.c.Kenle = 1.0;
|
|
ink.c.Kshap = 1.0;
|
|
} else if (inking == 4) { /* Use locus */
|
|
ink.k_rule = icxKlocus;
|
|
} else if (inking == 5) { /* Use K target */
|
|
ink.k_rule = icxKvalue;
|
|
} else if (inking == 6) { /* Use specified curve */
|
|
ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
|
|
ink.c.Kstle = Kstle;
|
|
ink.c.Kstpo = Kstpo;
|
|
ink.c.Kenpo = Kenpo;
|
|
ink.c.Kenle = Kenle;
|
|
ink.c.Kshap = Kshap;
|
|
} else { /* Use dual curves */
|
|
ink.k_rule = locus ? icxKl5l : icxKl5lk; /* Locus or value target */
|
|
ink.c.Kstle = Kstle;
|
|
ink.c.Kstpo = Kstpo;
|
|
ink.c.Kenpo = Kenpo;
|
|
ink.c.Kenle = Kenle;
|
|
ink.c.Kshap = Kshap;
|
|
ink.x.Kstle = Kstle1;
|
|
ink.x.Kstpo = Kstpo1;
|
|
ink.x.Kenpo = Kenpo1;
|
|
ink.x.Kenle = Kenle1;
|
|
ink.x.Kshap = Kshap1;
|
|
}
|
|
|
|
/* Setup the viewing conditions in case we need them */
|
|
if (xicc_enum_viewcond(xicco, &vc, -1, NULL, 0, NULL) == -999) {
|
|
if (camclip || pcsor == icxSigJabData) /* If it will be needed */
|
|
error("%d, %s",xicco->e.c, xicco->e.m);
|
|
}
|
|
|
|
//xicc_dump_viewcond(&vc);
|
|
if (vc_e != -1)
|
|
if (xicc_enum_viewcond(xicco, &vc, vc_e, NULL, 0, NULL) == -999)
|
|
error ("%d, %s",xicco->e.c, xicco->e.m);
|
|
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_l >= 0.0)
|
|
vc.Lv = vc_l;
|
|
if (vc_f >= 0.0)
|
|
vc.Yf = vc_f/100.0;
|
|
if (vc_g >= 0.0)
|
|
vc.Yg = vc_g/100.0;
|
|
if (vc_gXYZ[1] > 0.0) {
|
|
/* Normalise it to current media white */
|
|
vc.Gxyz[0] = vc_gXYZ[0]/vc_gXYZ[1] * vc.Gxyz[1];
|
|
vc.Gxyz[2] = vc_gXYZ[2]/vc_gXYZ[1] * vc.Gxyz[1];
|
|
}
|
|
if (vc_gxy[0] >= 0.0) {
|
|
double x = vc_gxy[0];
|
|
double y = vc_gxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
|
|
double z = 1.0 - x - y;
|
|
vc.Gxyz[0] = x/y * vc.Gxyz[1];
|
|
vc.Gxyz[2] = z/y * vc.Gxyz[1];
|
|
}
|
|
if (vc_hkscale >= 0.0)
|
|
vc.hkscale = vc_hkscale;
|
|
if (vc_mtaf >= 0.0)
|
|
vc.mtaf = vc_mtaf;
|
|
if (vc_Wxyz2[0] >= 0.0 && vc_Wxyz2[1] > 0.0 && vc_Wxyz2[2] >= 0.0) {
|
|
/* Normalise XYZ */
|
|
vc.Wxyz2[0] = vc_Wxyz2[0]/vc_Wxyz2[1] * vc.Wxyz2[1];
|
|
vc.Wxyz2[2] = vc_Wxyz2[2]/vc_Wxyz2[1] * vc.Wxyz2[1];
|
|
}
|
|
if (vc_Wxyz2[0] >= 0.0 && vc_Wxyz2[1] >= 0.0 && vc_Wxyz2[2] < 0.0) {
|
|
/* Convert Yxy to XYZ */
|
|
double x = vc_Wxyz2[0];
|
|
double y = vc_Wxyz2[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
|
|
double z = 1.0 - x - y;
|
|
vc.Wxyz2[0] = x/y * vc.Wxyz2[1];
|
|
vc.Wxyz2[2] = z/y * vc.Wxyz2[1];
|
|
}
|
|
//xicc_dump_viewcond(&vc);
|
|
|
|
/* Get a expanded color conversion object */
|
|
if ((luo = xicco->get_luobj(xicco, 0
|
|
#ifdef USE_NEARCLIP
|
|
| ICX_CLIP_NEAREST
|
|
#endif
|
|
| (intsep ? ICX_INT_SEPARATE : 0)
|
|
| (merge ? ICX_MERGE_CLUT : 0)
|
|
| (camclip ? ICX_CAM_CLIP : 0)
|
|
#ifdef USE_FASTNNSETP
|
|
| ICX_FAST_SETUP
|
|
#endif
|
|
, func, intent, pcsor, order, &vc, &ink)) == NULL)
|
|
error ("%d, %s",xicco->e.c, xicco->e.m);
|
|
|
|
/* Get details of conversion (Arguments may be NULL if info not needed) */
|
|
if (invert)
|
|
luo->spaces(luo, &outs, &outn, &ins, &inn, &alg, NULL, NULL, NULL);
|
|
else
|
|
luo->spaces(luo, &ins, &inn, &outs, &outn, &alg, NULL, NULL, NULL);
|
|
|
|
/* If we can do check on clipped values */
|
|
if (actual != 0) {
|
|
if (invert == 0) {
|
|
if (func == icmFwd || func == icmBwd) {
|
|
if ((aluo = xicco->get_luobj(xicco, ICX_CLIP_NEAREST,
|
|
func == icmFwd ? icmBwd : icmFwd, intent, pcsor, order, &vc, &ink)) == NULL)
|
|
error ("%d, %s",xicco->e.c, xicco->e.m);
|
|
}
|
|
} else {
|
|
aluo = luo; /* We can use the same one */
|
|
}
|
|
}
|
|
|
|
/* More information */
|
|
if (verb > 1) {
|
|
int j;
|
|
double inmin[MAX_CHAN], inmax[MAX_CHAN];
|
|
double outmin[MAX_CHAN], outmax[MAX_CHAN];
|
|
|
|
luo->get_native_ranges(luo, inmin, inmax, outmin, outmax);
|
|
printf("Internal input value range: ");
|
|
for (j = 0; j < inn; j++) {
|
|
if (j > 0)
|
|
fprintf(stdout," %f..%f",inmin[j], inmax[j]);
|
|
else
|
|
fprintf(stdout,"%f..%f",inmin[j], inmax[j]);
|
|
}
|
|
printf("\nInternal output value range: ");
|
|
for (j = 0; j < outn; j++) {
|
|
if (j > 0)
|
|
fprintf(stdout," %f..%f",outmin[j], outmax[j]);
|
|
else
|
|
fprintf(stdout,"%f..%f",outmin[j], outmax[j]);
|
|
}
|
|
|
|
luo->get_ranges(luo, inmin, inmax, outmin,outmax);
|
|
printf("\nInput value range: ");
|
|
for (j = 0; j < inn; j++) {
|
|
if (j > 0)
|
|
fprintf(stdout," %f..%f",inmin[j], inmax[j]);
|
|
else
|
|
fprintf(stdout,"%f..%f",inmin[j], inmax[j]);
|
|
}
|
|
printf("\nOutput value range: ");
|
|
for (j = 0; j < outn; j++) {
|
|
if (j > 0)
|
|
fprintf(stdout," %f..%f",outmin[j], outmax[j]);
|
|
else
|
|
fprintf(stdout,"%f..%f",outmin[j], outmax[j]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
if (repYxy) { /* report Yxy rather than XYZ */
|
|
if (ins == icSigXYZData)
|
|
ins = icSigYxyData;
|
|
if (outs == icSigXYZData)
|
|
outs = icSigYxyData;
|
|
}
|
|
|
|
if (repYuv) { /* report Yuv rather than XYZ */
|
|
if (ins == icSigXYZData)
|
|
ins = icmSigYuvData;
|
|
if (outs == icSigXYZData)
|
|
outs = icmSigYuvData;
|
|
}
|
|
|
|
if (repJCh) { /* report JCh rather than Jab */
|
|
if (ins == icxSigJabData)
|
|
ins = icxSigJChData;
|
|
if (outs == icxSigJabData)
|
|
outs = icxSigJChData;
|
|
}
|
|
if (repLCh) { /* report LCh rather than Lab */
|
|
if (ins == icSigLabData)
|
|
ins = icxSigLChData;
|
|
if (outs == icSigLabData)
|
|
outs = icxSigLChData;
|
|
}
|
|
|
|
#ifdef SPTEST
|
|
if (sptest) {
|
|
icxLuLut *clu;
|
|
double cent[3] = { 50.0, 0.0, 0.0 };
|
|
|
|
if (luo->plu->ttype != icmLutType)
|
|
error("Special test only works on CLUT profiles");
|
|
|
|
clu = (icxLuLut *)luo;
|
|
|
|
clu->clutTable->comp_gamut(clu->clutTable, cent, NULL, spoutf, clu, spioutf, clu);
|
|
rspl_gam_plot(clu->clutTable, "sp_test.wrl", sptest-1);
|
|
exit(0);
|
|
}
|
|
#endif
|
|
|
|
/* If we are doing display absolute measurement PCS */
|
|
if (absmeas) {
|
|
icmXYZArray *wo;
|
|
if ((wo = (icmXYZArray *)icco->read_tag(
|
|
icco, icSigLuminanceTag)) != NULL
|
|
&& wo->ttype == icSigXYZArrayType) {
|
|
dispLuminance = wo->data[0].Y;
|
|
}
|
|
if (verb)
|
|
printf("Display Luminance = %f cd/m^2\n", dispLuminance);
|
|
|
|
if (dispLuminance <= 0.0)
|
|
error("Unable to read display luminance tag from '%s'\n",prof_name);
|
|
}
|
|
|
|
} else { /* See if it's a .cal */
|
|
icco->del(icco);
|
|
icco = NULL;
|
|
fp->del(fp);
|
|
fp = NULL;
|
|
|
|
if ((cal = new_xcal()) == NULL)
|
|
error("new_xcal failed");
|
|
|
|
if ((cal->read(cal, prof_name)) != 0) {
|
|
error ("File '%s' is not an ICC or .cal file",prof_name);
|
|
}
|
|
|
|
/* Get details of conversion (Arguments may be NULL if info not needed) */
|
|
outs = ins = cal->colspace;
|
|
outn = inn = cal->devchan;
|
|
}
|
|
|
|
|
|
/* Sanity check */
|
|
if (absmeas && ins != icSigXYZData && outs != icSigXYZData) {
|
|
error("Must be XYZ PCS space for display absolute measurement");
|
|
}
|
|
|
|
if (verb > 1 && icco != NULL) {
|
|
icmFile *op;
|
|
if ((op = new_icmFileStd_fp(&err, stdout)) == NULL)
|
|
error ("Can't open stdout (0x%x, '%s')",err.c,err.m);
|
|
icco->header->dump(icco->header, op, 1);
|
|
op->del(op);
|
|
}
|
|
|
|
if (doplot) {
|
|
int i, j;
|
|
double xx[XRES];
|
|
double yy[6][XRES];
|
|
double start[3], end[3];
|
|
|
|
if (cal != NULL) {
|
|
for (i = 0; i < XRES; i++) {
|
|
double ival = (double)i/(XRES-1.0);
|
|
|
|
for (j = 0; j < inn; j++)
|
|
in[j] = ival;
|
|
|
|
/* Do the conversion */
|
|
if (func == icmBwd || invert) {
|
|
if ((rv = cal->inv_interp(cal, out, in)) != 0)
|
|
error ("%d, %s",cal->e.c,cal->e.m);
|
|
} else {
|
|
cal->interp(cal, out, in);
|
|
}
|
|
|
|
xx[i] = 100.0 * ival;
|
|
for (j = 0; j < outn; j++)
|
|
yy[j][i] = 100.0 * out[j];
|
|
}
|
|
} else {
|
|
/* Plot from white to black by default */
|
|
luo->efv_wh_bk_points(luo, start, end, NULL);
|
|
|
|
if (pstart[0] == -1000.0)
|
|
icmCpy3(pstart, start);
|
|
|
|
if (pend[0] == -1000.0)
|
|
icmCpy3(pend, end);
|
|
|
|
if (verb) {
|
|
printf("Plotting from white %f %f %f to black %f %f %f\n",
|
|
pstart[0], pstart[1], pstart[2], pend[0], pend[1], pend[2]);
|
|
}
|
|
for (i = 0; i < XRES; i++) {
|
|
double ival = (double)i/(XRES-1.0);
|
|
|
|
/* Input is always Jab or Lab */
|
|
in[0] = ival * pend[0] + (1.0 - ival) * pstart[0];
|
|
in[1] = ival * pend[1] + (1.0 - ival) * pstart[1];
|
|
in[2] = ival * pend[2] + (1.0 - ival) * pstart[2];
|
|
//in[1] = in[2] = 0.0;
|
|
|
|
/* Do the conversion */
|
|
if (invert) {
|
|
if ((rv = luo->inv_lookup(luo, out, in)) > 1)
|
|
error ("%d, %s",xicco->e.c,xicco->e.m);
|
|
//printf("~1 %f: %f %f %f -> %f %f %f %f\n", ival, in[0], in[1], in[2], out[0], out[1], out[2], out[3]);
|
|
} else {
|
|
if ((rv = luo->lookup(luo, out, in)) > 1)
|
|
error ("%d, %s",xicco->e.c,xicco->e.m);
|
|
}
|
|
|
|
xx[i] = 100.0 * ival;
|
|
for (j = 0; j < outn; j++)
|
|
yy[j][i] = 100.0 * out[j];
|
|
}
|
|
//fflush(stdout);
|
|
}
|
|
|
|
/* plot order: Black Red Green Blue Yellow Purple */
|
|
if (outs == icSigRgbData) {
|
|
do_plot6(xx, NULL, yy[0], yy[1], yy[2], NULL, NULL, XRES);
|
|
|
|
} else if (outs == icSigCmykData) {
|
|
do_plot6(xx, yy[3], yy[1], NULL, yy[0], yy[2], NULL, XRES);
|
|
|
|
} else {
|
|
|
|
switch(outn) {
|
|
case 1:
|
|
do_plot6(xx, yy[0], NULL, NULL, NULL, NULL, NULL, XRES);
|
|
break;
|
|
case 2:
|
|
do_plot6(xx, yy[0], yy[1], NULL, NULL, NULL, NULL, XRES);
|
|
break;
|
|
case 3:
|
|
do_plot6(xx, yy[0], yy[1], yy[2], NULL, NULL, NULL, XRES);
|
|
break;
|
|
case 4:
|
|
do_plot6(xx, yy[0], yy[1], yy[2], yy[3], NULL, NULL, XRES);
|
|
break;
|
|
case 5:
|
|
do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], NULL, XRES);
|
|
break;
|
|
case 6:
|
|
do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], XRES);
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
if (slocwarn && outs != icSigXYZData
|
|
&& outs != icSigYxyData
|
|
&& outs != icSigLabData
|
|
&& outs != icxSigLChData) {
|
|
error("Can't warn if outside spectrum locus unless XYZ like space");
|
|
}
|
|
|
|
/* Process colors to translate */
|
|
for (;;) {
|
|
int i,j;
|
|
char *bp, *nbp;
|
|
int outsloc = 0;
|
|
double in_tac = -1.0, out_tac = -1.0;
|
|
double in_ctac = -1.0, out_ctac = -1.0;
|
|
|
|
/* Read in the next line */
|
|
if (fgets(buf, 200, stdin) == NULL)
|
|
break;
|
|
if (buf[0] == '#') {
|
|
if (verb > 0)
|
|
fprintf(stdout,"%s\n",buf);
|
|
continue;
|
|
}
|
|
/* For each input number */
|
|
for (nbp = buf, i = 0; i < MAX_CHAN; i++) {
|
|
bp = nbp;
|
|
uout[i] = out[i] = in[i] = uin[i] = strtod(bp, &nbp);
|
|
if (nbp == bp)
|
|
break; /* Failed */
|
|
}
|
|
if (i == 0)
|
|
break;
|
|
for (; i < MAX_CHAN; i++)
|
|
uout[i] = out[i] = in[i] = uin[i] = 0.0;
|
|
|
|
/* If device data and scale */
|
|
if( ins != icxSigJabData
|
|
&& ins != icxSigJChData
|
|
&& ins != icSigXYZData
|
|
&& ins != icSigLabData
|
|
&& ins != icxSigLChData
|
|
&& ins != icSigLuvData
|
|
&& ins != icSigYCbCrData
|
|
&& ins != icSigYxyData
|
|
&& ins != icSigHsvData
|
|
&& ins != icSigHlsData) {
|
|
if (scale > 0.0) {
|
|
for (i = 0; i < MAX_CHAN; i++)
|
|
in[i] /= scale;
|
|
}
|
|
if (showtac) {
|
|
in_tac = 0.0;
|
|
for (i = 0; i < inn; i++)
|
|
in_tac += in[i];
|
|
|
|
if (tcal != NULL) {
|
|
in_ctac = 0.0;
|
|
for (i = 0; i < inn; i++)
|
|
in_ctac += tcal->interp_ch(tcal, i, in[i]);
|
|
}
|
|
}
|
|
if (inn == 3 && in_tvenc != 0) {
|
|
if (in_tvenc == 1) { /* Video 16-235 range */
|
|
icmRGB_2_VidRGB(in, in);
|
|
} else if (in_tvenc == 2) { /* Rec601 YCbCr */
|
|
icmRec601_RGBd_2_YPbPr(in, in);
|
|
icmRecXXX_YPbPr_2_YCbCr(in, in);
|
|
} else if (in_tvenc == 3) { /* Rec709 YCbCr */
|
|
icmRec709_RGBd_2_YPbPr(in, in);
|
|
icmRecXXX_YPbPr_2_YCbCr(in, in);
|
|
} else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */
|
|
icmRec709_50_RGBd_2_YPbPr(in, in);
|
|
icmRecXXX_YPbPr_2_YCbCr(in, in);
|
|
} else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */
|
|
icmRec2020_NCL_RGBd_2_YPbPr(in, in);
|
|
icmRecXXX_YPbPr_2_YCbCr(in, in);
|
|
} else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */
|
|
icmRec2020_CL_RGBd_2_YPbPr(in, in);
|
|
icmRecXXX_YPbPr_2_YCbCr(in, in);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (repXYZ100 && ins == icSigXYZData) {
|
|
in[0] /= 100.0;
|
|
in[1] /= 100.0;
|
|
in[2] /= 100.0;
|
|
}
|
|
|
|
if (repYxy && ins == icSigYxyData) {
|
|
icmYxy2XYZ(in, in);
|
|
}
|
|
|
|
if (repYuv && ins == icmSigYuvData) {
|
|
icmYuv2XYZ(in, in);
|
|
}
|
|
|
|
/* JCh -> Jab & LCh -> Lab */
|
|
if ((repJCh && ins == icxSigJChData)
|
|
|| (repLCh && ins == icxSigLChData)) {
|
|
double C = in[1];
|
|
double h = in[2];
|
|
in[1] = C * cos(3.14159265359/180.0 * h);
|
|
in[2] = C * sin(3.14159265359/180.0 * h);
|
|
}
|
|
|
|
/* display absolute measurement */
|
|
if (absmeas && ins == icSigXYZData) {
|
|
in[0] /= dispLuminance;
|
|
in[1] /= dispLuminance;
|
|
in[2] /= dispLuminance;
|
|
}
|
|
|
|
/* Do conversion */
|
|
if (cal != NULL) { /* .cal */
|
|
if (func == icmBwd || invert) {
|
|
if ((rv = cal->inv_interp(cal, out, in)) != 0)
|
|
error ("%d, %s",cal->e.c,cal->e.m);
|
|
} else {
|
|
cal->interp(cal, out, in);
|
|
rv = 0;
|
|
}
|
|
|
|
} else { /* ICC */
|
|
if (invert) {
|
|
for (j = 0; j < MAX_CHAN; j++)
|
|
out[j] = in[j]; /* Carry any auxiliary value to out for lookup */
|
|
if ((rv = luo->inv_lookup(luo, out, in)) > 1)
|
|
error ("%d, %s",xicco->e.c,xicco->e.m);
|
|
} else {
|
|
if ((rv = luo->lookup(luo, out, in)) > 1)
|
|
error ("%d, %s",xicco->e.c,xicco->e.m);
|
|
}
|
|
}
|
|
|
|
if (slocwarn) {
|
|
double xyz[3];
|
|
|
|
if (outs == icSigLabData || outs == icxSigLChData)
|
|
icmLab2XYZ(&icmD50, out, xyz);
|
|
else
|
|
icmCpy3(xyz, out);
|
|
|
|
outsloc = icx_outside_spec_locus(chlp, xyz);
|
|
}
|
|
|
|
/* Copy conversion out value so that we can create user values */
|
|
for (i = 0; i < MAX_CHAN; i++)
|
|
uout[i] = out[i];
|
|
|
|
/* display absolute measurement */
|
|
if (absmeas && outs == icSigXYZData) {
|
|
uout[0] *= dispLuminance;
|
|
uout[1] *= dispLuminance;
|
|
uout[2] *= dispLuminance;
|
|
}
|
|
|
|
if (repXYZ100 && outs == icSigXYZData) {
|
|
uout[0] *= 100.0;
|
|
uout[1] *= 100.0;
|
|
uout[2] *= 100.0;
|
|
}
|
|
|
|
if (repYxy && outs == icSigYxyData) {
|
|
icmXYZ2Yxy(uout, uout);
|
|
}
|
|
|
|
if (repYuv && outs == icmSigYuvData) {
|
|
icmXYZ2Yuv(uout, uout);
|
|
}
|
|
|
|
/* Jab -> JCh and Lab -> LCh */
|
|
if ((repJCh && outs == icxSigJChData)
|
|
|| (repLCh && outs == icxSigLChData)) {
|
|
double a = uout[1];
|
|
double b = uout[2];
|
|
uout[1] = sqrt(a * a + b * b);
|
|
uout[2] = (180.0/3.14159265359) * atan2(b, a);
|
|
uout[2] = (uout[2] < 0.0) ? uout[2] + 360.0 : uout[2];
|
|
}
|
|
|
|
/* If device data and scale */
|
|
if( outs != icxSigJabData
|
|
&& outs != icxSigJChData
|
|
&& outs != icSigXYZData
|
|
&& outs != icSigLabData
|
|
&& outs != icxSigLChData
|
|
&& outs != icSigLuvData
|
|
&& outs != icSigYCbCrData
|
|
&& outs != icSigYxyData
|
|
&& outs != icSigHsvData
|
|
&& outs != icSigHlsData) {
|
|
if (outn == 3 && out_tvenc != 0) {
|
|
if (out_tvenc == 1) { /* Video 16-235 range */
|
|
icmVidRGB_2_RGB(uout, uout);
|
|
} else if (out_tvenc == 2) { /* Rec601 YCbCr */
|
|
icmRecXXX_YCbCr_2_YPbPr(uout, uout);
|
|
icmRec601_YPbPr_2_RGBd(uout, uout);
|
|
} else if (out_tvenc == 3) { /* Rec709 1150/60/2:1 YCbCr */
|
|
icmRecXXX_YCbCr_2_YPbPr(uout, uout);
|
|
icmRec709_YPbPr_2_RGBd(uout, uout);
|
|
} else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */
|
|
icmRecXXX_YCbCr_2_YPbPr(uout, uout);
|
|
icmRec709_50_YPbPr_2_RGBd(uout, uout);
|
|
} else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */
|
|
icmRecXXX_YCbCr_2_YPbPr(uout, uout);
|
|
icmRec2020_NCL_YPbPr_2_RGBd(uout, uout);
|
|
} else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */
|
|
icmRecXXX_YCbCr_2_YPbPr(uout, uout);
|
|
icmRec2020_CL_YPbPr_2_RGBd(uout, uout);
|
|
}
|
|
}
|
|
if (showtac || (verb > 0 && tlimit >= 0)) {
|
|
out_tac = 0.0;
|
|
for (i = 0; i < outn; i++)
|
|
out_tac += uout[i];
|
|
|
|
if (tcal != NULL) {
|
|
out_ctac = 0.0;
|
|
for (i = 0; i < outn; i++)
|
|
out_ctac += tcal->interp_ch(tcal, i, uout[i]);
|
|
}
|
|
}
|
|
if (scale > 0.0) {
|
|
for (i = 0; i < MAX_CHAN; i++)
|
|
uout[i] *= scale;
|
|
}
|
|
}
|
|
|
|
/* Output the results */
|
|
if (verb > 0) {
|
|
for (j = 0; j < inn; j++) {
|
|
if (j > 0)
|
|
fprintf(stdout," %f",uin[j]);
|
|
else
|
|
fprintf(stdout,"%f",uin[j]);
|
|
}
|
|
if (verb && in_tac >= 0.0)
|
|
fprintf(stdout," TAC %f",in_tac);
|
|
if (verb && in_ctac >= 0.0)
|
|
fprintf(stdout," calTAC %f",in_ctac);
|
|
|
|
if (cal != NULL)
|
|
printf(" [%s] -> ", icx2str(icmColorSpaceSig, ins));
|
|
else
|
|
printf(" [%s] -> %s -> ", icx2str(icmColorSpaceSig, ins),
|
|
icm2str(icmTransformLookupAlgorithm, alg));
|
|
}
|
|
|
|
for (j = 0; j < outn; j++) {
|
|
if (j > 0)
|
|
fprintf(stdout," %f",uout[j]);
|
|
else
|
|
fprintf(stdout,"%f",uout[j]);
|
|
}
|
|
|
|
if (verb && (out_tac > 0.0 || tlimit >= 0))
|
|
printf(" TAC %f",out_tac);
|
|
if (verb && out_ctac >= 0.0)
|
|
fprintf(stdout," calTAC %f",out_ctac);
|
|
if (verb > 0)
|
|
printf(" [%s]", icx2str(icmColorSpaceSig, outs));
|
|
if (outsloc)
|
|
fprintf(stdout,"(Imaginary)");
|
|
|
|
if (verb == 0 || rv == 0)
|
|
fprintf(stdout,"\n");
|
|
else {
|
|
fprintf(stdout," (clip)\n");
|
|
|
|
/* This probably isn't right - we need to convert */
|
|
/* in[] to Lab to Jab if it is not in that space, */
|
|
/* so we can do a delta E on it. */
|
|
if (actual && aluo != NULL) {
|
|
double cin[MAX_CHAN], de;
|
|
if ((rv = aluo->lookup(aluo, cin, out)) > 1)
|
|
error ("%d, %s",xicco->e.c,xicco->e.m);
|
|
|
|
for (de = 0.0, j = 0; j < inn; j++) {
|
|
de += (cin[j] - in[j]) * (cin[j] - in[j]);
|
|
}
|
|
de = sqrt(de);
|
|
printf("[Actual ");
|
|
for (j = 0; j < inn; j++) {
|
|
if (j > 0)
|
|
fprintf(stdout," %f",cin[j]);
|
|
else
|
|
fprintf(stdout,"%f",cin[j]);
|
|
}
|
|
printf(", deltaE %f]\n",de);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Done with lookup object */
|
|
if (aluo != NULL && aluo != luo)
|
|
luo->del(aluo);
|
|
if (luo != NULL)
|
|
luo->del(luo);
|
|
if (cal != NULL)
|
|
cal->del(cal);
|
|
if (xicco != NULL)
|
|
xicco->del(xicco); /* Expansion wrapper */
|
|
if (icco != NULL)
|
|
icco->del(icco); /* Icc */
|
|
if (fp != NULL)
|
|
fp->del(fp);
|
|
if (tcal)
|
|
tcal->del(tcal);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|