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