Files
gronod b94487108a
Linux x86_64 Release Build / build (push) Failing after 1m4s
Windows x86_64 Release Build / build (push) Has been cancelled
macOS Release Build / build (arm64, macOS_arm64_bin, arm64-apple-darwin, macos-latest) (push) Has been cancelled
macOS Release Build / build (x86_64, osx10.6_x86_64_bin, x86_64-apple-darwin, macos-13) (push) Has been cancelled
Fix ui.h includes in gamut and imdi tools
2026-08-22 23:00:36 +01:00

1100 lines
30 KiB
C

/*
* Color Correct a TIFF file, using an ICC Device link profile.
*
* Author: Graeme W. Gill
* Date: 00/3/8
* Version: 1.30
*
* Copyright 2000 - 2004 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.
*/
/*
* Thanks to Neil Okamoto for the 16 bit TIFF mods.
*/
/* TTBD:
*/
/*
This program is a framework that exercises the
IMDI code, as well as a demonstration of simple
profile linking. It can also do the conversion using the
floating point code in ICCLIB as a reference.
*/
#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 "tiffio.h"
#include "icc.h"
#include "imdi.h"
#undef TREAT_CMY_AS_RGB
void error(char *fmt, ...), warning(char *fmt, ...);
void usage(void) {
fprintf(stderr,"Color Correct a TIFF file using an ICC device link profile, V%s\n",ARGYLL_VERSION_STR);
fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
fprintf(stderr,"usage: cctiff [-options] devlinkprofile.icm infile.tif outfile.tif\n");
fprintf(stderr,"usage: cctiff [-options] -l inprofile.icm outprofile.icm infile.tif outfile.tif\n");
fprintf(stderr," -v Verbose\n");
fprintf(stderr," -c Combine linearisation curves into one transform\n");
fprintf(stderr," -p Use slow precise correction\n");
fprintf(stderr," -k Check fast result against precise, and report\n");
fprintf(stderr," -l Link input and output profiles\n");
fprintf(stderr," -i in_intent p = perceptual, r = relative colorimetric,\n");
fprintf(stderr," s = saturation, a = absolute colorimetric\n");
fprintf(stderr," -o out_intent p = perceptual, r = relative colorimetric,\n");
fprintf(stderr," s = saturation, a = absolute colorimetric\n");
exit(1);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Convert an ICC colorspace to the corresponding possible TIFF Photometric tags. */
/* Return the number of matching tags, and 0 if there is no corresponding tag. */
int
ColorSpaceSignature2TiffPhotometric(
uint16 tags[10], /* Pointer to return array, up to 10 */
icColorSpaceSignature cspace /* Input ICC colorspace */
) {
switch(cspace) {
case icSigGrayData:
tags[0] = PHOTOMETRIC_MINISBLACK;
return 1;
case icSigRgbData:
#ifdef TREAT_CMY_AS_RGB
case icSigCmyData:
#endif
tags[0] = PHOTOMETRIC_RGB;
return 1;
#ifndef TREAT_CMY_AS_RGB
case icSigCmyData:
#endif
case icSigCmykData:
tags[0] = PHOTOMETRIC_SEPARATED;
return 1;
case icSigYCbCrData:
tags[0] = PHOTOMETRIC_YCBCR;
return 1;
case icSigLabData:
tags[0] = PHOTOMETRIC_CIELAB;
#ifdef PHOTOMETRIC_ICCLAB
tags[1] = PHOTOMETRIC_ICCLAB;
tags[2] = PHOTOMETRIC_ITULAB;
#endif
return 3;
case icSigXYZData:
case icSigLuvData:
case icSigYxyData:
case icSigHsvData:
case icSigHlsData:
return 0;
case icSig2colorData:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 2; /* Cheat */
return 1;
case icSig3colorData:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 3; /* Cheat */
return 1;
case icSig4colorData:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 4; /* Cheat */
return 1;
case icSig5colorData:
case icSigMch5Data:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 5; /* Cheat */
return 1;
case icSig6colorData:
case icSigMch6Data:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 6; /* Cheat */
return 1;
case icSig7colorData:
case icSigMch7Data:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 7; /* Cheat */
return 1;
case icSig8colorData:
case icSigMch8Data:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 8; /* Cheat */
return 1;
case icSig9colorData:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 9; /* Cheat */
return 1;
case icSig10colorData:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 10; /* Cheat */
return 1;
case icSig11colorData:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 11; /* Cheat */
return 1;
case icSig12colorData:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 12; /* Cheat */
return 1;
case icSig13colorData:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 13; /* Cheat */
return 1;
case icSig14colorData:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 14; /* Cheat */
return 1;
case icSig15colorData:
tags[0] = PHOTOMETRIC_SEPARATED;
tags[1] = 15; /* Cheat */
return 1;
default:
return 0;
}
return 0;
}
/* Compute the length of a double nul terminated string, including */
/* the nuls. */
static int zzstrlen(char *s) {
int i;
for (i = 0;; i++) {
if (s[i] == '\000' && s[i+1] == '\000')
return i+2;
}
return 0;
}
/* Convert an ICC colorspace to the corresponding TIFF Inkset tag */
/* return 0xffff if not possible or applicable. */
int
ColorSpaceSignature2TiffInkset(
icColorSpaceSignature cspace,
int *len, /* Return length of ASCII inknames */
char **inknames /* Return ASCII inknames if non NULL */
) {
switch(cspace) {
case icSigCmyData:
return 0xffff; // ~~9999
if (inknames != NULL) {
*inknames = "cyan\000magenta\000yellow\000\000";
*len = zzstrlen(*inknames);
}
return 0; /* Not CMYK */
case icSigCmykData:
if (inknames != NULL) {
*inknames = NULL; /* No inknames */
*len = 0;
}
return INKSET_CMYK;
case icSigGrayData:
case icSigRgbData:
case icSigYCbCrData:
case icSigLabData:
case icSigXYZData:
case icSigLuvData:
case icSigYxyData:
case icSigHsvData:
case icSigHlsData:
case icSig2colorData:
case icSig3colorData:
case icSig4colorData:
case icSig5colorData:
case icSigMch5Data:
return 0xffff;
case icSig6colorData:
case icSigMch6Data:
/* This is a cheat and a hack. Should really make use of the */
/* ColorantTable to determine the colorant names. */
/* allowing cctiff to read it. */
if (inknames != NULL) {
*inknames = "cyan\000magenta\000yellow\000black\000orange\000green\000\000";
*len = zzstrlen(*inknames);
}
return 0; /* Not CMYK */
case icSig7colorData:
case icSigMch7Data:
return 0xffff;
case icSig8colorData:
case icSigMch8Data:
/* This is a cheat and a hack. Should really make use of the */
/* ColorantTable to determine the colorant names. */
/* allowing cctiff to read it. */
if (inknames != NULL) {
*inknames = "cyan\000magenta\000yellow\000black\000orange\000green\000lightcyan\000lightmagenta\000\000";
*len = zzstrlen(*inknames);
}
return 0; /* Not CMYK */
case icSig9colorData:
case icSig10colorData:
case icSig11colorData:
case icSig12colorData:
case icSig13colorData:
case icSig14colorData:
case icSig15colorData:
default:
return 0xffff;
}
return 0xffff;
}
char *
Photometric2str(
int pmtc
) {
static char buf[80];
switch (pmtc) {
case PHOTOMETRIC_MINISWHITE:
return "Subtractive Gray";
case PHOTOMETRIC_MINISBLACK:
return "Additive Gray";
case PHOTOMETRIC_RGB:
return "RGB";
case PHOTOMETRIC_PALETTE:
return "Indexed";
case PHOTOMETRIC_MASK:
return "Transparency Mask";
case PHOTOMETRIC_SEPARATED:
return "Separated";
case PHOTOMETRIC_YCBCR:
return "YCbCr";
case PHOTOMETRIC_CIELAB:
return "CIELab";
#ifdef PHOTOMETRIC_ICCLAB
case PHOTOMETRIC_ICCLAB:
return "ICCLab";
case PHOTOMETRIC_ITULAB:
return "ITULab";
#endif
case PHOTOMETRIC_LOGL:
return "CIELog2L";
case PHOTOMETRIC_LOGLUV:
return "CIELog2Luv";
}
sprintf(buf,"Unknown Tag %d",pmtc);
return buf;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Callbacks used to initialise imdi */
/* Information needed from a profile */
struct _profinfo {
char name[MAXNAMEL+1];
icmFile *fp;
icc *c;
icmHeader *h;
icRenderingIntent intent;
icmLuBase *luo; /* Base Lookup type object */
icmLuAlgType alg; /* Type of lookup algorithm */
int chan; /* Device channels */
}; typedef struct _profinfo profinfo;
/* Context for imdi setup callbacks */
typedef struct {
/* Overall parameters */
int verb; /* Non-zero if verbose */
icColorSpaceSignature ins, outs; /* Input/Output spaces */
int id, od; /* Input/Output dimensions */
int icombine; /* Non-zero if input curves are to be combined */
int ocombine; /* Non-zero if output curves are to be combined */
int dolink; /* Non-zero if input and output profiles are to be linked */
profinfo dev; /* Device link profile */
profinfo in; /* Device to PCS profile */
profinfo out; /* PCS to Device profile */
} sucntx;
/* Input curve function */
static void input_curves(
void *cntx,
double *out_vals,
double *in_vals
) {
sucntx *rx = (sucntx *)cntx;
//printf("~1 incurve in %f %f %f %f\n",in_vals[0],in_vals[1],in_vals[2],in_vals[3]);
if (rx->icombine) {
int i;
for (i = 0; i < rx->id; i++)
out_vals[i] = in_vals[i];
} else {
if (rx->dolink) { /* Two ICC profiles */
rx->in.luo->lookup_in(rx->in.luo, out_vals, in_vals);
} else { /* Device link */
rx->dev.luo->lookup_in(rx->dev.luo, out_vals, in_vals);
}
}
//printf("~1 incurve out %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]);
}
/* Multi-dim table function */
static void md_table(
void *cntx,
double *out_vals,
double *in_vals
) {
sucntx *rx = (sucntx *)cntx;
double vals[MAX_CHAN];
//printf("~1 md_table in %f %f %f %f\n",in_vals[0],in_vals[1],in_vals[2]);
if (rx->dolink) { /* Two ICC profiles */
if (rx->icombine) {
rx->in.luo->lookup_in(rx->in.luo, vals, in_vals);
rx->in.luo->lookup_core(rx->in.luo, vals, vals);
} else {
rx->in.luo->lookup_core(rx->in.luo, vals, in_vals);
}
rx->in.luo->lookup_out(rx->in.luo, vals, vals);
rx->out.luo->lookup_in(rx->out.luo, vals, vals);
rx->out.luo->lookup_core(rx->out.luo, out_vals, vals);
if (rx->ocombine)
rx->out.luo->lookup_out(rx->out.luo, out_vals, out_vals);
} else { /* Device link */
if (rx->icombine) {
rx->dev.luo->lookup_in(rx->dev.luo, vals, in_vals);
rx->dev.luo->lookup_core(rx->dev.luo, out_vals, vals);
} else {
rx->dev.luo->lookup_core(rx->dev.luo, out_vals, in_vals);
}
if (rx->ocombine)
rx->dev.luo->lookup_out(rx->dev.luo, out_vals, out_vals);
}
//printf("~1 md_table returns %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]);
}
/* Output curve function */
static void output_curves(
void *cntx,
double *out_vals,
double *in_vals
) {
sucntx *rx = (sucntx *)cntx;
//printf("~1 outurve in %f %f %f %f\n",in_vals[0],in_vals[1],in_vals[2],in_vals[3]);
if (rx->ocombine) {
int i;
for (i = 0; i < rx->od; i++)
out_vals[i] = in_vals[i];
} else {
if (rx->dolink) { /* Two ICC profiles */
rx->out.luo->lookup_out(rx->out.luo, out_vals, in_vals);
} else { /* Device link */
rx->dev.luo->lookup_out(rx->dev.luo, out_vals, in_vals);
}
}
//printf("~1 outurve out %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "ui.h"
int
main(int argc, char *argv[]) {
int fa,nfa; /* argument we're looking at */
static char in_name[MAXNAMEL+1]; /* Raster file name */
static char out_name[MAXNAMEL+1]; /* Raster file name */
int slow = 0;
int check = 0;
int i, rv = 0;
TIFF *rh = NULL, *wh = NULL;
int x, y, width, height; /* Size of image */
uint16 samplesperpixel, bitspersample;
int no_pmtc; /* Number of input photometrics */
uint16 photometric, pmtc[10]; /* Photometrics of input file, and input profile */
uint16 pconfig; /* Planar configuration */
uint16 resunits;
float resx, resy;
tdata_t *inbuf, *outbuf, *checkbuf = NULL;
/* IMDI */
imdi *s = NULL;
sucntx su; /* Setup context */
unsigned char *inp[MAX_CHAN];
unsigned char *outp[MAX_CHAN];
int clutres = 33;
/* Error check */
int mxerr = 0;
double avgerr = 0.0;
double avgcount = 0.0;
if (argc < 2)
usage();
su.verb = 0;
su.icombine = 0;
su.ocombine = 0;
su.dolink = 0;
su.in.intent = icmDefaultIntent;
su.out.intent = icmDefaultIntent;
/* 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();
/* Slow, Precise */
else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') {
slow = 1;
}
/* Combine per channel curves */
else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') {
su.icombine = 1;
su.ocombine = 1;
}
/* Check curves */
else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') {
check = 1;
}
/* Link profiles */
else if (argv[fa][1] == 'l' || argv[fa][1] == 'L') {
su.dolink = 1;
}
/* Input profile Intent */
else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') {
fa = nfa;
if (na == NULL) usage();
switch (na[0]) {
case 'p':
case 'P':
su.in.intent = icPerceptual;
break;
case 'r':
case 'R':
su.in.intent = icRelativeColorimetric;
break;
case 's':
case 'S':
su.in.intent = icSaturation;
break;
case 'a':
case 'A':
su.in.intent = icAbsoluteColorimetric;
break;
default:
usage();
}
}
/* Output profile Intent */
else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') {
fa = nfa;
if (na == NULL) usage();
switch (na[0]) {
case 'p':
case 'P':
su.out.intent = icPerceptual;
break;
case 'r':
case 'R':
su.out.intent = icRelativeColorimetric;
break;
case 's':
case 'S':
su.out.intent = icSaturation;
break;
case 'a':
case 'A':
su.out.intent = icAbsoluteColorimetric;
break;
default:
usage();
}
}
/* Verbosity */
else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
su.verb = 1;
}
else
usage();
} else
break;
}
if (su.dolink) {
if (fa >= argc || argv[fa][0] == '-') usage();
strncpy(su.in.name,argv[fa++],MAXNAMEL); su.in.name[MAXNAMEL] = '\000';
if (fa >= argc || argv[fa][0] == '-') usage();
strncpy(su.out.name,argv[fa++],MAXNAMEL); su.out.name[MAXNAMEL] = '\000';
} else {
if (fa >= argc || argv[fa][0] == '-') usage();
strncpy(su.dev.name,argv[fa++],MAXNAMEL); su.dev.name[MAXNAMEL] = '\000';
}
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';
/* - - - - - - - - - - - - - - - - */
if (su.dolink) {
icColorSpaceSignature natpcs;
/* Open up the input device profile for reading */
if ((su.in.fp = new_icmFileStd_name(su.in.name,"r")) == NULL)
error ("Can't open file '%s'",su.in.name);
if ((su.in.c = new_icc()) == NULL)
error ("Creation of Input profile ICC object failed");
/* Read header etc. */
if ((rv = su.in.c->read(su.in.c,su.in.fp,0)) != 0)
error ("%d, %s on file '%s'",rv,su.in.c->err,su.in.name);
su.in.h = su.in.c->header;
/* Check that it is a suitable device input icc */
if (su.in.h->deviceClass != icSigInputClass
&& su.in.h->deviceClass != icSigDisplayClass
&& su.in.h->deviceClass != icSigOutputClass
&& su.in.h->deviceClass != icSigColorSpaceClass) /* For sRGB etc. */
error("Input profile isn't a device profile");
/* Get a conversion object */
if ((su.in.luo = su.in.c->get_luobj(su.in.c, icmFwd, su.in.intent,
icSigLabData, icmLuOrdNorm)) == NULL)
error ("%d, %s for profile '%s'",su.in.c->errc, su.in.c->err, su.in.name);
/* Get details of conversion (Arguments may be NULL if info not needed) */
su.in.luo->spaces(su.in.luo, &su.ins, &su.id, NULL, NULL, &su.in.alg, NULL, NULL, NULL);
/* Get native PCS space */
su.in.luo->lutspaces(su.in.luo, NULL, NULL, NULL, NULL, &natpcs);
if (natpcs == icSigXYZData) {
su.icombine = 1; /* XYZ is to non-linear to be a benefit */
}
/* Open up the output device profile for reading */
if ((su.out.fp = new_icmFileStd_name(su.out.name,"r")) == NULL)
error ("Can't open file '%s'",su.out.name);
if ((su.out.c = new_icc()) == NULL)
error ("Creation of Output profile ICC object failed");
/* Read header etc. */
if ((rv = su.out.c->read(su.out.c,su.out.fp,0)) != 0)
error ("%d, %s on file '%s'",rv,su.out.c->err,su.out.name);
su.out.h = su.out.c->header;
/* Check that it is a suitable device output icc */
if (su.out.h->deviceClass != icSigInputClass
&& su.out.h->deviceClass != icSigDisplayClass
&& su.out.h->deviceClass != icSigOutputClass
&& su.out.h->deviceClass != icSigColorSpaceClass) /* For sRGB etc. */
error("Output profile isn't a device profile");
/* Get a conversion object */
if ((su.out.luo = su.out.c->get_luobj(su.out.c, icmBwd, su.out.intent,
icSigLabData, icmLuOrdNorm)) == NULL)
error ("%d, %s for profile '%s'",su.out.c->errc, su.out.c->err, su.out.name);
/* Get details of conversion (Arguments may be NULL if info not needed) */
su.out.luo->spaces(su.out.luo, NULL, NULL, &su.outs, &su.od, &su.out.alg, NULL, NULL, NULL);
/* Get native PCS space */
su.out.luo->lutspaces(su.out.luo, NULL, NULL, NULL, NULL, &natpcs);
if (natpcs == icSigXYZData) {
su.ocombine = 1; /* XYZ is to non-linear to be a benefit */
}
/* See discussion in imdi/imdi_gen.c for ideal numbers */
/* Use "high quality" resolution numbers */
switch (su.id) {
case 0:
error ("Illegal number of input chanels");
case 1:
clutres = 256;
break;
case 2:
clutres = 256;
break;
case 3:
clutres = 33;
break;
case 4:
clutres = 18;
break;
case 5:
clutres = 16;
break;
case 6:
clutres = 9;
break;
case 7:
clutres = 7;
break;
case 8:
clutres = 6;
break;
default: /* > 8 chan */
clutres = 3;
break;
}
} else {
icmLut *lut; /* ICC LUT table */
icmLuLut *luluo; /* LUT lookup object */
/* Open up the device link profile for reading */
if ((su.dev.fp = new_icmFileStd_name(su.dev.name,"r")) == NULL)
error ("Can't open file '%s'",su.dev.name);
if ((su.dev.c = new_icc()) == NULL)
error ("Creation of ICC object failed");
if ((rv = su.dev.c->read(su.dev.c, su.dev.fp, 0)) != 0)
error ("%d, %s",rv,su.dev.c->err);
su.dev.h = su.dev.c->header;
if (su.verb) {
icmFile *op;
if ((op = new_icmFileStd_fp(stdout)) == NULL)
error ("Can't open stdout");
su.dev.h->dump(su.dev.h, op, 1);
op->del(op);
}
/* Check that the profile is appropriate */
if (su.dev.h->deviceClass != icSigLinkClass)
error("Profile isn't a device link profile");
/* Get a conversion object */
if ((su.dev.luo = su.dev.c->get_luobj(su.dev.c, icmFwd, icmDefaultIntent,
icmSigDefaultData, icmLuOrdNorm)) == NULL)
error ("%d, %s",su.dev.c->errc, su.dev.c->err);
/* Get details of conversion (Arguments may be NULL if info not needed) */
su.dev.luo->spaces(su.dev.luo, &su.ins, &su.id, &su.outs, &su.od, &su.dev.alg, NULL, NULL, NULL);
if (su.dev.alg != icmLutType)
error ("DeviceLink profile doesn't have Lut !");
luluo = (icmLuLut *)su.dev.luo; /* Safe to coerce */
luluo->get_info(luluo, &lut, NULL, NULL, NULL); /* Get some details */
clutres = lut->clutPoints; /* Desired table resolution */
}
/* - - - - - - - - - - - - - - - */
/* Open up input tiff file ready for reading */
/* Got arguments, so setup to process the file */
if ((rh = TIFFOpen(in_name, "r")) == NULL)
error("error opening read file '%s'",in_name);
TIFFGetField(rh, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(rh, TIFFTAG_IMAGELENGTH, &height);
TIFFGetField(rh, TIFFTAG_BITSPERSAMPLE, &bitspersample);
if (bitspersample != 8 && bitspersample != 16) {
error("TIFF Input file must be 8 or 16 bit/channel");
}
TIFFGetField(rh, TIFFTAG_PHOTOMETRIC, &photometric);
if ((no_pmtc = ColorSpaceSignature2TiffPhotometric(pmtc, su.ins)) == 0)
error("ICC input colorspace '%s' can't be handled by a TIFF file!",
icm2str(icmColorSpaceSignature, su.ins));
for (i = 0; i < no_pmtc; i++) {
if (pmtc[i] == photometric)
break; /* Matches */
}
if (i >= no_pmtc) {
/* These error reports are a bit sloppy */
switch (no_pmtc) {
case 1:
error("TIFF colorspace '%s' doesn't match ICC colorspace '%s'!",
Photometric2str(photometric), Photometric2str(pmtc[0]));
case 2:
error("TIFF colorspace '%s' doesn't match ICC colorspace '%s' or '%s'!",
Photometric2str(photometric), Photometric2str(pmtc[0]),
Photometric2str(pmtc[1]));
default:
error("TIFF colorspace '%s' doesn't match ICC colorspace '%s', '%s' or '%s'!",
Photometric2str(photometric), Photometric2str(pmtc[0]),
Photometric2str(pmtc[1]), Photometric2str(pmtc[2]));
}
}
TIFFGetField(rh, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
if (su.id != samplesperpixel)
error ("TIFF Input file has %d input channels mismatched to colorspace '%s'",
samplesperpixel, icm2str(icmColorSpaceSignature, su.ins));
TIFFGetField(rh, TIFFTAG_PLANARCONFIG, &pconfig);
if (pconfig != PLANARCONFIG_CONTIG)
error ("TIFF Input file must be planar");
TIFFGetField(rh, TIFFTAG_RESOLUTIONUNIT, &resunits);
TIFFGetField(rh, TIFFTAG_XRESOLUTION, &resx);
TIFFGetField(rh, TIFFTAG_YRESOLUTION, &resy);
/* - - - - - - - - - - - - - - - */
if ((wh = TIFFOpen(out_name, "w")) == NULL)
error("Can\'t create TIFF file '%s'!",out_name);
TIFFSetField(wh, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(wh, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(wh, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(wh, TIFFTAG_SAMPLESPERPIXEL, su.od);
TIFFSetField(wh, TIFFTAG_BITSPERSAMPLE, bitspersample);
TIFFSetField(wh, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
if ((no_pmtc = ColorSpaceSignature2TiffPhotometric(pmtc, su.outs)) == 0)
error("TIFF file can't handle output colorspace '%s'!",
icm2str(icmColorSpaceSignature, su.outs));
TIFFSetField(wh, TIFFTAG_PHOTOMETRIC, pmtc[0]); /* Use first returned */
if (pmtc[0] == PHOTOMETRIC_SEPARATED) {
int iset;
int inlen;
char *inames;
iset = ColorSpaceSignature2TiffInkset(su.outs, &inlen, &inames);
if (iset != 0xffff && inlen > 0 && inames != NULL) {
TIFFSetField(wh, TIFFTAG_INKSET, iset);
if (inames != NULL) {
TIFFSetField(wh, TIFFTAG_INKNAMES, inlen, inames);
}
}
}
TIFFSetField(wh, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
if (resunits) {
TIFFSetField(wh, TIFFTAG_RESOLUTIONUNIT, resunits);
TIFFSetField(wh, TIFFTAG_XRESOLUTION, resx);
TIFFSetField(wh, TIFFTAG_YRESOLUTION, resy);
}
TIFFSetField(wh, TIFFTAG_IMAGEDESCRIPTION, "Color corrected by Argyll");
/* - - - - - - - - - - - - - - - */
/* Setup the imdi */
if (!slow) {
s = new_imdi(
su.id, /* Number of input dimensions */
su.od, /* Number of output dimensions */
/* Input pixel representation */
bitspersample == 8 ? pixint8 : pixint16,
/* Output pixel representation */
0x0, /* Treat every channel as unsigned */
NULL, /* No raster to callback mapping */
prec_min, /* Minimum of input and output precision */
bitspersample == 8 ? pixint8 : pixint16,
0x0, /* Treat every channel as unsigned */
NULL, /* No raster to callback mapping */
clutres, /* Desired table resolution */
oopts_none, /* Desired per channel output options */
NULL, /* Output channel check values */
opts_none, /* Desired processing direction and stride support */
input_curves, /* Callback functions */
md_table,
output_curves,
(void *)&su /* Context to callbacks */
);
if (s == NULL)
error("new_imdi failed");
}
/* - - - - - - - - - - - - - - - */
/* Process colors to translate */
/* (Should fix this to process a group of lines at a time ?) */
inbuf = _TIFFmalloc(TIFFScanlineSize(rh));
outbuf = _TIFFmalloc(TIFFScanlineSize(wh));
if (check)
checkbuf = _TIFFmalloc(TIFFScanlineSize(wh));
inp[0] = (unsigned char *)inbuf;
outp[0] = (unsigned char *)outbuf;
if (!slow) { /* Fast */
for (y = 0; y < height; y++) {
/* Read in the next line */
if (TIFFReadScanline(rh, inbuf, y, 0) < 0)
error ("Failed to read TIFF line %d",y);
/* Do fast conversion */
s->interp(s, (void **)outp, 0, (void **)inp, 0, width);
if (check) {
/* Do floating point conversion */
for (x = 0; x < width; x++) {
int i;
double in[MAX_CHAN], out[MAX_CHAN];
if (bitspersample == 8)
for (i = 0; i < su.id; i++)
in[i] = ((unsigned char *)inbuf)[x * su.id + i]/255.0;
else
for (i = 0; i < su.id; i++)
in[i] = ((unsigned short *)inbuf)[x * su.id + i]/65535.0;
#ifdef NEVER
if (su.dolink) {
if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1)
error ("%d, %s",su.dev.c->errc,su.dev.c->err);
if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1)
error ("%d, %s",su.dev.c->errc,su.dev.c->err);
} else {
if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1)
error ("%d, %s",su.dev.c->errc,su.dev.c->err);
}
#else
/* Apply the reference conversion */
input_curves((void *)&su, out, in);
md_table((void *)&su, out, out);
output_curves((void *)&su, out, out);
#endif
if (bitspersample == 8)
for (i = 0; i < su.od; i++)
((unsigned char *)checkbuf)[x * su.od + i] = (int)(out[i] * 255.0 + 0.5);
else
for (i = 0; i < su.od; i++)
((unsigned short *)checkbuf)[x * su.od + i] = (int)(out[i] * 65535.0 + 0.5);
}
/* Compute the errors */
for (x = 0; x < (width * su.od); x++) {
int err;
if (bitspersample == 8)
err = ((unsigned char *)outbuf)[x] - ((unsigned char *)checkbuf)[x];
else
err = ((unsigned short *)outbuf)[x] - ((unsigned short *)checkbuf)[x];
if (err < 0)
err = -err;
if (err > mxerr)
mxerr = err;
avgerr += (double)err;
avgcount++;
}
}
if (TIFFWriteScanline(wh, outbuf, y, 0) < 0)
error ("Failed to write TIFF line %d",y);
}
} else { /* Slow but precise */
if (bitspersample == 8) {
for (y = 0; y < height; y++) {
/* Read in the next line */
if (TIFFReadScanline(rh, inbuf, y, 0) < 0)
error ("Failed to read TIFF line %d",y);
/* Do floating point conversion */
for (x = 0; x < width; x++) {
int i;
double in[MAX_CHAN], out[MAX_CHAN];
for (i = 0; i < su.id; i++) {
in[i] = ((unsigned char *)inbuf)[x * su.id + i]/255.0;
}
#ifdef NEVER
if (su.dolink) {
if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1)
error ("%d, %s",su.dev.c->errc,su.dev.c->err);
if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1)
error ("%d, %s",su.dev.c->errc,su.dev.c->err);
} else {
if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1)
error ("%d, %s",su.dev.c->errc,su.dev.c->err);
}
#else
/* Apply the reference conversion */
input_curves((void *)&su, out, in);
md_table((void *)&su, out, out);
output_curves((void *)&su, out, out);
#endif
for (i = 0; i < su.od; i++) {
double outi = out[i];
if (outi < 0.0) /* Protect against sillies */
outi = 0.0;
else if (outi > 1.0)
outi = 1.0;
((unsigned char *)outbuf)[x * su.od + i] = (int)(outi * 255.0 + 0.5);
}
}
if (TIFFWriteScanline(wh, outbuf, y, 0) < 0)
error ("Failed to write TIFF line %d",y);
}
} else if (bitspersample == 16) {
for (y = 0; y < height; y++) {
/* Read in the next line */
if (TIFFReadScanline(rh, inbuf, y, 0) < 0)
error ("Failed to read TIFF line %d",y);
/* Do floating point conversion */
for (x = 0; x < width; x++) {
int i;
double in[MAX_CHAN], out[MAX_CHAN];
for (i = 0; i < su.id; i++) {
in[i] = ((unsigned short *)inbuf)[x * su.id + i]/65535.0;
}
#ifdef NEVER
if (su.dolink) {
if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1)
error ("%d, %s",su.dev.c->errc,su.dev.c->err);
if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1)
error ("%d, %s",su.dev.c->errc,su.dev.c->err);
} else {
if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1)
error ("%d, %s",su.dev.c->errc,su.dev.c->err);
}
#else
/* Apply the reference conversion */
input_curves((void *)&su, out, in);
md_table((void *)&su, out, out);
output_curves((void *)&su, out, out);
#endif
for (i = 0; i < su.od; i++) {
double outi = out[i];
if (outi < 0.0) /* Protect against sillies */
outi = 0.0;
else if (outi > 1.0)
outi = 1.0;
((unsigned short *)outbuf)[x * su.od + i] = (int)(outi * 65535.0 + 0.5);
}
}
if (TIFFWriteScanline(wh, outbuf, y, 0) < 0)
error ("Failed to write TIFF line %d",y);
}
}
}
if (check) {
printf("Worst error = %d bits, average error = %f bits\n", mxerr, avgerr/avgcount);
if (bitspersample == 8)
printf("Worst error = %f%%, average error = %f%%\n",
mxerr/2.55, avgerr/(2.55 * avgcount));
else
printf("Worst error = %f%%, average error = %f%%\n",
mxerr/655.35, avgerr/(655.35 * avgcount));
}
/* Done with lookup object */
if (s != NULL)
s->del(s);
if (su.dolink) {
su.in.luo->del(su.in.luo);
su.in.c->del(su.in.c);
su.in.fp->del(su.in.fp);
su.out.luo->del(su.out.luo);
su.out.c->del(su.out.c);
su.out.fp->del(su.out.fp);
} else {
su.dev.luo->del(su.dev.luo);
su.dev.c->del(su.dev.c);
su.dev.fp->del(su.dev.fp);
}
_TIFFfree(inbuf);
_TIFFfree(outbuf);
if (check)
_TIFFfree(checkbuf);
TIFFClose(rh); /* Close Input file */
TIFFClose(wh); /* Close Output file */
return 0;
}
/* Basic printf type error() and warning() routines */
void
error(char *fmt, ...)
{
va_list args;
fprintf(stderr,"cctiff: Error - ");
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
exit (-1);
}
void
warning(char *fmt, ...)
{
va_list args;
fprintf(stderr,"cctiff: Warning - ");
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
}