// Tabs:3 Indent:3

/*
 * JpegToTiff.m, JPEG (JFIF) to TIFF converter.
 * Author: Michael McCulloch
 * Written for 3.2, January 15, 1994.
 *
 * You may freely copy, distribute and reuse the code in this example.
 * Michael McCulloch disclaims any warranty of any kind, expressed or 
 * implied, as to its fitness for any particular use.
 */

#import <appkit/appkit.h>
#import <libc.h>
#import <string.h>
#import <stdio.h>
#import <jpeg/jinclude.h>
#import <setjmp.h>

static jmp_buf setjmp_buffer;				/* for return to caller */
static external_methods_ptr emethods;	/* needed for access to message_parm */
NXBitmapImageRep *tiff = nil;
unsigned char *rgb[5];
int rowCount;

unsigned char	gamtab[256];		// gamma correction table

#define	IMAX		((1<<8)-1)	/* max intensity value */



METHODDEF void trace_message (const char *msgtext)
{
	fprintf(stderr, msgtext,
		emethods->message_parm[0], emethods->message_parm[1],
		emethods->message_parm[2], emethods->message_parm[3],
		emethods->message_parm[4], emethods->message_parm[5],
		emethods->message_parm[6], emethods->message_parm[7]);
	fprintf(stderr, "\n");		/* there is no \n in the format string! */
}


METHODDEF void error_exit (const char *msgtext)
{
	trace_message(msgtext);		/* report the error message */
	(*emethods->free_all) ();	/* clean up memory allocation & temp files */
	longjmp(setjmp_buffer, 1);	/* return control to outer routine */
#ifdef DEBUG
		fprintf(stderr, "Conversion died.\n");
#endif
}


METHODDEF void output_init (decompress_info_ptr cinfo)
/* This routine should do any setup required */
{
  /* This routine can initialize for output based on the data passed in cinfo.
   * Useful fields include:
   *	image_width, image_height	Pretty obvious, I hope.
   *	data_precision			bits per pixel value; typically 8.
   *	out_color_space			output colorspace previously requested
   *	color_out_comps			number of color components in same
   *	final_out_comps			number of components actually output
   * final_out_comps is 1 if quantize_colors is true, else it is equal to
   * color_out_comps.
   *
   * If you have requested color quantization, the colormap is NOT yet set.
   * You may wish to defer output initialization until put_color_map is called.
   */
	NXSize pSize={cinfo->image_width, cinfo->image_height};
	
	if (cinfo->jpeg_color_space == CS_GRAYSCALE){
#ifdef DEBUG
		fprintf(stderr, "CS_GRAYSCALE image type.\n");
#endif
		tiff = [[NXBitmapImageRep alloc] initDataPlanes:NULL
				pixelsWide:cinfo->image_width
				pixelsHigh:cinfo->image_height
				bitsPerSample:cinfo->data_precision
				samplesPerPixel:cinfo->color_out_comps
				hasAlpha:NO
				isPlanar:NO
				colorSpace:NX_OneIsWhiteColorSpace
				bytesPerRow:0
				bitsPerPixel:0];
	}
	else if(cinfo->jpeg_color_space == CS_CMYK){
#ifdef DEBUG
		fprintf(stderr, "CS_CMYK image type.\n");
#endif
		tiff = [[NXBitmapImageRep alloc] initDataPlanes:NULL
				pixelsWide:cinfo->image_width
				pixelsHigh:cinfo->image_height
				bitsPerSample:cinfo->data_precision
				samplesPerPixel:cinfo->color_out_comps
				hasAlpha:NO
				isPlanar:YES
				colorSpace:NX_CMYKColorSpace
				bytesPerRow:0
				bitsPerPixel:0];
	}
	else{
#ifdef DEBUG
		fprintf(stderr, "CS_RGB image type.\n");
#endif
		tiff = [[NXBitmapImageRep alloc] initDataPlanes:NULL
				pixelsWide:cinfo->image_width
				pixelsHigh:cinfo->image_height
				bitsPerSample:cinfo->data_precision
				samplesPerPixel:cinfo->color_out_comps
				hasAlpha:NO
				isPlanar:YES
				colorSpace:NX_RGBColorSpace
				bytesPerRow:0
				bitsPerPixel:0];
	}

#ifdef DEBUG
		fprintf(stderr, "Data precsn = %d  smps/pixel = %d.\n", cinfo->data_precision, cinfo->color_out_comps);
#endif

	/* init bitmap image rep */
	[tiff setSize:&pSize];
	[tiff getDataPlanes:rgb];
	rowCount = 0;
}


METHODDEF void put_pixel_rows (decompress_info_ptr cinfo, int num_rows, JSAMPIMAGE pixel_data)
/* Write some rows of output data */
{
	register JSAMPROW ptr0, ptr1, ptr2,ptr3;
	register long col;
	register int row;

	if(cinfo->jpeg_color_space == CS_GRAYSCALE){
		for (row = 0; row < num_rows; row++) {
			ptr0 = pixel_data[0][row];
			for (col = 0; col < cinfo->image_width; col++) {
				*(rgb[0]+col+(rowCount*cinfo->image_width)) = gamtab[*ptr0++];
			}
			rowCount++;
		}
	}
	else if(cinfo->jpeg_color_space == CS_CMYK){
		for (row = 0; row < num_rows; row++) {
			ptr0 = pixel_data[0][row];
			ptr1 = pixel_data[1][row];
			ptr2 = pixel_data[2][row];
			ptr3 = pixel_data[3][row];
			for (col = 0; col < cinfo->image_width; col++) {
				*(rgb[0]+col+(rowCount*cinfo->image_width)) = gamtab[*ptr0++];
				*(rgb[1]+col+(rowCount*cinfo->image_width)) = gamtab[*ptr1++];
				*(rgb[2]+col+(rowCount*cinfo->image_width)) = gamtab[*ptr2++];
				*(rgb[3]+col+(rowCount*cinfo->image_width)) = gamtab[*ptr3++];
			}
			rowCount++;
		}
	}
	else{
		for (row = 0; row < num_rows; row++) {
			ptr0 = pixel_data[0][row];
			ptr1 = pixel_data[1][row];
			ptr2 = pixel_data[2][row];
			for (col = 0; col < cinfo->image_width; col++) {
				*(rgb[0]+col+(rowCount*cinfo->image_width)) = gamtab[*ptr0++];
				*(rgb[1]+col+(rowCount*cinfo->image_width)) = gamtab[*ptr1++];
				*(rgb[2]+col+(rowCount*cinfo->image_width)) = gamtab[*ptr2++];
			}
			rowCount++;
		}
	}
}


METHODDEF void output_term (decompress_info_ptr cinfo)
/* Finish up at the end of the output */
{
	/* This termination routine may not need to do anything. */
	/* Note that the JPEG code will only call it during successful exit; */
	/* if you want it called during error exit, you gotta do that yourself. */
}


METHODDEF void d_ui_method_selection (decompress_info_ptr cinfo)
{
	/* if grayscale input, force grayscale output; */
	/* else leave the output colorspace as set by main routine. */
	if (cinfo->jpeg_color_space == CS_GRAYSCALE)
		cinfo->out_color_space = CS_GRAYSCALE;
	if(cinfo->jpeg_color_space == CS_CMYK)
		cinfo->out_color_space = CS_CMYK;
	
	/* select output routines */
	cinfo->methods->output_init = output_init;
	cinfo->methods->put_pixel_rows = put_pixel_rows;
	cinfo->methods->output_term = output_term;
}


NXBitmapImageRep *convertJPEGToTIFF(const char *imageFile,float gammaFactor, BOOL showMessage)
{
	struct Decompress_info_struct cinfo;
	struct Decompress_methods_struct dc_methods;
	struct External_methods_struct e_methods;
	int i;

	/* create gamma table */
	for(i=0; i<256; i++)
		gamtab[i] = (IMAX*pow(i/255.0,gammaFactor)+0.5);

	if ((cinfo.input_file = fopen(imageFile, "rb")) == NULL) {
		fprintf(stderr, "can't open %s\n", imageFile);
		return 0;
	}

	cinfo.output_file = NULL;		/* no actual output file involved */
	cinfo.quantize_colors = FALSE;	/* no colormap junk, let PS do the dithering */

	/* Initialize the system-dependent method pointers. */
	cinfo.methods = &dc_methods;	/* links to method structs */
	cinfo.emethods = &e_methods;

	/* Here we supply our own error handler; compare to use of standard
	 * error handler in the previous write_JPEG_file example.
	 */
	emethods = &e_methods;				/* save struct addr for possible access */
	e_methods.error_exit = error_exit;	/* supply error-exit routine */
	e_methods.trace_message = trace_message;	/* supply trace-message routine */
	e_methods.trace_level = 0;				/* default = no tracing */
	e_methods.num_warnings = 0;			/* no warnings emitted yet */
	e_methods.first_warning_level = 0;	/* display first corrupt-data warning */
	e_methods.more_warning_level = 3;	/* but suppress additional ones */

	/* prepare setjmp context for possible exit from error_exit */
	if (setjmp(setjmp_buffer)) {
		fclose(cinfo.input_file);
		return 0;
	}

	jselmemmgr(&e_methods);				/* select std memory allocation routines */

#ifdef DEBUG
	fprintf(stderr, "Called jselmemmgr().\n");
#endif

	dc_methods.d_ui_method_selection = d_ui_method_selection;

	/* Set up default decompression parameters. */
	j_d_defaults(&cinfo, TRUE);

#ifdef DEBUG
	fprintf(stderr, "Called j_d_defaults().\n");
#endif

	/* Set up to read a JFIF or baseline-JPEG file. */
	/* This is the only JPEG file format currently supported. */
	jselrjfif(&cinfo);
	
#ifdef DEBUG
		fprintf(stderr, "Header was read successfully.\n");
#endif
			
	/* Here we go! */
	jpeg_decompress(&cinfo);
	
#ifdef DEBUG
		fprintf(stderr, "Image was decompressed.\n");
#endif
	
	if(showMessage)
		fprintf(stderr, "McFilter:JPEGFilter converting %s: %d x %d\n", (strrchr(imageFile,'/')+1), cinfo.image_width, cinfo.image_height);

	fclose(cinfo.input_file);

	return tiff;
}
