Subversion Repositories Scribus

Rev

Rev 5937 | Rev 5949 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
5937 jghali 1
#include <qfile.h>
5947 jghali 2
#include <qfileinfo.h>
5937 jghali 3
#include <setjmp.h>
4
#include "scconfig.h"
5
#include "scimgdataloader_jpeg.h"
6
#include "exif.h"
7
 
8
#ifdef HAVE_CMS
9
        #include CMS_INC
10
#endif
11
 
12
typedef struct my_error_mgr
13
{
14
        struct jpeg_error_mgr pub;            /* "public" fields */
15
        jmp_buf setjmp_buffer;  /* for return to caller */
16
} *my_error_ptr;
17
 
18
static void my_error_exit (j_common_ptr cinfo)
19
{
20
        my_error_ptr myerr = (my_error_ptr) cinfo->err;
21
        (*cinfo->err->output_message) (cinfo);
22
        longjmp (myerr->setjmp_buffer, 1);
23
}
24
 
25
#define ICC_MARKER  (JPEG_APP0 + 2)     /* JPEG marker code for ICC */
26
#define PHOTOSHOP_MARKER  (JPEG_APP0 + 13)      /* JPEG marker code for PHOTOSHOP */
27
#define ICC_OVERHEAD_LEN  14            /* size of non-profile data in APP2 */
28
#define MAX_BYTES_IN_MARKER  65533      /* maximum data len of a JPEG marker */
29
#define MAX_DATA_BYTES_IN_MARKER  (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
30
 
31
 
32
ScImgDataLoader_JPEG::ScImgDataLoader_JPEG(void)
33
{
34
        initSupportedFormatList();
35
}
36
 
37
void ScImgDataLoader_JPEG::initSupportedFormatList(void)
38
{
39
        m_supportedFormats.clear();
40
        m_supportedFormats.append( "jpg" );
41
        m_supportedFormats.append( "jpeg" );
42
}
43
 
44
void ScImgDataLoader_JPEG::loadEmbeddedProfile(const QString& fn)
45
{
46
        m_embeddedProfile.resize(0);
47
        m_profileComponents = 0;
48
#ifdef HAVE_CMS
49
        if (!QFile::exists(fn))
50
                return;
51
        struct jpeg_decompress_struct cinfo;
52
        struct my_error_mgr         jerr;
53
        FILE     *infile;
54
        cinfo.err = jpeg_std_error (&jerr.pub);
55
        jerr.pub.error_exit = my_error_exit;
56
        infile = NULL;
57
        if (setjmp (jerr.setjmp_buffer))
58
        {
59
                jpeg_destroy_decompress (&cinfo);
60
                if (infile)
61
                        fclose (infile);
62
                return;
63
        }
64
        jpeg_create_decompress (&cinfo);
65
        if ((infile = fopen (fn.local8Bit(), "rb")) == NULL)
66
                return;
67
        jpeg_stdio_src(&cinfo, infile);
68
        jpeg_save_markers(&cinfo, ICC_MARKER, 0xFFFF);
69
        jpeg_read_header(&cinfo, true);
70
        jpeg_start_decompress(&cinfo);
71
        unsigned int EmbedLen = 0;
72
        unsigned char* EmbedBuffer;
73
        if (read_jpeg_marker(ICC_MARKER,&cinfo, &EmbedBuffer, &EmbedLen))
74
        {
75
                cmsHPROFILE prof = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);
76
                if (prof)
77
                {
78
                        if (static_cast<int>(cmsGetColorSpace(prof)) == icSigRgbData)
79
                                m_profileComponents = 3;
80
                        if (static_cast<int>(cmsGetColorSpace(prof)) == icSigCmykData)
81
                                m_profileComponents = 4;
82
                        m_embeddedProfile.duplicate((const char*) EmbedBuffer, EmbedLen);
83
                }
84
                cmsCloseProfile(prof);
85
                free(EmbedBuffer);
86
        }
87
        (void) jpeg_finish_decompress(&cinfo);
88
        fclose (infile);
89
        jpeg_destroy_decompress (&cinfo);
90
#endif
91
}
92
 
93
void ScImgDataLoader_JPEG::preloadAlphaChannel(const QString& fn, int res)
94
{
95
        // No support for aplha in jpeg pictures
96
        initialize();
97
}
98
 
99
bool ScImgDataLoader_JPEG::loadPicture(const QString& fn, int res, bool thumbnail)
100
{
101
        bool isCMYK = false;
102
        float xres = 72.0, yres = 72.0;
103
        if (!QFile::exists(fn))
104
                return false;
105
        ExifData ExifInf;
106
        struct jpeg_decompress_struct cinfo;
107
        struct my_error_mgr         jerr;
108
        FILE     *infile;
109
        cinfo.err = jpeg_std_error (&jerr.pub);
110
        jerr.pub.error_exit = my_error_exit;
111
        infile = NULL;
112
 
113
        initialize();
114
        m_imageInfoRecord.type = 0;
115
        m_imageInfoRecord.exifInfo.thumbnail = QImage();
116
        if (setjmp (jerr.setjmp_buffer))
117
        {
118
                jpeg_destroy_decompress (&cinfo);
119
                if (infile)
120
                        fclose (infile);
121
                return false;
122
        }
123
        jpeg_create_decompress (&cinfo);
124
        if ((infile = fopen (fn.local8Bit(), "rb")) == NULL)
125
                return false;
126
        jpeg_stdio_src(&cinfo, infile);
127
        jpeg_save_markers(&cinfo, ICC_MARKER, 0xFFFF);
128
        jpeg_save_markers(&cinfo, PHOTOSHOP_MARKER, 0xFFFF);
129
        jpeg_read_header(&cinfo, true);
130
        jpeg_start_decompress(&cinfo);
131
        bool exi = ExifInf.scan(fn);
132
        if ((exi) && (ExifInf.exifDataValid))
133
        {
134
                if ((!ExifInf.isNullThumbnail()) && thumbnail)
135
                        m_image = ExifInf.getThumbnail();
136
                m_imageInfoRecord.exifInfo.cameraName = ExifInf.getCameraModel();
137
                m_imageInfoRecord.exifInfo.cameraVendor = ExifInf.getCameraMake();
138
                m_imageInfoRecord.exifInfo.thumbnail = ExifInf.getThumbnail();
139
                m_imageInfoRecord.exifInfo.comment = ExifInf.getComment();
140
                m_imageInfoRecord.exifInfo.width = ExifInf.getWidth();
141
                m_imageInfoRecord.exifInfo.height = ExifInf.getHeight();
142
                m_imageInfoRecord.exifInfo.userComment = ExifInf.getUserComment();
143
                m_imageInfoRecord.exifInfo.dateTime = ExifInf.getDateTime();
144
                m_imageInfoRecord.exifDataValid = true;
145
                if (cinfo.density_unit == 0)
146
                {
147
                        xres = 72;
148
                        yres = 72;
149
                }
150
                else if ( cinfo.density_unit == 1 )
151
                {
152
                        xres = cinfo.X_density;
153
                        yres = cinfo.Y_density;
154
                }
155
                else if ( cinfo.density_unit == 2 )
156
                {
157
                        xres = cinfo.X_density * 2.54;
158
                        yres = cinfo.Y_density * 2.54;
159
                }
5947 jghali 160
                if( xres <= 1.0 || yres <= 1.0 )
161
                {
162
                        xres = yres = 72.0;
163
                        QFileInfo qfi(fn);
164
                        qWarning("Warning: %s may be corrupted", qfi.fileName().local8Bit());
165
                }
5937 jghali 166
                m_imageInfoRecord.xres = qRound(xres);
167
                m_imageInfoRecord.yres = qRound(yres);
168
                if (cinfo.output_components == 4)
169
                        m_imageInfoRecord.colorspace = 1;
170
                else if (cinfo.output_components == 3)
171
                        m_imageInfoRecord.colorspace = 0;
172
                else if (cinfo.output_components == 1)
173
                        m_imageInfoRecord.colorspace = 2;
174
                m_imageInfoRecord.progressive = jpeg_has_multiple_scans(&cinfo);
175
                if ((!ExifInf.isNullThumbnail()) && thumbnail)
176
                {
177
                        jpeg_destroy_decompress(&cinfo);
178
                        fclose(infile);
179
                        return true;
180
                }
181
        }
182
        else
183
                m_imageInfoRecord.exifDataValid = false;
184
#ifdef HAVE_CMS
185
        unsigned int EmbedLen = 0;
186
        unsigned char* EmbedBuffer;
187
        if (read_jpeg_marker(ICC_MARKER,&cinfo, &EmbedBuffer, &EmbedLen))
188
        {
189
                const char *Descriptor;
190
                cmsHPROFILE prof = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);
191
                Descriptor = cmsTakeProductDesc(prof);
192
                m_embeddedProfile.duplicate((const char*) EmbedBuffer, EmbedLen);
193
                m_imageInfoRecord.profileName = QString(Descriptor);
194
                m_imageInfoRecord.isEmbedded = true;
195
                free(EmbedBuffer);
196
        }
197
        else
198
        {
199
                m_imageInfoRecord.isEmbedded = false;
200
                m_imageInfoRecord.profileName = "";
201
        }
202
#endif // HAVE_CMS
203
        unsigned int PhotoshopLen = 0;
204
        unsigned char * PhotoshopBuffer;
205
        if (cinfo.density_unit == 0)
206
        {
207
                xres = 72;
208
                yres = 72;
209
                m_image.setDotsPerMeterX(2834);
210
                m_image.setDotsPerMeterY(2834);
211
        }
212
        else if ( cinfo.density_unit == 1 )
213
        {
214
                xres = cinfo.X_density;
215
                yres = cinfo.Y_density;
216
                m_image.setDotsPerMeterX( int(100. * cinfo.X_density / 2.54) );
217
                m_image.setDotsPerMeterY( int(100. * cinfo.Y_density / 2.54) );
218
        }
219
        else if ( cinfo.density_unit == 2 )
220
        {
221
                xres = cinfo.X_density * 2.54;
222
                yres = cinfo.Y_density * 2.54;
223
                m_image.setDotsPerMeterX( int(100. * cinfo.X_density) );
224
                m_image.setDotsPerMeterY( int(100. * cinfo.Y_density) );
225
        }
5947 jghali 226
        if( xres <= 1.0 || yres <= 1.0 )
227
        {
228
                xres = yres = 72.0;
229
                QFileInfo qfi(fn);
230
                qWarning("Warning: %s may be corrupted", qfi.fileName().local8Bit());
231
        }
5937 jghali 232
        m_imageInfoRecord.xres = qRound(xres);
233
        m_imageInfoRecord.yres = qRound(yres);
234
        if (cinfo.output_components == 4)
235
        {
236
                isCMYK = true;
237
                m_imageInfoRecord.colorspace = 1;
238
        }
239
        else if (cinfo.output_components == 3)
240
                m_imageInfoRecord.colorspace = 0;
241
        else if (cinfo.output_components == 1)
242
                m_imageInfoRecord.colorspace = 2;
243
        m_imageInfoRecord.progressive = jpeg_has_multiple_scans(&cinfo);
244
 
245
        if (read_jpeg_marker(PHOTOSHOP_MARKER,&cinfo, &PhotoshopBuffer, &PhotoshopLen) )
246
        {
247
                if (PhotoshopLen != 0)
248
                {
249
                        bool savEx = m_imageInfoRecord.exifDataValid;
250
                        QByteArray arrayPhot(PhotoshopLen);
251
                        arrayPhot.setRawData((const char*)PhotoshopBuffer,PhotoshopLen);
252
                        QDataStream strPhot(arrayPhot,IO_ReadOnly);
253
                        strPhot.setByteOrder( QDataStream::BigEndian );
254
                        PSDHeader fakeHeader;
255
                        fakeHeader.width = cinfo.output_width;
256
                        fakeHeader.height = cinfo.output_height;
257
                        if (cinfo.output_components == 4)
258
                                m_imageInfoRecord.colorspace = 1;
259
                        else if (cinfo.output_components == 3)
260
                                m_imageInfoRecord.colorspace = 0;
261
                        else if (cinfo.output_components == 1)
262
                                m_imageInfoRecord.colorspace = 2;
263
                        m_imageInfoRecord.progressive = jpeg_has_multiple_scans(&cinfo);
264
                        parseRessourceData(strPhot, fakeHeader, PhotoshopLen);
265
                        // Photoshop resolution is more accurate than jpeg header resolution
266
                        xres = m_imageInfoRecord.xres;
267
                        yres = m_imageInfoRecord.yres;
268
                        m_image.setDotsPerMeterX( int(100. * m_imageInfoRecord.xres / 2.54) );
269
                        m_image.setDotsPerMeterY( int(100. * m_imageInfoRecord.yres / 2.54) );
270
                        m_imageInfoRecord.valid = (m_imageInfoRecord.PDSpathData.size())>0?true:false; // The only interest is vectormask
271
                        arrayPhot.resetRawData((const char*)PhotoshopBuffer,PhotoshopLen);
272
                        free( PhotoshopBuffer );
273
                        m_imageInfoRecord.exifDataValid = savEx;
274
                }
275
        }
276
        if ( cinfo.output_components == 3 || cinfo.output_components == 4)
277
                m_image.create( cinfo.output_width, cinfo.output_height, 32 );
278
        else if ( cinfo.output_components == 1 )
279
        {
280
                m_image.create( cinfo.output_width, cinfo.output_height, 8, 256 );
281
                for (int i=0; i<256; i++)
282
                        m_image.setColor(i, qRgb(i,i,i));
283
        }
284
        if (!m_image.isNull())
285
        {
286
                uchar** lines = m_image.jumpTable();
287
                while (cinfo.output_scanline < cinfo.output_height)
288
                        (void) jpeg_read_scanlines(&cinfo, lines + cinfo.output_scanline, cinfo.output_height);
289
                if ( cinfo.output_components == 3 )
290
                {
291
                        for (uint j=0; j<cinfo.output_height; j++)
292
                        {
293
                                uchar *in = m_image.scanLine(j) + cinfo.output_width * 3;
294
                                QRgb *out = (QRgb*) m_image.scanLine(j);
295
                                for (uint i=cinfo.output_width; i--; )
296
                                {
297
                                        in -= 3;
298
                                        out[i] = qRgb(in[0], in[1], in[2]);
299
                                }
300
                        }
301
                }
302
                if ( cinfo.output_components == 4 )
303
                {
304
                        for (int i = 0; i < m_image.height(); i++)
305
                        {
306
                                QRgb *ptr = (QRgb*)  m_image.scanLine(i);
307
                                unsigned char c, m, y ,k;
308
                                if ((cinfo.jpeg_color_space == JCS_YCCK) || ((cinfo.jpeg_color_space == JCS_CMYK) && (cinfo.saw_Adobe_marker)))
309
                                {
310
                                        for (int j = 0; j <  m_image.width(); j++)
311
                                        {
312
                                                unsigned char *p = (unsigned char *) ptr;
313
                                                c = p[0];
314
                                                m = p[1];
315
                                                y =  p[2];
316
                                                k =  p[3];
317
                                                *ptr = qRgba(255 - c, 255 - m, 255 - y, 255 - k);
318
                                                ptr++;
319
                                        }
320
                                }
321
                                else
322
                                {
323
                                        for (int j = 0; j <  m_image.width(); j++)
324
                                        {
325
                                                unsigned char *p = (unsigned char *) ptr;
326
                                                c = p[0];
327
                                                m = p[1];
328
                                                y =  p[2];
329
                                                k =  p[3];
330
                                                *ptr = qRgba(y, m, c, k);
331
                                                ptr++;
332
                                        }
333
                                }
334
                        }
335
                        isCMYK = true;
336
                }
337
                else
338
                        isCMYK = false;
339
                if ( cinfo.output_components == 1 )
340
                {
341
                        QImage tmpImg = m_image.convertDepth(32);
342
                         m_image.create( cinfo.output_width, cinfo.output_height, 32 );
343
                        for( int yi=0; yi < tmpImg.height(); ++yi )
344
                        {
345
                                QRgb *s = (QRgb*)(tmpImg.scanLine( yi ));
346
                                QRgb *d = (QRgb*)(m_image.scanLine( yi ));
347
                                for(int xi=0; xi < tmpImg.width(); ++xi )
348
                                {
349
                                        (*d) = (*s);
350
                                        s++;
351
                                        d++;
352
                                }
353
                        }
354
                }
355
                m_image.setAlphaBuffer(true);
356
        }
357
        (void) jpeg_finish_decompress(&cinfo);
358
        fclose (infile);
359
        jpeg_destroy_decompress (&cinfo);
360
        m_imageInfoRecord.layerInfo.clear();
361
        m_imageInfoRecord.BBoxX = 0;
362
        m_imageInfoRecord.BBoxH = m_image.height();
363
        return (!m_image.isNull());
364
}
365
 
366
bool ScImgDataLoader_JPEG::marker_is_icc (jpeg_saved_marker_ptr marker)
367
{
368
        return
369
            marker->marker == ICC_MARKER &&
370
            marker->data_length >= ICC_OVERHEAD_LEN &&
371
            /* verify the identifying string */
372
            GETJOCTET(marker->data[0]) == 0x49 &&
373
            GETJOCTET(marker->data[1]) == 0x43 &&
374
            GETJOCTET(marker->data[2]) == 0x43 &&
375
            GETJOCTET(marker->data[3]) == 0x5F &&
376
            GETJOCTET(marker->data[4]) == 0x50 &&
377
            GETJOCTET(marker->data[5]) == 0x52 &&
378
            GETJOCTET(marker->data[6]) == 0x4F &&
379
            GETJOCTET(marker->data[7]) == 0x46 &&
380
            GETJOCTET(marker->data[8]) == 0x49 &&
381
            GETJOCTET(marker->data[9]) == 0x4C &&
382
            GETJOCTET(marker->data[10]) == 0x45 &&
383
            GETJOCTET(marker->data[11]) == 0x0;
384
}
385
 
386
bool ScImgDataLoader_JPEG::marker_is_photoshop (jpeg_saved_marker_ptr marker)
387
{
388
        return
389
            marker->marker == PHOTOSHOP_MARKER &&
390
            marker->data_length >= ICC_OVERHEAD_LEN &&
391
            /* verify the identifying string */
392
            GETJOCTET(marker->data[0]) == 0x50 &&
393
            GETJOCTET(marker->data[1]) == 0x68 &&
394
            GETJOCTET(marker->data[2]) == 0x6F &&
395
            GETJOCTET(marker->data[3]) == 0x74 &&
396
            GETJOCTET(marker->data[4]) == 0x6F &&
397
            GETJOCTET(marker->data[5]) == 0x73 &&
398
            GETJOCTET(marker->data[6]) == 0x68 &&
399
            GETJOCTET(marker->data[7]) == 0x6F &&
400
            GETJOCTET(marker->data[8]) == 0x70 &&
401
            GETJOCTET(marker->data[9]) == 0x20 &&
402
            GETJOCTET(marker->data[10]) == 0x33 &&
403
            GETJOCTET(marker->data[11]) == 0x2E &&
404
            GETJOCTET(marker->data[12]) == 0x30 &&
405
            GETJOCTET(marker->data[13]) == 0x0;
406
}
407
 
408
/* Small modification of original read_icc_profile method from jpegicc of lcms project
409
 * to enable read of Photoshop marker
410
 */
411
bool ScImgDataLoader_JPEG::read_jpeg_marker (UINT8 requestmarker, j_decompress_ptr cinfo, JOCTET **icc_data_ptr, unsigned int *icc_data_len)
412
{
413
        jpeg_saved_marker_ptr marker;
414
        int num_markers = 0;
415
        int seq_no;
416
        JOCTET *icc_data;
417
        unsigned int total_length;
418
#define MAX_SEQ_NO  255         /* sufficient since marker numbers are bytes */
419
        char marker_present[MAX_SEQ_NO+1];        /* 1 if marker found */
420
        unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */
421
        unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */
422
 
423
        *icc_data_ptr = NULL;           /* avoid confusion if false return */
424
        *icc_data_len = 0;
425
 
426
        /* This first pass over the saved markers discovers whether there are
427
         * any ICC markers and verifies the consistency of the marker numbering.
428
         */
429
 
430
        for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++)
431
                marker_present[seq_no] = 0;
432
        seq_no = 0;
433
        for (marker = cinfo->marker_list; marker != NULL; marker = marker->next)
434
        {
435
                if (requestmarker == ICC_MARKER && marker_is_icc(marker))
436
                {
437
                        if (num_markers == 0)
438
                                num_markers = GETJOCTET(marker->data[13]);
439
                        else if (num_markers != GETJOCTET(marker->data[13]))
440
                                return false;           /* inconsistent num_markers fields */
441
                        seq_no = GETJOCTET(marker->data[12]);
442
                        if (seq_no <= 0 || seq_no > num_markers)
443
                                return false;           /* bogus sequence number */
444
                        if (marker_present[seq_no])
445
                                return false;           /* duplicate sequence numbers */
446
                        marker_present[seq_no] = 1;
447
                        data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN;
448
                }
449
                else if(requestmarker == PHOTOSHOP_MARKER && marker_is_photoshop(marker))
450
                {
451
                        num_markers = ++seq_no;
452
                        marker_present[seq_no] = 1;
453
                        data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN;
454
                }
455
        }
456
 
457
        if (num_markers == 0)
458
                return false;
459
 
460
        /* Check for missing markers, count total space needed,
461
         * compute offset of each marker's part of the data.
462
         */
463
 
464
        total_length = 0;
465
        for (seq_no = 1; seq_no <= num_markers; seq_no++)
466
        {
467
                if (marker_present[seq_no] == 0)
468
                        return false;           /* missing sequence number */
469
                data_offset[seq_no] = total_length;
470
                total_length += data_length[seq_no];
471
        }
472
 
473
        if (total_length <= 0)
474
                return false;           /* found only empty markers? */
475
 
476
        /* Allocate space for assembled data */
477
        icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET));
478
        if (icc_data == NULL)
479
                return false;           /* oops, out of memory */
480
        seq_no=0;
481
        /* and fill it in */
482
        for (marker = cinfo->marker_list; marker != NULL; marker = marker->next)
483
        {
484
                if ( (requestmarker == ICC_MARKER && marker_is_icc(marker)) ||
485
                        (requestmarker == PHOTOSHOP_MARKER && marker_is_photoshop(marker)) || (requestmarker == 0xE1))
486
                {
487
                        JOCTET FAR *src_ptr;
488
                        JOCTET *dst_ptr;
489
                        unsigned int length;
490
                        if(requestmarker == ICC_MARKER)
491
                                seq_no = GETJOCTET(marker->data[12]);
492
                        else if(requestmarker == PHOTOSHOP_MARKER)
493
                                seq_no++;
494
                        dst_ptr = icc_data + data_offset[seq_no];
495
                        src_ptr = marker->data + ICC_OVERHEAD_LEN;
496
                        length = data_length[seq_no];
497
                        while (length--)
498
                        {
499
                                *dst_ptr++ = *src_ptr++;
500
                        }
501
                }
502
        }
503
 
504
        *icc_data_ptr = icc_data;
505
        *icc_data_len = total_length;
506
 
507
        return true;
508
}