
/* 
 * 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
#if defined(__IBMC__) && defined(_M_IX86)
#include <float.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;
}

/* ------------------------------------------------------ */
























