/******************************************************************************
 * Steve Rider - stever@transmission23.com
 * QuickPics
 *   Image galleries for the web made easy
 *
 * image.c - image utilities for reading/writing/resizing images
 *
 *****************************************************************************/
/* 17 February 98 */

#include <stdio.h>
#include "jpeglib.h"
#include "image.h"
#include "utility.h"
#include "qp.h"
#include <setjmp.h>


int IsImage(char *filepath, int *type)
{
	char *ext;

	*type = 0;
	ext = FilenameExtension(filepath);
	if(ext == NULL)
		return 0;

	if(isequal(ext,"jpg") || isequal(ext,"jpeg"))
	{
		*type = IMAGE_JPG;
		return 1;
	}
	else if(isequal(ext,"gif"))
	{
		*type = IMAGE_GIF;
		return 1;
	}
	else
		return 0;
}

struct my_error_mgr {
	struct jpeg_error_mgr pub;	/* "public" fields */

	jmp_buf setjmp_buffer;	/* for return to caller */
};

typedef struct my_error_mgr * my_error_ptr;

static void my_error_exit(j_common_ptr cinfo)
{
	/* cinfo->err points to a my_error_mgr struct, so coerce pointer */
	my_error_ptr myerr = (my_error_ptr) cinfo->err;

	/* Always display the message. */
	/* We could postpone this until after returning, if we chose. */
	(*cinfo->err->output_message) (cinfo);

	/* Return control to the setjmp point */
	longjmp(myerr->setjmp_buffer, 1);
}


int Image_GetDimensions(char *filename, int *width, int *height)
{
	struct jpeg_decompress_struct cinfo;
	struct my_error_mgr jerr;
	FILE *infile;

	*width = 0;
	*height = 0;

	/* Open image file */
	if((infile = fopen(filename,ReadBinaryType)) == NULL)
		return 0;


	/* Step 1: allocate and initialize JPEG decompression object */
	cinfo.err = jpeg_std_error(&jerr.pub);
	jerr.pub.error_exit = my_error_exit;

	/* Establish the setjmp return context for my_error_exit to use. */
	if(setjmp(jerr.setjmp_buffer))
	{
		jpeg_destroy_decompress(&cinfo);
		fclose(infile);
		return 0;
	}

	jpeg_create_decompress(&cinfo);


	/* Step 2: Specify data source, read header, start decompressor */
	jpeg_stdio_src(&cinfo, infile);
	jpeg_read_header(&cinfo, TRUE);
	jpeg_start_decompress(&cinfo);

	/* This is what we came for */
	*width = cinfo.image_width;
	*height = cinfo.image_height;


	/* Step 3: Release JPEG decompression object and close file. */
	jpeg_destroy_decompress(&cinfo);
	fclose(infile);

	/* At this point you may want to check to see whether any corrupt-data
	 * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
	 */

	/* And we're done! */
	return 1;
}

int Image_Scale(char *in, char *out, int scale_denom, int q, int *w, int *h)
{
	struct jpeg_decompress_struct dinfo;
	struct jpeg_compress_struct cinfo;
	struct my_error_mgr my_jerr;
	struct jpeg_error_mgr jerr;
	JSAMPARRAY image_buffer;
	JSAMPROW row_buffer[1];
	FILE	*infile, *outfile;
	int	row_stride, i;
	int	output_width, output_height, out_color_components;

	*w = 0;
	*h = 0;

	/* Open input file */
	if((infile = fopen(in,ReadBinaryType)) == NULL)
	{
		fprintf(stderr, "Unable to open %s for reading.\n",in);
		return 0;
	}

	/* Open output file */
	if((outfile = fopen(out,WriteBinaryType)) == NULL)
	{
		fprintf(stderr, "Unable to open %s for writing.\n",out);
		return 0;
	}


	/* Step 1: Allocate and initalize JPEG decompression object */
	dinfo.err = jpeg_std_error(&my_jerr.pub);
	my_jerr.pub.error_exit = my_error_exit;

	/* Establish the setjmp return context for my_error_exit to use. */
	if(setjmp(my_jerr.setjmp_buffer))
	{
		jpeg_destroy_decompress(&dinfo);
		fclose(infile);
		return 0;
	}

	/* Create object, specify source, and read header */
	jpeg_create_decompress(&dinfo);
	jpeg_stdio_src(&dinfo, infile);
	jpeg_read_header(&dinfo, TRUE);

	/* Set parameters for decompression */
	dinfo.scale_num = 1;
	dinfo.scale_denom = scale_denom;

	/* Start decompressor */
	jpeg_start_decompress(&dinfo);

	/* Store output properties */
	output_width = dinfo.output_width;
	output_height = dinfo.output_height;
	out_color_components = dinfo.out_color_components;

	/* Allocate memory to hold entire image  */
	row_stride = dinfo.output_width * dinfo.output_components;
	image_buffer = (JSAMPARRAY)malloc(dinfo.output_height*sizeof(JSAMPROW));
	for(i=0; i<dinfo.output_height; i++)
		image_buffer[i] = (JSAMPLE *)malloc(row_stride*sizeof(JSAMPLE));

	/* Allocate memory to hold a single row */
	row_buffer[0] = (JSAMPLE *)malloc(row_stride*sizeof(JSAMPLE));

	/* Read image from disk and copy scanlines to image_buffer. */
	while(dinfo.output_scanline < dinfo.output_height)
	{
		/* Read scanlines from decompressor into image_buffer */
		jpeg_read_scanlines(&dinfo, row_buffer, 1);

		/* Copy row into image_buffer */
		for(i=0; i<row_stride; i++)
			image_buffer[dinfo.output_scanline-1][i] = row_buffer[0][i];
	}

	/* Finish decompression and release jpeg object */
	jpeg_finish_decompress(&dinfo);
	jpeg_destroy_decompress(&dinfo);

	/* now compress back to disk */
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_compress(&cinfo);
	jpeg_stdio_dest(&cinfo, outfile);

	*w = output_width;
	*h = output_height;
	cinfo.image_width = output_width;
	cinfo.image_height = output_height;
	cinfo.input_components = out_color_components;
	cinfo.in_color_space = JCS_RGB;

	/* compression parameters */
	jpeg_set_defaults(&cinfo);
	jpeg_set_quality(&cinfo, q, TRUE);
	cinfo.optimize_coding = 1;
	cinfo.dct_method = JDCT_FLOAT;
	jpeg_start_compress(&cinfo, TRUE);

	/* Write scanlines into compressor from buffer */
	while(cinfo.next_scanline < cinfo.image_height)
	{
		/* No need to copy, just reassign pointer */
		row_buffer[0] = image_buffer[cinfo.next_scanline];
		jpeg_write_scanlines(&cinfo, row_buffer, 1);
	}

	jpeg_finish_compress(&cinfo);
	jpeg_destroy_compress(&cinfo);

	/* Close files */
	fclose(infile);
	fclose(outfile);

	/* Free memory */
	for(i=0; i<output_height; i++)
		free(image_buffer[i]);
#if 0
	free(image_buffer);
	free(row_buffer[0]);
#endif

	/* At this point you may want to check to see whether any corrupt-data
	 * warnings occurred (test whether my_jerr.pub.num_warnings is nonzero).
	 */

	return 1;
}
