1 // ==========================================================
2 // JPEG lossless transformations
3 //
4 // Design and implementation by
5 // - Petr Pytelka (pyta@lightcomp.com)
6 // - Hervé Drolon (drolon@infonie.fr)
7 // - Mihail Naydenov (mnaydenov@users.sourceforge.net)
8 //
9 // This file is part of FreeImage 3
10 //
11 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
12 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
13 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
14 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
15 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
16 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
17 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
18 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
19 // THIS DISCLAIMER.
20 //
21 // Use at your own risk!
22 // ==========================================================
23 
24 extern "C" {
25 #define XMD_H
26 #undef FAR
27 #include <setjmp.h>
28 
29 #include "../LibJPEG/jinclude.h"
30 #include "../LibJPEG/jpeglib.h"
31 #include "../LibJPEG/jerror.h"
32 #include "../LibJPEG/transupp.h"
33 }
34 
35 #include "FreeImage.h"
36 #include "Utilities.h"
37 #include "FreeImageIO.h"
38 
39 // ----------------------------------------------------------
40 //   Source manager & Destination manager setup
41 //   (see PluginJPEG.cpp)
42 // ----------------------------------------------------------
43 
44 void jpeg_freeimage_src(j_decompress_ptr cinfo, fi_handle infile, FreeImageIO *io);
45 void jpeg_freeimage_dst(j_compress_ptr cinfo, fi_handle outfile, FreeImageIO *io);
46 
47 // ----------------------------------------------------------
48 //   Error handling
49 //   (see also PluginJPEG.cpp)
50 // ----------------------------------------------------------
51 
52 /**
53 	Receives control for a fatal error.  Information sufficient to
54 	generate the error message has been stored in cinfo->err; call
55 	output_message to display it.  Control must NOT return to the caller;
56 	generally this routine will exit() or longjmp() somewhere.
57 */
58 METHODDEF(void)
ls_jpeg_error_exit(j_common_ptr cinfo)59 ls_jpeg_error_exit (j_common_ptr cinfo) {
60 	// always display the message
61 	(*cinfo->err->output_message)(cinfo);
62 
63 	// allow JPEG with a premature end of file
64 	if((cinfo)->err->msg_parm.i[0] != 13) {
65 
66 		// let the memory manager delete any temp files before we die
67 		jpeg_destroy(cinfo);
68 
69 		throw FIF_JPEG;
70 	}
71 }
72 
73 /**
74 	Actual output of any JPEG message.  Note that this method does not know
75 	how to generate a message, only where to send it.
76 */
77 METHODDEF(void)
ls_jpeg_output_message(j_common_ptr cinfo)78 ls_jpeg_output_message (j_common_ptr cinfo) {
79 	char buffer[JMSG_LENGTH_MAX];
80 
81 	// create the message
82 	(*cinfo->err->format_message)(cinfo, buffer);
83 	// send it to user's message proc
84 	FreeImage_OutputMessageProc(FIF_JPEG, buffer);
85 }
86 
87 // ----------------------------------------------------------
88 //   Main program
89 // ----------------------------------------------------------
90 
91 /**
92 Build a crop string.
93 
94 @param crop Output crop string
95 @param left Specifies the left position of the cropped rectangle
96 @param top Specifies the top position of the cropped rectangle
97 @param right Specifies the right position of the cropped rectangle
98 @param bottom Specifies the bottom position of the cropped rectangle
99 @param width Image width
100 @param height Image height
101 @return Returns TRUE if successful, returns FALSE otherwise
102 */
103 static BOOL
getCropString(char * crop,int * left,int * top,int * right,int * bottom,int width,int height)104 getCropString(char* crop, int* left, int* top, int* right, int* bottom, int width, int height) {
105 	if(!left || !top || !right || !bottom) {
106 		return FALSE;
107 	}
108 
109 	*left = CLAMP(*left, 0, width);
110 	*top = CLAMP(*top, 0, height);
111 
112 	// negative/zero right and bottom count from the edges inwards
113 
114 	if(*right <= 0) {
115 		*right = width + *right;
116 	}
117 	if(*bottom <= 0) {
118 		*bottom = height + *bottom;
119 	}
120 
121 	*right = CLAMP(*right, 0, width);
122 	*bottom = CLAMP(*bottom, 0, height);
123 
124 	// test for empty rect
125 
126 	if(((*left - *right) == 0) || ((*top - *bottom) == 0)) {
127 		return FALSE;
128 	}
129 
130 	// normalize the rectangle
131 
132 	if(*right < *left) {
133 		INPLACESWAP(*left, *right);
134 	}
135 	if(*bottom < *top) {
136 		INPLACESWAP(*top, *bottom);
137 	}
138 
139 	// test for "noop" rect
140 
141 	if(*left == 0 && *right == width && *top == 0 && *bottom == height) {
142 		return FALSE;
143 	}
144 
145 	// build the crop option
146 	sprintf(crop, "%dx%d+%d+%d", *right - *left, *bottom - *top, *left, *top);
147 
148 	return TRUE;
149 }
150 
151 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)152 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) {
153 	const BOOL onlyReturnCropRect = (dst_io == NULL) || (dst_handle == NULL);
154 	const long stream_start = onlyReturnCropRect ? 0 : dst_io->tell_proc(dst_handle);
155 	BOOL swappedDim = FALSE;
156 	BOOL trimH = FALSE;
157 	BOOL trimV = FALSE;
158 
159 	// Set up the jpeglib structures
160 	jpeg_decompress_struct srcinfo;
161 	jpeg_compress_struct dstinfo;
162 	jpeg_error_mgr jsrcerr, jdsterr;
163 	jvirt_barray_ptr *src_coef_arrays = NULL;
164 	jvirt_barray_ptr *dst_coef_arrays = NULL;
165 	// Support for copying optional markers from source to destination file
166 	JCOPY_OPTION copyoption;
167 	// Image transformation options
168 	jpeg_transform_info transfoptions;
169 
170 	// Initialize structures
171 	memset(&srcinfo, 0, sizeof(srcinfo));
172 	memset(&jsrcerr, 0, sizeof(jsrcerr));
173 	memset(&jdsterr, 0, sizeof(jdsterr));
174 	memset(&dstinfo, 0, sizeof(dstinfo));
175 	memset(&transfoptions, 0, sizeof(transfoptions));
176 
177 	// Copy all extra markers from source file
178 	copyoption = JCOPYOPT_ALL;
179 
180 	// Set up default JPEG parameters
181 	transfoptions.force_grayscale = FALSE;
182 	transfoptions.crop = FALSE;
183 
184 	// Select the transform option
185 	switch(operation) {
186 		case FIJPEG_OP_FLIP_H:		// horizontal flip
187 			transfoptions.transform = JXFORM_FLIP_H;
188 			trimH = TRUE;
189 			break;
190 		case FIJPEG_OP_FLIP_V:		// vertical flip
191 			transfoptions.transform = JXFORM_FLIP_V;
192 			trimV = TRUE;
193 			break;
194 		case FIJPEG_OP_TRANSPOSE:	// transpose across UL-to-LR axis
195 			transfoptions.transform = JXFORM_TRANSPOSE;
196 			swappedDim = TRUE;
197 			break;
198 		case FIJPEG_OP_TRANSVERSE:	// transpose across UR-to-LL axis
199 			transfoptions.transform = JXFORM_TRANSVERSE;
200 			trimH = TRUE;
201 			trimV = TRUE;
202 			swappedDim = TRUE;
203 			break;
204 		case FIJPEG_OP_ROTATE_90:	// 90-degree clockwise rotation
205 			transfoptions.transform = JXFORM_ROT_90;
206 			trimH = TRUE;
207 			swappedDim = TRUE;
208 			break;
209 		case FIJPEG_OP_ROTATE_180:	// 180-degree rotation
210 			trimH = TRUE;
211 			trimV = TRUE;
212 			transfoptions.transform = JXFORM_ROT_180;
213 			break;
214 		case FIJPEG_OP_ROTATE_270:	// 270-degree clockwise (or 90 ccw)
215 			transfoptions.transform = JXFORM_ROT_270;
216 			trimV = TRUE;
217 			swappedDim = TRUE;
218 			break;
219 		default:
220 		case FIJPEG_OP_NONE:		// no transformation
221 			transfoptions.transform = JXFORM_NONE;
222 			break;
223 	}
224 	// (perfect == TRUE) ==> fail if there is non-transformable edge blocks
225 	transfoptions.perfect = (perfect == TRUE) ? TRUE : FALSE;
226 	// Drop non-transformable edge blocks: trim off any partial edge MCUs that the transform can't handle.
227 	transfoptions.trim = TRUE;
228 
229 	try {
230 
231 		// Initialize the JPEG decompression object with default error handling
232 		srcinfo.err = jpeg_std_error(&jsrcerr);
233 		srcinfo.err->error_exit = ls_jpeg_error_exit;
234 		srcinfo.err->output_message = ls_jpeg_output_message;
235 		jpeg_create_decompress(&srcinfo);
236 
237 		// Initialize the JPEG compression object with default error handling
238 		dstinfo.err = jpeg_std_error(&jdsterr);
239 		dstinfo.err->error_exit = ls_jpeg_error_exit;
240 		dstinfo.err->output_message = ls_jpeg_output_message;
241 		jpeg_create_compress(&dstinfo);
242 
243 		// Specify data source for decompression
244 		jpeg_freeimage_src(&srcinfo, src_handle, src_io);
245 
246 		// Enable saving of extra markers that we want to copy
247 		jcopy_markers_setup(&srcinfo, copyoption);
248 
249 		// Read the file header
250 		jpeg_read_header(&srcinfo, TRUE);
251 
252 		// crop option
253 		char crop[64];
254 		const BOOL hasCrop = getCropString(crop, left, top, right, bottom, swappedDim ? srcinfo.image_height : srcinfo.image_width, swappedDim ? srcinfo.image_width : srcinfo.image_height);
255 
256 		if(hasCrop) {
257 			if(!jtransform_parse_crop_spec(&transfoptions, crop)) {
258 				FreeImage_OutputMessageProc(FIF_JPEG, "Bogus crop argument %s", crop);
259 				throw(1);
260 			}
261 		}
262 
263 		// Any space needed by a transform option must be requested before
264 		// jpeg_read_coefficients so that memory allocation will be done right
265 
266 		// Prepare transformation workspace
267 		// Fails right away if perfect flag is TRUE and transformation is not perfect
268 		if( !jtransform_request_workspace(&srcinfo, &transfoptions) ) {
269 			FreeImage_OutputMessageProc(FIF_JPEG, "Transformation is not perfect");
270 			throw(1);
271 		}
272 
273 		if(left || top) {
274 			// compute left and top offsets, it's a bit tricky, taking into account both
275 			// transform, which might have trimed the image,
276 			// and crop itself, which is adjusted to lie on a iMCU boundary
277 
278 			const int fullWidth = swappedDim ? srcinfo.image_height : srcinfo.image_width;
279 			const int fullHeight = swappedDim ? srcinfo.image_width : srcinfo.image_height;
280 
281 			int transformedFullWidth = fullWidth;
282 			int transformedFullHeight = fullHeight;
283 
284 			if(trimH && transformedFullWidth/transfoptions.iMCU_sample_width > 0) {
285 				transformedFullWidth = (transformedFullWidth/transfoptions.iMCU_sample_width) * transfoptions.iMCU_sample_width;
286 			}
287 			if(trimV && transformedFullHeight/transfoptions.iMCU_sample_height > 0) {
288 				transformedFullHeight = (transformedFullHeight/transfoptions.iMCU_sample_height) * transfoptions.iMCU_sample_height;
289 			}
290 
291 			const int trimmedWidth = fullWidth - transformedFullWidth;
292 			const int trimmedHeight = fullHeight - transformedFullHeight;
293 
294 			if(left) {
295 				*left = trimmedWidth + transfoptions.x_crop_offset * transfoptions.iMCU_sample_width;
296 			}
297 			if(top) {
298 				*top = trimmedHeight + transfoptions.y_crop_offset * transfoptions.iMCU_sample_height;
299 			}
300 		}
301 
302 		if(right) {
303 			*right = (left ? *left : 0) + transfoptions.output_width;
304 		}
305 		if(bottom) {
306 			*bottom = (top ? *top : 0) + transfoptions.output_height;
307 		}
308 
309 		// if only the crop rect is requested, we are done
310 
311 		if(onlyReturnCropRect) {
312 			jpeg_destroy_compress(&dstinfo);
313 			jpeg_destroy_decompress(&srcinfo);
314 			return TRUE;
315 		}
316 
317 		// Read source file as DCT coefficients
318 		src_coef_arrays = jpeg_read_coefficients(&srcinfo);
319 
320 		// Initialize destination compression parameters from source values
321 		jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
322 
323 		// Adjust destination parameters if required by transform options;
324 		// also find out which set of coefficient arrays will hold the output
325 		dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);
326 
327 		// Note: we assume that jpeg_read_coefficients consumed all input
328 		// until JPEG_REACHED_EOI, and that jpeg_finish_decompress will
329 		// only consume more while (! cinfo->inputctl->eoi_reached).
330 		// We cannot call jpeg_finish_decompress here since we still need the
331 		// virtual arrays allocated from the source object for processing.
332 
333 		if(src_handle == dst_handle) {
334 			dst_io->seek_proc(dst_handle, stream_start, SEEK_SET);
335 		}
336 
337 		// Specify data destination for compression
338 		jpeg_freeimage_dst(&dstinfo, dst_handle, dst_io);
339 
340 		// Start compressor (note no image data is actually written here)
341 		jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
342 
343 		// Copy to the output file any extra markers that we want to preserve
344 		jcopy_markers_execute(&srcinfo, &dstinfo, copyoption);
345 
346 		// Execute image transformation, if any
347 		jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);
348 
349 		// Finish compression and release memory
350 		jpeg_finish_compress(&dstinfo);
351 		jpeg_destroy_compress(&dstinfo);
352 		jpeg_finish_decompress(&srcinfo);
353 		jpeg_destroy_decompress(&srcinfo);
354 
355 	}
356 	catch(...) {
357 		jpeg_destroy_compress(&dstinfo);
358 		jpeg_destroy_decompress(&srcinfo);
359 		return FALSE;
360 	}
361 
362 	return TRUE;
363 }
364 
365 // ----------------------------------------------------------
366 //   FreeImage interface
367 // ----------------------------------------------------------
368 
369 BOOL DLL_CALLCONV
FreeImage_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)370 FreeImage_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) {
371 	return JPEGTransformFromHandle(src_io, src_handle, dst_io, dst_handle, operation, left, top, right, bottom, perfect);
372 }
373 
374 static void
closeStdIO(fi_handle src_handle,fi_handle dst_handle)375 closeStdIO(fi_handle src_handle, fi_handle dst_handle) {
376 	if(src_handle) {
377 		fclose((FILE*)src_handle);
378 	}
379 	if(dst_handle && (dst_handle != src_handle)) {
380 		fclose((FILE*)dst_handle);
381 	}
382 }
383 
384 static BOOL
openStdIO(const char * src_file,const char * dst_file,FreeImageIO * dst_io,fi_handle * src_handle,fi_handle * dst_handle)385 openStdIO(const char* src_file, const char* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
386 	*src_handle = NULL;
387 	*dst_handle = NULL;
388 
389 	FreeImageIO io;
390 	SetDefaultIO (&io);
391 
392 	const BOOL isSameFile = (dst_file && (strcmp(src_file, dst_file) == 0)) ? TRUE : FALSE;
393 
394 	FILE* srcp = NULL;
395 	FILE* dstp = NULL;
396 
397 	if(isSameFile) {
398 		srcp = fopen(src_file, "r+b");
399 		dstp = srcp;
400 	}
401 	else {
402 		srcp = fopen(src_file, "rb");
403 		if(dst_file) {
404 			dstp = fopen(dst_file, "wb");
405 		}
406 	}
407 
408 	if(!srcp || (dst_file && !dstp)) {
409 		if(!srcp) {
410 			FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for reading", src_file);
411 		} else {
412 			FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for writing", dst_file);
413 		}
414 		closeStdIO(srcp, dstp);
415 		return FALSE;
416 	}
417 
418 	if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) {
419 		FreeImage_OutputMessageProc(FIF_JPEG, " Source file \"%s\" is not jpeg", src_file);
420 		closeStdIO(srcp, dstp);
421 		return FALSE;
422 	}
423 
424 	*dst_io = io;
425 	*src_handle = srcp;
426 	*dst_handle = dstp;
427 
428 	return TRUE;
429 }
430 
431 static BOOL
openStdIOU(const wchar_t * src_file,const wchar_t * dst_file,FreeImageIO * dst_io,fi_handle * src_handle,fi_handle * dst_handle)432 openStdIOU(const wchar_t* src_file, const wchar_t* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
433 #ifdef _WIN32
434 
435 	*src_handle = NULL;
436 	*dst_handle = NULL;
437 
438 	FreeImageIO io;
439 	SetDefaultIO (&io);
440 
441 	const BOOL isSameFile = (dst_file && (wcscmp(src_file, dst_file) == 0)) ? TRUE : FALSE;
442 
443 	FILE* srcp = NULL;
444 	FILE* dstp = NULL;
445 
446 	if(isSameFile) {
447 		srcp = _wfopen(src_file, L"r+b");
448 		dstp = srcp;
449 	} else {
450 		srcp = _wfopen(src_file, L"rb");
451 		if(dst_file) {
452 			dstp = _wfopen(dst_file, L"wb");
453 		}
454 	}
455 
456 	if(!srcp || (dst_file && !dstp)) {
457 		if(!srcp) {
458 			FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open source file for reading");
459 		} else {
460 			FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open destination file for writing");
461 		}
462 		closeStdIO(srcp, dstp);
463 		return FALSE;
464 	}
465 
466 	if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) {
467 		FreeImage_OutputMessageProc(FIF_JPEG, " Source file is not jpeg");
468 		closeStdIO(srcp, dstp);
469 		return FALSE;
470 	}
471 
472 	*dst_io = io;
473 	*src_handle = srcp;
474 	*dst_handle = dstp;
475 
476 	return TRUE;
477 
478 #else
479 	return FALSE;
480 #endif // _WIN32
481 }
482 
483 BOOL DLL_CALLCONV
FreeImage_JPEGTransform(const char * src_file,const char * dst_file,FREE_IMAGE_JPEG_OPERATION operation,BOOL perfect)484 FreeImage_JPEGTransform(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
485 	FreeImageIO io;
486 	fi_handle src;
487 	fi_handle dst;
488 
489 	if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
490 		return FALSE;
491 	}
492 
493 	BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect);
494 
495 	closeStdIO(src, dst);
496 
497 	return ret;
498 }
499 
500 BOOL DLL_CALLCONV
FreeImage_JPEGCrop(const char * src_file,const char * dst_file,int left,int top,int right,int bottom)501 FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom) {
502 	FreeImageIO io;
503 	fi_handle src;
504 	fi_handle dst;
505 
506 	if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
507 		return FALSE;
508 	}
509 
510 	BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE);
511 
512 	closeStdIO(src, dst);
513 
514 	return ret;
515 }
516 
517 BOOL DLL_CALLCONV
FreeImage_JPEGTransformU(const wchar_t * src_file,const wchar_t * dst_file,FREE_IMAGE_JPEG_OPERATION operation,BOOL perfect)518 FreeImage_JPEGTransformU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
519 	FreeImageIO io;
520 	fi_handle src;
521 	fi_handle dst;
522 
523 	if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
524 		return FALSE;
525 	}
526 
527 	BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect);
528 
529 	closeStdIO(src, dst);
530 
531 	return ret;
532 }
533 
534 BOOL DLL_CALLCONV
FreeImage_JPEGCropU(const wchar_t * src_file,const wchar_t * dst_file,int left,int top,int right,int bottom)535 FreeImage_JPEGCropU(const wchar_t *src_file, const wchar_t *dst_file, int left, int top, int right, int bottom) {
536 	FreeImageIO io;
537 	fi_handle src;
538 	fi_handle dst;
539 
540 	if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
541 		return FALSE;
542 	}
543 
544 	BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE);
545 
546 	closeStdIO(src, dst);
547 
548 	return ret;
549 }
550 
551 BOOL DLL_CALLCONV
FreeImage_JPEGTransformCombined(const char * src_file,const char * dst_file,FREE_IMAGE_JPEG_OPERATION operation,int * left,int * top,int * right,int * bottom,BOOL perfect)552 FreeImage_JPEGTransformCombined(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
553 	FreeImageIO io;
554 	fi_handle src;
555 	fi_handle dst;
556 
557 	if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
558 		return FALSE;
559 	}
560 
561 	BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
562 
563 	closeStdIO(src, dst);
564 
565 	return ret;
566 }
567 
568 BOOL DLL_CALLCONV
FreeImage_JPEGTransformCombinedU(const wchar_t * src_file,const wchar_t * dst_file,FREE_IMAGE_JPEG_OPERATION operation,int * left,int * top,int * right,int * bottom,BOOL perfect)569 FreeImage_JPEGTransformCombinedU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
570 	FreeImageIO io;
571 	fi_handle src;
572 	fi_handle dst;
573 
574 	if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
575 		return FALSE;
576 	}
577 
578 	BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
579 
580 	closeStdIO(src, dst);
581 
582 	return ret;
583 }
584 
585 // --------------------------------------------------------------------------
586 
587 static BOOL
getMemIO(FIMEMORY * src_stream,FIMEMORY * dst_stream,FreeImageIO * dst_io,fi_handle * src_handle,fi_handle * dst_handle)588 getMemIO(FIMEMORY* src_stream, FIMEMORY* dst_stream, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
589 	*src_handle = NULL;
590 	*dst_handle = NULL;
591 
592 	FreeImageIO io;
593 	SetMemoryIO (&io);
594 
595 	if(dst_stream) {
596 		FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(dst_stream->data);
597 		if(mem_header->delete_me != TRUE) {
598 			// do not save in a user buffer
599 			FreeImage_OutputMessageProc(FIF_JPEG, "Destination memory buffer is read only");
600 			return FALSE;
601 		}
602 	}
603 
604 	*dst_io = io;
605 	*src_handle = src_stream;
606 	*dst_handle = dst_stream;
607 
608 	return TRUE;
609 }
610 
611 BOOL DLL_CALLCONV
FreeImage_JPEGTransformCombinedFromMemory(FIMEMORY * src_stream,FIMEMORY * dst_stream,FREE_IMAGE_JPEG_OPERATION operation,int * left,int * top,int * right,int * bottom,BOOL perfect)612 FreeImage_JPEGTransformCombinedFromMemory(FIMEMORY* src_stream, FIMEMORY* dst_stream, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
613 	FreeImageIO io;
614 	fi_handle src;
615 	fi_handle dst;
616 
617 	if(!getMemIO(src_stream, dst_stream, &io, &src, &dst)) {
618 		return FALSE;
619 	}
620 
621 	return FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
622 }
623 
624