836 lines
24 KiB
C
836 lines
24 KiB
C
|
|
/*
|
|
* ICC reprocess to give true BtoA1 by inverting
|
|
* the AtoB1 table, and also correct the neutral
|
|
* axis of the BtoA0 and BtoA2 tables.
|
|
*
|
|
* Currently this only works with profiles with icSigLut16Type & icSigLut8Type Luts.
|
|
*
|
|
* Author: Graeme W. Gill
|
|
* Date: 9/7/00
|
|
* Version: 1.00
|
|
*
|
|
* Copyright 2000 Graeme W. Gill
|
|
* Please refer to License.txt file for details.
|
|
*/
|
|
|
|
/* TTBD:
|
|
*
|
|
* Add support for proper gamut mapping, just like profile,
|
|
* or deprecate revfix by modifying profile to work from
|
|
* an existing profile ?
|
|
*
|
|
* Remove the auxiliary fixup stuff when we have implemented
|
|
* optimised separations.
|
|
*
|
|
*/
|
|
|
|
#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 "conv.h"
|
|
#include "xicc.h"
|
|
|
|
#define USE_CAM_CLIP_OPT /* Clip in CAM Jab space rather than Lab */
|
|
#undef DEBUG /* Print each value changed */
|
|
|
|
|
|
typedef icmLut1 icmLut; /* Will only work for icSigLut16Type & icSigLut8Type */
|
|
|
|
void usage(void) {
|
|
fprintf(stderr,"Invert AtoB1 to make BtoA1 for CMYK profiles, Version %s\n",ARGYLL_VERSION_STR);
|
|
fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
|
|
fprintf(stderr,"usage: revfix [-options] iccin iccout\n");
|
|
fprintf(stderr," -v Verbose\n");
|
|
fprintf(stderr," -0 Process perceptual\n");
|
|
fprintf(stderr," -1 Process absolute/relative colorimetric\n");
|
|
fprintf(stderr," -2 Process saturation\n");
|
|
fprintf(stderr," -r res Override BtoA1 Clut res\n");
|
|
fprintf(stderr," -k [ezhxr] e = same K as existing BtoA table (def)\n");
|
|
fprintf(stderr," z = zero, h = 0.5 K, x = max K, r = ramp K\n");
|
|
fprintf(stderr," -k p stle stpo enle enpo shape\n");
|
|
fprintf(stderr," p = curve parameters\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 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," -p absprof Include abstract profile in output tables\n");
|
|
// fprintf(stderr," -s Use internal optimized separation for CMYK\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* ------------------------------------------- */
|
|
/* structure to support icc Lut initialisation calbacks */
|
|
|
|
/* ~~~Note that we're not coping with a matrix or XYZ PCS properly here. */
|
|
|
|
struct _callback {
|
|
int verb; /* Verbosity */
|
|
int total, count, last; /* Progress count information */
|
|
icColorSpaceSignature pcsspace;
|
|
int inking; /* k inking algorithm */
|
|
icxLuLut *BtoA; /* BtoA of table being processed */
|
|
icxLuLut *AtoB; /* AtoB of table being processed */
|
|
icxLuLut *AtoB1; /* AtoB of colorimetric table */
|
|
|
|
icRenderingIntent abs_intent; /* Desired abstract profile rendering intent */
|
|
icxLuBase *abs_luo; /* abstract profile tranform in PCS, NULL if none */
|
|
|
|
}; typedef struct _callback callback;
|
|
|
|
|
|
/* Utility to handle abstract profile application to PCS */
|
|
/* PCS in creating output table is always XYZ or Lab relative colorimetric, */
|
|
/* and abstract profile is absolute or relative, and will be */
|
|
/* XYZ if absolute, and PCS if relative. */
|
|
static void do_abstract(callback *p, double out[3], double in[3]) {
|
|
out[0] = in[0];
|
|
out[1] = in[1];
|
|
out[2] = in[2];
|
|
|
|
//printf("~1 do_abstract got %f %f %f\n",in[0],in[1],in[2]);
|
|
|
|
if (p->abs_intent == icAbsoluteColorimetric) {
|
|
if (p->pcsspace == icSigLabData) {
|
|
icmLab2XYZ(&icmD50, out, out);
|
|
//printf("~1 after Lab 2 XYZ got %f %f %f\n",out[0],out[1],out[2]);
|
|
}
|
|
p->AtoB1->plu->XYZ_Rel2Abs(p->AtoB1->plu, out, out);
|
|
//printf("~1 after rel to abs got %f %f %f\n",out[0],out[1],out[2]);
|
|
}
|
|
|
|
p->abs_luo->lookup(p->abs_luo, out, out);
|
|
//printf("~1 after abs_luo got %f %f %f\n",out[0],out[1],out[2]);
|
|
|
|
if (p->abs_intent == icAbsoluteColorimetric) {
|
|
p->AtoB1->plu->XYZ_Abs2Rel(p->AtoB1->plu, out, out);
|
|
//printf("~1 after abs2rel got %f %f %f\n",out[0],out[1],out[2]);
|
|
if (p->pcsspace == icSigLabData) {
|
|
icmXYZ2Lab(&icmD50, out, out);
|
|
//printf("~1 after XYZ to Lab got %f %f %f\n",out[0],out[1],out[2]);
|
|
}
|
|
}
|
|
//printf("~1 returning %f %f %f\n\n",out[0],out[1],out[2]);
|
|
}
|
|
|
|
/* - - - - - - - - - */
|
|
/* New input table */
|
|
void Lab_Labp(void *cntx, double out[3], double in[3]
|
|
, int tn
|
|
) {
|
|
callback *p = (callback *)cntx;
|
|
|
|
#ifdef DEBUG
|
|
printf("Got Lab %f %f %f\n",in[0],in[1],in[2]);
|
|
#endif
|
|
if (p->AtoB != p->AtoB1) {
|
|
/* Non-colorimetric, use existing input table */
|
|
if (p->BtoA->input(p->BtoA, out, in) > 1)
|
|
error ("%d, %s",p->BtoA->pp->e.c,p->BtoA->pp->e.m);
|
|
} else {
|
|
/* Colorimetric, use inverse AtoB output */
|
|
if (p->AtoB1->inv_output(p->AtoB1, out, in) > 1)
|
|
error ("%d, %s",p->AtoB1->pp->e.c,p->AtoB1->pp->e.m);
|
|
}
|
|
#ifdef DEBUG
|
|
printf("New Lab' %f %f %f\n",out[0],out[1],out[2]);
|
|
#endif
|
|
}
|
|
|
|
/* - - - - */
|
|
/* clut */
|
|
|
|
/* Normal CLUT routine */
|
|
void Labp_CMYKp(void *cntx, double out[4], double in[3]
|
|
, int tn
|
|
) {
|
|
double temp[4], targetk = 0.0;
|
|
int rv;
|
|
callback *p = (callback *)cntx;
|
|
|
|
#ifdef DEBUG
|
|
printf("Got Lab' %f %f %f\n",in[0],in[1],in[2]);
|
|
#endif
|
|
|
|
if (p->inking == 0) { /* If we are copying existing K value */
|
|
|
|
/* Figure out what K value was previously here */
|
|
if (p->AtoB != p->AtoB1) {
|
|
/* Simple because BtoA input & output tables don't change */
|
|
/* Figure out what DEV' K value the BtoA table currently has for this PCS' */
|
|
if (p->BtoA->clut(p->BtoA, temp, in) > 1)
|
|
error ("%d, %s",p->BtoA->pp->e.c,p->BtoA->pp->e.m);
|
|
|
|
/* Convert DEV' to DEV */
|
|
if (p->BtoA->output(p->BtoA, temp, temp) > 1)
|
|
error ("%d, %s",p->BtoA->pp->e.c,p->AtoB->pp->e.m);
|
|
} else {
|
|
/* More complicated because old BtoA in/out tables are different */
|
|
/* from the new ones. */
|
|
/* We know that new BtoA in/out tables are inverse of AtoB in/out, */
|
|
/* so we don't have to use BtoA1->inv_input, & BtoA1->inv_output */
|
|
/* Convert PCS' to PCS */
|
|
if (p->AtoB->output(p->AtoB, temp, in) > 1)
|
|
error ("%d, %s",p->AtoB->pp->e.c,p->AtoB->pp->e.m);
|
|
|
|
/* Figure out what DEV K value the BtoA table currently has for this PCS */
|
|
if (((icxLuBase *)p->BtoA)->lookup((icxLuBase *)p->BtoA, temp, temp) > 1)
|
|
error ("%d, %s",p->BtoA->pp->e.c,p->BtoA->pp->e.m);
|
|
}
|
|
targetk = temp[3];
|
|
#ifdef DEBUG
|
|
printf("Got existing CMYK %f %f %f %f\n",temp[0],temp[1],temp[2],temp[3]);
|
|
#endif
|
|
}
|
|
|
|
/* Copy the Lab in */
|
|
temp[0] = in[0];
|
|
temp[1] = in[1];
|
|
temp[2] = in[2];
|
|
|
|
if (p->AtoB != p->AtoB1) {
|
|
double tt[4];
|
|
|
|
/* Can't assume B2A in/out tables are inverses of AtoB */
|
|
/* Convert PCS' -> PCS for this table */
|
|
if (p->BtoA->inv_input(p->BtoA, temp, temp) > 1)
|
|
error ("%d, %s",p->BtoA->pp->e.c,p->BtoA->pp->e.m);
|
|
/* Convert PCS -> PCS' for colorimetric */
|
|
if (p->AtoB1->inv_output(p->AtoB1, temp, temp) > 1)
|
|
error ("%d, %s",p->AtoB->pp->e.c,p->AtoB->pp->e.m);
|
|
}
|
|
|
|
/* Abstract profile applied before inversion */
|
|
if (p->abs_luo != NULL) {
|
|
do_abstract(p, temp, temp);
|
|
}
|
|
|
|
/* Invert AtoB1 clut, using set inking policy */
|
|
out[3] = targetk;
|
|
|
|
/* PCS' -> DEV' colorimetric (aux target is DEV space) */
|
|
if ((rv = p->AtoB1->inv_clut(p->AtoB1, out, temp)) > 1)
|
|
error ("%d, %s",p->AtoB1->pp->e.c,p->AtoB1->pp->e.m);
|
|
|
|
/* ~~~ Note that the ink limit will be wrong for non-colorimetric, */
|
|
/* since AtoB1->inv_clut will be assuming A->toB1->inv_input as the output table, */
|
|
/* while we will actually be using BtoA->output ~~~~ */
|
|
/* Need to override ink limit computation function in icx for non-colorimetric. */
|
|
|
|
//#ifdef DEBUG
|
|
// if (rv != 0) {
|
|
// printf("Inversion clipped\n");
|
|
// }
|
|
//#endif
|
|
if (p->AtoB != p->AtoB1) {
|
|
/* Can't assume B2A in/out tables are inverses of AtoB */
|
|
/* Converts DEV' -> DEV colorimetric */
|
|
if (p->AtoB1->inv_input(p->AtoB1, out, out) > 1)
|
|
error ("%d, %s",p->AtoB->pp->e.c,p->AtoB->pp->e.m);
|
|
/* Convert DEV -> DEV' for this table */
|
|
if (p->BtoA->inv_output(p->BtoA, out, out) > 1)
|
|
error ("%d, %s",p->BtoA->pp->e.c,p->BtoA->pp->e.m);
|
|
}
|
|
#ifdef DEBUG
|
|
printf("New CMYK' %f %f %f %f\n",out[0],out[1],out[2],out[3]);
|
|
printf("\n");
|
|
#endif
|
|
|
|
if (p->verb) { /* Output percent intervals */
|
|
int pc;
|
|
p->count++;
|
|
pc = (int)(p->count * 100.0/p->total + 0.5);
|
|
if (pc != p->last) {
|
|
printf("%c%2d%%",cr_char,pc), fflush(stdout);
|
|
p->last = pc;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* - - - - - - - - - */
|
|
/* New output table */
|
|
void CMYKp_CMYK(void *cntx, double out[4], double in[4]
|
|
, int tn
|
|
) {
|
|
callback *p = (callback *)cntx;
|
|
|
|
#ifdef DEBUG
|
|
printf("Got CMYK' %f %f %f %f\n",in[0],in[1],in[2],in[3]);
|
|
#endif
|
|
if (p->AtoB != p->AtoB1) {
|
|
/* Non-colorimetric, use existing output table */
|
|
if (p->BtoA->output(p->BtoA, out, in) > 1)
|
|
error ("%d, %s",p->BtoA->pp->e.c,p->BtoA->pp->e.m);
|
|
} else {
|
|
/* Colorimetric, use inverse AtoB input */
|
|
if (p->AtoB1->inv_input(p->AtoB1, out, in) > 1)
|
|
error ("%d, %s",p->AtoB1->pp->e.c,p->AtoB1->pp->e.m);
|
|
}
|
|
#ifdef DEBUG
|
|
printf("New CMYK %f %f %f %f\n",out[0],out[1],out[2],out[3]);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* ------------------------------------------- */
|
|
|
|
int
|
|
main(int argc, char *argv[]) {
|
|
int fa,nfa; /* argument we're looking at */
|
|
static char in_name[MAXNAMEL+1];
|
|
static char out_name[MAXNAMEL+1];
|
|
static char abs_name[MAXNAMEL+1] = "\000"; /* Abstract profile name */
|
|
icmFile *rd_fp, *wr_fp;
|
|
icc *icco;
|
|
icmErr err = { 0, { '\000'} };
|
|
int verb = 0;
|
|
int clutres = 0;
|
|
int do0 = 0;
|
|
int do1 = 0;
|
|
int do2 = 0;
|
|
int inking = 0; /* Default copy from existing */
|
|
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 */
|
|
double klimit = -1.0; /* Black ink limit */
|
|
int intsep = 0; /* Not implemented in xicc yet ??? */
|
|
int rv = 0;
|
|
|
|
|
|
error_program = argv[0];
|
|
check_if_not_interactive();
|
|
|
|
if (argc < 2)
|
|
usage();
|
|
|
|
/* Process the 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) < argc) {
|
|
if (argv[fa+1][0] != '-') {
|
|
nfa = fa + 1;
|
|
na = argv[nfa]; /* next is seperate non-flag argument */
|
|
}
|
|
}
|
|
}
|
|
|
|
if (argv[fa][1] == '?')
|
|
usage();
|
|
|
|
/* Verbosity */
|
|
else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
|
|
verb = 1;
|
|
}
|
|
else if (argv[fa][1] == '0') {
|
|
do0 = 1;
|
|
}
|
|
else if (argv[fa][1] == '1') {
|
|
do1 = 1;
|
|
}
|
|
else if (argv[fa][1] == '2') {
|
|
do2 = 1;
|
|
}
|
|
else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') {
|
|
fa = nfa;
|
|
if (na == NULL) usage();
|
|
clutres = atoi(na);
|
|
}
|
|
|
|
/* Inking rule */
|
|
else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') {
|
|
fa = nfa;
|
|
if (na == NULL) usage();
|
|
if (argv[fa][1] == 'k')
|
|
locus = 0; /* K value target */
|
|
else
|
|
locus = 1; /* K locus target */
|
|
switch (na[0]) {
|
|
case 'e':
|
|
case 'E':
|
|
inking = 0; /* Use existing table as guide */
|
|
break;
|
|
case 'z':
|
|
case 'Z':
|
|
inking = 1; /* Use minimum k */
|
|
break;
|
|
case 'h':
|
|
case 'H':
|
|
inking = 2; /* Use 0.5 k */
|
|
break;
|
|
case 'x':
|
|
case 'X':
|
|
inking = 3; /* Use maximum k */
|
|
break;
|
|
case 'r':
|
|
case 'R':
|
|
inking = 4; /* Use ramp */
|
|
break;
|
|
case 'p':
|
|
case 'P':
|
|
inking = 5; /* Use curve parameter */
|
|
++fa;
|
|
if (fa >= argc) usage();
|
|
Kstle = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc) usage();
|
|
Kstpo = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc || argv[fa][0] == '-') usage();
|
|
Kenpo = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc) usage();
|
|
Kenle = atof(argv[fa]);
|
|
|
|
++fa;
|
|
if (fa >= argc || argv[fa][0] == '-') usage();
|
|
Kshap = atof(argv[fa]);
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
}
|
|
else if (argv[fa][1] == 'l') {
|
|
fa = nfa;
|
|
if (na == NULL) usage();
|
|
tlimit = atoi(na)/100.0;
|
|
}
|
|
else if (argv[fa][1] == 'L') {
|
|
fa = nfa;
|
|
if (na == NULL) usage();
|
|
klimit = atoi(na)/100.0;
|
|
}
|
|
|
|
/* Use internal separation */
|
|
else if (argv[fa][1] == 's' || argv[fa][1] == 'S') {
|
|
intsep = 1;
|
|
}
|
|
|
|
/* Abstract profile */
|
|
else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') {
|
|
if (na == NULL) usage();
|
|
fa = nfa;
|
|
strncpy(abs_name,na,MAXNAMEL); abs_name[MAXNAMEL] = '\000';
|
|
}
|
|
|
|
else
|
|
usage();
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (fa >= argc || argv[fa][0] == '-') usage();
|
|
strncpy(in_name,argv[fa++],MAXNAMEL); in_name[MAXNAMEL] = '\000';
|
|
|
|
if (fa >= argc || argv[fa][0] == '-') usage();
|
|
strncpy(out_name,argv[fa++],MAXNAMEL); out_name[MAXNAMEL] = '\000';
|
|
|
|
|
|
/* Open up the profile for reading */
|
|
if ((rd_fp = new_icmFileStd_name(&err, in_name,"r")) == NULL)
|
|
error ("Can't open file '%s' (0x%x, '%s')",in_name,err.c,err.m);
|
|
|
|
if ((icco = new_icc(&err)) == NULL)
|
|
error ("Creation of ICC object failed (0x%x, '%s')",err.c,err.m);
|
|
|
|
/* Read header etc. */
|
|
if ((rv = icco->read(icco,rd_fp,0)) != 0)
|
|
error ("%d, %s",rv,icco->e.m);
|
|
|
|
/* Read every tag */
|
|
if (icco->read_all_tags(icco) != 0) {
|
|
error("Unable to read all tags: %d, %s",icco->e.c,icco->e.m);
|
|
}
|
|
|
|
rd_fp->del(rd_fp);
|
|
|
|
/* ======================= */
|
|
/* Check that it is a suitable icc */
|
|
{
|
|
icmHeader *rh = icco->header;
|
|
|
|
if (rh->deviceClass != icSigOutputClass)
|
|
error("Profile isn't an output device profile");
|
|
|
|
if (rh->colorSpace != icSigCmykData)
|
|
error("Profile isn't for a CMYK device");
|
|
|
|
if (rh->pcs != icSigLabData)
|
|
error("Profile is not using a PCS of Lab - can't cope with this yet");
|
|
}
|
|
|
|
|
|
if (verb && inking == 5) {
|
|
double tL;
|
|
printf("K parameters are are %f %f %f %f %f\n",Kstle, Kstpo, Kenpo, Kenle, Kshap);
|
|
for (tL = 100.0; tL >= 0.0; tL -= 10.0) {
|
|
double rv, L;
|
|
|
|
L = 0.01 * tL;
|
|
|
|
/* Code from xlut.c: */
|
|
/* Invert sense of L, so that 0.0 = white, 1.0 = black */
|
|
L = 1.0 - L;
|
|
|
|
if (L <= Kstpo && L <= Kenpo) {
|
|
/* We are at white level */
|
|
rv = Kstle;
|
|
} else if (L >= Kstpo && L >= Kenpo) {
|
|
/* We are at black level */
|
|
rv = Kenle;
|
|
} else {
|
|
double g;
|
|
/* We must be on the curve from start to end levels */
|
|
|
|
if (Kstpo > Kenpo) {
|
|
rv = (L - Kenpo)/(Kstpo - Kenpo);
|
|
} else {
|
|
rv = (L - Kstpo)/(Kenpo - Kstpo);
|
|
}
|
|
|
|
g = Kshap/2.0;
|
|
|
|
/* A value of 0.5 will be tranlated to g */
|
|
rv = rv/((1.0/g - 2.0) * (1.0 - rv) + 1.0);
|
|
|
|
/* Transition between start end end levels */
|
|
rv = rv * (Kenle - Kstle) + Kstle;
|
|
}
|
|
|
|
/* To be safe */
|
|
if (rv < 0.0)
|
|
rv = 0.0;
|
|
else if (rv > 1.0)
|
|
rv = 1.0;
|
|
|
|
printf("L = %f, K %s = %f\n",tL, locus ? "locus" : "value", rv);
|
|
}
|
|
}
|
|
|
|
/* ======================= */
|
|
{
|
|
int ii; /* Current intent */
|
|
icmLut *done[3]; /* record pointers to done Luts here */
|
|
icRenderingIntent intent = 0;
|
|
icTagSignature sig = 0;
|
|
xicc *xicco;
|
|
callback cb; /* Callback support stucture */
|
|
icxInk ink; /* Ink parameters */
|
|
icmLuAlgType alg; /* Type of lookup algorithm */
|
|
icmFile *abs_fp = NULL; /* Abstract profile transform: */
|
|
icc *abs_icc = NULL;
|
|
xicc *abs_xicc = NULL;
|
|
|
|
/* Wrap with an expanded icc */
|
|
if ((xicco = new_xicc(icco)) == NULL)
|
|
error ("Creation of xicc failed");
|
|
|
|
/* Setup CMYK -> Lab conversion (Fwd) object */
|
|
|
|
/* Set the default ink limits if not set on command line */
|
|
icxDefaultLimits(xicco, &ink.tlimit, tlimit, &ink.klimit, klimit);
|
|
|
|
if (verb) {
|
|
if (!do0 && !do1 && !do2)
|
|
printf("WARNING: nothing to do!\n");
|
|
if (ink.tlimit >= 0.0)
|
|
printf("Total ink limit assumed is %3.0f%%\n",100.0 * ink.tlimit);
|
|
if (ink.klimit >= 0.0)
|
|
printf("Black ink limit assumed is %3.0f%%\n",100.0 * ink.klimit);
|
|
}
|
|
|
|
ink.KonlyLmin = 0; /* Use normal black Lmin for locus */
|
|
|
|
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) {
|
|
ink.k_rule = icxKvalue; /* K is auxiliary target */
|
|
|
|
} else if (inking == 1) { /* 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 == 2) { /* 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 == 3) { /* 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 == 4) { /* 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;
|
|
}
|
|
|
|
cb.verb = verb;
|
|
cb.count = 0;
|
|
cb.last = -1;
|
|
cb.inking = inking;
|
|
|
|
/* Setup our access to the device characteristic */
|
|
if ((cb.AtoB1 = (icxLuLut *)xicco->get_luobj(xicco,
|
|
ICX_CLIP_NEAREST
|
|
#ifdef USE_CAM_CLIP_OPT
|
|
| ICX_CAM_CLIP
|
|
#endif
|
|
| (intsep ? ICX_INT_SEPARATE : 0),
|
|
icmFwd, icRelativeColorimetric,
|
|
icmSigDefaultData, icmLuOrdNorm, NULL, &ink)) == NULL)
|
|
error ("%d, %s",xicco->e.c, xicco->e.m);
|
|
|
|
cb.AtoB1->spaces((icxLuBase *)cb.AtoB1, NULL, NULL, NULL, NULL, &alg,
|
|
NULL, NULL, &cb.pcsspace);
|
|
if (alg != icmLutType)
|
|
error("Forward conversion is not a Lut");
|
|
|
|
/* Open up the abstract profile if supplied, and setup luo */
|
|
if (abs_name[0] != '\000') {
|
|
if ((abs_fp = new_icmFileStd_name(&err, abs_name,"r")) == NULL)
|
|
error ("Can't open abstract profile file '%s' (0x%x, '%s')",abs_name,err.c,err.m);
|
|
|
|
if ((abs_icc = new_icc(&err)) == NULL)
|
|
error ("Creation of Abstract profile ICC object failed (0x%x, '%s')",err.c,err.m);
|
|
|
|
/* Read header etc. */
|
|
if ((rv = abs_icc->read(abs_icc,abs_fp,0)) != 0)
|
|
error ("%d, %s",rv,abs_icc->e.m);
|
|
|
|
if (abs_icc->header->deviceClass != icSigAbstractClass)
|
|
error("Abstract profile isn't an abstract profile");
|
|
|
|
/* Take intended abstract intent from profile itself */
|
|
if ((cb.abs_intent = abs_icc->header->renderingIntent) != icAbsoluteColorimetric)
|
|
cb.abs_intent = icRelativeColorimetric;
|
|
|
|
/* Wrap with an expanded icc */
|
|
if ((abs_xicc = new_xicc(abs_icc)) == NULL)
|
|
error ("Creation of abstract profile xicc failed");
|
|
|
|
/* The abstract profile intent is assumed to determine how it gets applied. */
|
|
/* Make abstract PCS XYZ if icAbsoluteColorimetric is needed. */
|
|
if ((cb.abs_luo = abs_xicc->get_luobj(abs_xicc, ICX_CLIP_NEAREST, icmFwd, cb.abs_intent,
|
|
(cb.pcsspace == icSigLabData && cb.abs_intent == icRelativeColorimetric)
|
|
? icSigLabData : icSigXYZData,
|
|
icmLuOrdNorm, NULL, NULL)) == NULL)
|
|
error ("%d, %s",abs_icc->e.c, abs_icc->e.m);
|
|
} else {
|
|
cb.abs_luo = NULL;
|
|
}
|
|
|
|
/* for all intents, and not already processed */
|
|
for (ii = 0; ii <= 2; ii++) {
|
|
int i;
|
|
icmLut *wo;
|
|
|
|
switch(ii) {
|
|
case 0:
|
|
intent = icRelativeColorimetric;
|
|
sig = icSigBToA1Tag;
|
|
if (do1 == 0)
|
|
continue;
|
|
break;
|
|
case 1:
|
|
intent = icPerceptual;
|
|
sig = icSigBToA0Tag;
|
|
if (do0 == 0)
|
|
continue;
|
|
break;
|
|
case 2:
|
|
intent = icSaturation;
|
|
sig = icSigBToA2Tag;
|
|
if (do2 == 0)
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
if (intent == icRelativeColorimetric) {
|
|
cb.AtoB = cb.AtoB1;
|
|
} else {
|
|
/* Setup CMYK -> Lab conversion (Fwd) object */
|
|
if ((cb.AtoB = (icxLuLut *)xicco->get_luobj(xicco, ICX_CLIP_NEAREST, icmFwd, intent,
|
|
icmSigDefaultData, icmLuOrdNorm, NULL, NULL)) == NULL)
|
|
error ("%d, %s",xicco->e.c, xicco->e.m);
|
|
|
|
cb.AtoB->spaces((icxLuBase *)cb.AtoB, NULL, NULL, NULL, NULL, &alg, NULL, NULL, NULL);
|
|
if (alg != icmLutType)
|
|
error("Forwards conversion is not a Lut");
|
|
}
|
|
|
|
/* Setup Lab -> CMYK conversion (Bwd) object */
|
|
if ((cb.BtoA = (icxLuLut *)xicco->get_luobj(xicco, ICX_CLIP_NEAREST, icmBwd, intent,
|
|
icmSigDefaultData, icmLuOrdNorm, NULL, NULL)) == NULL)
|
|
error ("%d, %s",xicco->e.c, xicco->e.m);
|
|
|
|
cb.BtoA->spaces((icxLuBase *)cb.BtoA, NULL, NULL, NULL, NULL, &alg, NULL, NULL, NULL);
|
|
if (alg != icmLutType)
|
|
error("Backwards conversion is not a Lut");
|
|
|
|
/* Try and read the tag from the file */
|
|
wo = (icmLut *)icco->read_tag(icco, sig);
|
|
if (wo == NULL)
|
|
error("Can't find %s", icm2str(icmRenderingIntent, intent));
|
|
|
|
/* Need to check that the cast is appropriate. */
|
|
if (wo->ttype != icSigLut16Type && wo->ttype != icSigLut8Type)
|
|
error("Lut table isn't Lut8 or Lut16 Type");
|
|
|
|
|
|
/* Make sure that we don't process a table twice */
|
|
done[ii] = wo;
|
|
for (i = ii-1; i >= 0; i--) {
|
|
if (done[i] == wo) {
|
|
break;
|
|
}
|
|
}
|
|
if (i >= 0) {
|
|
if (verb)
|
|
printf("Skipping %s table - already done\n", icm2str(icmRenderingIntent, intent));
|
|
|
|
} else {
|
|
icmXformSigs sigs[1];
|
|
unsigned int inputEnt;
|
|
unsigned int cres, clutPoints[MAX_CHAN];
|
|
unsigned int outputEnt;
|
|
|
|
sigs[0].sig = sig;
|
|
sigs[0].ttype = icSigLut16Type;
|
|
|
|
if ((inputEnt = cb.AtoB->plu->max_out_res(cb.AtoB->plu, NULL)) == 0)
|
|
inputEnt = 1024;
|
|
|
|
if ((cres = cb.AtoB->plu->max_clut_res(cb.AtoB->plu, NULL)) == 0)
|
|
cres = 17;
|
|
|
|
/* Let user override for BtoA1 */
|
|
if (sig == icSigBToA1Tag && clutres > 0) {
|
|
if (verb)
|
|
printf("Overriding existing clut resolution %d with %d\n",cres,clutres);
|
|
cres = clutres;
|
|
}
|
|
for (i = 0; i < 3; i++)
|
|
clutPoints[i] = cres;
|
|
if ((outputEnt = cb.AtoB->plu->max_in_res(cb.AtoB->plu, NULL)) == 0)
|
|
outputEnt = 1024;
|
|
|
|
if (verb)
|
|
printf("About to start processing %s\n", icm2str(icmRenderingIntent, intent));
|
|
|
|
if (cb.verb) {
|
|
unsigned int ui;
|
|
int extra;
|
|
for (cb.total = 1, ui = 0; ui < 3; cb.total *= clutPoints[ui], ui++)
|
|
;
|
|
/* Add in cell center points */
|
|
for (extra = 1, ui = 0; ui < 3; extra *= clutPoints[ui]-1, ui++)
|
|
;
|
|
cb.total += extra;
|
|
printf(" 0%%"), fflush(stdout);
|
|
}
|
|
|
|
if (icco->create_lut_xforms(
|
|
icco,
|
|
ICM_CLUT_SET_APXLS,
|
|
&cb, /* Context */
|
|
1, /* One table */
|
|
sigs, /* signatures and tag types for each table */
|
|
2, /* Bytes per value of AToB or BToA CLUT, 1 or 2 */
|
|
inputEnt, clutPoints, outputEnt, /* Table resolutions */
|
|
icSigLabData, /* Input color space */
|
|
icSigCmykData, /* Output color space */
|
|
NULL, NULL, /* Use default input range */
|
|
Lab_Labp, /* Linear input transform Lab->Lab' */
|
|
NULL, NULL, /* Use default Lab' range */
|
|
Labp_CMYKp, /* Lab' -> CMYK' transfer function */
|
|
NULL, NULL, /* Use default CMYK' range */
|
|
CMYKp_CMYK, /* Output transfer function, CMYK'->CMYK */
|
|
NULL, NULL /* default APXLS range */
|
|
) != ICM_ERR_OK)
|
|
error("Setting 16 bit Lab->CMYK Lut failed: %d, %s",icco->e.c,icco->e.m);
|
|
|
|
|
|
if (verb)
|
|
printf("\nDone processing %s\n", icm2str(icmRenderingIntent, intent));
|
|
|
|
}
|
|
|
|
/* Done with this intents lookup object */
|
|
cb.BtoA->del((icxLuBase *)cb.BtoA);
|
|
if (cb.AtoB != cb.AtoB1)
|
|
cb.AtoB->del((icxLuBase *)cb.AtoB);
|
|
}
|
|
/* Done with colorimetric intents AtoB1 lookup objects, and xicc */
|
|
cb.AtoB1->del((icxLuBase *)cb.AtoB1);
|
|
xicco->del(xicco);
|
|
|
|
if (cb.abs_luo != NULL) { /* Free up abstract transform */
|
|
cb.abs_luo->del(cb.abs_luo);
|
|
abs_xicc->del(abs_xicc);
|
|
abs_icc->del(abs_icc);
|
|
abs_fp->del(abs_fp);
|
|
}
|
|
|
|
}
|
|
/* ======================================= */
|
|
|
|
/* Open up the other profile for writing */
|
|
if ((wr_fp = new_icmFileStd_name(&err, out_name,"w")) == NULL)
|
|
error ("Can't open file '%s' (0x%x, '%s')",out_name,err.c,err.m);
|
|
|
|
/* Relax format so we don't barf on input profile contents */
|
|
icco->set_cflag(icco, icmCFlagWrFormatWarn);
|
|
icco->set_cflag(icco, icmCFlagWrVersionWarn);
|
|
|
|
if ((rv = icco->write(icco,wr_fp,0)) != 0)
|
|
error ("Write file: %d, %s",rv,icco->e.m);
|
|
|
|
wr_fp->del(wr_fp);
|
|
icco->del(icco);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|