Files
argyllcms/spectro/synthcal.c
T
gronod d0209ec246
Linux x86_64 Release Build / build (push) Failing after 1m5s
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 macOS UI link errors by adding missing ui.h includes
2026-08-22 21:27:56 +01:00

367 lines
8.5 KiB
C

/*
* Argyll Color Management System
* Create a linear display calibration file.
*
* Author: Graeme W. Gill
* Date: 15/11/2005
*
* Copyright 1996 - 2025 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.
*/
#undef DEBUG
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <time.h>
#include <string.h>
#include "copyright.h"
#include "aconfig.h"
#include "numlib.h"
#include "cgats.h"
#include "conv.h"
#include "xicc.h"
#include "sort.h"
#include <stdarg.h>
#if defined (NT)
#include <conio.h>
#endif
void
usage(int level) {
int i;
fprintf(stderr,"Create a synthetic calibration file, Version %s\n",ARGYLL_VERSION_STR);
fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
fprintf(stderr,"usage: synthcal [-options] outfile\n");
fprintf(stderr," -r res Set the calibration resolution (default 256)\n");
fprintf(stderr," -t N i = input, o = output, d = display (default)\n");
fprintf(stderr," -d col_comb choose colorant combination from the following (default 3):\n");
for (i = 0; ; i++) {
char *desc;
if (icx_enum_colorant_comb(i, &desc) == 0)
break;
fprintf(stderr," %d: %s\n",i,desc);
}
fprintf(stderr," -D colorant Add or delete colorant from combination:\n");
if (level == 0)
fprintf(stderr," (Use -?? to list known colorants)\n");
else {
fprintf(stderr," %d: %s\n",0,"Additive");
for (i = 0; ; i++) {
char *desc;
if (icx_enum_colorant(i, &desc) == 0)
break;
fprintf(stderr," %d: %s\n",i+1,desc);
}
}
fprintf(stderr," -o o1,o2,o3,... Set non-linear curve offset, last to all chan. (default 0.0)\n");
fprintf(stderr," -s s1,s2,s3,... Set non-linear curve scale, last to all chan. (default 1.0)\n");
fprintf(stderr," -p p1,p2,p3,... Set non-linear curve powers, last to all chan. (default 1.0)\n");
fprintf(stderr," -E description Set the profile dEscription string\n");
fprintf(stderr," outfile Base name for output .cal file\n");
exit(1);
}
#include "ui.h"
int main(int argc, char *argv[])
{
int fa,nfa; /* current argument we're looking at */
int j;
int verb = 0;
static char outname[MAXNAMEL+1] = { 0 }; /* Output cgats file base name */
char *profDesc = NULL; /* Description */
int devtype = -1; /* device type, 0 = in, 1 = out, 2 = display */
inkmask devmask = 0; /* ICX ink mask of device space */
int calres = 256; /* Resolution of resulting file */
int devchan; /* Number of chanels in device space */
char *ident; /* Ink combination identifier (includes possible leading 'i') */
char *bident; /* Base ink combination identifier */
double off[MAX_CHAN]; /* Output offset */
double sca[MAX_CHAN]; /* Output scale */
double gam[MAX_CHAN]; /* Gamma applied */
int off_i = MAX_CHAN, sca_i = MAX_CHAN, gam_i = MAX_CHAN; /* Last index set in param */
for (j = 0; j < MAX_CHAN; j++)
off[j] = 0.0, sca[j] = gam[j] = 1.0;
error_program = "synthcal";
if (argc <= 1)
usage(0);
/* 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] == '?' || argv[fa][1] == '-') {
if (argv[fa][2] == '?' || argv[fa][2] == '-')
usage(1);
usage(0);
}
else if (argv[fa][1] == 'v')
verb = 1;
/* Calibration file resolution */
else if (argv[fa][1] == 'r') {
fa = nfa;
if (na == NULL) usage(0);
calres = atoi(na);
if (calres < 2 || calres > MAX_CAL_ENT)
usage(0);
}
/* Select the device type */
else if (argv[fa][1] == 't') {
fa = nfa;
if (na == NULL) usage(0);
if (na[0] == 'i' || na[0] == 'I')
devtype = 0;
else if (na[0] == 'o' || na[0] == 'O')
devtype = 1;
else if (na[0] == 'd' || na[0] == 'D')
devtype = 2;
else
usage(0);
}
/* Select the ink enumeration */
else if (argv[fa][1] == 'd') {
int ix;
fa = nfa;
if (na == NULL) usage(0);
ix = atoi(na);
if (ix == 0 && na[0] != '0')
usage(0);
if ((devmask = icx_enum_colorant_comb(ix, NULL)) == 0)
usage(0);
}
/* Toggle the colorant in ink combination */
else if (argv[fa][1] == 'D') {
int ix, tmask;
fa = nfa;
if (na == NULL) usage(0);
ix = atoi(na);
if (ix == 0 && na[0] != '0')
usage(0);
if (ix == 0)
tmask = ICX_ADDITIVE;
else
if ((tmask = icx_enum_colorant(ix-1, NULL)) == 0)
usage(0);
devmask ^= tmask;
}
/* curve offset */
else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') {
fa = nfa;
if (na == NULL) usage(0);
for (j = 0; j < MAX_CHAN; j++) {
int i;
for (i = 0; ; i++) {
if (na[i] == ','){
na[i] = '\000';
break;
}
if (na[i] == '\000') {
i = 0;
break;
}
}
off[j] = atof(na);
off_i = j;
if (i == 0)
break;
na += i+1;
}
}
/* curve scale */
else if (argv[fa][1] == 's' || argv[fa][1] == 'S') {
fa = nfa;
if (na == NULL) usage(0);
for (j = 0; j < MAX_CHAN; j++) {
int i;
for (i = 0; ; i++) {
if (na[i] == ','){
na[i] = '\000';
break;
}
if (na[i] == '\000') {
i = 0;
break;
}
}
sca[j] = atof(na);
sca_i = j;
if (i == 0)
break;
na += i+1;
}
}
/* curve power */
else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') {
fa = nfa;
if (na == NULL) usage(0);
for (j = 0; j < MAX_CHAN; j++) {
int i;
for (i = 0; ; i++) {
if (na[i] == ','){
na[i] = '\000';
break;
}
if (na[i] == '\000') {
i = 0;
break;
}
}
gam[j] = atof(na);
gam_i = j;
if (i == 0)
break;
na += i+1;
}
}
/* Profile Description */
else if (argv[fa][1] == 'E') {
fa = nfa;
if (na == NULL) usage(0);
profDesc = na;
}
else
usage(0);
} else
break;
}
/* Get the file name argument */
if (fa >= argc || argv[fa][0] == '-') usage(0);
strcpy(outname,argv[fa]);
if (strlen(outname) < 4 || strcmp(".cal",outname + strlen(outname)-4) != 0)
strcat(outname,".cal");
/* Implement defaults */
if (devtype < 0) {
devtype = 2; /* Display */
}
if (devmask == 0) {
if (devtype == 0 || devtype == 2)
devmask = ICX_RGB;
else
devmask = ICX_CMYK;
}
ident = icx_inkmask2char(devmask, 1);
bident = icx_inkmask2char(devmask, 0);
devchan = icx_noofinks(devmask);
/* If not all params were set, extend them to the rest */
for (j = 0; j < devchan; j++) {
if (j > off_i)
off[j] = off[off_i];
if (j > sca_i)
sca[j] = sca[sca_i];
if (j > gam_i)
gam[j] = gam[gam_i];
}
if (verb) {
if (devtype == 0)
printf("Device type: input\n");
else if (devtype == 1)
printf("Device type: output\n");
else
printf("Device type: display\n");
printf("Colorspace: %s\n", ident);
printf("Curve parameters:\n");
for (j = 0; j < devchan; j++)
printf("off[%d] = %f, sca[%d] = %f, gam[%d] = %f\n",j,off[j],j,sca[j],j, gam[j]);
}
/* Write out the resulting calibration file */
{
xcal *xc;
double **curves;
int i, j;
if ((xc = new_xcal()) == NULL)
error("new_xcal() failed");
/* All this should really be encapsulated in xcal methods ? */
xc->originator = strdup("Argyll synthcal");
if (devtype == 0)
xc->devclass = icSigInputClass;
else if (devtype == 1)
xc->devclass = icSigOutputClass;
else
xc->devclass = icSigDisplayClass;
xc->set_inkmask(xc, devmask);
curves = dmatrix(0, devchan-1, 0, calres-1);
for (i = 0; i < calres; i++) {
double ii = i/(calres-1.0);
for (j = 0; j < devchan; j++) {
double vv = off[j] + sca[j] * pow(ii, gam[j]);
if (vv < 0.0)
vv = 0.0;
else if (vv > 1.0)
vv = 1.0;
curves[j][i] = vv;
}
}
if (xc->set_curves(xc, calres, curves))
error("xcal set_curves error : %s",xc->e.m);
if (xc->write(xc, outname))
error("xcal write error : %s",xc->e.m);
xc->del(xc);
free_dmatrix(curves, 0, devchan-1, 0, calres-1);
}
return 0;
}