Rev 24665 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5977 | jghali | 1 | /* |
2 | For general Scribus (>=1.3.2) copyright and licensing information please refer |
||
3 | to the COPYING file provided with the program. Following this notice may exist |
||
4 | a copyright and/or license notice that predates the release of Scribus 1.3.2 |
||
5 | for which a new license (GPL+exception) is in place. |
||
6 | */ |
||
10223 | cbradney | 7 | #include <QFile> |
8 | #include <QFileInfo> |
||
9 | #include <QObject> |
||
22640 | craig | 10 | #include <csetjmp> |
5937 | jghali | 11 | #include "scconfig.h" |
14491 | jghali | 12 | #include "colormgmt/sccolormgmtengine.h" |
5937 | jghali | 13 | #include "scimgdataloader_jpeg.h" |
14170 | jghali | 14 | #include "scribuscore.h" |
5937 | jghali | 15 | #include "exif.h" |
16 | |||
17 | typedef struct my_error_mgr |
||
18 | { |
||
19 | struct jpeg_error_mgr pub; /* "public" fields */ |
||
20 | jmp_buf setjmp_buffer; /* for return to caller */ |
||
21 | } *my_error_ptr; |
||
22 | |||
23 | static void my_error_exit (j_common_ptr cinfo) |
||
24 | { |
||
25 | my_error_ptr myerr = (my_error_ptr) cinfo->err; |
||
26 | (*cinfo->err->output_message) (cinfo); |
||
27 | longjmp (myerr->setjmp_buffer, 1); |
||
28 | } |
||
29 | |||
30 | #define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */ |
||
31 | #define PHOTOSHOP_MARKER (JPEG_APP0 + 13) /* JPEG marker code for PHOTOSHOP */ |
||
32 | #define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */ |
||
33 | #define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */ |
||
34 | #define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN) |
||
35 | |||
36 | |||
22635 | craig | 37 | ScImgDataLoader_JPEG::ScImgDataLoader_JPEG() |
5937 | jghali | 38 | { |
39 | initSupportedFormatList(); |
||
40 | } |
||
41 | |||
22635 | craig | 42 | void ScImgDataLoader_JPEG::initSupportedFormatList() |
5937 | jghali | 43 | { |
44 | m_supportedFormats.clear(); |
||
45 | m_supportedFormats.append( "jpg" ); |
||
46 | m_supportedFormats.append( "jpeg" ); |
||
47 | } |
||
48 | |||
12141 | avox | 49 | void ScImgDataLoader_JPEG::loadEmbeddedProfile(const QString& fn, int /*page*/) |
5937 | jghali | 50 | { |
51 | m_embeddedProfile.resize(0); |
||
52 | m_profileComponents = 0; |
||
53 | if (!QFile::exists(fn)) |
||
54 | return; |
||
55 | struct jpeg_decompress_struct cinfo; |
||
56 | struct my_error_mgr jerr; |
||
57 | FILE *infile; |
||
58 | cinfo.err = jpeg_std_error (&jerr.pub); |
||
59 | jerr.pub.error_exit = my_error_exit; |
||
22518 | craig | 60 | infile = nullptr; |
5937 | jghali | 61 | if (setjmp (jerr.setjmp_buffer)) |
62 | { |
||
63 | jpeg_destroy_decompress (&cinfo); |
||
64 | if (infile) |
||
65 | fclose (infile); |
||
66 | return; |
||
67 | } |
||
68 | jpeg_create_decompress (&cinfo); |
||
20720 | jghali | 69 | #if defined(Q_OS_WIN32) |
22518 | craig | 70 | if ((infile = _wfopen((const wchar_t*) fn.utf16(), L"rb")) == nullptr) |
20720 | jghali | 71 | return; |
72 | #else |
||
22518 | craig | 73 | if ((infile = fopen (fn.toLocal8Bit(), "rb")) == nullptr) |
5937 | jghali | 74 | return; |
20720 | jghali | 75 | #endif |
5937 | jghali | 76 | jpeg_stdio_src(&cinfo, infile); |
77 | jpeg_save_markers(&cinfo, ICC_MARKER, 0xFFFF); |
||
78 | jpeg_read_header(&cinfo, true); |
||
6158 | jghali | 79 | //jpeg_start_decompress(&cinfo); |
5937 | jghali | 80 | unsigned int EmbedLen = 0; |
22605 | craig | 81 | unsigned char* EmbedBuffer = nullptr; |
20828 | jghali | 82 | if (read_jpeg_marker(ICC_MARKER, &cinfo, &EmbedBuffer, &EmbedLen)) |
5937 | jghali | 83 | { |
21679 | jghali | 84 | bool profileIsValid = false; |
24735 | jghali | 85 | QByteArray profArray((const char*) EmbedBuffer, EmbedLen); |
14170 | jghali | 86 | ScColorProfile prof = ScCore->defaultEngine.openProfileFromMem(profArray); |
5937 | jghali | 87 | if (prof) |
88 | { |
||
21679 | jghali | 89 | eColorSpaceType colorSpace = prof.colorSpace(); |
90 | if (colorSpace == ColorSpace_Rgb) |
||
22123 | jghali | 91 | profileIsValid = (cinfo.num_components == 3); |
21679 | jghali | 92 | if (colorSpace == ColorSpace_Cmyk) |
22123 | jghali | 93 | profileIsValid = (cinfo.num_components == 4); |
21679 | jghali | 94 | if (colorSpace == ColorSpace_Gray) |
22123 | jghali | 95 | profileIsValid = (cinfo.num_components == 1); |
21679 | jghali | 96 | } |
97 | if (profileIsValid) |
||
98 | { |
||
14772 | jghali | 99 | if (prof.colorSpace() == ColorSpace_Rgb) |
5937 | jghali | 100 | m_profileComponents = 3; |
14772 | jghali | 101 | if (prof.colorSpace() == ColorSpace_Cmyk) |
5937 | jghali | 102 | m_profileComponents = 4; |
14772 | jghali | 103 | if (prof.colorSpace() == ColorSpace_Gray) |
12732 | fschmid | 104 | m_profileComponents = 1; |
14170 | jghali | 105 | m_embeddedProfile = profArray; |
5937 | jghali | 106 | } |
107 | free(EmbedBuffer); |
||
108 | } |
||
6158 | jghali | 109 | //(void) jpeg_finish_decompress(&cinfo); |
5937 | jghali | 110 | fclose (infile); |
111 | jpeg_destroy_decompress (&cinfo); |
||
112 | } |
||
113 | |||
12141 | avox | 114 | bool ScImgDataLoader_JPEG::preloadAlphaChannel(const QString& fn, int /*page*/, int res, bool& hasAlpha) |
5937 | jghali | 115 | { |
24665 | jghali | 116 | // No support for alpha in jpeg pictures |
5937 | jghali | 117 | initialize(); |
11468 | fschmid | 118 | hasAlpha = false; |
119 | return true; |
||
5937 | jghali | 120 | } |
121 | |||
12141 | avox | 122 | bool ScImgDataLoader_JPEG::loadPicture(const QString& fn, int /*page*/, int res, bool thumbnail) |
5937 | jghali | 123 | { |
19730 | fschmid | 124 | // bool isCMYK = false; |
7041 | fschmid | 125 | bool fromPS = false; |
5937 | jghali | 126 | float xres = 72.0, yres = 72.0; |
127 | if (!QFile::exists(fn)) |
||
128 | return false; |
||
129 | ExifData ExifInf; |
||
130 | struct jpeg_decompress_struct cinfo; |
||
131 | struct my_error_mgr jerr; |
||
132 | FILE *infile; |
||
133 | cinfo.err = jpeg_std_error (&jerr.pub); |
||
134 | jerr.pub.error_exit = my_error_exit; |
||
22518 | craig | 135 | infile = nullptr; |
5937 | jghali | 136 | |
137 | initialize(); |
||
11331 | jghali | 138 | m_imageInfoRecord.type = ImageTypeJPG; |
5937 | jghali | 139 | m_imageInfoRecord.exifInfo.thumbnail = QImage(); |
140 | if (setjmp (jerr.setjmp_buffer)) |
||
141 | { |
||
142 | jpeg_destroy_decompress (&cinfo); |
||
143 | if (infile) |
||
144 | fclose (infile); |
||
145 | return false; |
||
146 | } |
||
147 | jpeg_create_decompress (&cinfo); |
||
20720 | jghali | 148 | #if defined(Q_OS_WIN32) |
22518 | craig | 149 | if ((infile = _wfopen((const wchar_t*) fn.utf16(), L"rb")) == nullptr) |
20720 | jghali | 150 | return false; |
151 | #else |
||
22518 | craig | 152 | if ((infile = fopen (fn.toLocal8Bit(), "rb")) == nullptr) |
5937 | jghali | 153 | return false; |
20720 | jghali | 154 | #endif |
5937 | jghali | 155 | jpeg_stdio_src(&cinfo, infile); |
156 | jpeg_save_markers(&cinfo, ICC_MARKER, 0xFFFF); |
||
157 | jpeg_save_markers(&cinfo, PHOTOSHOP_MARKER, 0xFFFF); |
||
158 | jpeg_read_header(&cinfo, true); |
||
159 | jpeg_start_decompress(&cinfo); |
||
160 | bool exi = ExifInf.scan(fn); |
||
161 | if ((exi) && (ExifInf.exifDataValid)) |
||
162 | { |
||
6716 | fschmid | 163 | if (cinfo.output_components == 4) |
11331 | jghali | 164 | m_imageInfoRecord.colorspace = ColorSpaceCMYK; |
6716 | fschmid | 165 | else if (cinfo.output_components == 3) |
11331 | jghali | 166 | m_imageInfoRecord.colorspace = ColorSpaceRGB; |
6716 | fschmid | 167 | else if (cinfo.output_components == 1) |
11331 | jghali | 168 | m_imageInfoRecord.colorspace = ColorSpaceGray; |
10911 | fschmid | 169 | if ((!ExifInf.Thumbnail.isNull()) && thumbnail) |
6716 | fschmid | 170 | { |
5937 | jghali | 171 | m_image = ExifInf.getThumbnail(); |
6716 | fschmid | 172 | m_imageInfoRecord.exifInfo.thumbnail = ExifInf.getThumbnail(); |
173 | if (cinfo.output_components == 4) |
||
174 | { |
||
7175 | fschmid | 175 | QRgb *s; |
176 | unsigned char cc, cm, cy, ck; |
||
22535 | jghali | 177 | for (int yit=0; yit < m_image.height(); ++yit) |
6716 | fschmid | 178 | { |
7175 | fschmid | 179 | s = (QRgb*)(m_image.scanLine( yit )); |
22535 | jghali | 180 | for (int xit=0; xit < m_image.width(); ++xit) |
6716 | fschmid | 181 | { |
7175 | fschmid | 182 | cc = 255 - qRed(*s); |
183 | cm = 255 - qGreen(*s); |
||
184 | cy = 255 - qBlue(*s); |
||
8562 | jghali | 185 | ck = qMin(qMin(cc, cm), cy); |
6716 | fschmid | 186 | *s = qRgba(cc-ck,cm-ck,cy-ck,ck); |
7040 | fschmid | 187 | s++; |
6716 | fschmid | 188 | } |
189 | } |
||
190 | } |
||
191 | } |
||
192 | else |
||
193 | m_imageInfoRecord.exifInfo.thumbnail = QImage(); |
||
5937 | jghali | 194 | m_imageInfoRecord.exifInfo.cameraName = ExifInf.getCameraModel(); |
195 | m_imageInfoRecord.exifInfo.cameraVendor = ExifInf.getCameraMake(); |
||
196 | m_imageInfoRecord.exifInfo.comment = ExifInf.getComment(); |
||
197 | m_imageInfoRecord.exifInfo.width = ExifInf.getWidth(); |
||
198 | m_imageInfoRecord.exifInfo.height = ExifInf.getHeight(); |
||
19938 | jghali | 199 | m_imageInfoRecord.exifInfo.orientation = ExifInf.getOrientation(); |
5937 | jghali | 200 | m_imageInfoRecord.exifInfo.userComment = ExifInf.getUserComment(); |
201 | m_imageInfoRecord.exifInfo.dateTime = ExifInf.getDateTime(); |
||
7362 | fschmid | 202 | m_imageInfoRecord.exifInfo.ApertureFNumber = ExifInf.getApertureFNumber(); |
203 | m_imageInfoRecord.exifInfo.ExposureTime = ExifInf.getExposureTime(); |
||
204 | m_imageInfoRecord.exifInfo.ISOequivalent = ExifInf.getISOequivalent(); |
||
5937 | jghali | 205 | m_imageInfoRecord.exifDataValid = true; |
206 | if (cinfo.density_unit == 0) |
||
207 | { |
||
208 | xres = 72; |
||
209 | yres = 72; |
||
210 | } |
||
22535 | jghali | 211 | else if (cinfo.density_unit == 1) |
5937 | jghali | 212 | { |
213 | xres = cinfo.X_density; |
||
214 | yres = cinfo.Y_density; |
||
215 | } |
||
22535 | jghali | 216 | else if (cinfo.density_unit == 2) |
5937 | jghali | 217 | { |
218 | xres = cinfo.X_density * 2.54; |
||
219 | yres = cinfo.Y_density * 2.54; |
||
220 | } |
||
22535 | jghali | 221 | if (xres <= 1.0 || yres <= 1.0 || xres > 3000.0 || yres > 3000.0) |
5947 | jghali | 222 | { |
223 | xres = yres = 72.0; |
||
224 | QFileInfo qfi(fn); |
||
6650 | fschmid | 225 | m_message = QObject::tr("%1 may be corrupted : missing or wrong resolution tags").arg(qfi.fileName()); |
5977 | jghali | 226 | m_msgType = warningMsg; |
5947 | jghali | 227 | } |
5937 | jghali | 228 | m_imageInfoRecord.xres = qRound(xres); |
229 | m_imageInfoRecord.yres = qRound(yres); |
||
230 | m_imageInfoRecord.progressive = jpeg_has_multiple_scans(&cinfo); |
||
10911 | fschmid | 231 | if ((!ExifInf.Thumbnail.isNull()) && thumbnail) |
5937 | jghali | 232 | { |
233 | jpeg_destroy_decompress(&cinfo); |
||
234 | fclose(infile); |
||
235 | return true; |
||
236 | } |
||
237 | } |
||
238 | else |
||
239 | m_imageInfoRecord.exifDataValid = false; |
||
6716 | fschmid | 240 | m_imageInfoRecord.exifInfo.thumbnail = QImage(); |
20828 | jghali | 241 | |
5937 | jghali | 242 | if (cinfo.density_unit == 0) |
243 | { |
||
244 | xres = 72; |
||
245 | yres = 72; |
||
246 | m_image.setDotsPerMeterX(2834); |
||
247 | m_image.setDotsPerMeterY(2834); |
||
248 | } |
||
22535 | jghali | 249 | else if (cinfo.density_unit == 1) |
5937 | jghali | 250 | { |
251 | xres = cinfo.X_density; |
||
252 | yres = cinfo.Y_density; |
||
253 | m_image.setDotsPerMeterX( int(100. * cinfo.X_density / 2.54) ); |
||
254 | m_image.setDotsPerMeterY( int(100. * cinfo.Y_density / 2.54) ); |
||
255 | } |
||
22535 | jghali | 256 | else if (cinfo.density_unit == 2) |
5937 | jghali | 257 | { |
258 | xres = cinfo.X_density * 2.54; |
||
259 | yres = cinfo.Y_density * 2.54; |
||
260 | m_image.setDotsPerMeterX( int(100. * cinfo.X_density) ); |
||
261 | m_image.setDotsPerMeterY( int(100. * cinfo.Y_density) ); |
||
262 | } |
||
22535 | jghali | 263 | if (xres <= 1.0 || yres <= 1.0 || xres > 3000.0 || yres > 3000.0) |
5947 | jghali | 264 | { |
265 | xres = yres = 72.0; |
||
6650 | fschmid | 266 | m_image.setDotsPerMeterX(2834); |
267 | m_image.setDotsPerMeterY(2834); |
||
5947 | jghali | 268 | QFileInfo qfi(fn); |
6650 | fschmid | 269 | m_message = QObject::tr("%1 may be corrupted : missing or wrong resolution tags").arg(qfi.fileName()); |
5977 | jghali | 270 | m_msgType = warningMsg; |
5947 | jghali | 271 | } |
5937 | jghali | 272 | m_imageInfoRecord.xres = qRound(xres); |
273 | m_imageInfoRecord.yres = qRound(yres); |
||
274 | if (cinfo.output_components == 4) |
||
275 | { |
||
19730 | fschmid | 276 | // isCMYK = true; |
11331 | jghali | 277 | m_imageInfoRecord.colorspace = ColorSpaceCMYK; |
5937 | jghali | 278 | } |
279 | else if (cinfo.output_components == 3) |
||
11331 | jghali | 280 | m_imageInfoRecord.colorspace = ColorSpaceRGB; |
5937 | jghali | 281 | else if (cinfo.output_components == 1) |
11331 | jghali | 282 | m_imageInfoRecord.colorspace = ColorSpaceGray; |
5937 | jghali | 283 | m_imageInfoRecord.progressive = jpeg_has_multiple_scans(&cinfo); |
284 | |||
21679 | jghali | 285 | unsigned int EmbedLen = 0; |
286 | unsigned char* EmbedBuffer; |
||
287 | if (read_jpeg_marker(ICC_MARKER, &cinfo, &EmbedBuffer, &EmbedLen)) |
||
288 | { |
||
289 | bool profileIsValid = false; |
||
290 | QByteArray profArray((const char*) EmbedBuffer, EmbedLen); |
||
291 | ScColorProfile prof = ScCore->defaultEngine.openProfileFromMem(profArray); |
||
292 | if (prof) |
||
293 | { |
||
294 | // #14494: some tools out there produce broken JPEGs with inconsistent |
||
295 | // profile and image colorspaces. Check consistency, and if profile |
||
296 | // and image data are not consistent, discard embedded profile. |
||
297 | eColorSpaceType colorSpace = prof.colorSpace(); |
||
298 | if (colorSpace == ColorSpace_Rgb) |
||
299 | profileIsValid = (cinfo.output_components == 3); |
||
300 | if (colorSpace == ColorSpace_Cmyk) |
||
301 | profileIsValid = (cinfo.output_components == 4); |
||
302 | if (colorSpace == ColorSpace_Gray) |
||
303 | profileIsValid = (cinfo.output_components == 1); |
||
304 | } |
||
305 | if (profileIsValid) |
||
306 | { |
||
307 | m_embeddedProfile = profArray; |
||
308 | m_imageInfoRecord.profileName = prof.productDescription(); |
||
23537 | jghali | 309 | m_imageInfoRecord.embeddedProfileName = m_imageInfoRecord.profileName; |
21679 | jghali | 310 | } |
311 | m_imageInfoRecord.isEmbedded = profileIsValid; |
||
312 | free(EmbedBuffer); |
||
313 | } |
||
314 | else |
||
315 | { |
||
316 | m_imageInfoRecord.isEmbedded = false; |
||
23537 | jghali | 317 | m_imageInfoRecord.profileName.clear(); |
21679 | jghali | 318 | } |
319 | |||
20828 | jghali | 320 | unsigned int PhotoshopLen = 0; |
22605 | craig | 321 | unsigned char * PhotoshopBuffer = nullptr; |
20828 | jghali | 322 | if (read_jpeg_marker(PHOTOSHOP_MARKER, &cinfo, &PhotoshopBuffer, &PhotoshopLen) ) |
5937 | jghali | 323 | { |
20828 | jghali | 324 | bool savEx = m_imageInfoRecord.exifDataValid; |
325 | QByteArray arrayPhot(PhotoshopLen, ' '); |
||
326 | arrayPhot = QByteArray::fromRawData((const char*)PhotoshopBuffer,PhotoshopLen); |
||
327 | QDataStream strPhot(&arrayPhot,QIODevice::ReadOnly); |
||
328 | strPhot.setByteOrder( QDataStream::BigEndian ); |
||
329 | PSDHeader fakeHeader; |
||
330 | fakeHeader.width = cinfo.output_width; |
||
331 | fakeHeader.height = cinfo.output_height; |
||
332 | if (cinfo.output_components == 4) |
||
333 | m_imageInfoRecord.colorspace = ColorSpaceCMYK; |
||
334 | else if (cinfo.output_components == 3) |
||
335 | m_imageInfoRecord.colorspace = ColorSpaceRGB; |
||
336 | else if (cinfo.output_components == 1) |
||
337 | m_imageInfoRecord.colorspace = ColorSpaceGray; |
||
338 | m_imageInfoRecord.progressive = jpeg_has_multiple_scans(&cinfo); |
||
24186 | craig | 339 | parseResourceData(strPhot, fakeHeader, PhotoshopLen); |
20828 | jghali | 340 | // Photoshop resolution is more accurate than jpeg header resolution |
341 | xres = m_imageInfoRecord.xres; |
||
342 | yres = m_imageInfoRecord.yres; |
||
343 | m_image.setDotsPerMeterX( int(100. * m_imageInfoRecord.xres / 2.54) ); |
||
344 | m_image.setDotsPerMeterY( int(100. * m_imageInfoRecord.yres / 2.54) ); |
||
22535 | jghali | 345 | if (xres <= 1.0 || yres <= 1.0 || xres > 3000.0 || yres > 3000.0) |
5937 | jghali | 346 | { |
20828 | jghali | 347 | xres = yres = 72.0; |
348 | m_imageInfoRecord.xres = qRound(xres); |
||
349 | m_imageInfoRecord.yres = qRound(yres); |
||
350 | m_image.setDotsPerMeterX(2834); |
||
351 | m_image.setDotsPerMeterY(2834); |
||
352 | QFileInfo qfi(fn); |
||
353 | m_message = QObject::tr("%1 may be corrupted : missing or wrong resolution tags").arg(qfi.fileName()); |
||
354 | m_msgType = warningMsg; |
||
355 | } |
||
356 | if (m_imageInfoRecord.exifDataValid && !m_imageInfoRecord.exifInfo.thumbnail.isNull() && thumbnail) |
||
357 | { |
||
358 | m_image = QImage(m_imageInfoRecord.exifInfo.width, m_imageInfoRecord.exifInfo.height, QImage::Format_ARGB32 ); |
||
359 | m_imageInfoRecord.exifInfo.width = cinfo.output_width; |
||
360 | m_imageInfoRecord.exifInfo.height = cinfo.output_height; |
||
5937 | jghali | 361 | if (cinfo.output_components == 4) |
6650 | fschmid | 362 | { |
20828 | jghali | 363 | QRgb *d; |
364 | QRgb *s; |
||
365 | unsigned char cc, cm, cy, ck; |
||
22535 | jghali | 366 | for (int yit=0; yit < m_image.height(); ++yit) |
7040 | fschmid | 367 | { |
20828 | jghali | 368 | d = (QRgb*)(m_image.scanLine( yit )); |
369 | s = (QRgb*)(m_imageInfoRecord.exifInfo.thumbnail.scanLine( yit )); |
||
22535 | jghali | 370 | for (int xit=0; xit < m_image.width(); ++xit) |
7040 | fschmid | 371 | { |
20828 | jghali | 372 | cc = 255 - qRed(*s); |
373 | cm = 255 - qGreen(*s); |
||
374 | cy = 255 - qBlue(*s); |
||
375 | ck = qMin(qMin(cc, cm), cy); |
||
376 | *d = qRgba(cc-ck,cm-ck,cy-ck,ck); |
||
377 | s++; |
||
378 | d++; |
||
7040 | fschmid | 379 | } |
380 | } |
||
381 | } |
||
20828 | jghali | 382 | else |
383 | m_image = m_imageInfoRecord.exifInfo.thumbnail.copy(); |
||
384 | m_pixelFormat = Format_BGRA_8; |
||
5937 | jghali | 385 | } |
22605 | craig | 386 | m_imageInfoRecord.valid = (m_imageInfoRecord.PDSpathData.size())>0; // The only interest is vectormask |
20828 | jghali | 387 | arrayPhot.clear(); |
388 | free(PhotoshopBuffer ); |
||
22605 | craig | 389 | PhotoshopBuffer = nullptr; |
20828 | jghali | 390 | if (m_imageInfoRecord.exifDataValid && !m_imageInfoRecord.exifInfo.thumbnail.isNull() && thumbnail) |
391 | { |
||
392 | jpeg_destroy_decompress(&cinfo); |
||
393 | fclose(infile); |
||
394 | return true; |
||
395 | } |
||
396 | m_imageInfoRecord.exifInfo.thumbnail = QImage(); |
||
397 | m_imageInfoRecord.exifDataValid = savEx; |
||
398 | fromPS = true; |
||
5937 | jghali | 399 | } |
22535 | jghali | 400 | if (cinfo.output_components == 3 || cinfo.output_components == 4) |
10561 | fschmid | 401 | m_image = QImage( cinfo.output_width, cinfo.output_height, QImage::Format_ARGB32 ); |
22535 | jghali | 402 | else if (cinfo.output_components == 1) |
5937 | jghali | 403 | { |
10561 | fschmid | 404 | m_image = QImage( cinfo.output_width, cinfo.output_height, QImage::Format_Indexed8 ); |
18194 | fschmid | 405 | m_image.setColorCount(256); |
23537 | jghali | 406 | for (int i = 0; i < 256; i++) |
407 | m_image.setColor(i, qRgb(i, i, i)); |
||
5937 | jghali | 408 | } |
409 | if (!m_image.isNull()) |
||
410 | { |
||
10561 | fschmid | 411 | uchar* data = m_image.bits(); |
412 | int bpl = m_image.bytesPerLine(); |
||
5937 | jghali | 413 | while (cinfo.output_scanline < cinfo.output_height) |
10561 | fschmid | 414 | { |
415 | uchar *d = data + cinfo.output_scanline * bpl; |
||
416 | (void) jpeg_read_scanlines(&cinfo, &d, 1); |
||
417 | } |
||
22535 | jghali | 418 | if (cinfo.output_components == 3) |
5937 | jghali | 419 | { |
7175 | fschmid | 420 | uchar *in; |
421 | QRgb *out; |
||
5937 | jghali | 422 | for (uint j=0; j<cinfo.output_height; j++) |
423 | { |
||
7175 | fschmid | 424 | in = m_image.scanLine(j) + cinfo.output_width * 3; |
425 | out = (QRgb*) m_image.scanLine(j); |
||
5937 | jghali | 426 | for (uint i=cinfo.output_width; i--; ) |
427 | { |
||
428 | in -= 3; |
||
429 | out[i] = qRgb(in[0], in[1], in[2]); |
||
430 | } |
||
431 | } |
||
15128 | jghali | 432 | m_pixelFormat = Format_BGRA_8; |
5937 | jghali | 433 | } |
22535 | jghali | 434 | if (cinfo.output_components == 4) |
5937 | jghali | 435 | { |
7041 | fschmid | 436 | int method = 0; |
21036 | jghali | 437 | if ((cinfo.jpeg_color_space == JCS_YCCK) || (cinfo.out_color_space == JCS_CMYK)) |
7041 | fschmid | 438 | method = 1; |
439 | else if (fromPS) |
||
440 | { |
||
441 | if ((cinfo.jpeg_color_space == JCS_CMYK) && (cinfo.saw_Adobe_marker) && (cinfo.Adobe_transform == 0)) |
||
442 | method = 2; |
||
443 | } |
||
444 | else if ((cinfo.jpeg_color_space == JCS_CMYK) && (cinfo.saw_Adobe_marker)) |
||
445 | method = 1; |
||
7175 | fschmid | 446 | QRgb *ptr; |
447 | unsigned char c, m, y ,k; |
||
448 | unsigned char *p; |
||
5937 | jghali | 449 | for (int i = 0; i < m_image.height(); i++) |
450 | { |
||
7175 | fschmid | 451 | ptr = (QRgb*) m_image.scanLine(i); |
7041 | fschmid | 452 | if (method == 1) |
5937 | jghali | 453 | { |
454 | for (int j = 0; j < m_image.width(); j++) |
||
455 | { |
||
7175 | fschmid | 456 | p = (unsigned char *) ptr; |
5937 | jghali | 457 | c = p[0]; |
458 | m = p[1]; |
||
459 | y = p[2]; |
||
460 | k = p[3]; |
||
461 | *ptr = qRgba(255 - c, 255 - m, 255 - y, 255 - k); |
||
462 | ptr++; |
||
463 | } |
||
464 | } |
||
7041 | fschmid | 465 | else if (method == 2) |
466 | { |
||
467 | for (int j = 0; j < m_image.width(); j++) |
||
468 | { |
||
7175 | fschmid | 469 | p = (unsigned char *) ptr; |
7041 | fschmid | 470 | c = p[0]; |
471 | m = p[1]; |
||
472 | y = p[2]; |
||
473 | k = p[3]; |
||
474 | *ptr = qRgba(255 - c, 255 - m, 255 - y, k); |
||
475 | ptr++; |
||
476 | } |
||
477 | } |
||
5937 | jghali | 478 | else |
479 | { |
||
480 | for (int j = 0; j < m_image.width(); j++) |
||
481 | { |
||
7175 | fschmid | 482 | p = (unsigned char *) ptr; |
5937 | jghali | 483 | c = p[0]; |
484 | m = p[1]; |
||
485 | y = p[2]; |
||
486 | k = p[3]; |
||
487 | *ptr = qRgba(y, m, c, k); |
||
488 | ptr++; |
||
489 | } |
||
490 | } |
||
491 | } |
||
15128 | jghali | 492 | m_pixelFormat = Format_YMCK_8; |
19730 | fschmid | 493 | // isCMYK = true; |
5937 | jghali | 494 | } |
19730 | fschmid | 495 | //else |
496 | // isCMYK = false; |
||
22535 | jghali | 497 | if (cinfo.output_components == 1) |
5937 | jghali | 498 | { |
10561 | fschmid | 499 | QImage tmpImg = m_image.convertToFormat(QImage::Format_ARGB32); |
500 | m_image = QImage( cinfo.output_width, cinfo.output_height, QImage::Format_ARGB32 ); |
||
7175 | fschmid | 501 | QRgb *s; |
502 | QRgb *d; |
||
23537 | jghali | 503 | for (int yi = 0; yi < tmpImg.height(); ++yi) |
5937 | jghali | 504 | { |
7175 | fschmid | 505 | s = (QRgb*)(tmpImg.scanLine( yi )); |
506 | d = (QRgb*)(m_image.scanLine( yi )); |
||
23537 | jghali | 507 | for (int xi = 0; xi < tmpImg.width(); ++xi) |
5937 | jghali | 508 | { |
509 | (*d) = (*s); |
||
510 | s++; |
||
511 | d++; |
||
512 | } |
||
513 | } |
||
24467 | jghali | 514 | m_pixelFormat = Format_GRAY_8; |
5937 | jghali | 515 | } |
516 | } |
||
517 | (void) jpeg_finish_decompress(&cinfo); |
||
518 | fclose (infile); |
||
519 | jpeg_destroy_decompress (&cinfo); |
||
14866 | fschmid | 520 | if ((exi) && (ExifInf.exifDataValid)) |
521 | { |
||
522 | int ori = ExifInf.getOrientation(); |
||
523 | int oxres, oyres; |
||
524 | if (ori != 1) |
||
525 | { |
||
526 | QTransform M; |
||
24735 | jghali | 527 | QTransform flip(-1, 0, 0, 1, 0, 0); |
14866 | fschmid | 528 | switch ( ori ) |
529 | { // notice intentional fallthroughs |
||
530 | case 2: |
||
531 | M = flip; |
||
532 | break; |
||
533 | case 4: |
||
534 | M = flip; |
||
23749 | jghali | 535 | /* fall through */ |
14866 | fschmid | 536 | case 3: |
537 | M.rotate (180); |
||
538 | break; |
||
539 | case 5: |
||
540 | M = flip; |
||
23749 | jghali | 541 | /* fall through */ |
14866 | fschmid | 542 | case 6: |
543 | M.rotate(90); |
||
544 | oxres = m_imageInfoRecord.xres; |
||
545 | oyres = m_imageInfoRecord.yres; |
||
546 | m_imageInfoRecord.xres = oyres; |
||
547 | m_imageInfoRecord.yres = oxres; |
||
548 | break; |
||
549 | case 7: |
||
550 | M = flip; |
||
23749 | jghali | 551 | /* fall through */ |
14866 | fschmid | 552 | case 8: |
553 | M.rotate(270); |
||
554 | oxres = m_imageInfoRecord.xres; |
||
555 | oyres = m_imageInfoRecord.yres; |
||
556 | m_imageInfoRecord.xres = oyres; |
||
557 | m_imageInfoRecord.yres = oxres; |
||
558 | break; |
||
23749 | jghali | 559 | default: |
560 | break; // should never happen |
||
14866 | fschmid | 561 | } |
562 | m_image = m_image.transformed(M); |
||
563 | } |
||
564 | } |
||
5937 | jghali | 565 | m_imageInfoRecord.layerInfo.clear(); |
566 | m_imageInfoRecord.BBoxX = 0; |
||
567 | m_imageInfoRecord.BBoxH = m_image.height(); |
||
568 | return (!m_image.isNull()); |
||
569 | } |
||
570 | |||
571 | bool ScImgDataLoader_JPEG::marker_is_icc (jpeg_saved_marker_ptr marker) |
||
572 | { |
||
573 | return |
||
574 | marker->marker == ICC_MARKER && |
||
575 | marker->data_length >= ICC_OVERHEAD_LEN && |
||
576 | /* verify the identifying string */ |
||
577 | GETJOCTET(marker->data[0]) == 0x49 && |
||
578 | GETJOCTET(marker->data[1]) == 0x43 && |
||
579 | GETJOCTET(marker->data[2]) == 0x43 && |
||
580 | GETJOCTET(marker->data[3]) == 0x5F && |
||
581 | GETJOCTET(marker->data[4]) == 0x50 && |
||
582 | GETJOCTET(marker->data[5]) == 0x52 && |
||
583 | GETJOCTET(marker->data[6]) == 0x4F && |
||
584 | GETJOCTET(marker->data[7]) == 0x46 && |
||
585 | GETJOCTET(marker->data[8]) == 0x49 && |
||
586 | GETJOCTET(marker->data[9]) == 0x4C && |
||
587 | GETJOCTET(marker->data[10]) == 0x45 && |
||
588 | GETJOCTET(marker->data[11]) == 0x0; |
||
589 | } |
||
590 | |||
591 | bool ScImgDataLoader_JPEG::marker_is_photoshop (jpeg_saved_marker_ptr marker) |
||
592 | { |
||
593 | return |
||
594 | marker->marker == PHOTOSHOP_MARKER && |
||
595 | marker->data_length >= ICC_OVERHEAD_LEN && |
||
596 | /* verify the identifying string */ |
||
597 | GETJOCTET(marker->data[0]) == 0x50 && |
||
598 | GETJOCTET(marker->data[1]) == 0x68 && |
||
599 | GETJOCTET(marker->data[2]) == 0x6F && |
||
600 | GETJOCTET(marker->data[3]) == 0x74 && |
||
601 | GETJOCTET(marker->data[4]) == 0x6F && |
||
602 | GETJOCTET(marker->data[5]) == 0x73 && |
||
603 | GETJOCTET(marker->data[6]) == 0x68 && |
||
604 | GETJOCTET(marker->data[7]) == 0x6F && |
||
605 | GETJOCTET(marker->data[8]) == 0x70 && |
||
606 | GETJOCTET(marker->data[9]) == 0x20 && |
||
607 | GETJOCTET(marker->data[10]) == 0x33 && |
||
608 | GETJOCTET(marker->data[11]) == 0x2E && |
||
609 | GETJOCTET(marker->data[12]) == 0x30 && |
||
610 | GETJOCTET(marker->data[13]) == 0x0; |
||
611 | } |
||
612 | |||
613 | /* Small modification of original read_icc_profile method from jpegicc of lcms project |
||
614 | * to enable read of Photoshop marker |
||
615 | */ |
||
616 | bool ScImgDataLoader_JPEG::read_jpeg_marker (UINT8 requestmarker, j_decompress_ptr cinfo, JOCTET **icc_data_ptr, unsigned int *icc_data_len) |
||
617 | { |
||
618 | jpeg_saved_marker_ptr marker; |
||
619 | int num_markers = 0; |
||
620 | int seq_no; |
||
621 | JOCTET *icc_data; |
||
622 | unsigned int total_length; |
||
623 | #define MAX_SEQ_NO 255 /* sufficient since marker numbers are bytes */ |
||
624 | char marker_present[MAX_SEQ_NO+1]; /* 1 if marker found */ |
||
625 | unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */ |
||
626 | unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */ |
||
627 | |||
22518 | craig | 628 | *icc_data_ptr = nullptr; /* avoid confusion if false return */ |
5937 | jghali | 629 | *icc_data_len = 0; |
630 | |||
631 | /* This first pass over the saved markers discovers whether there are |
||
632 | * any ICC markers and verifies the consistency of the marker numbering. |
||
633 | */ |
||
634 | |||
635 | for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++) |
||
636 | marker_present[seq_no] = 0; |
||
637 | seq_no = 0; |
||
22518 | craig | 638 | for (marker = cinfo->marker_list; marker != nullptr; marker = marker->next) |
5937 | jghali | 639 | { |
640 | if (requestmarker == ICC_MARKER && marker_is_icc(marker)) |
||
641 | { |
||
642 | if (num_markers == 0) |
||
643 | num_markers = GETJOCTET(marker->data[13]); |
||
644 | else if (num_markers != GETJOCTET(marker->data[13])) |
||
645 | return false; /* inconsistent num_markers fields */ |
||
646 | seq_no = GETJOCTET(marker->data[12]); |
||
647 | if (seq_no <= 0 || seq_no > num_markers) |
||
648 | return false; /* bogus sequence number */ |
||
649 | if (marker_present[seq_no]) |
||
650 | return false; /* duplicate sequence numbers */ |
||
651 | marker_present[seq_no] = 1; |
||
652 | data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN; |
||
653 | } |
||
22535 | jghali | 654 | else if (requestmarker == PHOTOSHOP_MARKER && marker_is_photoshop(marker)) |
5937 | jghali | 655 | { |
656 | num_markers = ++seq_no; |
||
657 | marker_present[seq_no] = 1; |
||
658 | data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN; |
||
659 | } |
||
660 | } |
||
661 | |||
662 | if (num_markers == 0) |
||
663 | return false; |
||
664 | |||
665 | /* Check for missing markers, count total space needed, |
||
666 | * compute offset of each marker's part of the data. |
||
667 | */ |
||
668 | |||
669 | total_length = 0; |
||
670 | for (seq_no = 1; seq_no <= num_markers; seq_no++) |
||
671 | { |
||
672 | if (marker_present[seq_no] == 0) |
||
673 | return false; /* missing sequence number */ |
||
674 | data_offset[seq_no] = total_length; |
||
675 | total_length += data_length[seq_no]; |
||
676 | } |
||
677 | |||
678 | if (total_length <= 0) |
||
679 | return false; /* found only empty markers? */ |
||
680 | |||
681 | /* Allocate space for assembled data */ |
||
682 | icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET)); |
||
22518 | craig | 683 | if (icc_data == nullptr) |
5937 | jghali | 684 | return false; /* oops, out of memory */ |
685 | seq_no=0; |
||
686 | /* and fill it in */ |
||
22518 | craig | 687 | for (marker = cinfo->marker_list; marker != nullptr; marker = marker->next) |
5937 | jghali | 688 | { |
689 | if ( (requestmarker == ICC_MARKER && marker_is_icc(marker)) || |
||
690 | (requestmarker == PHOTOSHOP_MARKER && marker_is_photoshop(marker)) || (requestmarker == 0xE1)) |
||
691 | { |
||
692 | JOCTET FAR *src_ptr; |
||
693 | JOCTET *dst_ptr; |
||
694 | unsigned int length; |
||
22535 | jghali | 695 | if (requestmarker == ICC_MARKER) |
5937 | jghali | 696 | seq_no = GETJOCTET(marker->data[12]); |
22535 | jghali | 697 | else if (requestmarker == PHOTOSHOP_MARKER) |
5937 | jghali | 698 | seq_no++; |
699 | dst_ptr = icc_data + data_offset[seq_no]; |
||
700 | src_ptr = marker->data + ICC_OVERHEAD_LEN; |
||
701 | length = data_length[seq_no]; |
||
702 | while (length--) |
||
703 | { |
||
704 | *dst_ptr++ = *src_ptr++; |
||
705 | } |
||
706 | } |
||
707 | } |
||
708 | |||
709 | *icc_data_ptr = icc_data; |
||
710 | *icc_data_len = total_length; |
||
711 | |||
712 | return true; |
||
6650 | fschmid | 713 | } |