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