/src/FreeImage/Source/FreeImageToolkit/JPEGTransform.cpp
C++ | 410 lines | 241 code | 65 blank | 104 comment | 38 complexity | 70cce1fdb5764c20144a3dfa3e97e2bd MD5 | raw file
1// ========================================================== 2// JPEG lossless transformations 3// 4// Design and implementation by 5// - Petr Pytelka (pyta@lightcomp.com) 6// - Hervé Drolon (drolon@infonie.fr) 7// 8// This file is part of FreeImage 3 9// 10// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY 11// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES 12// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE 13// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED 14// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT 15// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY 16// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL 17// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER 18// THIS DISCLAIMER. 19// 20// Use at your own risk! 21// ========================================================== 22 23extern "C" { 24#define XMD_H 25#undef FAR 26#include <setjmp.h> 27 28#include "../LibJPEG/jinclude.h" 29#include "../LibJPEG/jpeglib.h" 30#include "../LibJPEG/jerror.h" 31#include "../LibJPEG/transupp.h" 32} 33 34#include "FreeImage.h" 35#include "Utilities.h" 36 37// ---------------------------------------------------------- 38// IO filename handling 39// ---------------------------------------------------------- 40 41typedef struct tagFilenameIO { 42 const char *src_file; 43 const char *dst_file; 44 const wchar_t *wsrc_file; 45 const wchar_t *wdst_file; 46} FilenameIO; 47 48// ---------------------------------------------------------- 49// Error handling 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*/ 58METHODDEF(void) 59ls_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*/ 77METHODDEF(void) 78ls_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 91static BOOL 92LosslessTransform(const FilenameIO *filenameIO, FREE_IMAGE_JPEG_OPERATION operation, const char *crop, BOOL perfect) { 93 // We assume all-in-memory processing and can therefore use only a 94 // single file pointer for sequential input and output operation 95 FILE *fp = NULL; 96 97 // check for UNICODE filenames - previous structure filling was done before 98 bool bUseUnicode = filenameIO && filenameIO->wsrc_file && filenameIO->wdst_file; 99 100 // Set up the jpeglib structures 101 jpeg_decompress_struct srcinfo; 102 jpeg_compress_struct dstinfo; 103 jpeg_error_mgr jsrcerr, jdsterr; 104 jvirt_barray_ptr *src_coef_arrays = NULL; 105 jvirt_barray_ptr *dst_coef_arrays = NULL; 106 // Support for copying optional markers from source to destination file 107 JCOPY_OPTION copyoption; 108 // Image transformation options 109 jpeg_transform_info transfoptions; 110 111 // Initialize structures 112 memset(&srcinfo, 0, sizeof(srcinfo)); 113 memset(&jsrcerr, 0, sizeof(jsrcerr)); 114 memset(&jdsterr, 0, sizeof(jdsterr)); 115 memset(&dstinfo, 0, sizeof(dstinfo)); 116 memset(&transfoptions, 0, sizeof(transfoptions)); 117 118 // Copy all extra markers from source file 119 copyoption = JCOPYOPT_ALL; 120 121 // Set up default JPEG parameters 122 transfoptions.force_grayscale = FALSE; 123 transfoptions.crop = FALSE; 124 125 // Select the transform option 126 switch(operation) { 127 case FIJPEG_OP_FLIP_H: // horizontal flip 128 transfoptions.transform = JXFORM_FLIP_H; 129 break; 130 case FIJPEG_OP_FLIP_V: // vertical flip 131 transfoptions.transform = JXFORM_FLIP_V; 132 break; 133 case FIJPEG_OP_TRANSPOSE: // transpose across UL-to-LR axis 134 transfoptions.transform = JXFORM_TRANSPOSE; 135 break; 136 case FIJPEG_OP_TRANSVERSE: // transpose across UR-to-LL axis 137 transfoptions.transform = JXFORM_TRANSVERSE; 138 break; 139 case FIJPEG_OP_ROTATE_90: // 90-degree clockwise rotation 140 transfoptions.transform = JXFORM_ROT_90; 141 break; 142 case FIJPEG_OP_ROTATE_180: // 180-degree rotation 143 transfoptions.transform = JXFORM_ROT_180; 144 break; 145 case FIJPEG_OP_ROTATE_270: // 270-degree clockwise (or 90 ccw) 146 transfoptions.transform = JXFORM_ROT_270; 147 break; 148 default: 149 case FIJPEG_OP_NONE: // no transformation 150 transfoptions.transform = JXFORM_NONE; 151 break; 152 } 153 // (perfect == TRUE) ==> fail if there is non-transformable edge blocks 154 transfoptions.perfect = (perfect == TRUE) ? TRUE : FALSE; 155 // Drop non-transformable edge blocks: trim off any partial edge MCUs that the transform can't handle. 156 transfoptions.trim = TRUE; 157 158 try { 159 160 // Initialize the JPEG decompression object with default error handling 161 srcinfo.err = jpeg_std_error(&jsrcerr); 162 srcinfo.err->error_exit = ls_jpeg_error_exit; 163 srcinfo.err->output_message = ls_jpeg_output_message; 164 jpeg_create_decompress(&srcinfo); 165 166 // Initialize the JPEG compression object with default error handling 167 dstinfo.err = jpeg_std_error(&jdsterr); 168 dstinfo.err->error_exit = ls_jpeg_error_exit; 169 dstinfo.err->output_message = ls_jpeg_output_message; 170 jpeg_create_compress(&dstinfo); 171 172 // crop option 173 if(crop != NULL) { 174 if(!jtransform_parse_crop_spec(&transfoptions, crop)) { 175 FreeImage_OutputMessageProc(FIF_JPEG, "Bogus crop argument %s", crop); 176 throw(1); 177 } 178 } 179 180 // Open the input file 181 if(bUseUnicode) { 182#ifdef _WIN32 183 if((fp = _wfopen(filenameIO->wsrc_file, L"rb")) == NULL) { 184 FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open input file for reading"); 185 } 186#else 187 fp = NULL; 188#endif // _WIN32 189 } else { 190 if((fp = fopen(filenameIO->src_file, "rb")) == NULL) { 191 FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open %s for reading", filenameIO->src_file); 192 } 193 } 194 if(fp == NULL) { 195 jpeg_destroy_compress(&dstinfo); 196 jpeg_destroy_decompress(&srcinfo); 197 return FALSE; 198 } 199 200 // Specify data source for decompression 201 jpeg_stdio_src(&srcinfo, fp); 202 203 // Enable saving of extra markers that we want to copy 204 jcopy_markers_setup(&srcinfo, copyoption); 205 206 // Read the file header 207 jpeg_read_header(&srcinfo, TRUE); 208 209 // Any space needed by a transform option must be requested before 210 // jpeg_read_coefficients so that memory allocation will be done right 211 212 // Prepare transformation workspace 213 // Fails right away if perfect flag is TRUE and transformation is not perfect 214 if( !jtransform_request_workspace(&srcinfo, &transfoptions) ) { 215 FreeImage_OutputMessageProc(FIF_JPEG, "Transformation is not perfect"); 216 throw(1); 217 } 218 219 // Read source file as DCT coefficients 220 src_coef_arrays = jpeg_read_coefficients(&srcinfo); 221 222 // Initialize destination compression parameters from source values 223 jpeg_copy_critical_parameters(&srcinfo, &dstinfo); 224 225 // Adjust destination parameters if required by transform options; 226 // also find out which set of coefficient arrays will hold the output 227 dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions); 228 229 // Close the input file. 230 // Note: we assume that jpeg_read_coefficients consumed all input 231 // until JPEG_REACHED_EOI, and that jpeg_finish_decompress will 232 // only consume more while (! cinfo->inputctl->eoi_reached). 233 // We cannot call jpeg_finish_decompress here since we still need the 234 // virtual arrays allocated from the source object for processing. 235 fclose(fp); 236 237 // Open the output file 238 if(bUseUnicode) { 239#ifdef _WIN32 240 if((fp = _wfopen(filenameIO->wdst_file, L"wb")) == NULL) { 241 FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open output file for writing"); 242 } 243#else 244 fp = NULL; 245#endif // _WIN32 246 } else { 247 if((fp = fopen(filenameIO->dst_file, "wb")) == NULL) { 248 FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open %s for writing", filenameIO->dst_file); 249 } 250 } 251 if(fp == NULL) { 252 throw(1); 253 } 254 255 // Specify data destination for compression 256 jpeg_stdio_dest(&dstinfo, fp); 257 258 // Start compressor (note no image data is actually written here) 259 jpeg_write_coefficients(&dstinfo, dst_coef_arrays); 260 261 // Copy to the output file any extra markers that we want to preserve 262 jcopy_markers_execute(&srcinfo, &dstinfo, copyoption); 263 264 // Execute image transformation, if any 265 jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions); 266 267 // Finish compression and release memory 268 jpeg_finish_compress(&dstinfo); 269 jpeg_destroy_compress(&dstinfo); 270 jpeg_finish_decompress(&srcinfo); 271 jpeg_destroy_decompress(&srcinfo); 272 273 // Close output file and return 274 fclose(fp); 275 } 276 catch(...) { 277 if(fp) fclose(fp); 278 jpeg_destroy_compress(&dstinfo); 279 jpeg_destroy_decompress(&srcinfo); 280 return FALSE; 281 } 282 283 return TRUE; 284} 285 286// ---------------------------------------------------------- 287// FreeImage interface 288// ---------------------------------------------------------- 289 290BOOL DLL_CALLCONV 291FreeImage_JPEGTransform(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) { 292 try { 293 // check the src file format 294 if(FreeImage_GetFileType(src_file) != FIF_JPEG) { 295 throw FI_MSG_ERROR_MAGIC_NUMBER; 296 } 297 298 // setup IO 299 FilenameIO filenameIO; 300 memset(&filenameIO, 0, sizeof(FilenameIO)); 301 filenameIO.src_file = src_file; 302 filenameIO.dst_file = dst_file; 303 304 // perform the transformation 305 return LosslessTransform(&filenameIO, operation, NULL, perfect); 306 307 } catch(const char *text) { 308 FreeImage_OutputMessageProc(FIF_JPEG, text); 309 return FALSE; 310 } 311} 312 313BOOL DLL_CALLCONV 314FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom) { 315 char crop[64]; 316 317 try { 318 // check the src file format 319 if(FreeImage_GetFileType(src_file) != FIF_JPEG) { 320 throw FI_MSG_ERROR_MAGIC_NUMBER; 321 } 322 323 // normalize the rectangle 324 if(right < left) { 325 INPLACESWAP(left, right); 326 } 327 if(bottom < top) { 328 INPLACESWAP(top, bottom); 329 } 330 331 // build the crop option 332 sprintf(crop, "%dx%d+%d+%d", right - left, bottom - top, left, top); 333 334 // setup IO 335 FilenameIO filenameIO; 336 memset(&filenameIO, 0, sizeof(FilenameIO)); 337 filenameIO.src_file = src_file; 338 filenameIO.dst_file = dst_file; 339 340 // perform the transformation 341 return LosslessTransform(&filenameIO, FIJPEG_OP_NONE, crop, FALSE); 342 343 } catch(const char *text) { 344 FreeImage_OutputMessageProc(FIF_JPEG, text); 345 return FALSE; 346 } 347} 348 349BOOL DLL_CALLCONV 350FreeImage_JPEGTransformU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) { 351#ifdef _WIN32 352 try { 353 // check the src file format 354 if(FreeImage_GetFileTypeU(src_file) != FIF_JPEG) { 355 throw FI_MSG_ERROR_MAGIC_NUMBER; 356 } 357 358 // setup IO 359 FilenameIO filenameIO; 360 memset(&filenameIO, 0, sizeof(FilenameIO)); 361 filenameIO.wsrc_file = src_file; 362 filenameIO.wdst_file = dst_file; 363 364 // perform the transformation 365 return LosslessTransform(&filenameIO, operation, NULL, perfect); 366 367 } catch(const char *text) { 368 FreeImage_OutputMessageProc(FIF_JPEG, text); 369 } 370#endif /// _WIN32 371 return FALSE; 372} 373 374BOOL DLL_CALLCONV 375FreeImage_JPEGCropU(const wchar_t *src_file, const wchar_t *dst_file, int left, int top, int right, int bottom) { 376#ifdef _WIN32 377 char crop[64]; 378 379 try { 380 // check the src file format 381 if(FreeImage_GetFileTypeU(src_file) != FIF_JPEG) { 382 throw FI_MSG_ERROR_MAGIC_NUMBER; 383 } 384 385 // normalize the rectangle 386 if(right < left) { 387 INPLACESWAP(left, right); 388 } 389 if(bottom < top) { 390 INPLACESWAP(top, bottom); 391 } 392 393 // build the crop option 394 sprintf(crop, "%dx%d+%d+%d", right - left, bottom - top, left, top); 395 396 // setup IO 397 FilenameIO filenameIO; 398 memset(&filenameIO, 0, sizeof(FilenameIO)); 399 filenameIO.wsrc_file = src_file; 400 filenameIO.wdst_file = dst_file; 401 402 // perform the transformation 403 return LosslessTransform(&filenameIO, FIJPEG_OP_NONE, crop, FALSE); 404 405 } catch(const char *text) { 406 FreeImage_OutputMessageProc(FIF_JPEG, text); 407 } 408#endif // _WIN32 409 return FALSE; 410}