Subversion Repositories Scribus

Rev

Rev 5947 | Rev 5977 | 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);
5949 jghali 164
                        QCString fname = qfi.fileName().local8Bit();
165
                        qWarning("Warning: %s may be corrupted", fname.data());
5947 jghali 166
                }
5937 jghali 167
                m_imageInfoRecord.xres = qRound(xres);
168
                m_imageInfoRecord.yres = qRound(yres);
169
                if (cinfo.output_components == 4)
170
                        m_imageInfoRecord.colorspace = 1;
171
                else if (cinfo.output_components == 3)
172
                        m_imageInfoRecord.colorspace = 0;
173
                else if (cinfo.output_components == 1)
174
                        m_imageInfoRecord.colorspace = 2;
175
                m_imageInfoRecord.progressive = jpeg_has_multiple_scans(&cinfo);
176
                if ((!ExifInf.isNullThumbnail()) && thumbnail)
177
                {
178
                        jpeg_destroy_decompress(&cinfo);
179
                        fclose(infile);
180
                        return true;
181
                }
182
        }
183
        else
184
                m_imageInfoRecord.exifDataValid = false;
185
#ifdef HAVE_CMS
186
        unsigned int EmbedLen = 0;
187
        unsigned char* EmbedBuffer;
188
        if (read_jpeg_marker(ICC_MARKER,&cinfo, &EmbedBuffer, &EmbedLen))
189
        {
190
                const char *Descriptor;
191
                cmsHPROFILE prof = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);
192
                Descriptor = cmsTakeProductDesc(prof);
193
                m_embeddedProfile.duplicate((const char*) EmbedBuffer, EmbedLen);
194
                m_imageInfoRecord.profileName = QString(Descriptor);
195
                m_imageInfoRecord.isEmbedded = true;
196
                free(EmbedBuffer);
197
        }
198
        else
199
        {
200
                m_imageInfoRecord.isEmbedded = false;
201
                m_imageInfoRecord.profileName = "";
202
        }
203
#endif // HAVE_CMS
204
        unsigned int PhotoshopLen = 0;
205
        unsigned char * PhotoshopBuffer;
206
        if (cinfo.density_unit == 0)
207
        {
208
                xres = 72;
209
                yres = 72;
210
                m_image.setDotsPerMeterX(2834);
211
                m_image.setDotsPerMeterY(2834);
212
        }
213
        else if ( cinfo.density_unit == 1 )
214
        {
215
                xres = cinfo.X_density;
216
                yres = cinfo.Y_density;
217
                m_image.setDotsPerMeterX( int(100. * cinfo.X_density / 2.54) );
218
                m_image.setDotsPerMeterY( int(100. * cinfo.Y_density / 2.54) );
219
        }
220
        else if ( cinfo.density_unit == 2 )
221
        {
222
                xres = cinfo.X_density * 2.54;
223
                yres = cinfo.Y_density * 2.54;
224
                m_image.setDotsPerMeterX( int(100. * cinfo.X_density) );
225
                m_image.setDotsPerMeterY( int(100. * cinfo.Y_density) );
226
        }
5947 jghali 227
        if( xres <= 1.0 || yres <= 1.0 )
228
        {
229
                xres = yres = 72.0;
230
                QFileInfo qfi(fn);
5949 jghali 231
                QCString fname = qfi.fileName().local8Bit();
232
                qWarning("Warning: %s may be corrupted", fname.data());
5947 jghali 233
        }
5937 jghali 234
        m_imageInfoRecord.xres = qRound(xres);
235
        m_imageInfoRecord.yres = qRound(yres);
236
        if (cinfo.output_components == 4)
237
        {
238
                isCMYK = true;
239
                m_imageInfoRecord.colorspace = 1;
240
        }
241
        else if (cinfo.output_components == 3)
242
                m_imageInfoRecord.colorspace = 0;
243
        else if (cinfo.output_components == 1)
244
                m_imageInfoRecord.colorspace = 2;
245
        m_imageInfoRecord.progressive = jpeg_has_multiple_scans(&cinfo);
246
 
247
        if (read_jpeg_marker(PHOTOSHOP_MARKER,&cinfo, &PhotoshopBuffer, &PhotoshopLen) )
248
        {
249
                if (PhotoshopLen != 0)
250
                {
251
                        bool savEx = m_imageInfoRecord.exifDataValid;
252
                        QByteArray arrayPhot(PhotoshopLen);
253
                        arrayPhot.setRawData((const char*)PhotoshopBuffer,PhotoshopLen);
254
                        QDataStream strPhot(arrayPhot,IO_ReadOnly);
255
                        strPhot.setByteOrder( QDataStream::BigEndian );
256
                        PSDHeader fakeHeader;
257
                        fakeHeader.width = cinfo.output_width;
258
                        fakeHeader.height = cinfo.output_height;
259
                        if (cinfo.output_components == 4)
260
                                m_imageInfoRecord.colorspace = 1;
261
                        else if (cinfo.output_components == 3)
262
                                m_imageInfoRecord.colorspace = 0;
263
                        else if (cinfo.output_components == 1)
264
                                m_imageInfoRecord.colorspace = 2;
265
                        m_imageInfoRecord.progressive = jpeg_has_multiple_scans(&cinfo);
266
                        parseRessourceData(strPhot, fakeHeader, PhotoshopLen);
267
                        // Photoshop resolution is more accurate than jpeg header resolution
268
                        xres = m_imageInfoRecord.xres;
269
                        yres = m_imageInfoRecord.yres;
270
                        m_image.setDotsPerMeterX( int(100. * m_imageInfoRecord.xres / 2.54) );
271
                        m_image.setDotsPerMeterY( int(100. * m_imageInfoRecord.yres / 2.54) );
272
                        m_imageInfoRecord.valid = (m_imageInfoRecord.PDSpathData.size())>0?true:false; // The only interest is vectormask
273
                        arrayPhot.resetRawData((const char*)PhotoshopBuffer,PhotoshopLen);
274
                        free( PhotoshopBuffer );
275
                        m_imageInfoRecord.exifDataValid = savEx;
276
                }
277
        }
278
        if ( cinfo.output_components == 3 || cinfo.output_components == 4)
279
                m_image.create( cinfo.output_width, cinfo.output_height, 32 );
280
        else if ( cinfo.output_components == 1 )
281
        {
282
                m_image.create( cinfo.output_width, cinfo.output_height, 8, 256 );
283
                for (int i=0; i<256; i++)
284
                        m_image.setColor(i, qRgb(i,i,i));
285
        }
286
        if (!m_image.isNull())
287
        {
288
                uchar** lines = m_image.jumpTable();
289
                while (cinfo.output_scanline < cinfo.output_height)
290
                        (void) jpeg_read_scanlines(&cinfo, lines + cinfo.output_scanline, cinfo.output_height);
291
                if ( cinfo.output_components == 3 )
292
                {
293
                        for (uint j=0; j<cinfo.output_height; j++)
294
                        {
295
                                uchar *in = m_image.scanLine(j) + cinfo.output_width * 3;
296
                                QRgb *out = (QRgb*) m_image.scanLine(j);
297
                                for (uint i=cinfo.output_width; i--; )
298
                                {
299
                                        in -= 3;
300
                                        out[i] = qRgb(in[0], in[1], in[2]);
301
                                }
302
                        }
303
                }
304
                if ( cinfo.output_components == 4 )
305
                {
306
                        for (int i = 0; i < m_image.height(); i++)
307
                        {
308
                                QRgb *ptr = (QRgb*)  m_image.scanLine(i);
309
                                unsigned char c, m, y ,k;
310
                                if ((cinfo.jpeg_color_space == JCS_YCCK) || ((cinfo.jpeg_color_space == JCS_CMYK) && (cinfo.saw_Adobe_marker)))
311
                                {
312
                                        for (int j = 0; j <  m_image.width(); j++)
313
                                        {
314
                                                unsigned char *p = (unsigned char *) ptr;
315
                                                c = p[0];
316
                                                m = p[1];
317
                                                y =  p[2];
318
                                                k =  p[3];
319
                                                *ptr = qRgba(255 - c, 255 - m, 255 - y, 255 - k);
320
                                                ptr++;
321
                                        }
322
                                }
323
                                else
324
                                {
325
                                        for (int j = 0; j <  m_image.width(); j++)
326
                                        {
327
                                                unsigned char *p = (unsigned char *) ptr;
328
                                                c = p[0];
329
                                                m = p[1];
330
                                                y =  p[2];
331
                                                k =  p[3];
332
                                                *ptr = qRgba(y, m, c, k);
333
                                                ptr++;
334
                                        }
335
                                }
336
                        }
337
                        isCMYK = true;
338
                }
339
                else
340
                        isCMYK = false;
341
                if ( cinfo.output_components == 1 )
342
                {
343
                        QImage tmpImg = m_image.convertDepth(32);
344
                         m_image.create( cinfo.output_width, cinfo.output_height, 32 );
345
                        for( int yi=0; yi < tmpImg.height(); ++yi )
346
                        {
347
                                QRgb *s = (QRgb*)(tmpImg.scanLine( yi ));
348
                                QRgb *d = (QRgb*)(m_image.scanLine( yi ));
349
                                for(int xi=0; xi < tmpImg.width(); ++xi )
350
                                {
351
                                        (*d) = (*s);
352
                                        s++;
353
                                        d++;
354
                                }
355
                        }
356
                }
357
                m_image.setAlphaBuffer(true);
358
        }
359
        (void) jpeg_finish_decompress(&cinfo);
360
        fclose (infile);
361
        jpeg_destroy_decompress (&cinfo);
362
        m_imageInfoRecord.layerInfo.clear();
363
        m_imageInfoRecord.BBoxX = 0;
364
        m_imageInfoRecord.BBoxH = m_image.height();
365
        return (!m_image.isNull());
366
}
367
 
368
bool ScImgDataLoader_JPEG::marker_is_icc (jpeg_saved_marker_ptr marker)
369
{
370
        return
371
            marker->marker == ICC_MARKER &&
372
            marker->data_length >= ICC_OVERHEAD_LEN &&
373
            /* verify the identifying string */
374
            GETJOCTET(marker->data[0]) == 0x49 &&
375
            GETJOCTET(marker->data[1]) == 0x43 &&
376
            GETJOCTET(marker->data[2]) == 0x43 &&
377
            GETJOCTET(marker->data[3]) == 0x5F &&
378
            GETJOCTET(marker->data[4]) == 0x50 &&
379
            GETJOCTET(marker->data[5]) == 0x52 &&
380
            GETJOCTET(marker->data[6]) == 0x4F &&
381
            GETJOCTET(marker->data[7]) == 0x46 &&
382
            GETJOCTET(marker->data[8]) == 0x49 &&
383
            GETJOCTET(marker->data[9]) == 0x4C &&
384
            GETJOCTET(marker->data[10]) == 0x45 &&
385
            GETJOCTET(marker->data[11]) == 0x0;
386
}
387
 
388
bool ScImgDataLoader_JPEG::marker_is_photoshop (jpeg_saved_marker_ptr marker)
389
{
390
        return
391
            marker->marker == PHOTOSHOP_MARKER &&
392
            marker->data_length >= ICC_OVERHEAD_LEN &&
393
            /* verify the identifying string */
394
            GETJOCTET(marker->data[0]) == 0x50 &&
395
            GETJOCTET(marker->data[1]) == 0x68 &&
396
            GETJOCTET(marker->data[2]) == 0x6F &&
397
            GETJOCTET(marker->data[3]) == 0x74 &&
398
            GETJOCTET(marker->data[4]) == 0x6F &&
399
            GETJOCTET(marker->data[5]) == 0x73 &&
400
            GETJOCTET(marker->data[6]) == 0x68 &&
401
            GETJOCTET(marker->data[7]) == 0x6F &&
402
            GETJOCTET(marker->data[8]) == 0x70 &&
403
            GETJOCTET(marker->data[9]) == 0x20 &&
404
            GETJOCTET(marker->data[10]) == 0x33 &&
405
            GETJOCTET(marker->data[11]) == 0x2E &&
406
            GETJOCTET(marker->data[12]) == 0x30 &&
407
            GETJOCTET(marker->data[13]) == 0x0;
408
}
409
 
410
/* Small modification of original read_icc_profile method from jpegicc of lcms project
411
 * to enable read of Photoshop marker
412
 */
413
bool ScImgDataLoader_JPEG::read_jpeg_marker (UINT8 requestmarker, j_decompress_ptr cinfo, JOCTET **icc_data_ptr, unsigned int *icc_data_len)
414
{
415
        jpeg_saved_marker_ptr marker;
416
        int num_markers = 0;
417
        int seq_no;
418
        JOCTET *icc_data;
419
        unsigned int total_length;
420
#define MAX_SEQ_NO  255         /* sufficient since marker numbers are bytes */
421
        char marker_present[MAX_SEQ_NO+1];        /* 1 if marker found */
422
        unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */
423
        unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */
424
 
425
        *icc_data_ptr = NULL;           /* avoid confusion if false return */
426
        *icc_data_len = 0;
427
 
428
        /* This first pass over the saved markers discovers whether there are
429
         * any ICC markers and verifies the consistency of the marker numbering.
430
         */
431
 
432
        for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++)
433
                marker_present[seq_no] = 0;
434
        seq_no = 0;
435
        for (marker = cinfo->marker_list; marker != NULL; marker = marker->next)
436
        {
437
                if (requestmarker == ICC_MARKER && marker_is_icc(marker))
438
                {
439
                        if (num_markers == 0)
440
                                num_markers = GETJOCTET(marker->data[13]);
441
                        else if (num_markers != GETJOCTET(marker->data[13]))
442
                                return false;           /* inconsistent num_markers fields */
443
                        seq_no = GETJOCTET(marker->data[12]);
444
                        if (seq_no <= 0 || seq_no > num_markers)
445
                                return false;           /* bogus sequence number */
446
                        if (marker_present[seq_no])
447
                                return false;           /* duplicate sequence numbers */
448
                        marker_present[seq_no] = 1;
449
                        data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN;
450
                }
451
                else if(requestmarker == PHOTOSHOP_MARKER && marker_is_photoshop(marker))
452
                {
453
                        num_markers = ++seq_no;
454
                        marker_present[seq_no] = 1;
455
                        data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN;
456
                }
457
        }
458
 
459
        if (num_markers == 0)
460
                return false;
461
 
462
        /* Check for missing markers, count total space needed,
463
         * compute offset of each marker's part of the data.
464
         */
465
 
466
        total_length = 0;
467
        for (seq_no = 1; seq_no <= num_markers; seq_no++)
468
        {
469
                if (marker_present[seq_no] == 0)
470
                        return false;           /* missing sequence number */
471
                data_offset[seq_no] = total_length;
472
                total_length += data_length[seq_no];
473
        }
474
 
475
        if (total_length <= 0)
476
                return false;           /* found only empty markers? */
477
 
478
        /* Allocate space for assembled data */
479
        icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET));
480
        if (icc_data == NULL)
481
                return false;           /* oops, out of memory */
482
        seq_no=0;
483
        /* and fill it in */
484
        for (marker = cinfo->marker_list; marker != NULL; marker = marker->next)
485
        {
486
                if ( (requestmarker == ICC_MARKER && marker_is_icc(marker)) ||
487
                        (requestmarker == PHOTOSHOP_MARKER && marker_is_photoshop(marker)) || (requestmarker == 0xE1))
488
                {
489
                        JOCTET FAR *src_ptr;
490
                        JOCTET *dst_ptr;
491
                        unsigned int length;
492
                        if(requestmarker == ICC_MARKER)
493
                                seq_no = GETJOCTET(marker->data[12]);
494
                        else if(requestmarker == PHOTOSHOP_MARKER)
495
                                seq_no++;
496
                        dst_ptr = icc_data + data_offset[seq_no];
497
                        src_ptr = marker->data + ICC_OVERHEAD_LEN;
498
                        length = data_length[seq_no];
499
                        while (length--)
500
                        {
501
                                *dst_ptr++ = *src_ptr++;
502
                        }
503
                }
504
        }
505
 
506
        *icc_data_ptr = icc_data;
507
        *icc_data_len = total_length;
508
 
509
        return true;
510
}