Esempio n. 1
0
parse_switches (j_compress_ptr cinfo, int argc, char **argv,
                int last_file_arg_seen, boolean for_real)
/* Parse optional switches.
 * Returns argv[] index of first file-name argument (== argc if none).
 * Any file names with indexes <= last_file_arg_seen are ignored;
 * they have presumably been processed in a previous iteration.
 * (Pass 0 for last_file_arg_seen on the first or only iteration.)
 * for_real is FALSE on the first (dummy) pass; we may skip any expensive
 * processing.
 */
{
  int argn;
  char * arg;
  boolean simple_progressive;
  char * scansarg = NULL;       /* saves -scans parm if any */

  /* Set up default JPEG parameters. */
#ifdef C_PROGRESSIVE_SUPPORTED
  simple_progressive = cinfo->num_scans == 0 ? FALSE : TRUE;
#else
  simple_progressive = FALSE;
#endif
  outfilename = NULL;
  copyoption = JCOPYOPT_DEFAULT;
  transformoption.transform = JXFORM_NONE;
  transformoption.perfect = FALSE;
  transformoption.trim = FALSE;
  transformoption.force_grayscale = FALSE;
  transformoption.crop = FALSE;
  transformoption.slow_hflip = FALSE;
  cinfo->err->trace_level = 0;

  /* Scan command line options, adjust parameters */

  for (argn = 1; argn < argc; argn++) {
    arg = argv[argn];
    if (*arg != '-') {
      /* Not a switch, must be a file name argument */
      if (argn <= last_file_arg_seen) {
        outfilename = NULL;     /* -outfile applies to just one input file */
        continue;               /* ignore this name if previously processed */
      }
      break;                    /* else done parsing switches */
    }
    arg++;                      /* advance past switch marker character */

    if (keymatch(arg, "arithmetic", 1)) {
      /* Use arithmetic coding. */
#ifdef C_ARITH_CODING_SUPPORTED
      cinfo->arith_code = TRUE;
#else
      fprintf(stderr, "%s: sorry, arithmetic coding not supported\n",
              progname);
      exit(EXIT_FAILURE);
#endif

    } else if (keymatch(arg, "copy", 2)) {
      /* Select which extra markers to copy. */
      if (++argn >= argc)       /* advance to next argument */
        usage();
      if (keymatch(argv[argn], "none", 1)) {
        copyoption = JCOPYOPT_NONE;
      } else if (keymatch(argv[argn], "comments", 1)) {
        copyoption = JCOPYOPT_COMMENTS;
      } else if (keymatch(argv[argn], "all", 1)) {
        copyoption = JCOPYOPT_ALL;
      } else
        usage();

    } else if (keymatch(arg, "crop", 2)) {
      /* Perform lossless cropping. */
#if TRANSFORMS_SUPPORTED
      if (++argn >= argc)       /* advance to next argument */
        usage();
      if (! jtransform_parse_crop_spec(&transformoption, argv[argn])) {
        fprintf(stderr, "%s: bogus -crop argument '%s'\n",
                progname, argv[argn]);
        exit(EXIT_FAILURE);
      }
#else
      select_transform(JXFORM_NONE);    /* force an error */
#endif

    } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) {
      /* Enable debug printouts. */
      /* On first -d, print version identification */
      static boolean printed_version = FALSE;

      if (! printed_version) {
        fprintf(stderr, "%s version %s (build %s)\n",
                PACKAGE_NAME, VERSION, BUILD);
        fprintf(stderr, "%s\n\n", JCOPYRIGHT);
        fprintf(stderr, "Emulating The Independent JPEG Group's software, version %s\n\n",
                JVERSION);
        printed_version = TRUE;
      }
      cinfo->err->trace_level++;

    } else if (keymatch(arg, "flip", 1)) {
      /* Mirror left-right or top-bottom. */
      if (++argn >= argc)       /* advance to next argument */
        usage();
      if (keymatch(argv[argn], "horizontal", 1))
        select_transform(JXFORM_FLIP_H);
      else if (keymatch(argv[argn], "vertical", 1))
        select_transform(JXFORM_FLIP_V);
      else
        usage();

    } else if (keymatch(arg, "fastcrush", 4)) {
      cinfo->optimize_scans = FALSE;
      
    } else if (keymatch(arg, "grayscale", 1) || keymatch(arg, "greyscale",1)) {
      /* Force to grayscale. */
#if TRANSFORMS_SUPPORTED
      transformoption.force_grayscale = TRUE;
#else
      select_transform(JXFORM_NONE);    /* force an error */
#endif

    } else if (keymatch(arg, "maxmemory", 3)) {
      /* Maximum memory in Kb (or Mb with 'm'). */
      long lval;
      char ch = 'x';

      if (++argn >= argc)       /* advance to next argument */
        usage();
      if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1)
        usage();
      if (ch == 'm' || ch == 'M')
        lval *= 1000L;
      cinfo->mem->max_memory_to_use = lval * 1000L;

    } else if (keymatch(arg, "optimize", 1) || keymatch(arg, "optimise", 1)) {
      /* Enable entropy parm optimization. */
#ifdef ENTROPY_OPT_SUPPORTED
      cinfo->optimize_coding = TRUE;
#else
      fprintf(stderr, "%s: sorry, entropy optimization was not compiled\n",
              progname);
      exit(EXIT_FAILURE);
#endif

    } else if (keymatch(arg, "outfile", 4)) {
      /* Set output file name. */
      if (++argn >= argc)       /* advance to next argument */
        usage();
      outfilename = argv[argn]; /* save it away for later use */

    } else if (keymatch(arg, "perfect", 2)) {
      /* Fail if there is any partial edge MCUs that the transform can't
       * handle. */
      transformoption.perfect = TRUE;

    } else if (keymatch(arg, "progressive", 2)) {
      /* Select simple progressive mode. */
#ifdef C_PROGRESSIVE_SUPPORTED
      simple_progressive = TRUE;
      /* We must postpone execution until num_components is known. */
#else
      fprintf(stderr, "%s: sorry, progressive output was not compiled\n",
              progname);
      exit(EXIT_FAILURE);
#endif

    } else if (keymatch(arg, "restart", 1)) {
      /* Restart interval in MCU rows (or in MCUs with 'b'). */
      long lval;
      char ch = 'x';

      if (++argn >= argc)       /* advance to next argument */
        usage();
      if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1)
        usage();
      if (lval < 0 || lval > 65535L)
        usage();
      if (ch == 'b' || ch == 'B') {
        cinfo->restart_interval = (unsigned int) lval;
        cinfo->restart_in_rows = 0; /* else prior '-restart n' overrides me */
      } else {
        cinfo->restart_in_rows = (int) lval;
        /* restart_interval will be computed during startup */
      }

    } else if (keymatch(arg, "revert", 3)) {
      /* revert to old JPEG default */
      cinfo->use_moz_defaults = FALSE;
      
    } else if (keymatch(arg, "rotate", 2)) {
      /* Rotate 90, 180, or 270 degrees (measured clockwise). */
      if (++argn >= argc)       /* advance to next argument */
        usage();
      if (keymatch(argv[argn], "90", 2))
        select_transform(JXFORM_ROT_90);
      else if (keymatch(argv[argn], "180", 3))
        select_transform(JXFORM_ROT_180);
      else if (keymatch(argv[argn], "270", 3))
        select_transform(JXFORM_ROT_270);
      else
        usage();

    } else if (keymatch(arg, "scans", 1)) {
      /* Set scan script. */
#ifdef C_MULTISCAN_FILES_SUPPORTED
      if (++argn >= argc)       /* advance to next argument */
        usage();
      scansarg = argv[argn];
      /* We must postpone reading the file in case -progressive appears. */
#else
      fprintf(stderr, "%s: sorry, multi-scan output was not compiled\n",
              progname);
      exit(EXIT_FAILURE);
#endif

    } else if (keymatch(arg, "transpose", 1)) {
      /* Transpose (across UL-to-LR axis). */
      select_transform(JXFORM_TRANSPOSE);

    } else if (keymatch(arg, "transverse", 6)) {
      /* Transverse transpose (across UR-to-LL axis). */
      select_transform(JXFORM_TRANSVERSE);

    } else if (keymatch(arg, "trim", 3)) {
      /* Trim off any partial edge MCUs that the transform can't handle. */
      transformoption.trim = TRUE;

    } else {
      usage();                  /* bogus switch */
    }
  }

  /* Post-switch-scanning cleanup */

  if (for_real) {

#ifdef C_PROGRESSIVE_SUPPORTED
    if (simple_progressive)     /* process -progressive; -scans can override */
      jpeg_simple_progression(cinfo);
#endif

#ifdef C_MULTISCAN_FILES_SUPPORTED
    if (scansarg != NULL)       /* process -scans if it was present */
      if (! read_scan_script(cinfo, scansarg))
        usage();
#endif
  }

  return argn;                  /* return index of next arg (file name) */
}
Esempio n. 2
0
static BOOL
JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
	const BOOL onlyReturnCropRect = (dst_io == NULL) || (dst_handle == NULL);
	const long stream_start = onlyReturnCropRect ? 0 : dst_io->tell_proc(dst_handle);
	BOOL swappedDim = FALSE;
	BOOL trimH = FALSE;
	BOOL trimV = FALSE;

	// Set up the jpeglib structures
	jpeg_decompress_struct srcinfo;
	jpeg_compress_struct dstinfo;
	jpeg_error_mgr jsrcerr, jdsterr;
	jvirt_barray_ptr *src_coef_arrays = NULL;
	jvirt_barray_ptr *dst_coef_arrays = NULL;
	// Support for copying optional markers from source to destination file
	JCOPY_OPTION copyoption;
	// Image transformation options
	jpeg_transform_info transfoptions;

	// Initialize structures
	memset(&srcinfo, 0, sizeof(srcinfo));
	memset(&jsrcerr, 0, sizeof(jsrcerr));
	memset(&jdsterr, 0, sizeof(jdsterr));
	memset(&dstinfo, 0, sizeof(dstinfo));
	memset(&transfoptions, 0, sizeof(transfoptions));

	// Copy all extra markers from source file
	copyoption = JCOPYOPT_ALL;

	// Set up default JPEG parameters
	transfoptions.force_grayscale = FALSE;
	transfoptions.crop = FALSE;

	// Select the transform option
	switch(operation) {
		case FIJPEG_OP_FLIP_H:		// horizontal flip
			transfoptions.transform = JXFORM_FLIP_H;
			trimH = TRUE;
			break;
		case FIJPEG_OP_FLIP_V:		// vertical flip
			transfoptions.transform = JXFORM_FLIP_V;
			trimV = TRUE;
			break;
		case FIJPEG_OP_TRANSPOSE:	// transpose across UL-to-LR axis
			transfoptions.transform = JXFORM_TRANSPOSE;
			swappedDim = TRUE;
			break;
		case FIJPEG_OP_TRANSVERSE:	// transpose across UR-to-LL axis
			transfoptions.transform = JXFORM_TRANSVERSE;
			trimH = TRUE;
			trimV = TRUE;
			swappedDim = TRUE;
			break;
		case FIJPEG_OP_ROTATE_90:	// 90-degree clockwise rotation
			transfoptions.transform = JXFORM_ROT_90;
			trimH = TRUE;
			swappedDim = TRUE;
			break;
		case FIJPEG_OP_ROTATE_180:	// 180-degree rotation
			trimH = TRUE;
			trimV = TRUE;
			transfoptions.transform = JXFORM_ROT_180;
			break;
		case FIJPEG_OP_ROTATE_270:	// 270-degree clockwise (or 90 ccw)
			transfoptions.transform = JXFORM_ROT_270;
			trimV = TRUE;
			swappedDim = TRUE;
			break;
		default:
		case FIJPEG_OP_NONE:		// no transformation
			transfoptions.transform = JXFORM_NONE;
			break;
	}
	// (perfect == TRUE) ==> fail if there is non-transformable edge blocks
	transfoptions.perfect = (perfect == TRUE) ? TRUE : FALSE;
	// Drop non-transformable edge blocks: trim off any partial edge MCUs that the transform can't handle.
	transfoptions.trim = TRUE;

	try {

		// Initialize the JPEG decompression object with default error handling
		srcinfo.err = jpeg_std_error(&jsrcerr);
		srcinfo.err->error_exit = ls_jpeg_error_exit;
		srcinfo.err->output_message = ls_jpeg_output_message;
		jpeg_create_decompress(&srcinfo);

		// Initialize the JPEG compression object with default error handling
		dstinfo.err = jpeg_std_error(&jdsterr);
		dstinfo.err->error_exit = ls_jpeg_error_exit;
		dstinfo.err->output_message = ls_jpeg_output_message;
		jpeg_create_compress(&dstinfo);

		// Specify data source for decompression
		jpeg_freeimage_src(&srcinfo, src_handle, src_io);

		// Enable saving of extra markers that we want to copy
		jcopy_markers_setup(&srcinfo, copyoption);

		// Read the file header
		jpeg_read_header(&srcinfo, TRUE);

		// crop option
		char crop[64];
		const BOOL hasCrop = getCropString(crop, left, top, right, bottom, swappedDim ? srcinfo.image_height : srcinfo.image_width, swappedDim ? srcinfo.image_width : srcinfo.image_height);

		if(hasCrop) {
			if(!jtransform_parse_crop_spec(&transfoptions, crop)) {
				FreeImage_OutputMessageProc(FIF_JPEG, "Bogus crop argument %s", crop);
				throw(1);
			}
		}

		// Any space needed by a transform option must be requested before
		// jpeg_read_coefficients so that memory allocation will be done right

		// Prepare transformation workspace
		// Fails right away if perfect flag is TRUE and transformation is not perfect
		if( !jtransform_request_workspace(&srcinfo, &transfoptions) ) {
			FreeImage_OutputMessageProc(FIF_JPEG, "Transformation is not perfect");
			throw(1);
		}

		if(left || top) {
			// compute left and top offsets, it's a bit tricky, taking into account both
			// transform, which might have trimed the image,
			// and crop itself, which is adjusted to lie on a iMCU boundary

			const int fullWidth = swappedDim ? srcinfo.image_height : srcinfo.image_width;
			const int fullHeight = swappedDim ? srcinfo.image_width : srcinfo.image_height;

			int transformedFullWidth = fullWidth;
			int transformedFullHeight = fullHeight;

			if(trimH && transformedFullWidth/transfoptions.iMCU_sample_width > 0) {
				transformedFullWidth = (transformedFullWidth/transfoptions.iMCU_sample_width) * transfoptions.iMCU_sample_width;
			}
			if(trimV && transformedFullHeight/transfoptions.iMCU_sample_height > 0) {
				transformedFullHeight = (transformedFullHeight/transfoptions.iMCU_sample_height) * transfoptions.iMCU_sample_height;
			}

			const int trimmedWidth = fullWidth - transformedFullWidth;
			const int trimmedHeight = fullHeight - transformedFullHeight;

			if(left) {
				*left = trimmedWidth + transfoptions.x_crop_offset * transfoptions.iMCU_sample_width;
			}
			if(top) {
				*top = trimmedHeight + transfoptions.y_crop_offset * transfoptions.iMCU_sample_height;
			}
		}

		if(right) {
			*right = (left ? *left : 0) + transfoptions.output_width;
		}
		if(bottom) {
			*bottom = (top ? *top : 0) + transfoptions.output_height;
		}

		// if only the crop rect is requested, we are done

		if(onlyReturnCropRect) {
			jpeg_destroy_compress(&dstinfo);
			jpeg_destroy_decompress(&srcinfo);
			return TRUE;
		}

		// 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, &transfoptions);

		// 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.

		if(src_handle == dst_handle) {
			dst_io->seek_proc(dst_handle, stream_start, SEEK_SET);
		}

		// Specify data destination for compression
		jpeg_freeimage_dst(&dstinfo, dst_handle, dst_io);

		// 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);

		// Execute image transformation, if any
		jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);

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

	}
	catch(...) {
		jpeg_destroy_compress(&dstinfo);
		jpeg_destroy_decompress(&srcinfo);
		return FALSE;
	}

	return TRUE;
}
Esempio n. 3
0
static BOOL  
LosslessTransform(const FilenameIO *filenameIO, FREE_IMAGE_JPEG_OPERATION operation, const char *crop, BOOL perfect) {
	// We assume all-in-memory processing and can therefore use only a
	// single file pointer for sequential input and output operation
	FILE *fp = NULL;

	// check for UNICODE filenames - previous structure filling was done before
	bool bUseUnicode = filenameIO && filenameIO->wsrc_file && filenameIO->wdst_file;

	// Set up the jpeglib structures
	jpeg_decompress_struct srcinfo;
	jpeg_compress_struct dstinfo;
	jpeg_error_mgr jsrcerr, jdsterr;
	jvirt_barray_ptr *src_coef_arrays = NULL;
	jvirt_barray_ptr *dst_coef_arrays = NULL;
	// Support for copying optional markers from source to destination file
	JCOPY_OPTION copyoption;
	// Image transformation options
	jpeg_transform_info transfoptions;
	
	// Initialize structures
	memset(&srcinfo, 0, sizeof(srcinfo));
	memset(&jsrcerr, 0, sizeof(jsrcerr));
	memset(&jdsterr, 0, sizeof(jdsterr));
	memset(&dstinfo, 0, sizeof(dstinfo));
	memset(&transfoptions, 0, sizeof(transfoptions));
	
	// Copy all extra markers from source file
	copyoption = JCOPYOPT_ALL;
	
	// Set up default JPEG parameters
	transfoptions.force_grayscale = FALSE;
	transfoptions.crop = FALSE;

	// Select the transform option
	switch(operation) {
		case FIJPEG_OP_FLIP_H:		// horizontal flip
			transfoptions.transform = JXFORM_FLIP_H;
			break;
		case FIJPEG_OP_FLIP_V:		// vertical flip
			transfoptions.transform = JXFORM_FLIP_V;
			break;
		case FIJPEG_OP_TRANSPOSE:	// transpose across UL-to-LR axis
			transfoptions.transform = JXFORM_TRANSPOSE;
			break;
		case FIJPEG_OP_TRANSVERSE:	// transpose across UR-to-LL axis
			transfoptions.transform = JXFORM_TRANSVERSE;
			break;
		case FIJPEG_OP_ROTATE_90:	// 90-degree clockwise rotation
			transfoptions.transform = JXFORM_ROT_90;
			break;
		case FIJPEG_OP_ROTATE_180:	// 180-degree rotation
			transfoptions.transform = JXFORM_ROT_180;
			break;
		case FIJPEG_OP_ROTATE_270:	// 270-degree clockwise (or 90 ccw)
			transfoptions.transform = JXFORM_ROT_270;
			break;
		default:
		case FIJPEG_OP_NONE:		// no transformation
			transfoptions.transform = JXFORM_NONE;
			break;
	}
	// (perfect == TRUE) ==> fail if there is non-transformable edge blocks
	transfoptions.perfect = (perfect == TRUE) ? TRUE : FALSE;
	// Drop non-transformable edge blocks: trim off any partial edge MCUs that the transform can't handle.
	transfoptions.trim = TRUE;

	try {

		// Initialize the JPEG decompression object with default error handling
		srcinfo.err = jpeg_std_error(&jsrcerr);
		srcinfo.err->error_exit = ls_jpeg_error_exit;
		srcinfo.err->output_message = ls_jpeg_output_message;
		jpeg_create_decompress(&srcinfo);

		// Initialize the JPEG compression object with default error handling
		dstinfo.err = jpeg_std_error(&jdsterr);
		dstinfo.err->error_exit = ls_jpeg_error_exit;
		dstinfo.err->output_message = ls_jpeg_output_message;
		jpeg_create_compress(&dstinfo);

		// crop option
		if(crop != NULL) {
			if(!jtransform_parse_crop_spec(&transfoptions, crop)) {
				FreeImage_OutputMessageProc(FIF_JPEG, "Bogus crop argument %s", crop);
				throw(1);
			}
		}

		// Open the input file
		if(bUseUnicode) {
#ifdef _WIN32
			if((fp = _wfopen(filenameIO->wsrc_file, L"rb")) == NULL) {
				FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open input file for reading");
			}
#else
			fp = NULL;
#endif // _WIN32
		} else {
			if((fp = fopen(filenameIO->src_file, "rb")) == NULL) {
				FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open %s for reading", filenameIO->src_file);
			}
		}
		if(fp == NULL) {
			jpeg_destroy_compress(&dstinfo);
			jpeg_destroy_decompress(&srcinfo);
			return FALSE;
		}
		
		// Specify data source for decompression
		jpeg_stdio_src(&srcinfo, fp);
		
		// Enable saving of extra markers that we want to copy
		jcopy_markers_setup(&srcinfo, copyoption);
		
		// Read the file header
		jpeg_read_header(&srcinfo, TRUE);
		
		// Any space needed by a transform option must be requested before
		// jpeg_read_coefficients so that memory allocation will be done right

		// Prepare transformation workspace
		// Fails right away if perfect flag is TRUE and transformation is not perfect
		if( !jtransform_request_workspace(&srcinfo, &transfoptions) ) {
			FreeImage_OutputMessageProc(FIF_JPEG, "Transformation is not perfect");
			throw(1);
		}

		// 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, &transfoptions);
		
		// Close the input file.
		// 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(bUseUnicode) {
#ifdef _WIN32
			if((fp = _wfopen(filenameIO->wdst_file, L"wb")) == NULL) {
				FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open output file for writing");
			}
#else
			fp = NULL;
#endif // _WIN32
		} else {
			if((fp = fopen(filenameIO->dst_file, "wb")) == NULL) {
				FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open %s for writing", filenameIO->dst_file);
			}
		}
		if(fp == NULL) {
			throw(1);
		}
		
		// 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);
		
		// Execute image transformation, if any
		jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);
		
		// Finish compression and release memory
		jpeg_finish_compress(&dstinfo);
		jpeg_destroy_compress(&dstinfo);
		jpeg_finish_decompress(&srcinfo);
		jpeg_destroy_decompress(&srcinfo);
		
		// Close output file and return
		fclose(fp);
	}
	catch(...) {
		if(fp) fclose(fp);
		jpeg_destroy_compress(&dstinfo);
		jpeg_destroy_decompress(&srcinfo);
		return FALSE;
	}

	return TRUE;
}