]> Creatis software - clitk.git/blob - utilities/CxImage/ximajpg.cpp
Merge branch 'master' of git.creatis.insa-lyon.fr:clitk
[clitk.git] / utilities / CxImage / ximajpg.cpp
1 /*\r
2  * File:        ximajpg.cpp\r
3  * Purpose:     Platform Independent JPEG Image Class Loader and Writer\r
4  * 07/Aug/2001 Davide Pizzolato - www.xdp.it\r
5  * CxImage version 6.0.0 02/Feb/2008\r
6  */\r
7  \r
8 #include "ximajpg.h"\r
9 \r
10 #if CXIMAGE_SUPPORT_JPG\r
11 \r
12 #include "../jpeg/jmorecfg.h"\r
13 \r
14 #include "ximaiter.h"\r
15          \r
16 #include <setjmp.h>\r
17 \r
18 struct jpg_error_mgr {\r
19         struct jpeg_error_mgr pub;      /* "public" fields */\r
20         jmp_buf setjmp_buffer;          /* for return to caller */\r
21         char* buffer;                           /* error message <CSC>*/\r
22 };\r
23 typedef jpg_error_mgr *jpg_error_ptr;\r
24 \r
25 ////////////////////////////////////////////////////////////////////////////////\r
26 // Here's the routine that will replace the standard error_exit method:\r
27 ////////////////////////////////////////////////////////////////////////////////\r
28 static void\r
29 ima_jpeg_error_exit (j_common_ptr cinfo)\r
30 {\r
31         /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */\r
32         jpg_error_ptr myerr = (jpg_error_ptr) cinfo->err;\r
33         /* Create the message */\r
34         myerr->pub.format_message (cinfo, myerr->buffer);\r
35         /* Send it to stderr, adding a newline */\r
36         /* Return control to the setjmp point */\r
37         longjmp(myerr->setjmp_buffer, 1);\r
38 }\r
39 ////////////////////////////////////////////////////////////////////////////////\r
40 CxImageJPG::CxImageJPG(): CxImage(CXIMAGE_FORMAT_JPG)\r
41 {\r
42 #if CXIMAGEJPG_SUPPORT_EXIF\r
43         m_exif = NULL;\r
44         memset(&m_exifinfo, 0, sizeof(EXIFINFO));\r
45 #endif\r
46 }\r
47 ////////////////////////////////////////////////////////////////////////////////\r
48 CxImageJPG::~CxImageJPG()\r
49 {\r
50 #if CXIMAGEJPG_SUPPORT_EXIF\r
51         if (m_exif) delete m_exif;\r
52 #endif\r
53 }\r
54 ////////////////////////////////////////////////////////////////////////////////\r
55 #if CXIMAGEJPG_SUPPORT_EXIF\r
56 bool CxImageJPG::DecodeExif(CxFile * hFile)\r
57 {\r
58         m_exif = new CxExifInfo(&m_exifinfo);\r
59         if (m_exif){\r
60                 long pos=hFile->Tell();\r
61                 m_exif->DecodeExif(hFile);\r
62                 hFile->Seek(pos,SEEK_SET);\r
63                 return m_exif->m_exifinfo->IsExif;\r
64         } else {\r
65                 return false;\r
66         }\r
67 }\r
68 #endif //CXIMAGEJPG_SUPPORT_EXIF\r
69 ////////////////////////////////////////////////////////////////////////////////\r
70 #if CXIMAGE_SUPPORT_DECODE\r
71 ////////////////////////////////////////////////////////////////////////////////\r
72 bool CxImageJPG::Decode(CxFile * hFile)\r
73 {\r
74 \r
75         bool is_exif = false;\r
76 #if CXIMAGEJPG_SUPPORT_EXIF\r
77         is_exif = DecodeExif(hFile);\r
78 #endif\r
79 \r
80         CImageIterator iter(this);\r
81         /* This struct contains the JPEG decompression parameters and pointers to\r
82         * working space (which is allocated as needed by the JPEG library).\r
83         */\r
84         struct jpeg_decompress_struct cinfo;\r
85         /* We use our private extension JPEG error handler. <CSC> */\r
86         struct jpg_error_mgr jerr;\r
87         jerr.buffer=info.szLastError;\r
88         /* More stuff */\r
89         JSAMPARRAY buffer;      /* Output row buffer */\r
90         int row_stride;         /* physical row width in output buffer */\r
91 \r
92         /* In this example we want to open the input file before doing anything else,\r
93         * so that the setjmp() error recovery below can assume the file is open.\r
94         * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that\r
95         * requires it in order to read binary files.\r
96         */\r
97 \r
98         /* Step 1: allocate and initialize JPEG decompression object */\r
99         /* We set up the normal JPEG error routines, then override error_exit. */\r
100         cinfo.err = jpeg_std_error(&jerr.pub);\r
101         jerr.pub.error_exit = ima_jpeg_error_exit;\r
102 \r
103         /* Establish the setjmp return context for my_error_exit to use. */\r
104         if (setjmp(jerr.setjmp_buffer)) {\r
105                 /* If we get here, the JPEG code has signaled an error.\r
106                 * We need to clean up the JPEG object, close the input file, and return.\r
107                 */\r
108                 jpeg_destroy_decompress(&cinfo);\r
109                 return 0;\r
110         }\r
111         /* Now we can initialize the JPEG decompression object. */\r
112         jpeg_create_decompress(&cinfo);\r
113 \r
114         /* Step 2: specify data source (eg, a file) */\r
115         //jpeg_stdio_src(&cinfo, infile);\r
116         CxFileJpg src(hFile);\r
117     cinfo.src = &src;\r
118 \r
119         /* Step 3: read file parameters with jpeg_read_header() */\r
120         (void) jpeg_read_header(&cinfo, TRUE);\r
121 \r
122         /* Step 4 <chupeev> handle decoder options*/\r
123         if ((GetCodecOption(CXIMAGE_FORMAT_JPG) & DECODE_GRAYSCALE) != 0)\r
124                 cinfo.out_color_space = JCS_GRAYSCALE;\r
125         if ((GetCodecOption(CXIMAGE_FORMAT_JPG) & DECODE_QUANTIZE) != 0) {\r
126                 cinfo.quantize_colors = TRUE;\r
127                 cinfo.desired_number_of_colors = GetJpegQuality();\r
128         }\r
129         if ((GetCodecOption(CXIMAGE_FORMAT_JPG) & DECODE_DITHER) != 0)\r
130                 cinfo.dither_mode = m_nDither;\r
131         if ((GetCodecOption(CXIMAGE_FORMAT_JPG) & DECODE_ONEPASS) != 0)\r
132                 cinfo.two_pass_quantize = FALSE;\r
133         if ((GetCodecOption(CXIMAGE_FORMAT_JPG) & DECODE_NOSMOOTH) != 0)\r
134                 cinfo.do_fancy_upsampling = FALSE;\r
135 \r
136 //<DP>: Load true color images as RGB (no quantize) \r
137 /* Step 4: set parameters for decompression */\r
138 /*  if (cinfo.jpeg_color_space!=JCS_GRAYSCALE) {\r
139  *      cinfo.quantize_colors = TRUE;\r
140  *      cinfo.desired_number_of_colors = 128;\r
141  *}\r
142  */ //</DP>\r
143 \r
144         // Set the scale <ignacio>\r
145         cinfo.scale_denom = GetJpegScale();\r
146 \r
147         // Borrowed the idea from GIF implementation <ignacio>\r
148         if (info.nEscape == -1) {\r
149                 // Return output dimensions only\r
150                 jpeg_calc_output_dimensions(&cinfo);\r
151                 head.biWidth = cinfo.output_width;\r
152                 head.biHeight = cinfo.output_height;\r
153                 info.dwType = CXIMAGE_FORMAT_JPG;\r
154                 jpeg_destroy_decompress(&cinfo);\r
155                 return true;\r
156         }\r
157 \r
158         /* Step 5: Start decompressor */\r
159         jpeg_start_decompress(&cinfo);\r
160 \r
161         /* We may need to do some setup of our own at this point before reading\r
162         * the data.  After jpeg_start_decompress() we have the correct scaled\r
163         * output image dimensions available, as well as the output colormap\r
164         * if we asked for color quantization.\r
165         */\r
166         //Create the image using output dimensions <ignacio>\r
167         //Create(cinfo.image_width, cinfo.image_height, 8*cinfo.output_components, CXIMAGE_FORMAT_JPG);\r
168         Create(cinfo.output_width, cinfo.output_height, 8*cinfo.output_components, CXIMAGE_FORMAT_JPG);\r
169 \r
170         if (!pDib) longjmp(jerr.setjmp_buffer, 1);  //<DP> check if the image has been created\r
171 \r
172         if (is_exif){\r
173 #if CXIMAGEJPG_SUPPORT_EXIF\r
174         if ((m_exifinfo.Xresolution != 0.0) && (m_exifinfo.ResolutionUnit != 0))\r
175                 SetXDPI((long)(m_exifinfo.Xresolution/m_exifinfo.ResolutionUnit));\r
176         if ((m_exifinfo.Yresolution != 0.0) && (m_exifinfo.ResolutionUnit != 0))\r
177                 SetYDPI((long)(m_exifinfo.Yresolution/m_exifinfo.ResolutionUnit));\r
178 #endif\r
179         } else {\r
180                 switch (cinfo.density_unit) {\r
181                 case 0: // [andy] fix for aspect ratio...\r
182                         if((cinfo.Y_density > 0) && (cinfo.X_density > 0)){\r
183                                 SetYDPI((long)(GetXDPI()*(float(cinfo.Y_density)/float(cinfo.X_density))));\r
184                         }\r
185                         break;\r
186                 case 2: // [andy] fix: cinfo.X/Y_density is pixels per centimeter\r
187                         SetXDPI((long)floor(cinfo.X_density * 2.54 + 0.5));\r
188                         SetYDPI((long)floor(cinfo.Y_density * 2.54 + 0.5));\r
189                         break;\r
190                 default:\r
191                         SetXDPI(cinfo.X_density);\r
192                         SetYDPI(cinfo.Y_density);\r
193                 }\r
194         }\r
195 \r
196         if (cinfo.out_color_space==JCS_GRAYSCALE){\r
197                 SetGrayPalette();\r
198                 head.biClrUsed =256;\r
199         } else {\r
200                 if (cinfo.quantize_colors){\r
201                         SetPalette(cinfo.actual_number_of_colors, cinfo.colormap[0], cinfo.colormap[1], cinfo.colormap[2]);\r
202                         head.biClrUsed=cinfo.actual_number_of_colors;\r
203                 } else {\r
204                         head.biClrUsed=0;\r
205                 }\r
206         }\r
207 \r
208         /* JSAMPLEs per row in output buffer */\r
209         row_stride = cinfo.output_width * cinfo.output_components;\r
210 \r
211         /* Make a one-row-high sample array that will go away when done with image */\r
212         buffer = (*cinfo.mem->alloc_sarray)\r
213                 ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);\r
214 \r
215         /* Step 6: while (scan lines remain to be read) */\r
216         /*           jpeg_read_scanlines(...); */\r
217         /* Here we use the library's state variable cinfo.output_scanline as the\r
218         * loop counter, so that we don't have to keep track ourselves.\r
219         */\r
220         iter.Upset();\r
221         while (cinfo.output_scanline < cinfo.output_height) {\r
222 \r
223                 if (info.nEscape) longjmp(jerr.setjmp_buffer, 1); // <vho> - cancel decoding\r
224                 \r
225                 (void) jpeg_read_scanlines(&cinfo, buffer, 1);\r
226                 // info.nProgress = (long)(100*cinfo.output_scanline/cinfo.output_height);\r
227                 //<DP> Step 6a: CMYK->RGB */ \r
228                 if ((cinfo.num_components==4)&&(cinfo.quantize_colors==FALSE)){\r
229                         BYTE k,*dst,*src;\r
230                         dst=iter.GetRow();\r
231                         src=buffer[0];\r
232                         for(long x3=0,x4=0; x3<(long)info.dwEffWidth && x4<row_stride; x3+=3, x4+=4){\r
233                                 k=src[x4+3];\r
234                                 dst[x3]  =(BYTE)((k * src[x4+2])/255);\r
235                                 dst[x3+1]=(BYTE)((k * src[x4+1])/255);\r
236                                 dst[x3+2]=(BYTE)((k * src[x4+0])/255);\r
237                         }\r
238                 } else {\r
239                         /* Assume put_scanline_someplace wants a pointer and sample count. */\r
240                         iter.SetRow(buffer[0], row_stride);\r
241                 }\r
242                         iter.PrevRow();\r
243         }\r
244 \r
245         /* Step 7: Finish decompression */\r
246         (void) jpeg_finish_decompress(&cinfo);\r
247         /* We can ignore the return value since suspension is not possible\r
248         * with the stdio data source.\r
249         */\r
250 \r
251         //<DP> Step 7A: Swap red and blue components\r
252         // not necessary if swapped red and blue definition in jmorecfg.h;ln322 <W. Morrison>\r
253         if ((cinfo.num_components==3)&&(cinfo.quantize_colors==FALSE)){\r
254                 BYTE* r0=GetBits();\r
255                 for(long y=0;y<head.biHeight;y++){\r
256                         if (info.nEscape) longjmp(jerr.setjmp_buffer, 1); // <vho> - cancel decoding\r
257                         RGBtoBGR(r0,3*head.biWidth);\r
258                         r0+=info.dwEffWidth;\r
259                 }\r
260         }\r
261 \r
262         /* Step 8: Release JPEG decompression object */\r
263         /* This is an important step since it will release a good deal of memory. */\r
264         jpeg_destroy_decompress(&cinfo);\r
265 \r
266         /* At this point you may want to check to see whether any corrupt-data\r
267         * warnings occurred (test whether jerr.pub.num_warnings is nonzero).\r
268         */\r
269 \r
270         /* And we're done! */\r
271         return true;\r
272 }\r
273 ////////////////////////////////////////////////////////////////////////////////\r
274 #endif //CXIMAGE_SUPPORT_DECODE\r
275 ////////////////////////////////////////////////////////////////////////////////\r
276 #if CXIMAGE_SUPPORT_ENCODE\r
277 ////////////////////////////////////////////////////////////////////////////////\r
278 bool CxImageJPG::Encode(CxFile * hFile)\r
279 {\r
280         if (EncodeSafeCheck(hFile)) return false;\r
281 \r
282         if (head.biClrUsed!=0 && !IsGrayScale()){\r
283                 strcpy(info.szLastError,"JPEG can save only RGB or GreyScale images");\r
284                 return false;\r
285         }       \r
286 \r
287         // necessary for EXIF, and for roll backs\r
288         long pos=hFile->Tell();\r
289 \r
290         /* This struct contains the JPEG compression parameters and pointers to\r
291         * working space (which is allocated as needed by the JPEG library).\r
292         * It is possible to have several such structures, representing multiple\r
293         * compression/decompression processes, in existence at once.  We refer\r
294         * to any one struct (and its associated working data) as a "JPEG object".\r
295         */\r
296         struct jpeg_compress_struct cinfo;\r
297         /* This struct represents a JPEG error handler.  It is declared separately\r
298         * because applications often want to supply a specialized error handler\r
299         * (see the second half of this file for an example).  But here we just\r
300         * take the easy way out and use the standard error handler, which will\r
301         * print a message on stderr and call exit() if compression fails.\r
302         * Note that this struct must live as long as the main JPEG parameter\r
303         * struct, to avoid dangling-pointer problems.\r
304         */\r
305         //struct jpeg_error_mgr jerr;\r
306         /* We use our private extension JPEG error handler. <CSC> */\r
307         struct jpg_error_mgr jerr;\r
308         jerr.buffer=info.szLastError;\r
309         /* More stuff */\r
310         int row_stride;         /* physical row width in image buffer */\r
311         JSAMPARRAY buffer;              /* Output row buffer */\r
312 \r
313         /* Step 1: allocate and initialize JPEG compression object */\r
314         /* We have to set up the error handler first, in case the initialization\r
315         * step fails.  (Unlikely, but it could happen if you are out of memory.)\r
316         * This routine fills in the contents of struct jerr, and returns jerr's\r
317         * address which we place into the link field in cinfo.\r
318         */\r
319         //cinfo.err = jpeg_std_error(&jerr); <CSC>\r
320         /* We set up the normal JPEG error routines, then override error_exit. */\r
321         cinfo.err = jpeg_std_error(&jerr.pub);\r
322         jerr.pub.error_exit = ima_jpeg_error_exit;\r
323 \r
324         /* Establish the setjmp return context for my_error_exit to use. */\r
325         if (setjmp(jerr.setjmp_buffer)) {\r
326                 /* If we get here, the JPEG code has signaled an error.\r
327                 * We need to clean up the JPEG object, close the input file, and return.\r
328                 */\r
329                 strcpy(info.szLastError, jerr.buffer); //<CSC>\r
330                 jpeg_destroy_compress(&cinfo);\r
331                 return 0;\r
332         }\r
333         \r
334         /* Now we can initialize the JPEG compression object. */\r
335         jpeg_create_compress(&cinfo);\r
336         /* Step 2: specify data destination (eg, a file) */\r
337         /* Note: steps 2 and 3 can be done in either order. */\r
338         /* Here we use the library-supplied code to send compressed data to a\r
339         * stdio stream.  You can also write your own code to do something else.\r
340         * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that\r
341         * requires it in order to write binary files.\r
342         */\r
343 \r
344         //jpeg_stdio_dest(&cinfo, outfile);\r
345         CxFileJpg dest(hFile);\r
346     cinfo.dest = &dest;\r
347 \r
348         /* Step 3: set parameters for compression */\r
349         /* First we supply a description of the input image.\r
350         * Four fields of the cinfo struct must be filled in:\r
351         */\r
352         cinfo.image_width = GetWidth();         // image width and height, in pixels\r
353         cinfo.image_height = GetHeight();\r
354 \r
355         if (IsGrayScale()){\r
356                 cinfo.input_components = 1;                     // # of color components per pixel\r
357                 cinfo.in_color_space = JCS_GRAYSCALE; /* colorspace of input image */\r
358         } else {\r
359                 cinfo.input_components = 3;     // # of color components per pixel\r
360                 cinfo.in_color_space = JCS_RGB; /* colorspace of input image */\r
361         }\r
362 \r
363         /* Now use the library's routine to set default compression parameters.\r
364         * (You must set at least cinfo.in_color_space before calling this,\r
365         * since the defaults depend on the source color space.)\r
366         */\r
367         jpeg_set_defaults(&cinfo);\r
368         /* Now you can set any non-default parameters you wish to.\r
369         * Here we just illustrate the use of quality (quantization table) scaling:\r
370         */\r
371 \r
372 //#ifdef C_ARITH_CODING_SUPPORTED\r
373         if ((GetCodecOption(CXIMAGE_FORMAT_JPG) & ENCODE_ARITHMETIC) != 0)\r
374                 cinfo.arith_code = TRUE;\r
375 //#endif\r
376 \r
377 //#ifdef ENTROPY_OPT_SUPPORTED\r
378         if ((GetCodecOption(CXIMAGE_FORMAT_JPG) & ENCODE_OPTIMIZE) != 0)\r
379                 cinfo.optimize_coding = TRUE;\r
380 //#endif\r
381 \r
382         if ((GetCodecOption(CXIMAGE_FORMAT_JPG) & ENCODE_GRAYSCALE) != 0)\r
383                 jpeg_set_colorspace(&cinfo, JCS_GRAYSCALE);\r
384 \r
385         if ((GetCodecOption(CXIMAGE_FORMAT_JPG) & ENCODE_SMOOTHING) != 0)\r
386                 cinfo.smoothing_factor = m_nSmoothing;\r
387 \r
388         jpeg_set_quality(&cinfo, GetJpegQuality(), (GetCodecOption(CXIMAGE_FORMAT_JPG) & ENCODE_BASELINE) != 0);\r
389 \r
390 //#ifdef C_PROGRESSIVE_SUPPORTED\r
391         if ((GetCodecOption(CXIMAGE_FORMAT_JPG) & ENCODE_PROGRESSIVE) != 0)\r
392                 jpeg_simple_progression(&cinfo);\r
393 //#endif\r
394 \r
395 #ifdef C_LOSSLESS_SUPPORTED\r
396         if ((GetCodecOption(CXIMAGE_FORMAT_JPG) & ENCODE_LOSSLESS) != 0)\r
397                 jpeg_simple_lossless(&cinfo, m_nPredictor, m_nPointTransform);\r
398 #endif\r
399 \r
400         //SetCodecOption(ENCODE_SUBSAMPLE_444 | GetCodecOption(CXIMAGE_FORMAT_JPG),CXIMAGE_FORMAT_JPG);\r
401 \r
402                 // 2x2, 1x1, 1x1 (4:1:1) : High (default sub sampling)\r
403                 cinfo.comp_info[0].h_samp_factor = 2;\r
404                 cinfo.comp_info[0].v_samp_factor = 2;\r
405                 cinfo.comp_info[1].h_samp_factor = 1;\r
406                 cinfo.comp_info[1].v_samp_factor = 1;\r
407                 cinfo.comp_info[2].h_samp_factor = 1;\r
408                 cinfo.comp_info[2].v_samp_factor = 1;\r
409 \r
410         if ((GetCodecOption(CXIMAGE_FORMAT_JPG) & ENCODE_SUBSAMPLE_422) != 0){\r
411                 // 2x1, 1x1, 1x1 (4:2:2) : Medium\r
412                 cinfo.comp_info[0].h_samp_factor = 2;\r
413                 cinfo.comp_info[0].v_samp_factor = 1;\r
414                 cinfo.comp_info[1].h_samp_factor = 1;\r
415                 cinfo.comp_info[1].v_samp_factor = 1;\r
416                 cinfo.comp_info[2].h_samp_factor = 1;\r
417                 cinfo.comp_info[2].v_samp_factor = 1;\r
418         }\r
419 \r
420         if ((GetCodecOption(CXIMAGE_FORMAT_JPG) & ENCODE_SUBSAMPLE_444) != 0){\r
421                 // 1x1 1x1 1x1 (4:4:4) : None\r
422                 cinfo.comp_info[0].h_samp_factor = 1;\r
423                 cinfo.comp_info[0].v_samp_factor = 1;\r
424                 cinfo.comp_info[1].h_samp_factor = 1;\r
425                 cinfo.comp_info[1].v_samp_factor = 1;\r
426                 cinfo.comp_info[2].h_samp_factor = 1;\r
427                 cinfo.comp_info[2].v_samp_factor = 1;\r
428         }\r
429 \r
430         cinfo.density_unit=1;\r
431         cinfo.X_density=(unsigned short)GetXDPI();\r
432         cinfo.Y_density=(unsigned short)GetYDPI();\r
433 \r
434         /* Step 4: Start compressor */\r
435         /* TRUE ensures that we will write a complete interchange-JPEG file.\r
436         * Pass TRUE unless you are very sure of what you're doing.\r
437         */\r
438         jpeg_start_compress(&cinfo, TRUE);\r
439 \r
440         /* Step 5: while (scan lines remain to be written) */\r
441         /*           jpeg_write_scanlines(...); */\r
442         /* Here we use the library's state variable cinfo.next_scanline as the\r
443         * loop counter, so that we don't have to keep track ourselves.\r
444         * To keep things simple, we pass one scanline per call; you can pass\r
445         * more if you wish, though.\r
446         */\r
447         row_stride = info.dwEffWidth;   /* JSAMPLEs per row in image_buffer */\r
448 \r
449         //<DP> "8+row_stride" fix heap deallocation problem during debug???\r
450         buffer = (*cinfo.mem->alloc_sarray)\r
451                 ((j_common_ptr) &cinfo, JPOOL_IMAGE, 8+row_stride, 1);\r
452 \r
453         CImageIterator iter(this);\r
454 \r
455         iter.Upset();\r
456         while (cinfo.next_scanline < cinfo.image_height) {\r
457                 // info.nProgress = (long)(100*cinfo.next_scanline/cinfo.image_height);\r
458                 iter.GetRow(buffer[0], row_stride);\r
459                 // not necessary if swapped red and blue definition in jmorecfg.h;ln322 <W. Morrison>\r
460                 if (head.biClrUsed==0){                          // swap R & B for RGB images\r
461                         RGBtoBGR(buffer[0], row_stride); // Lance : 1998/09/01 : Bug ID: EXP-2.1.1-9\r
462                 }\r
463                 iter.PrevRow();\r
464                 (void) jpeg_write_scanlines(&cinfo, buffer, 1);\r
465         }\r
466 \r
467         /* Step 6: Finish compression */\r
468         jpeg_finish_compress(&cinfo);\r
469 \r
470         /* Step 7: release JPEG compression object */\r
471         /* This is an important step since it will release a good deal of memory. */\r
472         jpeg_destroy_compress(&cinfo);\r
473 \r
474 \r
475 #if CXIMAGEJPG_SUPPORT_EXIF\r
476         if (m_exif && m_exif->m_exifinfo->IsExif){\r
477                 // discard useless sections (if any) read from original image\r
478                 m_exif->DiscardAllButExif();\r
479                 // read new created image, to split the sections\r
480                 hFile->Seek(pos,SEEK_SET);\r
481                 m_exif->DecodeExif(hFile,EXIF_READ_IMAGE);\r
482                 // save back the image, adding EXIF section\r
483                 hFile->Seek(pos,SEEK_SET);\r
484                 m_exif->EncodeExif(hFile);\r
485         }\r
486 #endif\r
487 \r
488 \r
489         /* And we're done! */\r
490         return true;\r
491 }\r
492 ////////////////////////////////////////////////////////////////////////////////\r
493 #endif // CXIMAGE_SUPPORT_ENCODE\r
494 ////////////////////////////////////////////////////////////////////////////////\r
495 #endif // CXIMAGE_SUPPORT_JPG\r
496 \r