Adds a -u command-line flag to colprof to emit newline-delimited JSON progress events (stage and percent) during ICC profile calculation. Fixes #4
1349 lines
46 KiB
C
1349 lines
46 KiB
C
/*
|
|
* Argyll Color Management System
|
|
* Color Device profile generator.
|
|
*
|
|
* Author: Graeme W. Gill
|
|
* Date: 15/2/97
|
|
*
|
|
* Copyright 1996-2011 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.
|
|
*/
|
|
|
|
/*
|
|
* This program takes in the scattered test chart
|
|
* points, and interpolates them into a gridded
|
|
* forward ICC device profile, as well as creating
|
|
* backward conversions based on the forward grid.
|
|
*
|
|
* Preview profiles are not currently generated.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* TTBD:
|
|
*
|
|
* See also ../gamut/gammap.c TTBD for notes on Colorimetric intent/black handling.
|
|
*
|
|
* Would be nice to allow perceptual black point neutrality vs low L*
|
|
* tradeoff to be tweaked by the user. (see xicc gmm_bendBP and brad value
|
|
* for perceptual version)
|
|
*
|
|
* Should add -ua option, to restore option to force input profile to be
|
|
* absolute intent for all ICC intents.
|
|
*
|
|
* Should allow ICC Device attributes to be set.
|
|
*
|
|
* Add Argyll private tag to record ink limit etc. to automate link parameters.
|
|
* Estimate ink limit from B2A tables if no private tag ?
|
|
* Add used option for black relative
|
|
* Add used option for separate high res reverse tables
|
|
* Fix 400% assumptions for > 4 color devices ?
|
|
*
|
|
* Should allow creating profiles from .MPP directly for <= 4 dev channels.
|
|
* Should allow creating profiles from existing ICC profiles (deprecate revfix ?)
|
|
*
|
|
* Should allow creating profiles >4 channels by providing .MPP for input,
|
|
* dev link .icm for psudo-dev to device & .ti3 for Pseudo-dev to PCS.
|
|
* Note gamut should come from psudo-dev to PCS.
|
|
*
|
|
* The gamut cLUT should be implemented with xicc/rspl
|
|
*/
|
|
|
|
#undef DEBUG
|
|
#undef DO_TIME /* Time the operation */
|
|
|
|
#define verbo stdout
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <ctype.h>
|
|
#include "copyright.h"
|
|
#include "aconfig.h"
|
|
#include "numlib.h"
|
|
#include "cgats.h"
|
|
#include "conv.h"
|
|
#include "xicc.h"
|
|
#include "prof.h"
|
|
#include "ui.h"
|
|
|
|
#define DEFAVGDEV 0.5 /* Default average deviation percentage */
|
|
/* This equates to a uniform added error of +/- 1% */
|
|
|
|
#define DEMPH_DEFAULT 1.0 /* Default dark region emphasis == none */
|
|
|
|
int json_progress = 0;
|
|
|
|
void emit_json_progress(const char *stage, int percent) {
|
|
if (!json_progress) return;
|
|
printf("{\"event\": \"progress\", \"stage\": \"%s\", \"percent\": %d}\n", stage, percent);
|
|
fflush(stdout);
|
|
}
|
|
|
|
/*
|
|
|
|
Flags used:
|
|
|
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
|
upper .... ...... .. ... . ..
|
|
lower .... .. . .. .........
|
|
|
|
*/
|
|
|
|
void usage(char *diag, ...) {
|
|
int i;
|
|
fprintf(stderr,"Create ICC profile, Version %s\n",ARGYLL_VERSION_STR);
|
|
fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
|
|
if (diag != NULL) {
|
|
va_list args;
|
|
fprintf(stderr," Diagnostic: ");
|
|
va_start(args, diag);
|
|
vfprintf(stderr, diag, args);
|
|
va_end(args);
|
|
fprintf(stderr,"\n");
|
|
}
|
|
fprintf(stderr,"usage: %s [-options] inoutfile\n",error_program);
|
|
fprintf(stderr," -v Verbose mode\n");
|
|
fprintf(stderr," -A manufacturer Manufacturer description string\n");
|
|
fprintf(stderr," -M model Model description string\n");
|
|
fprintf(stderr," -D description Profile Description string (Default \"inoutfile\")\n");
|
|
fprintf(stderr," -C copyright Copyright string\n");
|
|
fprintf(stderr," -Z tmnb Attributes: Transparency, Matte, Negative, BlackAndWhite\n");
|
|
fprintf(stderr," -Z prsa Default intent: Perceptual, Rel. Colorimetric, Saturation, Abs. Colorimetric\n");
|
|
fprintf(stderr," -q lmhu Quality - Low, Medium (def), High, Ultra\n");
|
|
// fprintf(stderr," -q fmsu Speed - Fast, Medium (def), Slow, Ultra Slow\n");
|
|
fprintf(stderr," -b [lmhun] Low quality B2A table - or specific B2A quality or none for input device\n");
|
|
// fprintf(stderr," -b [fmsun] B2A Speed - Fast, Medium, Slow, Ultra Slow, None, same as -q (def)\n");
|
|
fprintf(stderr," -ni Don't create input (Device) shaper curves\n");
|
|
fprintf(stderr," -np Don't create input (Device) grid position curves\n");
|
|
fprintf(stderr," -no Don't create output (PCS) shaper curves\n");
|
|
fprintf(stderr," -nc Don't put the input .ti3 data in the profile\n");
|
|
fprintf(stderr," -k zhxr Black Ink generation target: z = zero K,\n");
|
|
fprintf(stderr," h = 0.5 K, x = max K, r = ramp K (def.)\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 parameters Same as -k, but target is K locus rather than K value itself\n");
|
|
fprintf(stderr," -l tlimit override total ink limit, 0 - 400%% (default from .ti3)\n");
|
|
fprintf(stderr," -L klimit override black ink limit, 0 - 100%% (default from .ti3)\n");
|
|
fprintf(stderr," -a lxXYgsmGS Algorithm type override\n");
|
|
fprintf(stderr," l = Lab cLUT (def.), x = XYZ cLUT,\n");
|
|
fprintf(stderr," X = display XYZ cLUT + matrix, Y = display XYZ cLUT + debug matrix\n");
|
|
fprintf(stderr," g = gamma+matrix, s = shaper+matrix, m = matrix only,\n");
|
|
fprintf(stderr," G = single gamma+matrix, S = single shaper+matrix\n");
|
|
fprintf(stderr," -u Emit structured JSON calculation progress to stdout\n");
|
|
fprintf(stderr," -u If input profile, auto scale WP to allow extrapolation\n");
|
|
fprintf(stderr," -ua If input profile, force absolute intent\n");
|
|
fprintf(stderr," -uc If input profile, clip cLUT values above WP\n");
|
|
fprintf(stderr," -u scale If input profile, scale media white point by scale\n");
|
|
fprintf(stderr," -R Restrict white <= 1.0, black and primaries to be +ve\n");
|
|
fprintf(stderr," -V demphasis Degree of dark region cLUT grid emphasis 1.0-4.0 (default %.2f = none)\n",DEMPH_DEFAULT);
|
|
fprintf(stderr," -f [illum] Use Fluorescent Whitening Agent compensation [opt. simulated inst. illum.:\n");
|
|
fprintf(stderr," M0, M1, M2, A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp]\n");
|
|
fprintf(stderr," -i illum Choose illuminant for computation of CIE XYZ from spectral data & FWA:\n");
|
|
fprintf(stderr," A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp\n");
|
|
fprintf(stderr," -o observ Choose CIE Observer for spectral data:\n");
|
|
fprintf(stderr," 1931_2 (def), 1964_10, 2015_2, 2015_10, S&B 1955_2, shaw, J&V 1978_2, or file.cmf\n");
|
|
fprintf(stderr," -r avgdev Average deviation of device+instrument readings as a percentage (default %4.2f%%)\n",DEFAVGDEV);
|
|
/* Research options: */
|
|
/* fprintf(stderr," -r sSMOOTH RSPL or shaper suplimental optimised smoothing factor\n"); */
|
|
/* fprintf(stderr," -r rSMOOTH RSPL or shaper raw underlying smoothing factor\n"); */
|
|
fprintf(stderr," -s src%s|cperc Apply gamut mapping to output profile perceptual B2A table\n",ICC_FILE_EXT);
|
|
fprintf(stderr," for given source space, or compression percentage\n");
|
|
fprintf(stderr," -S src%s|experc Apply gamut mapping to output profile perceptual and\n",ICC_FILE_EXT);
|
|
fprintf(stderr," and saturation B2A table, or expansion percentage\n");
|
|
fprintf(stderr," -nP Use colormetric source gamut to make output profile perceptual table\n");
|
|
fprintf(stderr," -nS Use colormetric source gamut to make output profile saturation table\n");
|
|
fprintf(stderr," -nI Apply inverse gammut mapping to perceptual and saturation AtoB tables\n");
|
|
fprintf(stderr," -g src.gam Use source image gamut as well for output profile gamut mapping\n");
|
|
fprintf(stderr," -p absprof,... Incorporate abstract profile(s) into output tables\n");
|
|
fprintf(stderr," -t intent Override gamut mapping intent for output profile perceptual table:\n");
|
|
fprintf(stderr," -T intent Override gamut mapping intent for output profile saturation table:\n");
|
|
for (i = 0; ; i++) {
|
|
icxGMappingIntent gmi;
|
|
if (xicc_enum_gmapintent(&gmi, i, NULL) == icxIllegalGMIntent)
|
|
break;
|
|
fprintf(stderr," %s\n",gmi.desc);
|
|
}
|
|
fprintf(stderr," -c viewcond set input viewing conditions for output profile %s gamut mapping,\n",icxcam_description(cam_default));
|
|
fprintf(stderr," either an enumerated choice, or a parameter\n");
|
|
fprintf(stderr," -d viewcond set output viewing conditions for output profile %s gamut mapping\n",icxcam_description(cam_default));
|
|
fprintf(stderr," either an enumerated choice, or a parameter\n");
|
|
fprintf(stderr," Also sets out of gamut clipping CAM space.\n");
|
|
fprintf(stderr," either an enumerated choice, or a series of parameters: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," -P Create gamut gammap_p.wrl and gammap_s.wrl diagostics\n");
|
|
fprintf(stderr," -H charTarget Override the default CharTargetTag string\n");
|
|
fprintf(stderr," -O outputfile Override the default output filename.\n");
|
|
fprintf(stderr," -Y c:file.cal Override any calibration info from .ti3\n");
|
|
fprintf(stderr," inoutfile Base name for input.ti3/output%s file\n",ICC_FILE_EXT);
|
|
exit(1);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int fa,nfa,mfa; /* current argument we're looking at */
|
|
#ifdef DO_TIME /* Time the operation */
|
|
clock_t stime, ttime; /* Start and total times */
|
|
#endif
|
|
int verb = 0;
|
|
int iquality = 1; /* A2B quality */
|
|
int oquality = -1; /* B2A quality same as A2B */
|
|
int verify = 0; /* Not used anymore */
|
|
int noisluts = 0; /* No input shaper luts */
|
|
int noipluts = 0; /* No input position luts */
|
|
int nooluts = 0; /* No output shaper luts */
|
|
int nocied = 0; /* No .ti3 CIE data in profile */
|
|
int noptop = 0; /* Use colormetric source gamut to make perceptual table */
|
|
int nostos = 0; /* Use colormetric source gamut to make saturation table */
|
|
int igammap = 0; /* Apply inverse gamut mapping to perc and sat A2B tables */
|
|
int gamdiag = 0; /* Make gamut mapping diagnostic wrl plots */
|
|
int autowpsc = 0; /* 1 = Auto scale the WP to prevent clipping above WP patch */
|
|
/* 2 = Force absolute colorimetric */
|
|
int clipovwp = 0; /* Clip cLUT values above WP */
|
|
int clipprims = 0; /* Clip white, black and primaries */
|
|
double demph = 0.0; /* Emphasise dark region grid resolution in cLUT */
|
|
double iwpscale = -1.0; /* Input white point scale factor */
|
|
int doinextrap = 1; /* Sythesize extra sample points for input device cLUT */
|
|
int doinb2a = 1; /* Create an input device B2A table */
|
|
int inking = 3; /* Default K target ramp K */
|
|
int locus = 0; /* Default K value target */
|
|
double Kstle = 0.0, Kstpo = 0.0, Kenle = 0.0, Kenpo = 0.0, Kshap = 0.0;
|
|
double tlimit = -1.0; /* Total ink limit as a % */
|
|
double klimit = -1.0; /* Black ink limit as a % */
|
|
static char calname[MAXNAMEL+1] = ""; /* calibration file name */
|
|
int fwacomp = 0; /* FWA compensation */
|
|
double avgdev = DEFAVGDEV/100.0; /* Average measurement deviation */
|
|
double smooth = 1.0; /* RSPL Smoothness factor (relative, for verification) */
|
|
int spec = 0; /* Use spectral data flag */
|
|
icxIllumeType tillum = icxIT_none; /* Target/simulated instrument illuminant */
|
|
xspect cust_tillum; /* Custom target/simulated illumination spectrum */
|
|
/* xspect will use illum/cust_illum if tillum == none */
|
|
icxIllumeType illum = icxIT_none; /* Spectral illuminant (defaults to D50) */
|
|
xspect cust_illum; /* Custom illumination spectrum */
|
|
icxObserverType obType = icxOT_none; /* Observer (defaults to 1931 2 degree) */
|
|
xspect custObserver[3]; /* If obType = icxOT_custom */
|
|
int gcompr = 0, gexpr = 0; /* Gamut compression/expansion % instead of Input icc profile */
|
|
static char ipname[MAXNAMEL+1] = ""; /* Input icc profile - enables gamut map */
|
|
static char sgname[MAXNAMEL+1] = ""; /* Image source gamut name */
|
|
static char absstring[3 * MAXNAMEL +1]; /* Storage for absnames */
|
|
char *absnames[3] = { NULL, NULL, NULL }; /* Abstract profile name */
|
|
int sepsat = 0; /* Create separate saturation B2A table */
|
|
icxViewCond ivc_p; /* Input Viewing Parameters for CAM */
|
|
icxViewCond ovc_p; /* Output Viewing Parameters for CAM (enables CAM clip) */
|
|
int ivc_e = -1, ovc_e = -1; /* Enumerated viewing condition */
|
|
icxGMappingIntent pgmi; /* default Perceptual gamut mapping intent */
|
|
int pgmi_set = 0; /* Set by user option */
|
|
icxGMappingIntent sgmi; /* default Saturation gamut mapping intent */
|
|
int sgmi_set = 0; /* Set by user option */
|
|
static char baname[MAXNAMEL+1] = ""; /* Input & Output base name */
|
|
static char inname[MAXNAMEL+1] = ""; /* Input cgats file base name */
|
|
static char outname[MAXNAMEL+1] = ""; /* Output cgats file base name */
|
|
cgats *icg; /* input cgats structure */
|
|
int dti, ti; /* Device type index, Temporary CGATs index */
|
|
prof_atype ptype = prof_default; /* Default for each type of device = cLUT */
|
|
int mtxtoo = 0; /* 1 if matrix tags should be created for Display XYZ cLUT */
|
|
/* 2 if debug matrix tags should be created for Display XYZ cLUT */
|
|
icxTransformCreateType icctype = icxTCT_V2V2; /* ICC profile version to create */
|
|
profxinf xpi; /* Extra profile information */
|
|
|
|
|
|
#ifdef DO_TIME /* Time the operation */
|
|
stime = clock();
|
|
#endif /* DO_TIME */
|
|
error_program = argv[0];
|
|
check_if_not_interactive();
|
|
memset((void *)&xpi, 0, sizeof(profxinf)); /* Init extra profile info to defaults */
|
|
xpi.default_ri = icMaxEnumIntent; /* Default default */
|
|
|
|
/* Init VC overrides so that we know when the've been set */
|
|
ivc_p.Ev = -1;
|
|
ivc_p.Wxyz[0] = -1.0; ivc_p.Wxyz[1] = -1.0; ivc_p.Wxyz[2] = -1.0;
|
|
ivc_p.La = -1.0;
|
|
ivc_p.Yb = -1.0;
|
|
ivc_p.Lv = -1.0;
|
|
ivc_p.Yf = -1.0;
|
|
ivc_p.Yg = -1.0;
|
|
ivc_p.Gxyz[0] = -1.0; ivc_p.Gxyz[1] = -1.0; ivc_p.Gxyz[2] = -1.0;
|
|
ivc_p.hkscale = -1.0;
|
|
ivc_p.mtaf = -1.0;
|
|
ivc_p.Wxyz2[0] = -1.0; ivc_p.Wxyz2[1] = -1.0; ivc_p.Wxyz2[2] = -1.0;
|
|
|
|
ovc_p.Ev = -1;
|
|
ovc_p.Wxyz[0] = -1.0; ovc_p.Wxyz[1] = -1.0; ovc_p.Wxyz[2] = -1.0;
|
|
ovc_p.La = -1.0;
|
|
ovc_p.Yb = -1.0;
|
|
ovc_p.Lv = -1.0;
|
|
ovc_p.Yf = -1.0;
|
|
ovc_p.Yg = -1.0;
|
|
ovc_p.Gxyz[0] = -1.0; ovc_p.Gxyz[1] = -1.0; ovc_p.Gxyz[2] = -1.0;
|
|
ovc_p.hkscale = -1.0;
|
|
ovc_p.mtaf = -1.0;
|
|
ovc_p.Wxyz2[0] = -1.0; ovc_p.Wxyz2[1] = -1.0; ovc_p.Wxyz2[2] = -1.0;
|
|
|
|
xicc_enum_gmapintent(&pgmi, icxPerceptualGMIntent, NULL);
|
|
xicc_enum_gmapintent(&sgmi, icxSaturationGMIntent, NULL);
|
|
|
|
|
|
if (argc <= 1)
|
|
usage("Too few arguments, got %d expect at least %d",argc-1,1);
|
|
|
|
/* 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("Usage requested");
|
|
|
|
else if (argv[fa][1] == 'v')
|
|
verb = 1;
|
|
|
|
/* Manufacturer description string */
|
|
else if (argv[fa][1] == 'A') {
|
|
if (na == NULL) usage("Expect argument to manufacturer description flag -A");
|
|
fa = nfa;
|
|
xpi.deviceMfgDesc = na;
|
|
}
|
|
|
|
/* Model description string */
|
|
else if (argv[fa][1] == 'M') {
|
|
if (na == NULL) usage("Expect argument to model description flag -M");
|
|
fa = nfa;
|
|
xpi.modelDesc = na;
|
|
}
|
|
|
|
/* Profile Description */
|
|
else if (argv[fa][1] == 'D') {
|
|
if (na == NULL) usage("Expect argument to profile description flag -D");
|
|
fa = nfa;
|
|
xpi.profDesc = na;
|
|
}
|
|
|
|
/* Copyright string */
|
|
else if (argv[fa][1] == 'C') {
|
|
if (na == NULL) usage("Expect argument to copyright flag -C");
|
|
fa = nfa;
|
|
xpi.copyright = na;
|
|
}
|
|
|
|
/* CharTarget string */
|
|
else if (argv[fa][1] == 'H') {
|
|
if (na == NULL) usage("Expect argument to CharTarget flag -H");
|
|
fa = nfa;
|
|
xpi.chartarget = na;
|
|
}
|
|
|
|
/* Attribute bits */
|
|
else if (argv[fa][1] == 'Z') {
|
|
int i, j;
|
|
|
|
if (na == NULL) usage("Expect argument to attribute flag -Z");
|
|
fa = nfa;
|
|
for (j = i = 0; j == 0; i++) {
|
|
|
|
switch(na[i]) {
|
|
|
|
/* Attribute */
|
|
case 't':
|
|
case 'T':
|
|
xpi.transparency = 1;
|
|
break;
|
|
case 'm':
|
|
case 'M':
|
|
xpi.matte = 1;
|
|
break;
|
|
case 'n':
|
|
case 'N':
|
|
xpi.negative = 1;
|
|
break;
|
|
case 'b':
|
|
case 'B':
|
|
xpi.blackandwhite = 1;
|
|
break;
|
|
|
|
/* Default intent */
|
|
case 'p':
|
|
case 'P':
|
|
xpi.default_ri = icPerceptual;
|
|
break;
|
|
case 'r':
|
|
case 'R':
|
|
xpi.default_ri = icRelativeColorimetric;
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
xpi.default_ri = icSaturation;
|
|
break;
|
|
case 'a':
|
|
case 'A':
|
|
xpi.default_ri = icAbsoluteColorimetric;
|
|
break;
|
|
default:
|
|
j = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (na[i-1] != '\000') usage("Unknown argument '%c' to attribute flag -Z",na[i-1]);
|
|
}
|
|
|
|
/* Quality */
|
|
else if (argv[fa][1] == 'q') {
|
|
// if (na == NULL) usage("Expect argument to quality flag -q");
|
|
if (na == NULL) usage("Expect argument to speed flag -q");
|
|
fa = nfa;
|
|
switch (na[0]) {
|
|
case 'f': /* Fast */
|
|
case 'l':
|
|
case 'L':
|
|
iquality = 0;
|
|
break;
|
|
case 'm': /* Medium */
|
|
case 'M':
|
|
iquality = 1;
|
|
break;
|
|
case 's': /* Slow */
|
|
case 'h':
|
|
case 'H':
|
|
iquality = 2;
|
|
break;
|
|
case 'u': /* Ultra Slow */
|
|
case 'U':
|
|
iquality = 3;
|
|
break;
|
|
default:
|
|
usage("Unknown argument '%c' to quality flag -q",na[0]);
|
|
// usage("Unknown argument '%c' to speed flag -q",na[0]);
|
|
}
|
|
}
|
|
else if (argv[fa][1] == 'b') {
|
|
if (na != NULL) { /* Got a B2A quaiity */
|
|
fa = nfa;
|
|
switch (na[0]) {
|
|
case 'f': /* Fast */
|
|
case 'l':
|
|
case 'L':
|
|
oquality = 0;
|
|
break;
|
|
case 'm': /* Medium */
|
|
case 'M':
|
|
oquality = 1;
|
|
break;
|
|
case 's': /* Slow */
|
|
case 'h':
|
|
case 'H':
|
|
oquality = 2;
|
|
break;
|
|
case 'u': /* Ultra Slow */
|
|
case 'U':
|
|
oquality = 3;
|
|
break;
|
|
case 'n': /* No B2A for input device */
|
|
case 'N':
|
|
oquality = -2;
|
|
doinb2a = 0;
|
|
break;
|
|
default:
|
|
usage("Unknown argument '%c' to quality flag -q",na[0]);
|
|
}
|
|
} else
|
|
oquality = 0;
|
|
}
|
|
|
|
/* Disable input or output luts, or other details */
|
|
else if (argv[fa][1] == 'n') {
|
|
if (na == NULL) { /* Backwards compatible */
|
|
nooluts = 1;
|
|
} else {
|
|
fa = nfa;
|
|
if (na[0] == 'i')
|
|
noisluts = 1;
|
|
else if (na[0] == 'p')
|
|
noipluts = 1;
|
|
else if (na[0] == 'o')
|
|
nooluts = 1;
|
|
else if (na[0] == 'c')
|
|
nocied = 1;
|
|
else if (na[0] == 'P')
|
|
noptop = 1;
|
|
else if (na[0] == 'S')
|
|
nostos = 1;
|
|
else if (na[0] == 'I')
|
|
igammap = 1;
|
|
else
|
|
usage("Unknown argument '%c' to flag -n",na[0]);
|
|
}
|
|
}
|
|
|
|
else if (argv[fa][1] == 'u') {
|
|
if (argv[fa][2] == 'a') {
|
|
autowpsc = 2;
|
|
clipovwp = 0;
|
|
} else if (argv[fa][2] == 'c') {
|
|
autowpsc = 0;
|
|
clipovwp = 1;
|
|
} else if (argv[fa][2] != '\000') {
|
|
usage("Unknown flag '%c' after -u",argv[fa][2]);
|
|
} else if (na != NULL && (isdigit(na[0]) || (na[0] == '.' && isdigit(na[1])))) {
|
|
fa = nfa;
|
|
iwpscale = atof(na);
|
|
if (iwpscale < 0.0 || iwpscale > 200.0)
|
|
usage("Argument '%s' to flag -u out of range",na);
|
|
autowpsc = 1;
|
|
clipovwp = 0;
|
|
} else {
|
|
json_progress = 1;
|
|
}
|
|
}
|
|
/* Clip primaries */
|
|
else if (argv[fa][1] == 'R') {
|
|
clipprims = 1;
|
|
}
|
|
|
|
/* Degree of dark region emphasis */
|
|
else if (argv[fa][1] == 'V') {
|
|
if (na == NULL) usage(0,"Expected argument to dark emphasis flag -V");
|
|
fa = nfa;
|
|
demph = atof(na);
|
|
if (demph < 1.0 || demph > 3.0)
|
|
usage("Dark weighting argument %f to '-V' is out of range",demph);
|
|
}
|
|
|
|
/* Inking rule */
|
|
else if (argv[fa][1] == 'k'
|
|
|| argv[fa][1] == 'K') {
|
|
if (argv[fa][1] == 'k')
|
|
locus = 0; /* Use K value target */
|
|
else
|
|
locus = 1; /* Use K locus target */
|
|
if (na == NULL) usage("Expect argument to inking flag -k");
|
|
fa = nfa;
|
|
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 'p':
|
|
case 'P':
|
|
inking = 4; /* Use parameter curve */
|
|
++fa;
|
|
if (fa >= argc) usage("Too few arguments to inking flag -kp");
|
|
Kstle = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc) usage("Too few arguments to inking flag -kp");
|
|
Kstpo = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc || argv[fa][0] == '-') usage("Too few arguments to inking flag -kp");
|
|
Kenpo = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc) usage("Too few arguments to inking flag -kp");
|
|
Kenle = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc || argv[fa][0] == '-') usage("Too few arguments to inking flag -kp");
|
|
Kshap = atof(argv[fa]);
|
|
break;
|
|
default:
|
|
usage("Unknown inking rule (-k) argument '%c'",na[0]);
|
|
}
|
|
}
|
|
|
|
/* Total Ink Limit */
|
|
else if (argv[fa][1] == 'l') {
|
|
if (na == NULL) usage("Expected argument to total ink limit flag -l");
|
|
fa = nfa;
|
|
tlimit = atof(na);
|
|
}
|
|
|
|
/* Black Ink Limit */
|
|
else if (argv[fa][1] == 'L') {
|
|
if (na == NULL) usage("Expected argument to black ink limit flag -L");
|
|
fa = nfa;
|
|
klimit = atof(na);
|
|
}
|
|
|
|
/* Algorithm type */
|
|
else if (argv[fa][1] == 'a') {
|
|
if (na == NULL) usage("Expect argument to algorithm flag -a");
|
|
fa = nfa;
|
|
switch (na[0]) {
|
|
case 'l':
|
|
case 'L':
|
|
ptype = prof_clutLab;
|
|
break;
|
|
case 'x':
|
|
ptype = prof_clutXYZ;
|
|
break;
|
|
case 'X':
|
|
mtxtoo = 1;
|
|
ptype = prof_clutXYZ;
|
|
break;
|
|
case 'Y':
|
|
mtxtoo = 2;
|
|
ptype = prof_clutXYZ;
|
|
break;
|
|
case 'g':
|
|
ptype = prof_gammat;
|
|
break;
|
|
case 'G':
|
|
ptype = prof_gam1mat;
|
|
break;
|
|
case 's':
|
|
ptype = prof_shamat;
|
|
break;
|
|
case 'S':
|
|
ptype = prof_sha1mat;
|
|
break;
|
|
case 'm':
|
|
ptype = prof_matonly;
|
|
break;
|
|
default:
|
|
usage("Unknown argument '%c' to algorithm flag -a",na[0] );
|
|
}
|
|
}
|
|
|
|
/* FWA compensation */
|
|
else if (argv[fa][1] == 'f') {
|
|
fwacomp = 1;
|
|
spec = 1; /* Have to use spectral data */
|
|
|
|
if (na != NULL) { /* Argument is present - target/simulated instr. illum. */
|
|
fa = nfa;
|
|
if (strcmp(na, "A") == 0
|
|
|| strcmp(na, "M0") == 0) {
|
|
tillum = icxIT_A;
|
|
} else if (strcmp(na, "C") == 0) {
|
|
tillum = icxIT_C;
|
|
} else if (strcmp(na, "D50") == 0
|
|
|| strcmp(na, "M1") == 0) {
|
|
tillum = icxIT_D50;
|
|
} else if (strcmp(na, "D50M2") == 0
|
|
|| strcmp(na, "M2") == 0) {
|
|
tillum = icxIT_D50M2;
|
|
} else if (strcmp(na, "D65") == 0) {
|
|
tillum = icxIT_D65;
|
|
} else if (strcmp(na, "F5") == 0) {
|
|
tillum = icxIT_F5;
|
|
} else if (strcmp(na, "F8") == 0) {
|
|
tillum = icxIT_F8;
|
|
} else if (strcmp(na, "F10") == 0) {
|
|
tillum = icxIT_F10;
|
|
} else { /* Assume it's a filename */
|
|
inst_meas_type mt;
|
|
|
|
tillum = icxIT_custom;
|
|
if (read_xspect(&cust_tillum, &mt, NULL, na) != 0)
|
|
usage("Failed to read custom target illuminant spectrum in file '%s'",na);
|
|
|
|
if (mt != inst_mrt_none
|
|
&& mt != inst_mrt_emission
|
|
&& mt != inst_mrt_ambient
|
|
&& mt != inst_mrt_emission_flash
|
|
&& mt != inst_mrt_ambient_flash) {
|
|
error("Target illuminant '%s' is wrong measurement type",na);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* CIE Illuminant type */
|
|
else if (argv[fa][1] == 'i') {
|
|
if (na == NULL) usage("Expect argument to illuminant flag -i");
|
|
fa = nfa;
|
|
if (strcmp(na, "A") == 0) {
|
|
spec = 1;
|
|
illum = icxIT_A;
|
|
} else if (strcmp(na, "C") == 0) {
|
|
spec = 1;
|
|
illum = icxIT_C;
|
|
} else if (strcmp(na, "D50") == 0) {
|
|
spec = 1;
|
|
illum = icxIT_D50;
|
|
} else if (strcmp(na, "D50M2") == 0) {
|
|
spec = 1;
|
|
illum = icxIT_D50M2;
|
|
} else if (strcmp(na, "D65") == 0) {
|
|
spec = 1;
|
|
illum = icxIT_D65;
|
|
} else if (strcmp(na, "F5") == 0) {
|
|
spec = 1;
|
|
illum = icxIT_F5;
|
|
} else if (strcmp(na, "F8") == 0) {
|
|
spec = 1;
|
|
illum = icxIT_F8;
|
|
} else if (strcmp(na, "F10") == 0) {
|
|
spec = 1;
|
|
illum = icxIT_F10;
|
|
} else { /* Assume it's a filename */
|
|
inst_meas_type mt;
|
|
|
|
spec = 1;
|
|
illum = icxIT_custom;
|
|
if (read_xspect(&cust_illum, &mt, NULL, na) != 0)
|
|
usage("Failed to read custom illuminant spectrum in file '%s'",na);
|
|
|
|
if (mt != inst_mrt_none
|
|
&& mt != inst_mrt_emission
|
|
&& mt != inst_mrt_ambient
|
|
&& mt != inst_mrt_emission_flash
|
|
&& mt != inst_mrt_ambient_flash) {
|
|
error("CIE illuminant '%s' is wrong measurement type",na);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Spectral Observer type */
|
|
else if (argv[fa][1] == 'o') {
|
|
if (na == NULL) usage("Expect argument to observer flag -o");
|
|
fa = nfa;
|
|
if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */
|
|
spec = 1;
|
|
obType = icxOT_CIE_1931_2;
|
|
} else if (strcmp(na, "1964_10") == 0) { /* Classic 10 degree */
|
|
spec = 1;
|
|
obType = icxOT_CIE_1964_10;
|
|
} else if (strcmp(na, "2015_2") == 0) { /* Latest 2 degree */
|
|
spec = 1;
|
|
obType = icxOT_CIE_2015_2;
|
|
} else if (strcmp(na, "2015_10") == 0) { /* Latest 10 degree */
|
|
spec = 1;
|
|
obType = icxOT_CIE_2015_10;
|
|
} else if (strcmp(na, "1955_2") == 0) { /* Stiles and Burch 1955 2 degree */
|
|
spec = 1;
|
|
obType = icxOT_Stiles_Burch_2;
|
|
} else if (strcmp(na, "1978_2") == 0) { /* Judd and Voss 1978 2 degree */
|
|
spec = 1;
|
|
obType = icxOT_Judd_Voss_2;
|
|
} else if (strcmp(na, "shaw") == 0) { /* Shaw and Fairchilds 1997 2 degree */
|
|
spec = 1;
|
|
obType = icxOT_Shaw_Fairchild_2;
|
|
} else { /* Assume it's a filename */
|
|
obType = icxOT_custom;
|
|
if (read_cmf(custObserver, na) != 0)
|
|
usage(0,"Failed to read custom observer CMF from -o file '%s'",na);
|
|
}
|
|
}
|
|
|
|
/* Average Deviation percentage */
|
|
else if (argv[fa][1] == 'r') {
|
|
if (na == NULL) usage("Expected argument to average deviation flag -r");
|
|
fa = nfa;
|
|
if (na[0] == 's') { /* (relative, for verification) */
|
|
smooth = atof(na+1);
|
|
if (smooth < 0.0)
|
|
usage("Optimised smoothing factor argument to '-rs' must be over 0.0");
|
|
} else if (na[0] == 'r') { /* (absolute, for testing) */
|
|
smooth = atof(na+1);
|
|
if (smooth < 0.0)
|
|
usage("Raw smoothing factor argument to '-rr' must be over 0.0");
|
|
smooth = -smooth; /* Signal raw factor */
|
|
} else {
|
|
avgdev = 0.01 * atof(na);
|
|
if (avgdev < 0.0 || avgdev > 1.0)
|
|
usage("Average Deviation argument must be between 0.0 and 100.0");
|
|
}
|
|
}
|
|
|
|
/* Percetual Source Compression and Perceptual/Saturation Gamut Maping mode enable */
|
|
/* Percetual Source Gamut and Perceptual/Saturation Gamut Maping mode enable */
|
|
else if (argv[fa][1] == 's'
|
|
|| argv[fa][1] == 'S') {
|
|
int sat = 0;
|
|
if (argv[fa][1] == 'S') {
|
|
sepsat = 1;
|
|
sat = 1;
|
|
} else {
|
|
sepsat = 0;
|
|
}
|
|
if (na == NULL)
|
|
usage("Unrecognised argument to source gamut flag -%c",argv[fa][1]);
|
|
fa = nfa;
|
|
{
|
|
char *cp;
|
|
int comp = 0;
|
|
for (cp = na; *cp != '\000'; cp++) {
|
|
if (!isdigit(*cp))
|
|
break;
|
|
}
|
|
/* If general gamut compression/expansion mode */
|
|
if (*cp == '\000') {
|
|
int ratio = atoi(na);
|
|
if (ratio <= 0 || ratio > 100) {
|
|
usage("Gamut -%c %s %d%% must be > 0 and < 100",
|
|
argv[fa][1], sat ? "expansion" : "compression", ratio);
|
|
}
|
|
if (sat) {
|
|
gexpr = ratio;
|
|
if (gcompr == 0)
|
|
gcompr = ratio;
|
|
} else {
|
|
gcompr = ratio;
|
|
if (gexpr == 0)
|
|
gexpr = ratio;
|
|
}
|
|
}
|
|
}
|
|
/* Not compression %, so assume it's a filename. */
|
|
/* We allow both filename and general compression to allow */
|
|
/* for a non-default L mapping. */
|
|
if (gcompr == 0) {
|
|
strncpy(ipname,na,MAXNAMEL); ipname[MAXNAMEL] = '\000';
|
|
}
|
|
}
|
|
|
|
/* Source image gamut */
|
|
else if (argv[fa][1] == 'g') {
|
|
if (na == NULL)
|
|
usage("Unrecognised argument to source image gamut flag -g",argv[fa][1]);
|
|
fa = nfa;
|
|
strncpy(sgname,na,MAXNAMEL); sgname[MAXNAMEL] = '\000';
|
|
}
|
|
|
|
/* Abstract profile */
|
|
else if (argv[fa][1] == 'p') {
|
|
char *f1 = NULL, *f2 = NULL;
|
|
if (na == NULL) usage("Expected abstract profile filename after -p");
|
|
fa = nfa;
|
|
strncpy(absstring,na,MAXNAMEL*3); absstring[MAXNAMEL*3] = '\000';
|
|
if ((f1 = strchr(absstring, ',')) == NULL) { /* Only one profile */
|
|
absnames[2] = absnames[1] = absnames[0] = absstring; /* Duplicate */
|
|
} else { /* At least one comma */
|
|
*f1++ = '\000';
|
|
if ((f2 = strchr(f1, ',')) != NULL) /* Two commas */
|
|
*f2++ = '\000';
|
|
if (*absstring != '\000')
|
|
absnames[0] = absstring;
|
|
if (*f1 != '\000')
|
|
absnames[1] = f1;
|
|
if (f2 != NULL && *f2 != '\000')
|
|
absnames[2] = f2;
|
|
}
|
|
}
|
|
|
|
/* Perceptual Mapping intent override */
|
|
else if (argv[fa][1] == 't') {
|
|
if (na == NULL) usage("Expect argument to perceptul intent override flag -t");
|
|
fa = nfa;
|
|
if (xicc_enum_gmapintent(&pgmi, icxNoGMIntent, na) == icxIllegalGMIntent)
|
|
usage("Unrecognised intent '%s' to perceptual override flag -t",na);
|
|
pgmi_set = 1;
|
|
}
|
|
|
|
/* Saturation Mapping intent override */
|
|
else if (argv[fa][1] == 'T') {
|
|
if (na == NULL) usage("Expect argument to saturation intent override flag -T");
|
|
fa = nfa;
|
|
if (xicc_enum_gmapintent(&sgmi, icxNoGMIntent, na) == icxIllegalGMIntent)
|
|
usage("Unrecognised intent '%s' to saturation override flag -T",na);
|
|
sgmi_set = 1;
|
|
}
|
|
|
|
/* Viewing conditions */
|
|
else if (argv[fa][1] == 'c'
|
|
|| argv[fa][1] == 'd') {
|
|
icxViewCond *vc;
|
|
|
|
if (argv[fa][1] == 'c') {
|
|
vc = &ivc_p;
|
|
} else {
|
|
vc = &ovc_p;
|
|
}
|
|
|
|
if (na == NULL) usage("Viewing conditions flag (-%c) needs an argument",argv[fa][1]);
|
|
fa = nfa;
|
|
if (na[1] != ':') {
|
|
if (vc == &ivc_p) {
|
|
if ((ivc_e = xicc_enum_viewcond(NULL, NULL, -2, na, 1, NULL)) == -999)
|
|
usage("Urecognised Enumerated Viewing conditions '%s'",na);
|
|
} else {
|
|
if ((ovc_e = xicc_enum_viewcond(NULL, NULL, -2, na, 1, NULL)) == -999)
|
|
usage("Urecognised Enumerated Viewing conditions '%s'",na);
|
|
}
|
|
} else if (na[0] == 's' || na[0] == 'S') {
|
|
if (na[1] != ':')
|
|
usage("Viewing conditions (-%cs) missing ':'",argv[fa][1]);
|
|
if (na[2] == 'n' || na[2] == 'N') {
|
|
vc->Ev = vc_none; /* Automatic */
|
|
} else if (na[2] == 'a' || na[2] == 'A') {
|
|
vc->Ev = vc_average;
|
|
} else if (na[2] == 'm' || na[2] == 'M') {
|
|
vc->Ev = vc_dim;
|
|
} else if (na[2] == 'd' || na[2] == 'D') {
|
|
vc->Ev = vc_dark;
|
|
} else if (na[2] == 'c' || na[2] == 'C') {
|
|
vc->Ev = vc_cut_sheet;
|
|
} else
|
|
usage("Viewing condition (-%c) unrecognised surround '%c'",argv[fa][1],na[2]);
|
|
} 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->Wxyz[0] = x; vc->Wxyz[1] = y; vc->Wxyz[2] = -1;
|
|
} else
|
|
usage("Viewing condition (-%cw) unrecognised white point '%s'",argv[fa][1],na+1);
|
|
} else if (na[0] == 'a' || na[0] == 'A') {
|
|
if (na[1] != ':')
|
|
usage("Viewing conditions (-%ca) missing ':'",argv[fa][1]);
|
|
vc->La = atof(na+2);
|
|
} else if (na[0] == 'b' || na[0] == 'B') {
|
|
if (na[1] != ':')
|
|
usage("Viewing conditions (-%cb) missing ':'",argv[fa][1]);
|
|
vc->Yb = atof(na+2)/100.0;
|
|
} else if (na[0] == 'l' || na[0] == 'L') {
|
|
if (na[1] != ':')
|
|
usage("Viewing conditions (-%cl) missing ':'",argv[fa][1]);
|
|
vc->Lv = atof(na+2);
|
|
} else if (na[0] == 'f' || na[0] == 'F') {
|
|
if (na[1] != ':')
|
|
usage("Viewing conditions (-%cf) missing ':'",argv[fa][1]);
|
|
vc->Yf = atof(na+2)/100.0;
|
|
} 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->Gxyz[0] = x; vc->Gxyz[1] = y; vc->Gxyz[2] = -1;
|
|
} else if (sscanf(na+1,":%lf",&x) == 1) {
|
|
vc->Yg = x/100.0;
|
|
} else
|
|
usage("Viewing condition (-%cg) unrecognised flare '%s'",argv[fa][1],na+1);
|
|
|
|
} else if (na[0] == 'h' || na[0] == 'H') {
|
|
if (na[1] != ':')
|
|
usage("Viewing conditions (-%ch) missing ':'",argv[fa][1]);
|
|
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("Viewing condition (-%cm) unrecognised flare '%s'",argv[fa][1],na+1);
|
|
|
|
} else
|
|
usage("Viewing condition (-%c) unrecognised sub flag '%c'",argv[fa][1],na[0]);
|
|
}
|
|
|
|
/* Gammut mapping diagnostic plots */
|
|
else if (argv[fa][1] == 'P')
|
|
gamdiag = 1;
|
|
|
|
/* Output file name */
|
|
else if (argv[fa][1] == 'O') {
|
|
if (na == NULL) usage("Output filename override (-O) needs an argument");
|
|
fa = nfa;
|
|
strncpy(outname,na,MAXNAMEL); outname[MAXNAMEL] = '\000';
|
|
}
|
|
|
|
/* 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(calname,&na[2],MAXNAMEL); calname[MAXNAMEL] = '\000';
|
|
} else
|
|
{
|
|
usage("-Y parameter '%c' not recognised",na[0]);
|
|
}
|
|
}
|
|
|
|
else
|
|
usage("Unknown flag '%c'",argv[fa][1]);
|
|
} else
|
|
break;
|
|
}
|
|
|
|
|
|
/* Get the file name argument */
|
|
if (fa >= argc || argv[fa][0] == '-') usage("Missing input .ti3 and output ICC basename");
|
|
strncpy(baname,argv[fa++],MAXNAMEL-4); baname[MAXNAMEL-4] = '\000';
|
|
if (xpi.profDesc == NULL) {
|
|
char *cp;
|
|
if ((cp = strrchr(baname, '/')) == NULL
|
|
&& (cp = strrchr(baname, '\\')) == NULL)
|
|
cp = baname;
|
|
else
|
|
cp++;
|
|
xpi.profDesc = cp; /* Default description = file name of base path */
|
|
}
|
|
strcpy(inname, baname);
|
|
strcat(inname,".ti3");
|
|
if (outname[0] == '\000') { /* If not overridden */
|
|
strcpy(outname, baname);
|
|
strcat(outname, ICC_FILE_EXT);
|
|
}
|
|
|
|
/* Issue some errors & warnings for strange combinations */
|
|
if (fwacomp && spec == 0)
|
|
error("FWA compensation only works when viewer and/or illuminant selected");
|
|
|
|
if (pgmi_set && (ipname[0] == '\000' && gcompr == 0))
|
|
warning("-t perceptual intent override only works if -s srcprof or -S srcprof is used");
|
|
|
|
if (sgmi_set && (ipname[0] == '\000' && gcompr == 0))
|
|
warning("-T saturation intent override only works if -S srcprof is used");
|
|
|
|
if (sgmi_set && sepsat == 0) { /* Won't do much otherwise */
|
|
if (verb)
|
|
printf("Saturation intent override was set, so adding saturation intent table\n");
|
|
sepsat = 1;
|
|
}
|
|
|
|
if (gamdiag && (ipname[0] == '\000' && gcompr == 0))
|
|
warning("No gamut mapping called for, so -P will produce nothing");
|
|
|
|
if (sgname[0] != '\000' && ipname[0] == '\000')
|
|
warning("-g srcgam will do nothing without -s srcprof or -S srcprof");
|
|
|
|
/* Should warning input viewing condition set and ipname[0] == '\000' */
|
|
|
|
|
|
if (oquality == -1) { /* B2A tables will be used */
|
|
oquality = iquality;
|
|
}
|
|
|
|
/* Open and look at the .ti3 profile patches file */
|
|
icg = new_cgats(); /* Create a CGATS structure */
|
|
icg->add_other(icg, "CTI3"); /* our special input type is Calibration Target Information 3 */
|
|
icg->add_other(icg, "CAL"); /* our special device Calibration s into a .ti3 file..tate */
|
|
|
|
if (icg->read_name(icg, inname))
|
|
error("CGATS file read error : %s",icg->e.m);
|
|
|
|
if (icg->ntables == 0 || icg->t[0].tt != tt_other || icg->t[0].oi != 0)
|
|
error ("Input file isn't a CTI3 format file");
|
|
if (icg->ntables < 1)
|
|
error ("Input file doesn't contain at least one table");
|
|
|
|
/* Read the device/profile type tag */
|
|
if ((dti = icg->find_kword(icg, 0, "DEVICE_CLASS")) < 0)
|
|
error ("Input file doesn't contain keyword DEVICE_CLASS");
|
|
|
|
|
|
/* Issue some errors & warnings for strange combinations for profile type */
|
|
|
|
/* Reflective options when not a reflective profile type */
|
|
if (strcmp(icg->t[0].kdata[dti],"DISPLAY") == 0
|
|
|| strcmp(icg->t[0].kdata[dti],"EMISINPUT") == 0) {
|
|
if (illum != icxIT_none)
|
|
warning("-i illuminant ignored for emissive reference type");
|
|
if (fwacomp != 0)
|
|
warning("-f FWA compensation ignored for emissive reference type");
|
|
|
|
fwacomp = 0;
|
|
tillum = icxIT_none;
|
|
}
|
|
|
|
/* Set defaults */
|
|
if (illum == icxIT_none)
|
|
illum = icxIT_D50;
|
|
|
|
if (obType == icxOT_none)
|
|
obType = icxOT_CIE_1931_2;
|
|
|
|
/* See if CIE is actually available - some sources of .TI3 don't provide it */
|
|
if (!spec
|
|
&& icg->find_field(icg, 0, "LAB_L") < 0
|
|
&& icg->find_field(icg, 0, "XYZ_X") < 0) {
|
|
|
|
if (icg->find_kword(icg, 0, "SPECTRAL_BANDS") < 0)
|
|
error("Neither CIE nor spectral data found in file '%s'",inname);
|
|
|
|
/* Switch to using spectral information */
|
|
if (verb)
|
|
printf("No CIE data found, switching to spectral with standard observer & D50\n");
|
|
spec = 1;
|
|
illum = icxIT_D50;
|
|
obType = icxOT_CIE_1931_2;
|
|
}
|
|
|
|
/* If we requested spectral, check that it is available */
|
|
if (spec) {
|
|
if (icg->find_kword(icg, 0, "SPECTRAL_BANDS") < 0)
|
|
error ("Requested spectral interpretation when data not available");
|
|
}
|
|
|
|
/* If not set in arguments, set default demph from .ti1 or default none */
|
|
if (demph == 0.0) {
|
|
if ((ti = icg->find_kword(icg, 0, "DARK_REGION_EMPHASIS")) >= 0) {
|
|
demph = atof(icg->t[0].kdata[ti]);
|
|
demph = pow(demph, 0.7); /* Reduce intensity */
|
|
if (verb)
|
|
printf("Dark emphasis factor %f from targen\n",demph);
|
|
} else {
|
|
demph = DEMPH_DEFAULT;
|
|
}
|
|
}
|
|
|
|
/* check the device class, and call function to create profile. */
|
|
if (strcmp(icg->t[0].kdata[dti],"OUTPUT") == 0) { /* i.e. printer */
|
|
int hascal = 0; /* nz if there is a .cal in the .ti3 */
|
|
double filimit = - 1.0; /* file ink limit */
|
|
icxInk ink; /* Ink parameters */
|
|
|
|
/* Check if there is a .cal in the .ti3 or one provided in command line */
|
|
{
|
|
int oi, tab;
|
|
|
|
if ((oi = icg->get_oi(icg, "CAL")) >= 0) { /* CAL is registered */
|
|
for (tab = 0; tab < icg->ntables; tab++) {
|
|
if (icg->t[tab].tt == tt_other && icg->t[tab].oi == oi) {
|
|
hascal = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (calname[0] != '\000')
|
|
hascal = 1;
|
|
}
|
|
|
|
|
|
/* Try and grab appropriate ink limit from the .ti3 file. */
|
|
if (hascal && (ti = icg->find_kword(icg, 0, "FINAL_TOTAL_INK_LIMIT")) >= 0) {
|
|
filimit = atoi(icg->t[0].kdata[ti]);
|
|
} else if ((ti = icg->find_kword(icg, 0, "TOTAL_INK_LIMIT")) >= 0) {
|
|
filimit = atoi(icg->t[0].kdata[ti]);
|
|
}
|
|
|
|
/* Check against user set limit */
|
|
if (filimit > 0.0 && filimit <= 400.0) { /* File has limit */
|
|
if (tlimit > 0.0 && tlimit <= 400.0) { /* User has specified limit as option */
|
|
if (filimit < tlimit) {
|
|
warning("Ink limit is greater than original chart! (%.0f%% > %.0f%%)",tlimit,filimit);
|
|
}
|
|
} else { /* Use file limit */
|
|
if (filimit > 80.0)
|
|
tlimit = (int)filimit - 10.0; /* Rule of thumb - 10% below chart maximum */
|
|
else
|
|
tlimit = (int)filimit;
|
|
}
|
|
}
|
|
|
|
/* (Note that this isn't set by any of the Argyll tools currently, */
|
|
/* but can be edited manually into a .ti3 file...) */
|
|
if ((ti = icg->find_kword(icg, 0, "BLACK_INK_LIMIT")) >= 0) {
|
|
int kmax;
|
|
kmax = atoi(icg->t[0].kdata[ti]);
|
|
if (kmax > 0 && kmax <= 100.0) {
|
|
if (klimit >= 0 && klimit <= 100.0) { /* User has specified limit as option */
|
|
if (kmax < klimit) {
|
|
warning("Black ink limit greater than original chart! (%d%% > %d%%)",klimit,kmax);
|
|
}
|
|
} else {
|
|
klimit = (int)kmax;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tlimit >= 0 && tlimit < 400.0) {
|
|
if (verb) {
|
|
if (hascal)
|
|
printf("Final total ink limit being used is %.0f%%\n",tlimit);
|
|
else
|
|
printf("Total ink limit being used is %.0f%%\n",tlimit);
|
|
}
|
|
ink.tlimit = tlimit/100.0; /* Set a total ink limit */
|
|
} else {
|
|
if (verb)
|
|
printf("No total ink limit being used\n");
|
|
ink.tlimit = -1.0; /* Don't use a limit */
|
|
}
|
|
|
|
if (klimit >= 0.0 && klimit < 100.0) {
|
|
if (verb)
|
|
printf("Black ink limit being used is %.0f%%\n",klimit);
|
|
ink.klimit = klimit/100.0; /* Set a black ink limit */
|
|
} else {
|
|
if (verb)
|
|
printf("No black ink limit being used\n");
|
|
ink.klimit = -1.0; /* Don't use a limit */
|
|
}
|
|
|
|
ink.KonlyLmin = 0; /* Use normal black Lmin for locus */
|
|
ink.c.Ksmth = ICXINKDEFSMTH; /* default black curve smoothing */
|
|
ink.c.Kskew = ICXINKDEFSKEW; /* default black curve skew */
|
|
ink.x.Ksmth = ICXINKDEFSMTH;
|
|
ink.x.Kskew = ICXINKDEFSKEW;
|
|
|
|
if (inking == 0) { /* Use minimum */
|
|
ink.k_rule = locus ? icxKluma5 : icxKluma5k;
|
|
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;
|
|
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;
|
|
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;
|
|
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 { /* Use specified curve */
|
|
ink.k_rule = locus ? icxKluma5 : icxKluma5k;
|
|
ink.c.Kstle = Kstle;
|
|
ink.c.Kstpo = Kstpo;
|
|
ink.c.Kenpo = Kenpo;
|
|
ink.c.Kenle = Kenle;
|
|
ink.c.Kshap = Kshap;
|
|
}
|
|
|
|
if (ptype == prof_default)
|
|
ptype = prof_clutLab;
|
|
else if (ptype != prof_clutLab && ptype != prof_clutXYZ) {
|
|
error ("Output profile can only be a cLUT algorithm");
|
|
}
|
|
|
|
if (autowpsc == 1)
|
|
error ("Input auto WP scale mode isn't applicable to an output device");
|
|
if (autowpsc == 2)
|
|
error ("Force absolute colorimetric isn't applicable to an output device");
|
|
if (clipovwp)
|
|
error ("Input cLUT clipping above WP mode isn't applicable to an output device");
|
|
|
|
make_output_icc(ptype, 0, icctype, verb, iquality, oquality,
|
|
noisluts, noipluts, nooluts, nocied, noptop, nostos, igammap,
|
|
gamdiag, verify, clipprims, iwpscale,
|
|
&ink, inname, outname, icg,
|
|
spec, tillum, &cust_tillum, illum, &cust_illum, obType, custObserver,
|
|
fwacomp, smooth, avgdev, 1.0,
|
|
gcompr, gexpr,
|
|
ipname[0] != '\000' ? ipname : NULL,
|
|
sgname[0] != '\000' ? sgname : NULL,
|
|
absnames,
|
|
sepsat, &ivc_p, &ovc_p, ivc_e, ovc_e,
|
|
&pgmi, &sgmi, &xpi,
|
|
calname
|
|
);
|
|
|
|
/* Scanner, Camera with reflective target, or Camera with emissive target */
|
|
} else if (strcmp(icg->t[0].kdata[dti],"INPUT") == 0
|
|
|| strcmp(icg->t[0].kdata[dti],"EMISINPUT") == 0) {
|
|
|
|
int emis = strcmp(icg->t[0].kdata[dti],"EMISINPUT") == 0;
|
|
|
|
if (mtxtoo != 0) {
|
|
if (mtxtoo == 1)
|
|
warning("-aX not applicable to input profile, using -ax");
|
|
else
|
|
warning("-aY not applicable to input profile, using -ax");
|
|
}
|
|
|
|
if (ptype == prof_default)
|
|
ptype = prof_clutLab; /* For best possible quality */
|
|
|
|
if (clipovwp && ptype != prof_clutLab && ptype != prof_clutXYZ)
|
|
error ("Input cLUT clipping above WP mode isn't applicable to a matrix profile");
|
|
|
|
make_input_icc(ptype, icctype, verb, iquality, oquality, noisluts, noipluts, nooluts,
|
|
nocied, verify, autowpsc, clipovwp, iwpscale, doinb2a, doinextrap, clipprims,
|
|
inname, outname, icg, emis, spec, illum, &cust_illum, obType, custObserver,
|
|
smooth, avgdev, &xpi);
|
|
|
|
/* Display or Projector */
|
|
} else if (strcmp(icg->t[0].kdata[dti],"DISPLAY") == 0) {
|
|
|
|
if (autowpsc)
|
|
error ("Input auto WP scale mode isn't applicable to an output device");
|
|
if (clipovwp)
|
|
error ("Input cLUT clipping above WP mode isn't applicable to an output device");
|
|
|
|
if (fwacomp)
|
|
error ("FWA compensation isn't applicable to a display device");
|
|
|
|
if (ptype == prof_default)
|
|
ptype = prof_clutLab; /* ?? or should it default to prof_shamat ?? */
|
|
|
|
/* If a source gamut is provided for a Display, then a V2.4.0 profile will be created */
|
|
make_output_icc(ptype, mtxtoo, icctype, verb, iquality, oquality,
|
|
noisluts, noipluts, nooluts, nocied, noptop, nostos, igammap,
|
|
gamdiag, verify, clipprims, iwpscale,
|
|
NULL, inname, outname, icg,
|
|
spec, icxIT_none, NULL, illum, &cust_illum, obType, custObserver,
|
|
0, smooth, avgdev, demph,
|
|
gcompr, gexpr,
|
|
ipname[0] != '\000' ? ipname : NULL,
|
|
sgname[0] != '\000' ? sgname : NULL,
|
|
absnames,
|
|
sepsat, &ivc_p, &ovc_p, ivc_e, ovc_e,
|
|
&pgmi, &sgmi, &xpi,
|
|
NULL
|
|
);
|
|
|
|
} else
|
|
error ("Input file keyword DEVICE_CLASS has unknown value");
|
|
|
|
icg->del(icg); /* Clean up */
|
|
|
|
#ifdef DO_TIME /* Time the operation */
|
|
ttime = clock() - stime;
|
|
printf("Exectution time = %f seconds\n",(double)ttime/(double)CLOCKS_PER_SEC);
|
|
#endif /* DO_TIME */
|
|
|
|
return 0;
|
|
}
|
|
|
|
|