351 lines
8.5 KiB
C
351 lines
8.5 KiB
C
|
|
/*
|
|
* International Color Consortium Format Library (icclib)
|
|
* Profile read then re-write skeleton utility.
|
|
*
|
|
* Author: Graeme W. Gill
|
|
* Date: 1999/11/29
|
|
* Version: 2.15
|
|
*
|
|
* Copyright 1997 - 2012 Graeme W. Gill
|
|
*
|
|
* This material is licensed with an "MIT" free use license:-
|
|
* see the License4.txt file in this directory for licensing details.
|
|
*/
|
|
|
|
/* TTBD:
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "icc.h"
|
|
|
|
|
|
/* Default is to do nothing... */
|
|
#undef DUMP_INCOMING
|
|
#undef TEST_VIDGAMTAG /* Add ColorSync 2.5 VideoCardGamma tag with linear table */
|
|
#undef TEST_SRGB_FIX /* Some test code */
|
|
#undef WP_PATCH /* Overwrite the white point */
|
|
#undef INVERT_GRAY /* Invert single TRC gray profile */
|
|
#undef MOD_A2B /* 0 */ /* Modify A2B RGB tables */
|
|
|
|
void error(char *fmt, ...), warning(char *fmt, ...);
|
|
|
|
void usage(void) {
|
|
fprintf(stderr,"Read and then re-write an ICC profile V%s\n",ICCLIB_VERSION_STR);
|
|
fprintf(stderr,"Author: Graeme W. Gill\n");
|
|
fprintf(stderr,"usage: iccrw readprofile writeprofile\n");
|
|
exit(1);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[]) {
|
|
int fa,nfa; /* argument we're looking at */
|
|
icmErr e = { 0, { '\000'} };
|
|
static char in_name[MAXNAMEL+1];
|
|
static char out_name[MAXNAMEL+1];
|
|
icmFile *rd_fp, *wr_fp;
|
|
icc *icco;
|
|
int verb = 0;
|
|
int rv = 0;
|
|
|
|
if (argc < 3)
|
|
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();
|
|
|
|
if (argv[fa][1] == 'v' || argv[fa][1] == 'V')
|
|
verb = 1;
|
|
|
|
/* No options */
|
|
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';
|
|
|
|
icm_err_clear_e(&e);
|
|
|
|
/* Open up the profile for reading */
|
|
if ((rd_fp = new_icmFileStd_name(&e, in_name,"r")) == NULL)
|
|
error ("Can't open file '%s', failed with 0x%x, '%s'",in_name, e.c, e.m);
|
|
|
|
if ((icco = new_icc(&e)) == NULL)
|
|
error ("Creation of ICC object failed with 0x%x, '%s'", e.c, e.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);
|
|
|
|
/* ======================================= */
|
|
/* Change profile in here. */
|
|
|
|
#ifdef DUMP_INCOMING
|
|
{
|
|
icmFile *op = new_icmFileStd_fp(NULL, stdout);
|
|
icco->dump(icco, op, 2);
|
|
op->del(op);
|
|
}
|
|
#endif
|
|
|
|
#ifdef NEVER
|
|
/* Add a generic discription */
|
|
{
|
|
icmMultiLocalizedUnicode *ro;
|
|
int i;
|
|
|
|
/* Try and read the tag from the file */
|
|
ro = (icmMultiLocalizedUnicode *)icco->read_tag(icco, icmMakeTag('d','s','c','m'));
|
|
if (ro == NULL)
|
|
error("Unable to read MLU tag");
|
|
|
|
/* Need to check that the cast is appropriate. */
|
|
if (ro->ttype != icSigMultiLocalizedUnicodeType)
|
|
error("rTRC is not icSigMultiLocalizedUnicodeType");
|
|
|
|
ro->rcount = 0;
|
|
ro->allocate(ro);
|
|
|
|
ro->desc = strdup("This is an extra description");
|
|
ro->count = strlen(ro->desc) + 1;
|
|
}
|
|
#endif
|
|
|
|
#ifdef TEST_SRGB_FIX /* Some test code */
|
|
/* Try deleting the black point tag */
|
|
{
|
|
if (icco->delete_tag(icco, icSigMediaBlackPointTag) != 0) {
|
|
error("Unable to delete blackpoint tag: %d, %s",icco->e.c,icco->e.m);
|
|
}
|
|
}
|
|
|
|
/* Fix up sRGB profile curve */
|
|
{
|
|
icmCurve *ro;
|
|
int i;
|
|
|
|
/* Try and read the tag from the file */
|
|
ro = (icmCurve *)icco->read_tag(icco, icSigRedTRCTag);
|
|
if (ro == NULL)
|
|
error("Unable to read rTRC");
|
|
|
|
/* Need to check that the cast is appropriate. */
|
|
if (ro->ttype != icSigCurveType)
|
|
error("rTRC is not CurveType");
|
|
|
|
for (i = 0; i < ro->size; i++) {
|
|
double vi, vo;
|
|
vi = i/(double)(ro->size-1);
|
|
|
|
if (vi < 0.04045) {
|
|
vo = vi/12.92;
|
|
} else {
|
|
vo = pow((0.055+vi)/1.055,2.4);
|
|
}
|
|
ro->data[i] = vo;
|
|
}
|
|
}
|
|
|
|
/* Show we modified this ICC file */
|
|
icco->header->cmmId = str2tag("argl"); /* CMM for profile - Argyll CMM */
|
|
#endif
|
|
|
|
#ifdef TEST_VIDGAMTAG
|
|
/* delete video card gamma tag (c/o Neil Okamoto) */
|
|
{
|
|
if (icco->find_tag(icco, icSigVideoCardGammaTag) == 0)
|
|
if (icco->delete_tag(icco, icSigVideoCardGammaTag) != 0)
|
|
error("Unable to delete videocardgamma tag: %d, %s",icco->e.c,icco->e.m);
|
|
}
|
|
/* Add a video card gamma table */
|
|
{
|
|
int c,i;
|
|
icmVideoCardGamma *wo;
|
|
wo = (icmVideoCardGamma *)icco->add_tag(icco, icSigVideoCardGammaTag, icSigVideoCardGammaType);
|
|
if (wo == NULL)
|
|
error ("Unable to add VideoCardGamma tag");
|
|
|
|
wo->tagType = icmVideoCardGammaTableType;
|
|
wo->u.table.channels = 3; /* rgb */
|
|
wo->u.table.entryCount = 256; /* full lut */
|
|
wo->u.table.entrySize = 1; /* byte */
|
|
wo->allocate((icmBase*)wo);
|
|
for (c = 0; c < 3; c++)
|
|
for (i = 0; i < 256; i++)
|
|
wo->u.table.data[c][i] = (1.0 - i/255.0);
|
|
}
|
|
|
|
/* Show we modified this ICC file */
|
|
icco->header->cmmId = str2tag("argl"); /* CMM for profile - Argyll CMM */
|
|
#endif
|
|
|
|
#ifdef WP_PATCH /* Overwrite the white point */
|
|
/* delete white point tag */
|
|
{
|
|
if (icco->find_tag(icco, icSigMediaWhitePointTag) == 0)
|
|
if (icco->delete_tag(icco, icSigMediaWhitePointTag) != 0)
|
|
error("Unable to delete white point tag tag: %d, %s",icco->e.c,icco->e.m);
|
|
}
|
|
/* Add a new white point tag */
|
|
{
|
|
double lab[3];
|
|
icmXYZArray *wo;
|
|
/* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
|
|
if ((wo = (icmXYZArray *)icco->add_tag(
|
|
icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
|
|
error("add_tag failed: %d, %s",icco->e.c, icco->e.m);
|
|
|
|
wo->size = 1;
|
|
wo->allocate((icmBase *)wo); /* Allocate space */
|
|
lab[0] = 79.8296;
|
|
lab[1] = -0.004042 + 0.842312;
|
|
lab[2] = 3.019928 + 0.810044;
|
|
icmLab2XYZ(&icmD50, lab, lab);
|
|
icmAry2XYZ(wo->data[0], lab);
|
|
}
|
|
|
|
/* Show we modified this ICC file */
|
|
icco->header->cmmId = str2tag("argl"); /* CMM for profile - Argyll CMM */
|
|
#endif
|
|
#ifdef INVERT_GRAY /* Invert single TRC gray profile */
|
|
{
|
|
icmCurve *ro;
|
|
int i;
|
|
|
|
/* Try and read the tag from the file */
|
|
ro = (icmCurve *)icco->read_tag(icco, icSigGrayTRCTag);
|
|
if (ro == NULL)
|
|
error("Unable to read GrayTRC");
|
|
|
|
/* Need to check that the cast is appropriate. */
|
|
if (ro->ttype != icSigCurveType)
|
|
error("GrayTRC is not CurveType");
|
|
|
|
/* Swap the curve entries from top to bottom */
|
|
for (i = 0; i < (ro->size/2); i++) {
|
|
double temp;
|
|
int ii = 255 - i;
|
|
|
|
temp = ro->data[ii];
|
|
ro->data[ii] = ro->data[i];
|
|
ro->data[i] = temp;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef MOD_A2B
|
|
{
|
|
icmLut *ro;
|
|
unsigned long size;
|
|
unsigned int i, j;
|
|
icTagSignature sig;
|
|
|
|
if (MOD_A2B == 0)
|
|
sig = icSigAToB0Tag;
|
|
else if (MOD_A2B == 1)
|
|
sig = icSigAToB1Tag;
|
|
else
|
|
sig = icSigAToB2Tag;
|
|
|
|
/* Try and read the tag from the file */
|
|
ro = (icmLut *)icco->read_tag(icco, sig);
|
|
if (ro == NULL)
|
|
error("Failed to read A2B tag");
|
|
|
|
/* Need to check that the cast is appropriate. */
|
|
if (ro->ttype != icSigLut16Type)
|
|
error("A2B is not 16 bitg");
|
|
|
|
/* Simply apply a gamma to the corresponding input curve */
|
|
for (i = 0; i < ro->inputChan; i++) { /* Input tables */
|
|
double val;
|
|
j = MOD_A2B;
|
|
val = ro->pe_ic[i]->data[j];
|
|
val = pow(val, 2.0);
|
|
ro->pe_ic[i]->data[j] = val;
|
|
}
|
|
}
|
|
#endif /* MOD_A2B */
|
|
|
|
/* ======================================= */
|
|
|
|
icm_err_clear_e(&e);
|
|
|
|
/* Open up the other profile for writing */
|
|
if ((wr_fp = new_icmFileStd_name(&e, out_name,"w")) == NULL)
|
|
error ("Can't open file '%s', failed with 0x%x, '%s'",out_name, e.c, e.m);
|
|
|
|
if ((rv = icco->write(icco,wr_fp,0)) != 0)
|
|
error ("Write file: %d, %s",rv,icco->e.m);
|
|
|
|
icco->del(icco);
|
|
wr_fp->del(wr_fp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ------------------------------------------------ */
|
|
/* Basic printf type error() and warning() routines */
|
|
|
|
void
|
|
error(char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
fprintf(stderr,"iccrw: 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,"iccrw: Warning - ");
|
|
va_start(args, fmt);
|
|
vfprintf(stderr, fmt, args);
|
|
va_end(args);
|
|
fprintf(stderr, "\n");
|
|
}
|