From 22f703cab05b7cd368f4de9e03991b7664dc5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 13:56:46 +0200 Subject: Initial import of argyll version 1.5.1-8 --- profile/colprof.c | 1116 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1116 insertions(+) create mode 100644 profile/colprof.c (limited to 'profile/colprof.c') diff --git a/profile/colprof.c b/profile/colprof.c new file mode 100644 index 0000000..99e02cf --- /dev/null +++ b/profile/colprof.c @@ -0,0 +1,1116 @@ +/* + * Argyll Color Correction 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. + * + * The gamut cLUT should be implemented with xicc/rspl + */ + +/* + * TTBD: + * 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. + */ + +#undef DEBUG +#undef DO_TIME /* Time the operation */ + +#define verbo stdout + +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#include "numlib.h" +#include "cgats.h" +#include "xicc.h" +#include "prof.h" + +#define DEFAVGDEV 0.5 /* Default average deviation percentage */ + /* This equates to a uniform added error of +/- 1% */ + +/* + + 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," -y Verify A2B profile\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 value 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 lxXgsmGS Algorithm type override\n"); + fprintf(stderr," l = Lab cLUT (def.), x = XYZ cLUT, X = display XYZ cLUT + 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"); +// Development - not supported +// fprintf(stderr," -I ver Set ICC profile version > 2.2.0\n"); +// fprintf(stderr," ver = 4, Enable ICC V4 creation\n"); + fprintf(stderr," -u If input profile, auto scale WP to allow extrapolation\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," -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, S&B 1955_2, shaw, J&V 1978_2\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 Apply gamut mapping to output profile perceptual B2A table for given source space\n",ICC_FILE_EXT); + fprintf(stderr," -S src%s Apply gamut mapping to output profile perceptual and saturation B2A table\n",ICC_FILE_EXT); + 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," -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," -O outputfile Override the default output filename.\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; + 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 gamdiag = 0; /* Make gamut mapping diagnostic wrl plots */ + int autowpsc = 0; /* Auto scale the WP to prevent clipping above WP patch */ + int clipovwp = 0; /* Clip cLUT values above WP */ + int clipprims = 0; /* Clip white, black and primaries */ + 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; + int tlimit = -1; /* Total ink limit as a % */ + int klimit = -1; /* Black ink limit as a % */ + 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 */ + icxIllumeType illum = icxIT_D50; /* Spectral defaults */ + xspect cust_illum; /* Custom illumination spectrum */ + icxObserverType observ = icxOT_CIE_1931_2; /* The classic observer */ + char ipname[MAXNAMEL+1] = ""; /* Input icc profile - enables gamut map */ + char sgname[MAXNAMEL+1] = ""; /* Image source gamut name */ + 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 */ + char baname[MAXNAMEL+1] = ""; /* Input & Output base name */ + char inname[MAXNAMEL+1] = ""; /* Input cgats file base name */ + char outname[MAXNAMEL+1] = ""; /* Output cgats file base name */ + cgats *icg; /* input cgats structure */ + int ti; /* Temporary CGATs index */ + prof_atype ptype = prof_default; /* Default for each type of device */ + int mtxtoo = 0; /* NZ if matrix tags should be created for Display XYZ cLUT */ + icmICCVersion iccver = icmVersionDefault; /* 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.Fxyz[0] = -1.0; ivc_p.Fxyz[1] = -1.0; ivc_p.Fxyz[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.Fxyz[0] = -1.0; ovc_p.Fxyz[1] = -1.0; ovc_p.Fxyz[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' || argv[fa][1] == 'V') + verb = 1; + + /* Manufacturer description string */ + else if (argv[fa][1] == 'A') { + fa = nfa; + if (na == NULL) usage("Expect argument to manufacturer description flag -A"); + xpi.deviceMfgDesc = na; + } + + /* Model description string */ + else if (argv[fa][1] == 'M') { + fa = nfa; + if (na == NULL) usage("Expect argument to model description flag -M"); + xpi.modelDesc = na; + } + + /* Profile Description */ + else if (argv[fa][1] == 'D') { + fa = nfa; + if (na == NULL) usage("Expect argument to profile description flag -D"); + xpi.profDesc = na; + } + + /* Copyright string */ + else if (argv[fa][1] == 'C') { + fa = nfa; + if (na == NULL) usage("Expect argument to copyright flag -C"); + xpi.copyright = na; + } + + /* Attribute bits */ + else if (argv[fa][1] == 'Z') { + int i, j; + + fa = nfa; + if (na == NULL) usage("Expect argument to attribute flag -Z"); + 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' || argv[fa][1] == 'Q') { + fa = nfa; +// if (na == NULL) usage("Expect argument to quality flag -q"); + if (na == NULL) usage("Expect argument to speed flag -q"); + 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; + } + + else if (argv[fa][1] == 'B') { + oquality = -2; + doinb2a = 0; + } + + else if (argv[fa][1] == 'y' || argv[fa][1] == 'Y') + verify = 1; + + /* Disable input or output luts */ + else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') { + fa = nfa; + if (na == NULL) { /* Backwards compatible */ + nooluts = 1; + } else { + 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 + usage("Unknown argument '%c' to flag -n",na[0]); + } + } + + else if (argv[fa][1] == 'u') { + autowpsc = 1; + clipovwp = 0; + 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 (argv[fa][1] == 'U') { + fa = nfa; + if (na == NULL) usage("Expected argument to input white point scale flag -U"); + iwpscale = atof(na); + if (iwpscale < 0.0 || iwpscale > 100.0) + usage("Argument '%s' to flag -U out of range",na); + } + /* Clip primaries */ + else if (argv[fa][1] == 'R') { + clipprims = 1; + } + + /* Inking rule */ + else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') { + fa = nfa; + if (na == NULL) usage("Expect argument to inking flag -k"); + if (argv[fa][1] == 'k') + locus = 0; /* Use K value target */ + else + locus = 1; /* Use 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 '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') { + fa = nfa; + if (na == NULL) usage("Expected argument to total ink limit flag -l"); + tlimit = atoi(na); + } + + /* Black Ink Limit */ + else if (argv[fa][1] == 'L') { + fa = nfa; + if (na == NULL) usage("Expected argument to black ink limit flag -L"); + klimit = atoi(na); + } + + /* Algorithm type */ + else if (argv[fa][1] == 'a' || argv[fa][1] == 'A') { + fa = nfa; + if (na == NULL) usage("Expect argument to algorithm flag -a"); + switch (na[0]) { + case 'l': + case 'L': + ptype = prof_clutLab; + break; + case 'X': + mtxtoo = 1; + /* Fall though */ + case 'x': + 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] ); + } + } + /* Profile version */ + else if (argv[fa][1] == 'I') { + fa = nfa; + if (na == NULL) usage("Expect argument to version flag -I"); + switch (na[0]) { + case '4': + iccver = icmVersion4_1; + break; + default: + usage("Unknown argument '%c' to version flag -I",na[0] ); + } + } + + /* FWA compensation */ + else if (argv[fa][1] == 'f') { + fwacomp = 1; + + if (na != NULL) { /* Argument is present - target/simulated instr. illum. */ + fa = nfa; + if (strcmp(na, "A") == 0 + || strcmp(na, "M0") == 0) { + spec = 1; + tillum = icxIT_A; + } else if (strcmp(na, "C") == 0) { + spec = 1; + tillum = icxIT_C; + } else if (strcmp(na, "D50") == 0 + || strcmp(na, "M1") == 0) { + spec = 1; + tillum = icxIT_D50; + } else if (strcmp(na, "D50M2") == 0 + || strcmp(na, "M2") == 0) { + spec = 1; + tillum = icxIT_D50M2; + } else if (strcmp(na, "D65") == 0) { + spec = 1; + tillum = icxIT_D65; + } else if (strcmp(na, "F5") == 0) { + spec = 1; + tillum = icxIT_F5; + } else if (strcmp(na, "F8") == 0) { + spec = 1; + tillum = icxIT_F8; + } else if (strcmp(na, "F10") == 0) { + spec = 1; + tillum = icxIT_F10; + } else { /* Assume it's a filename */ + spec = 1; + tillum = icxIT_custom; + if (read_xspect(&cust_tillum, na) != 0) + usage("Failed to read custom target illuminant spectrum in file '%s'",na); + } + } + } + + /* Spectral Illuminant type */ + else if (argv[fa][1] == 'i') { + fa = nfa; + if (na == NULL) usage("Expect argument to illuminant flag -i"); + 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 */ + spec = 1; + illum = icxIT_custom; + if (read_xspect(&cust_illum, na) != 0) + usage("Failed to read custom illuminant spectrum in file '%s'",na); + } + } + + /* Spectral Observer type */ + else if (argv[fa][1] == 'o') { + fa = nfa; + if (na == NULL) usage("Expect argument to observer flag -o"); + if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */ + spec = 1; + observ = icxOT_CIE_1931_2; + } else if (strcmp(na, "1964_10") == 0) { /* Classic 10 degree */ + spec = 1; + observ = icxOT_CIE_1964_10; + } else if (strcmp(na, "1955_2") == 0) { /* Stiles and Burch 1955 2 degree */ + spec = 1; + observ = icxOT_Stiles_Burch_2; + } else if (strcmp(na, "1978_2") == 0) { /* Judd and Voss 1978 2 degree */ + spec = 1; + observ = icxOT_Judd_Voss_2; + } else if (strcmp(na, "shaw") == 0) { /* Shaw and Fairchilds 1997 2 degree */ + spec = 1; + observ = icxOT_Shaw_Fairchild_2; + } else + usage("Unrecognised argument '%s' to observer flag -o",na); + } + + + /* Average Deviation percentage */ + else if (argv[fa][1] == 'r') { + fa = nfa; + if (na == NULL) usage("Expected argument to average deviation flag -r"); + 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 Gamut and Perceptual/Saturation Gamut Maping mode enable */ + else if (argv[fa][1] == 's' || argv[fa][1] == 'S') { + if (argv[fa][1] == 'S') + sepsat = 1; + if (na == NULL) + usage("Unrecognised argument to source gamut flag -%c",argv[fa][1]); + + fa = nfa; + strncpy(ipname,na,MAXNAMEL); ipname[MAXNAMEL] = '\000'; + } + + /* Source image gamut */ + else if (argv[fa][1] == 'g' || 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') { + fa = nfa; + if (na == NULL) usage("Expect argument to perceptul intent override flag -t"); + 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') { + fa = nfa; + if (na == NULL) usage("Expect argument to saturation intent override flag -T"); + 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; + } + + fa = nfa; + if (na == NULL) usage("Viewing conditions flag (-c) needs an argument"); + 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 ':'"); + 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'",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; + } else + usage("Viewing condition (-cw) unrecognised white point '%s'",na+1); + } else if (na[0] == 'a' || na[0] == 'A') { + if (na[1] != ':') + usage("Viewing conditions (-ca) missing ':'"); + vc->La = atof(na+2); + } else if (na[0] == 'b' || na[0] == 'B') { + if (na[1] != ':') + usage("Viewing conditions (-cb) missing ':'"); + vc->Yb = atof(na+2)/100.0; + } else if (na[0] == 'l' || na[0] == 'L') { + if (na[1] != ':') + usage("Viewing conditions (-[cd]l) missing ':'"); + vc->Lv = atof(na+2); + } else if (na[0] == 'f' || na[0] == 'F') { + double x, y, z; + if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) { + vc->Fxyz[0] = x; vc->Fxyz[1] = y; vc->Fxyz[2] = z; + } else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) { + vc->Fxyz[0] = x; vc->Fxyz[1] = y; + } else if (sscanf(na+1,":%lf",&x) == 1) { + vc->Yf = x/100.0; + } else + usage("Viewing condition (-cf) unrecognised flare '%s'",na+1); + } else + usage("Viewing condition (-c) unrecognised sub flag '%c'",na[0]); + } + + /* Gammut mapping diagnostic plots */ + else if (argv[fa][1] == 'P') + gamdiag = 1; + + /* Output file name */ + else if (argv[fa][1] == 'O') { + fa = nfa; + if (na == NULL) usage("Output filename override (-O) needs an argument"); + strncpy(outname,na,MAXNAMEL); outname[MAXNAMEL] = '\000'; + } + + 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) + xpi.profDesc = baname; /* Default description */ + 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') + warning("-t perceptual intent override only works if -s srcprof or -S srcprof is used"); + + if (sgmi_set && ipname[0] == '\000') + 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') + 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"); + + 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 state */ + + if (icg->read_name(icg, inname)) + error("CGATS file read error : %s",icg->err); + + if (icg->ntables == 0 || icg->t[0].tt != tt_other || icg->t[0].oi != 0) + error ("Input file isn't a CTI3 format file"); + if (icg->ntables < 1) + error ("Input file doesn't contain at least one table"); + + /* 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; + observ = 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"); + } + + /* read the device class, and call function to create profile. */ + if ((ti = icg->find_kword(icg, 0, "DEVICE_CLASS")) < 0) + error ("Input file doesn't contain keyword DEVICE_CLASS"); + + if (strcmp(icg->t[0].kdata[ti],"OUTPUT") == 0) { + icxInk ink; /* Ink parameters */ + + if ((ti = icg->find_kword(icg, 0, "TOTAL_INK_LIMIT")) >= 0) { + int imax; + imax = atoi(icg->t[0].kdata[ti]); + if (imax > 0 && imax <= 400.0) { + if (tlimit > 0 && tlimit <= 400.0) { /* User has specified limit as option */ + if (imax < tlimit) { + warning("Ink limit greater than original chart! (%d%% > %d%%)",tlimit,imax); + } + } else { + if (imax > 80.0) + tlimit = (int)imax - 10; /* Rule of thumb - 10% below chart maximum */ + else + tlimit = (int)imax; + } + } + } + + /* (Note that this isn't set by any of the Argyll tools currently, */ + /* but can be set manually.) */ + 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) + printf("Total ink limit being used is %d%%\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 && klimit < 100.0) { + if (verb) + printf("Black ink limit being used is %d%%\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) + 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"); + + make_output_icc(ptype, 0, iccver, verb, iquality, oquality, + noisluts, noipluts, nooluts, nocied, noptop, nostos, + gamdiag, verify, clipprims, &ink, inname, outname, icg, + spec, tillum, &cust_tillum, illum, &cust_illum, observ, fwacomp, + smooth, avgdev, + ipname[0] != '\000' ? ipname : NULL, + sgname[0] != '\000' ? sgname : NULL, + absnames, + sepsat, &ivc_p, &ovc_p, ivc_e, ovc_e, + &pgmi, &sgmi, &xpi); + + } else if (strcmp(icg->t[0].kdata[ti],"INPUT") == 0) { + + 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, iccver, verb, iquality, oquality, noisluts, noipluts, nooluts, nocied, + verify, autowpsc, clipovwp, iwpscale, doinb2a, doinextrap, clipprims, + inname, outname, icg, spec, illum, &cust_illum, observ, + smooth, avgdev, &xpi); + + } else if (strcmp(icg->t[0].kdata[ti],"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, iccver, verb, iquality, oquality, + noisluts, noipluts, nooluts, nocied, noptop, nostos, + gamdiag, verify, clipprims, NULL, inname, outname, icg, + spec, icxIT_none, NULL, illum, &cust_illum, observ, 0, + smooth, avgdev, + ipname[0] != '\000' ? ipname : NULL, + sgname[0] != '\000' ? sgname : NULL, + absnames, + sepsat, &ivc_p, &ovc_p, ivc_e, ovc_e, + &pgmi, &sgmi, &xpi); + + } 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; +} + + + + + + + + + + -- cgit v1.2.3