Files
argyllcms/xicc/xutils.c
T
2026-08-20 20:28:40 +01:00

294 lines
6.9 KiB
C

/*
* xicc standalone utilities
*
* Author: Graeme W. Gill
* Date: 2/7/00
* Version: 1.00
*
* Copyright 2000 - 2006 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 module provides expanded capabilities,
* but is independent of other modules.
*/
#include <stdarg.h>
#include <sys/types.h>
#include <string.h>
#include <ctype.h>
#include <setjmp.h>
#ifdef __sun
#include <unistd.h>
#endif
#include "copyright.h"
#include "aconfig.h"
#include "icc.h"
#include "tiffio.h"
#include "jpeglib.h"
#include "iccjpeg.h"
#include "xutils.h" /* definitions for this library */
#undef DEBUG
#ifdef DEBUG
# define errout stderr
# define debug(xx) fprintf(errout, xx )
# define debug2(xx) fprintf xx
#else
# define debug(xx)
# define debug2(xx)
#endif
#if !defined(O_CREAT) && !defined(_O_CREAT)
# error "Need to #include fcntl.h!"
#endif
/* ------------------------------------------------------ */
/* Common clut table code */
/* Default table of clut resolutions */
/* See discussion in imdi/imdi_gen.c for ideal numbers */
static int lut_resolutions[9][4] = {
/* low, med, high, vhigh */
{ 0, 0, 0, 0 }, /* 0 */
{ 256, 772, 4370, 4370 }, /* 1 */
{ 86, 256, 256, 256 }, /* 2 */
{ 9, 17, 33, 52 }, /* 3 */
{ 6, 9, 18, 33 }, /* 4 */
{ 6, 9, 16, 18 }, /* 5 */
{ 6, 6, 9, 12 }, /* 6 */
{ 6, 7, 7, 9 }, /* 7 */
{ 3, 5, 5, 7 } /* 8 */
};
/* return a lut resolution given the input dimesion and quality */
/* Input dimension [0-8], quality: low 0, medium 1, high 2, very high 3 . */
/* A returned value of 0 indicates illegal. */
int dim_to_clutres(int dim, int quality) {
if (dim < 0)
dim = 0;
else if (dim > 8)
dim = 8;
if (quality < 0)
quality = 0;
if (quality > 3)
quality = 3;
return lut_resolutions[dim][quality];
}
/* ------------------------------------------------------ */
/* JPEG error information */
typedef struct {
jmp_buf env; /* setjmp/longjmp environment */
char message[JMSG_LENGTH_MAX];
} jpegerrorinfo;
/* JPEG error handler */
static void jpeg_error(j_common_ptr cinfo) {
jpegerrorinfo *p = (jpegerrorinfo *)cinfo->client_data;
(*cinfo->err->format_message) (cinfo, p->message);
longjmp(p->env, 1);
}
/* ------------------------------------------------------ */
/* Open an ICC file or a TIFF or JPEG file with an embedded ICC profile for reading. */
/* Return NULL on error */
icc *read_embedded_icc(char *file_name) {
TIFF *rh = NULL;
int size;
void *tag, *buf;
icmAlloc *al;
icmFile *fp;
icmErr err = { 0, { '\000'} };
icc *icco;
TIFFErrorHandler olderrh, oldwarnh;
TIFFErrorHandlerExt olderrhx, oldwarnhx;
int rv;
/* First see if the file can be opened as an ICC profile */
if ((fp = new_icmFileStd_name(&err, file_name,"r")) == NULL) {
debug2((errout,"Can't open file '%s' (0x%x, '%s')\n",file_name,err.c,err.m));
return NULL;
}
if ((icco = new_icc(&err)) == NULL) {
debug2(("Creation of ICC object failed (0x%x, '%s')\n",err.c,err.m));
fp->del(fp);
return NULL;
}
if ((rv = icco->read(icco,fp,0)) == 0) {
debug2((errout,"Opened '%s' as an icc profile\n",file_name));
return icco;
}
debug2((errout,"icc read failed with %d, %s\n",rv,icco->err));
icco->del(icco); /* icc wil fp->del() */
/* Not an ICC profile, see if it's a TIFF file */
olderrh = TIFFSetErrorHandler(NULL);
oldwarnh = TIFFSetWarningHandler(NULL);
olderrhx = TIFFSetErrorHandlerExt(NULL);
oldwarnhx = TIFFSetWarningHandlerExt(NULL);
if ((rh = TIFFOpen(file_name, "r")) != NULL) {
TIFFSetErrorHandler(olderrh);
TIFFSetWarningHandler(oldwarnh);
TIFFSetErrorHandlerExt(olderrhx);
TIFFSetWarningHandlerExt(oldwarnhx);
debug("TIFFOpen succeeded\n");
if (TIFFGetField(rh, TIFFTAG_ICCPROFILE, &size, &tag) == 0 || size == 0) {
debug2((errout,"no ICC profile found in '%s'\n",file_name));
TIFFClose(rh);
return NULL;
}
/* Make a copy of the profile to a memory buffer */
if ((al = new_icmAllocStd(&err)) == NULL) {
debug2(("new_icmAllocStd failed (0x%x, '%s')\n",err.c,err.m));
TIFFClose(rh);
return NULL;
}
if ((buf = al->malloc(al, size)) == NULL) {
debug("malloc of profile buffer failed\n");
al->del(al);
TIFFClose(rh);
return NULL;
}
memmove(buf, tag, size);
TIFFClose(rh);
} else {
jpegerrorinfo jpeg_rerr;
FILE *rf = NULL;
struct jpeg_decompress_struct rj;
struct jpeg_error_mgr jerr;
unsigned char *pdata;
unsigned int plen;
debug2((errout,"TIFFOpen failed for '%s'\n",file_name));
TIFFSetErrorHandler(olderrh);
TIFFSetWarningHandler(oldwarnh);
TIFFSetErrorHandlerExt(olderrhx);
TIFFSetWarningHandlerExt(oldwarnhx);
/* We cope with the horrible ijg jpeg library error handling */
/* by using a setjmp/longjmp. */
jpeg_std_error(&jerr);
jerr.error_exit = jpeg_error;
if (setjmp(jpeg_rerr.env)) {
debug2((errout,"jpeg_read_header failed for '%s'\n",file_name));
jpeg_destroy_decompress(&rj);
fclose(rf);
return NULL;
}
rj.err = &jerr;
rj.client_data = &jpeg_rerr;
jpeg_create_decompress(&rj);
#if defined(O_BINARY) || defined(_O_BINARY)
if ((rf = fopen(file_name,"rb")) == NULL)
#else
if ((rf = fopen(file_name,"r")) == NULL)
#endif
{
debug2((errout,"fopen failed for '%s'\n",file_name));
jpeg_destroy_decompress(&rj);
return NULL;
}
jpeg_stdio_src(&rj, rf);
setup_read_icc_profile(&rj);
/* we'll longjmp on error */
jpeg_read_header(&rj, TRUE);
if (!read_icc_profile(&rj, &pdata, &plen)) {
debug2((errout,"no ICC profile found in '%s'\n",file_name));
jpeg_destroy_decompress(&rj);
fclose(rf);
return NULL;
}
jpeg_destroy_decompress(&rj);
fclose(rf);
/* Make a copy of the profile to a memory buffer */
/* (icmAllocStd may not be the same as malloc ?) */
if ((al = new_icmAllocStd(&err)) == NULL) {
debug2(("new_icmAllocStd failed (0x%x, '%s')\n",err.c,err.m));
return NULL;
}
if ((buf = al->malloc(al, plen)) == NULL) {
debug("malloc of profile buffer failed\n");
al->del(al);
TIFFClose(rh);
return NULL;
}
memmove(buf, pdata, plen);
size = (int)plen;
free(pdata);
}
/* Memory File fp that will free the buffer when deleted: */
if ((fp = new_icmFileMem_ad(&err, buf, size, al)) == NULL) {
debug2(("Creating memory file from CMProfileLocation failed (0x%x, '%s')",err.c,err.m));
al->free(al, buf);
al->del(al);
return NULL;
}
if ((icco = new_icc(&err)) == NULL) {
debug2(("Creation of ICC object failed (0x%x, '%s')\n",err.c,err.m));
fp->del(fp); /* fp will delete al */
return NULL;
}
if ((rv = icco->read(icco,fp,0)) == 0) {
debug2((errout,"Opened '%s' embedded icc profile\n",file_name));
return icco;
}
debug2((errout,"Failed to read '%s' embedded icc profile\n",file_name));
icco->del(icco); /* icco will delete fp and al */
return NULL;
}
/* ------------------------------------------------------ */