The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"

#define TRANSFORMS_SUPPORTED   1
#define SAVE_MARKERS_SUPPORTED 1
#define ENTROPY_OPT_SUPPORTED  1

#include "jinclude.h"
#include <jpeglib.h>
#include "transupp.h"

#define DES_DECO_SRC 0x0001
#define DES_COMP_DST 0x0002

#define my_croak(...) \
	STMT_START { \
		if (fp != NULL) { fclose(fp); } \
		if (what_destroy & DES_COMP_DST) {\
			/*jpeg_finish_compress(&dstinfo);*/ \
			jpeg_destroy_compress(&dstinfo); \
		} \
		if (what_destroy & DES_DECO_SRC) { \
			/*(void) jpeg_finish_decompress(&srcinfo);*/\
			jpeg_destroy_decompress(&srcinfo);\
		}\
		croak(__VA_ARGS__);\
	} STMT_END

MODULE = Image::JpegTran		PACKAGE = Image::JpegTran

void
_jpegtran(src,dst,conf)
		char *src;
		char *dst;
		HV   *conf;
	PROTOTYPE: $$$
	CODE:
		SV **key;
		
		static JCOPY_OPTION             copyoption;      /* -copy switch */
		static jpeg_transform_info      transformoption; /* image transformation options */
		struct jpeg_decompress_struct   srcinfo;
		struct jpeg_compress_struct     dstinfo;
		
		struct jpeg_error_mgr jsrcerr, jdsterr;
		
		jvirt_barray_ptr * src_coef_arrays;
		jvirt_barray_ptr * dst_coef_arrays;
		int what_destroy = 0;
		/* We assume all-in-memory processing and can therefore use only a
		 * single file pointer for sequential input and output operation.
		 */
		FILE * fp = NULL;
		srcinfo.err = jpeg_std_error(&jsrcerr);
		jpeg_create_decompress(&srcinfo);
		what_destroy |= DES_DECO_SRC;
		/* Initialize the JPEG compression object with default error handling. */
		dstinfo.err = jpeg_std_error(&jdsterr);
		jpeg_create_compress(&dstinfo);
		what_destroy |= DES_COMP_DST;
		/*
			transformoption.transform = JXFORM_NONE;
			transformoption.perfect = FALSE;
			transformoption.trim = FALSE;
			transformoption.force_grayscale = FALSE;
			transformoption.crop = FALSE;
			
			scaleoption = NULL;
			copyoption = JCOPYOPT_DEFAULT;
			copyoption = JCOPYOPT_NONE;
			copyoption = JCOPYOPT_COMMENTS;
			copyoption = JCOPYOPT_ALL;
			
			select_transform(JXFORM_NONE);
			select_transform(JXFORM_FLIP_H);
			select_transform(JXFORM_FLIP_V);
			select_transform(JXFORM_ROT_90);
			select_transform(JXFORM_ROT_180);
			select_transform(JXFORM_ROT_270);
			select_transform(JXFORM_TRANSPOSE);
			select_transform(JXFORM_TRANSVERSE);
			transformoption.trim = TRUE;
			
			if (! jtransform_parse_crop_spec(&transformoption, argv[argn]));
		*/
		
		transformoption.transform = JXFORM_NONE;
		
		if ((key = hv_fetch(conf, "perfect", 7, 0)) && SvTRUE(*key)) {
			transformoption.perfect = TRUE;
		} else {
			transformoption.perfect = FALSE;
		}

		if ((key = hv_fetch(conf, "trim", 4, 0)) && SvTRUE(*key)) {
			transformoption.trim    = TRUE;
		} else {
			transformoption.trim    = FALSE;
		}


		if ((key = hv_fetch(conf, "grayscale", 9, 0)) && SvTRUE(*key)) {
			transformoption.force_grayscale = TRUE;
		} else {
			transformoption.force_grayscale = FALSE;
		}

		if (key = hv_fetch(conf, "rotate", 6, 0)) {
			if( transformoption.transform != JXFORM_NONE){ my_croak("Can't apply several transforms at once"); }
			if (SvIOK( *key )) {
				transformoption.transform = 
					SvIV(*key) ==  90 ? JXFORM_ROT_90 :
					SvIV(*key) == 180 ? JXFORM_ROT_180 :
					SvIV(*key) == 270 ? JXFORM_ROT_270 :
					JXFORM_NONE;
				if( transformoption.transform == JXFORM_NONE ) {
					my_croak("Bad value for rotate");
				}
			} else {
				my_croak("Bad value for rotate");
			}
		}
		if ((key = hv_fetch(conf, "transpose", 9, 0)) && SvTRUE(*key)) {
			if( transformoption.transform != JXFORM_NONE){ my_croak("Can't apply several transforms at once"); }
			transformoption.transform = JXFORM_TRANSPOSE;
		}
		if ((key = hv_fetch(conf, "transverse", 10, 0)) && SvTRUE(*key)) {
			if( transformoption.transform != JXFORM_NONE){ my_croak("Can't apply several transforms at once"); }
			transformoption.transform = JXFORM_TRANSVERSE;
		}
		if (key = hv_fetch(conf, "flip", 4, 0)) {
			if( transformoption.transform != JXFORM_NONE){ my_croak("Can't apply several transforms at once"); }
			if (SvPOK( *key )) {
				if (strEQ(SvPV_nolen(*key),"horizontal") || strEQ(SvPV_nolen(*key),"horisontal")) {
					transformoption.transform = JXFORM_FLIP_H;
				} else
				if (strEQ(SvPV_nolen(*key),"vertical")) {
					transformoption.transform = JXFORM_FLIP_V;
				} else
				{
					my_croak("Bad value for flip: %s",SvPV_nolen(*key));
				}
			} else {
				my_croak("Bad value for flip %s",SvPV_nolen(*key));
			}
		}
		
		if (key = hv_fetch(conf, "copy", 4, 0)) {
			if (SvPOK(*key)) {
				char *copyopt = SvPV_nolen(*key);
				// none comments exif all
				if (!strcmp(copyopt,"none")) {
					copyoption = JCOPYOPT_NONE;
				} else
				if (!strcmp(copyopt,"comments")) {
					copyoption = JCOPYOPT_COMMENTS;
				} else 
				if (!strcmp(copyopt,"exif")) {
					copyoption = JCOPYOPT_ALL;
				} else 
				if (!strcmp(copyopt,"all")) {
					copyoption = JCOPYOPT_ALL;
				} else
				{
					my_croak("Bad value for copy `%s'. Available are: none, exif, comments, all", copyopt);
				}
			} else {
				my_croak("Bad value for copy");
			}
		} else {
			copyoption = JCOPYOPT_ALL;
		}
		
		if ((key = hv_fetch(conf, "optimize", 8, 0)) && SvTRUE(*key)) {
			/* Enable entropy parm optimization. */
			dstinfo.optimize_coding = TRUE;
		}
		
		if ((key = hv_fetch(conf, "arithmetic", 10, 0)) && SvTRUE(*key)) {
			//#ifdef C_ARITH_CODING_SUPPORTED
				dstinfo.arith_code = TRUE;
			//#else
			//	warn("sorry, arithmetic coding not supported");
			//	dstinfo.arith_code = FALSE;
			//#endif
		} else {
			dstinfo.arith_code = FALSE;
		}
		
		if ((key = hv_fetch(conf, "maxmemory", 9, 0))) {
			if (SvIOK(*key)) {
				dstinfo.mem->max_memory_to_use = SvIV(*key);
			} else {
				my_croak("Bad value for maxmemory: %s",SvPV_nolen(*key));
			}
		}
		
		//transformoption.crop            = TRUE; // Not implemented
		
		if ((fp = fopen(src, READ_BINARY)) == NULL) {
			my_croak("can't open `%s' for reading",src);
		}

		jpeg_stdio_src(&srcinfo, fp);
		
		/* Enable saving of extra markers that we want to copy */
		jcopy_markers_setup(&srcinfo, copyoption);
		
		/* Read file header */
		(void) jpeg_read_header(&srcinfo, TRUE);
		if (!jtransform_request_workspace(&srcinfo, &transformoption)) {
			my_croak("transformation is not perfect");
		}
		
		/* Read source file as DCT coefficients */
		src_coef_arrays = jpeg_read_coefficients(&srcinfo);
		
		/* Initialize destination compression parameters from source values */
		jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
		
		/* Adjust destination parameters if required by transform options;
		 * also find out which set of coefficient arrays will hold the output.
		 */
		dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, src_coef_arrays, &transformoption);

		/* Close input file, if we opened it.
		 * Note: we assume that jpeg_read_coefficients consumed all input
		 * until JPEG_REACHED_EOI, and that jpeg_finish_decompress will
		 * only consume more while (! cinfo->inputctl->eoi_reached).
		 * We cannot call jpeg_finish_decompress here since we still need the
		 * virtual arrays allocated from the source object for processing.
		 */
		fclose(fp);

		/* Open the output file. */
		if ((fp = fopen(dst, WRITE_BINARY)) == NULL) {
			my_croak("can't open `%s' for writing",dst);
		}

		/* Adjust default compression parameters by re-parsing the options */
		//file_index = parse_switches(&dstinfo, argc, argv, 0, TRUE);
		//TODO
		if ((key = hv_fetch(conf, "progressive", 11, 0)) && SvTRUE(*key)) {
			jpeg_simple_progression(&dstinfo);
		}

		//#ifdef C_MULTISCAN_FILES_SUPPORTED
		//if ((key = hv_fetch(conf, "scans", 5, 0))) {
		//	if (SvPOK(*key)) {
		//		char *scansarg = SvPV_nolen(*key);
		//		if (! read_scan_script(&dstinfo, scansarg)) {
		//			my_croak("Can't read scans script `%s'",scansarg);
		//		}
		//	}
		//}
		//#endif

		/* Specify data destination for compression */
		jpeg_stdio_dest(&dstinfo, fp);

		/* Start compressor (note no image data is actually written here) */
		jpeg_write_coefficients(&dstinfo, dst_coef_arrays);

		/* Copy to the output file any extra markers that we want to preserve */
		jcopy_markers_execute(&srcinfo, &dstinfo, copyoption);

		jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transformoption);

		/* Finish compression and release memory */
		jpeg_finish_compress(&dstinfo);
		jpeg_destroy_compress(&dstinfo);
		(void) jpeg_finish_decompress(&srcinfo);
		jpeg_destroy_decompress(&srcinfo);

		fclose(fp);
		if( jsrcerr.num_warnings + jdsterr.num_warnings ) {
			warn("Compression/decompression have warings");
		}