File: | dplugins/dimg/png/dimgpngloader_load.cpp |
Warning: | line 145, column 15 Value of 'errno' was not checked and may be overwritten by function 'png_create_read_struct' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* ============================================================ | |||
2 | * | |||
3 | * This file is a part of digiKam project | |||
4 | * https://www.digikam.org | |||
5 | * | |||
6 | * Date : 2005-11-01 | |||
7 | * Description : a PNG image loader for DImg framework - load operations. | |||
8 | * | |||
9 | * SPDX-FileCopyrightText: 2005-2025 by Gilles Caulier <caulier dot gilles at gmail dot com> | |||
10 | * | |||
11 | * SPDX-License-Identifier: GPL-2.0-or-later | |||
12 | * | |||
13 | * ============================================================ */ | |||
14 | ||||
15 | #define PNG_BYTES_TO_CHECK4 4 | |||
16 | ||||
17 | #include "dimgpngloader.h" | |||
18 | ||||
19 | // C ANSI includes | |||
20 | ||||
21 | extern "C" | |||
22 | { | |||
23 | #ifndef Q_CC_MSVC | |||
24 | # include <unistd.h> | |||
25 | #endif | |||
26 | } | |||
27 | ||||
28 | // C++ includes | |||
29 | ||||
30 | #include <cstdlib> | |||
31 | #include <cstdio> | |||
32 | ||||
33 | // Qt includes | |||
34 | ||||
35 | #include <QFile> | |||
36 | #include <QByteArray> | |||
37 | #include <QSysInfo> | |||
38 | ||||
39 | // Local includes | |||
40 | ||||
41 | #include "metaengine.h" | |||
42 | #include "digikam_debug.h" | |||
43 | #include "digikam_config.h" | |||
44 | #include "digikam_version.h" | |||
45 | #include "dimgloaderobserver.h" | |||
46 | ||||
47 | // libPNG includes | |||
48 | ||||
49 | extern "C" | |||
50 | { | |||
51 | #include <png.h> | |||
52 | } | |||
53 | ||||
54 | #ifdef Q_OS_WIN | |||
55 | ||||
56 | void _ReadProc(struct png_struct_def* png_ptr, png_bytep data, png_size_t size) | |||
57 | { | |||
58 | FILE* const file_handle = (FILE*)png_get_io_ptr(png_ptr); | |||
59 | fread(data, size, 1, file_handle); | |||
60 | } | |||
61 | ||||
62 | #endif | |||
63 | ||||
64 | using namespace Digikam; | |||
65 | ||||
66 | namespace DigikamPNGDImgPlugin | |||
67 | { | |||
68 | ||||
69 | #if PNG_LIBPNG_VER_MAJOR1 >= 1 && PNG_LIBPNG_VER_MINOR6 >= 5 | |||
70 | ||||
71 | typedef png_bytep iCCP_data; | |||
72 | ||||
73 | #else | |||
74 | ||||
75 | typedef png_charp iCCP_data; | |||
76 | ||||
77 | #endif | |||
78 | ||||
79 | bool DImgPNGLoader::load(const QString& filePath, DImgLoaderObserver* const observer) | |||
80 | { | |||
81 | png_uint_32 w32, h32; | |||
82 | int width, height; | |||
83 | int bit_depth, color_type, interlace_type; | |||
84 | FILE* f = nullptr; | |||
85 | png_structp png_ptr = nullptr; | |||
86 | png_infop info_ptr = nullptr; | |||
87 | ||||
88 | // To prevent cppcheck warnings. | |||
89 | (void)f; | |||
90 | (void)png_ptr; | |||
91 | (void)info_ptr; | |||
92 | ||||
93 | readMetadata(filePath); | |||
94 | ||||
95 | // ------------------------------------------------------------------- | |||
96 | // Open the file | |||
97 | ||||
98 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 98, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category .name()).debug() << "Opening file" << filePath; | |||
| ||||
99 | ||||
100 | #ifdef Q_OS_WIN | |||
101 | ||||
102 | f = _wfopen((const wchar_t*)filePath.utf16(), L"rb"); | |||
103 | ||||
104 | #else | |||
105 | ||||
106 | f = fopen(filePath.toUtf8().constData(), "rb"); | |||
107 | ||||
108 | #endif | |||
109 | ||||
110 | if (!f
| |||
111 | { | |||
112 | qCWarning(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtWarningMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 112, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).warning() << "Cannot open image file."; | |||
113 | loadingFailed(); | |||
114 | ||||
115 | return false; | |||
116 | } | |||
117 | ||||
118 | unsigned char buf[PNG_BYTES_TO_CHECK4]; | |||
119 | ||||
120 | size_t membersRead = fread(buf, 1, PNG_BYTES_TO_CHECK4, f); | |||
121 | ||||
122 | #if PNG_LIBPNG_VER10644 >= 10400 | |||
123 | ||||
124 | if ((membersRead
| |||
125 | ||||
126 | #else | |||
127 | ||||
128 | if ((membersRead != PNG_BYTES_TO_CHECK4) || !png_check_sig(buf, PNG_BYTES_TO_CHECK)(png_sig_cmp((buf), 0, (4)) == 0)) | |||
129 | ||||
130 | #endif | |||
131 | ||||
132 | { | |||
133 | qCWarning(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtWarningMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 133, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).warning() << "Not a PNG image file."; | |||
134 | fclose(f); | |||
135 | loadingFailed(); | |||
136 | ||||
137 | return false; | |||
138 | } | |||
139 | ||||
140 | rewind(f); | |||
141 | ||||
142 | // ------------------------------------------------------------------- | |||
143 | // Initialize the internal structures | |||
144 | ||||
145 | png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING"1.6.44", nullptr, nullptr, nullptr); | |||
| ||||
146 | ||||
147 | if (!png_ptr) | |||
148 | { | |||
149 | qCWarning(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtWarningMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 149, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).warning() << "Invalid PNG image file structure."; | |||
150 | fclose(f); | |||
151 | loadingFailed(); | |||
152 | ||||
153 | return false; | |||
154 | } | |||
155 | ||||
156 | info_ptr = png_create_info_struct(png_ptr); | |||
157 | ||||
158 | if (!info_ptr) | |||
159 | { | |||
160 | qCWarning(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtWarningMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 160, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).warning() << "Cannot reading PNG image file structure."; | |||
161 | png_destroy_read_struct(&png_ptr, nullptr, nullptr); | |||
162 | fclose(f); | |||
163 | loadingFailed(); | |||
164 | ||||
165 | return false; | |||
166 | } | |||
167 | ||||
168 | // ------------------------------------------------------------------- | |||
169 | // PNG error handling. If an error occurs during reading, libpng | |||
170 | // will jump here | |||
171 | ||||
172 | // setjmp-save cleanup | |||
173 | ||||
174 | class Q_DECL_HIDDEN__attribute__((visibility("hidden"))) CleanupData | |||
175 | { | |||
176 | ||||
177 | public: | |||
178 | ||||
179 | CleanupData() = default; | |||
180 | ||||
181 | ~CleanupData() | |||
182 | { | |||
183 | delete [] data; | |||
184 | freeLines(); | |||
185 | ||||
186 | if (file) | |||
187 | { | |||
188 | fclose(file); | |||
189 | } | |||
190 | } | |||
191 | ||||
192 | void setData(uchar* const d) | |||
193 | { | |||
194 | data = d; | |||
195 | } | |||
196 | ||||
197 | void setLines(uchar** const l) | |||
198 | { | |||
199 | lines = l; | |||
200 | } | |||
201 | ||||
202 | void setFile(FILE* const f) | |||
203 | { | |||
204 | file = f; | |||
205 | } | |||
206 | ||||
207 | void setSize(const QSize& s) | |||
208 | { | |||
209 | size = s; | |||
210 | } | |||
211 | ||||
212 | void setColorModel(int c) | |||
213 | { | |||
214 | cmod = c; | |||
215 | } | |||
216 | ||||
217 | void takeData() | |||
218 | { | |||
219 | data = nullptr; | |||
220 | } | |||
221 | ||||
222 | void freeLines() | |||
223 | { | |||
224 | if (lines) | |||
225 | { | |||
226 | free(lines); | |||
227 | } | |||
228 | ||||
229 | lines = nullptr; | |||
230 | } | |||
231 | ||||
232 | public: | |||
233 | ||||
234 | uchar* data = nullptr; | |||
235 | uchar** lines = { nullptr }; | |||
236 | FILE* file = nullptr; | |||
237 | ||||
238 | QSize size; | |||
239 | int cmod = 0; | |||
240 | ||||
241 | ||||
242 | private: | |||
243 | ||||
244 | // Disable | |||
245 | CleanupData(const CleanupData&) = delete; | |||
246 | CleanupData& operator=(const CleanupData&) = delete; | |||
247 | }; | |||
248 | ||||
249 | CleanupData* const cleanupData = new CleanupData; | |||
250 | cleanupData->setFile(f); | |||
251 | ||||
252 | #if PNG_LIBPNG_VER10644 >= 10400 | |||
253 | ||||
254 | if (setjmp(png_jmpbuf(png_ptr))_setjmp ((*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf )))))) | |||
255 | ||||
256 | #else | |||
257 | ||||
258 | if (setjmp(png_ptr->jmpbuf)_setjmp (png_ptr->jmpbuf)) | |||
259 | ||||
260 | #endif | |||
261 | ||||
262 | { | |||
263 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) nullptr); | |||
264 | ||||
265 | if ( | |||
266 | !cleanupData->data || | |||
267 | !cleanupData->size.isValid() | |||
268 | ) | |||
269 | { | |||
270 | qCWarning(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtWarningMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 270, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).warning() << "Internal libPNG error during reading file. Process aborted!"; | |||
271 | delete cleanupData; | |||
272 | loadingFailed(); | |||
273 | ||||
274 | return false; | |||
275 | } | |||
276 | ||||
277 | // We check only Exif metadata for ICC profile to prevent endless loop | |||
278 | ||||
279 | if (m_loadFlags & LoadICCData) | |||
280 | { | |||
281 | checkExifWorkingColorSpace(); | |||
282 | } | |||
283 | ||||
284 | if (observer) | |||
285 | { | |||
286 | observer->progressInfo(1.0F); | |||
287 | } | |||
288 | ||||
289 | imageWidth() = cleanupData->size.width(); | |||
290 | imageHeight() = cleanupData->size.height(); | |||
291 | imageData() = cleanupData->data; | |||
292 | imageSetAttribute(QLatin1String("format"), QLatin1String("PNG")); | |||
293 | imageSetAttribute(QLatin1String("originalColorModel"), cleanupData->cmod); | |||
294 | imageSetAttribute(QLatin1String("originalBitDepth"), m_sixteenBit ? 16 : 8); | |||
295 | imageSetAttribute(QLatin1String("originalSize"), cleanupData->size); | |||
296 | ||||
297 | cleanupData->takeData(); | |||
298 | delete cleanupData; | |||
299 | ||||
300 | return true; | |||
301 | } | |||
302 | ||||
303 | #ifdef PNG_BENIGN_ERRORS_SUPPORTED | |||
304 | ||||
305 | // Change some libpng errors to warnings (e.g. bug 386396). | |||
306 | ||||
307 | png_set_benign_errors(png_ptr, true); | |||
308 | ||||
309 | png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE4, PNG_OPTION_ON3); | |||
310 | ||||
311 | #endif | |||
312 | ||||
313 | #ifdef Q_OS_WIN | |||
314 | ||||
315 | png_set_read_fn(png_ptr, f, _ReadProc); | |||
316 | ||||
317 | #else | |||
318 | ||||
319 | png_init_io(png_ptr, f); | |||
320 | ||||
321 | #endif | |||
322 | ||||
323 | // ------------------------------------------------------------------- | |||
324 | // Read all PNG info up to image data | |||
325 | ||||
326 | png_read_info(png_ptr, info_ptr); | |||
327 | ||||
328 | png_get_IHDR(png_ptr, | |||
329 | info_ptr, | |||
330 | reinterpret_cast<png_uint_32*>(&w32), | |||
331 | reinterpret_cast<png_uint_32*>(&h32), | |||
332 | &bit_depth, | |||
333 | &color_type, | |||
334 | &interlace_type, | |||
335 | nullptr, | |||
336 | nullptr); | |||
337 | ||||
338 | width = (int)w32; | |||
339 | height = (int)h32; | |||
340 | ||||
341 | int colorModel = DImg::COLORMODELUNKNOWN; | |||
342 | m_sixteenBit = (bit_depth == 16); | |||
343 | ||||
344 | switch (color_type) | |||
345 | { | |||
346 | case PNG_COLOR_TYPE_RGB(2): // RGB | |||
347 | { | |||
348 | m_hasAlpha = false; | |||
349 | colorModel = DImg::RGB; | |||
350 | ||||
351 | break; | |||
352 | } | |||
353 | ||||
354 | case PNG_COLOR_TYPE_RGB_ALPHA(2 | 4): // RGBA | |||
355 | { | |||
356 | m_hasAlpha = true; | |||
357 | colorModel = DImg::RGB; | |||
358 | ||||
359 | break; | |||
360 | } | |||
361 | ||||
362 | case PNG_COLOR_TYPE_GRAY0: // Grayscale | |||
363 | { | |||
364 | m_hasAlpha = false; | |||
365 | colorModel = DImg::GRAYSCALE; | |||
366 | ||||
367 | break; | |||
368 | } | |||
369 | ||||
370 | case PNG_COLOR_TYPE_GRAY_ALPHA(4): // Grayscale + Alpha | |||
371 | { | |||
372 | m_hasAlpha = true; | |||
373 | colorModel = DImg::GRAYSCALE; | |||
374 | ||||
375 | break; | |||
376 | } | |||
377 | ||||
378 | case PNG_COLOR_TYPE_PALETTE(2 | 1): // Indexed | |||
379 | { | |||
380 | m_hasAlpha = false; | |||
381 | colorModel = DImg::INDEXED; | |||
382 | ||||
383 | break; | |||
384 | } | |||
385 | } | |||
386 | ||||
387 | cleanupData->setColorModel(colorModel); | |||
388 | cleanupData->setSize(QSize(width, height)); | |||
389 | ||||
390 | uchar* data = nullptr; | |||
391 | ||||
392 | if (m_loadFlags & LoadImageData) | |||
393 | { | |||
394 | // TODO: Endianness: | |||
395 | // You may notice that the code for little and big endian | |||
396 | // below is now identical. This was found to work by PPC users. | |||
397 | // If this proves right, all the conditional clauses can be removed. | |||
398 | ||||
399 | if (bit_depth == 16) | |||
400 | { | |||
401 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 401, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "PNG in 16 bits/color/pixel."; | |||
402 | ||||
403 | switch (color_type) | |||
404 | { | |||
405 | case PNG_COLOR_TYPE_RGB(2): // RGB | |||
406 | { | |||
407 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 407, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "PNG in PNG_COLOR_TYPE_RGB"; | |||
408 | png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER1); | |||
409 | ||||
410 | break; | |||
411 | } | |||
412 | ||||
413 | case PNG_COLOR_TYPE_RGB_ALPHA(2 | 4): // RGBA | |||
414 | { | |||
415 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 415, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "PNG in PNG_COLOR_TYPE_RGB_ALPHA"; | |||
416 | ||||
417 | break; | |||
418 | } | |||
419 | ||||
420 | case PNG_COLOR_TYPE_GRAY0: // Grayscale | |||
421 | { | |||
422 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 422, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "PNG in PNG_COLOR_TYPE_GRAY"; | |||
423 | png_set_gray_to_rgb(png_ptr); | |||
424 | png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER1); | |||
425 | ||||
426 | break; | |||
427 | } | |||
428 | ||||
429 | case PNG_COLOR_TYPE_GRAY_ALPHA(4): // Grayscale + Alpha | |||
430 | { | |||
431 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 431, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "PNG in PNG_COLOR_TYPE_GRAY_ALPHA"; | |||
432 | png_set_gray_to_rgb(png_ptr); | |||
433 | ||||
434 | break; | |||
435 | } | |||
436 | ||||
437 | case PNG_COLOR_TYPE_PALETTE(2 | 1): // Indexed | |||
438 | { | |||
439 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 439, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "PNG in PNG_COLOR_TYPE_PALETTE"; | |||
440 | png_set_palette_to_rgb(png_ptr); | |||
441 | png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER1); | |||
442 | ||||
443 | break; | |||
444 | } | |||
445 | ||||
446 | default: | |||
447 | { | |||
448 | qCWarning(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtWarningMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 448, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).warning() << "PNG color type unknown."; | |||
449 | delete cleanupData; | |||
450 | loadingFailed(); | |||
451 | ||||
452 | return false; | |||
453 | } | |||
454 | } | |||
455 | } | |||
456 | else | |||
457 | { | |||
458 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 458, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "PNG in >=8 bits/color/pixel."; | |||
459 | png_set_packing(png_ptr); | |||
460 | ||||
461 | switch (color_type) | |||
462 | { | |||
463 | case PNG_COLOR_TYPE_RGB(2): // RGB | |||
464 | { | |||
465 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 465, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "PNG in PNG_COLOR_TYPE_RGB"; | |||
466 | png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER1); | |||
467 | ||||
468 | break; | |||
469 | } | |||
470 | ||||
471 | case PNG_COLOR_TYPE_RGB_ALPHA(2 | 4): // RGBA | |||
472 | { | |||
473 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 473, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "PNG in PNG_COLOR_TYPE_RGB_ALPHA"; | |||
474 | ||||
475 | break; | |||
476 | } | |||
477 | ||||
478 | case PNG_COLOR_TYPE_GRAY0: // Grayscale | |||
479 | { | |||
480 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 480, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "PNG in PNG_COLOR_TYPE_GRAY"; | |||
481 | ||||
482 | #if PNG_LIBPNG_VER10644 >= 10400 | |||
483 | ||||
484 | png_set_expand_gray_1_2_4_to_8(png_ptr); | |||
485 | ||||
486 | #else | |||
487 | ||||
488 | png_set_gray_1_2_4_to_8(png_ptr); | |||
489 | ||||
490 | #endif | |||
491 | ||||
492 | png_set_gray_to_rgb(png_ptr); | |||
493 | png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER1); | |||
494 | ||||
495 | break; | |||
496 | } | |||
497 | ||||
498 | case PNG_COLOR_TYPE_GRAY_ALPHA(4): // Grayscale + alpha | |||
499 | { | |||
500 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 500, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "PNG in PNG_COLOR_TYPE_GRAY_ALPHA"; | |||
501 | png_set_gray_to_rgb(png_ptr); | |||
502 | ||||
503 | break; | |||
504 | } | |||
505 | ||||
506 | case PNG_COLOR_TYPE_PALETTE(2 | 1): // Indexed | |||
507 | { | |||
508 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 508, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "PNG in PNG_COLOR_TYPE_PALETTE"; | |||
509 | png_set_packing(png_ptr); | |||
510 | png_set_palette_to_rgb(png_ptr); | |||
511 | png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER1); | |||
512 | ||||
513 | break; | |||
514 | } | |||
515 | ||||
516 | default: | |||
517 | { | |||
518 | qCWarning(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtWarningMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 518, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).warning() << "PNG color type unknown." << color_type; | |||
519 | delete cleanupData; | |||
520 | loadingFailed(); | |||
521 | ||||
522 | return false; | |||
523 | } | |||
524 | } | |||
525 | } | |||
526 | ||||
527 | if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS0x0010U)) | |||
528 | { | |||
529 | png_set_tRNS_to_alpha(png_ptr); | |||
530 | } | |||
531 | ||||
532 | double file_gamma; | |||
533 | ||||
534 | if (png_get_gAMA(png_ptr, info_ptr, &file_gamma)) | |||
535 | { | |||
536 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 536, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "Apply PNG file gamma" << file_gamma; | |||
537 | ||||
538 | png_set_gamma(png_ptr, 2.2, file_gamma); | |||
539 | } | |||
540 | ||||
541 | png_set_bgr(png_ptr); | |||
542 | ||||
543 | //png_set_swap_alpha(png_ptr); | |||
544 | ||||
545 | if (observer) | |||
546 | { | |||
547 | observer->progressInfo(0.1F); | |||
548 | } | |||
549 | ||||
550 | // ------------------------------------------------------------------- | |||
551 | // Get image data. | |||
552 | ||||
553 | // Call before png_read_update_info and png_start_read_image() | |||
554 | // for non-interlaced images number_passes will be 1 | |||
555 | ||||
556 | int number_passes = png_set_interlace_handling(png_ptr); | |||
557 | ||||
558 | png_read_update_info(png_ptr, info_ptr); | |||
559 | ||||
560 | if (m_sixteenBit) | |||
561 | { | |||
562 | data = new_failureTolerant(width, height, 8); // 16 bits/color/pixel | |||
563 | } | |||
564 | else | |||
565 | { | |||
566 | data = new_failureTolerant(width, height, 4); // 8 bits/color/pixel | |||
567 | } | |||
568 | ||||
569 | cleanupData->setData(data); | |||
570 | ||||
571 | uchar** lines = nullptr; | |||
572 | (void)lines; // to prevent cppcheck warnings. | |||
573 | lines = reinterpret_cast<unsigned char**>(malloc(height * sizeof(uchar*))); | |||
574 | cleanupData->setLines(lines); | |||
575 | ||||
576 | if (!data || !lines) | |||
577 | { | |||
578 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 578, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "Cannot allocate memory to load PNG image data."; | |||
579 | png_read_end(png_ptr, info_ptr); | |||
580 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) nullptr); | |||
581 | delete cleanupData; | |||
582 | loadingFailed(); | |||
583 | ||||
584 | return false; | |||
585 | } | |||
586 | ||||
587 | for (int i = 0 ; i < height ; ++i) | |||
588 | { | |||
589 | if (m_sixteenBit) | |||
590 | { | |||
591 | lines[i] = data + ((quint64)i * (quint64)width * 8); | |||
592 | } | |||
593 | else | |||
594 | { | |||
595 | lines[i] = data + ((quint64)i * (quint64)width * 4); | |||
596 | } | |||
597 | } | |||
598 | ||||
599 | // The easy way to read the whole image | |||
600 | // png_read_image(png_ptr, lines); | |||
601 | // The other way to read images is row by row. Necessary for observer. | |||
602 | // Now we need to deal with interlacing. | |||
603 | ||||
604 | for (int pass = 0 ; pass < number_passes ; ++pass) | |||
605 | { | |||
606 | int checkPoint = 0; | |||
607 | ||||
608 | for (int y = 0 ; y < height ; ++y) | |||
609 | { | |||
610 | if (observer && (y == checkPoint)) | |||
611 | { | |||
612 | checkPoint += granularity(observer, height, 0.7F); | |||
613 | ||||
614 | if (!observer->continueQuery()) | |||
615 | { | |||
616 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) nullptr); | |||
617 | delete cleanupData; | |||
618 | loadingFailed(); | |||
619 | ||||
620 | return false; | |||
621 | } | |||
622 | ||||
623 | // use 10% - 80% for progress while reading rows | |||
624 | ||||
625 | observer->progressInfo(0.1F + (0.7F * (((float)y) / ((float)height)))); | |||
626 | } | |||
627 | ||||
628 | png_read_rows(png_ptr, lines + y, nullptr, 1); | |||
629 | } | |||
630 | } | |||
631 | ||||
632 | cleanupData->freeLines(); | |||
633 | ||||
634 | if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) | |||
635 | { | |||
636 | // Swap bytes in 16 bits/color/pixel for DImg | |||
637 | ||||
638 | if (m_sixteenBit) | |||
639 | { | |||
640 | uchar ptr[8]; // One pixel to swap | |||
641 | ||||
642 | for (uint p = 0 ; p < (uint)width * height * 8 ; p += 8) | |||
643 | { | |||
644 | memcpy(&ptr[0], &data[p], 8); // Current pixel | |||
645 | ||||
646 | data[ p ] = ptr[1]; // Blue | |||
647 | data[p + 1] = ptr[0]; | |||
648 | data[p + 2] = ptr[3]; // Green | |||
649 | data[p + 3] = ptr[2]; | |||
650 | data[p + 4] = ptr[5]; // Red | |||
651 | data[p + 5] = ptr[4]; | |||
652 | data[p + 6] = ptr[7]; // Alpha | |||
653 | data[p + 7] = ptr[6]; | |||
654 | } | |||
655 | } | |||
656 | } | |||
657 | } | |||
658 | ||||
659 | if (observer) | |||
660 | { | |||
661 | observer->progressInfo(0.9F); | |||
662 | } | |||
663 | ||||
664 | // ------------------------------------------------------------------- | |||
665 | // Read image ICC profile | |||
666 | ||||
667 | if (m_loadFlags & LoadICCData) | |||
668 | { | |||
669 | png_charp profile_name; | |||
670 | iCCP_data profile_data = nullptr; | |||
671 | png_uint_32 profile_size; | |||
672 | int compression_type; | |||
673 | ||||
674 | png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &profile_size); | |||
675 | ||||
676 | if (profile_data != nullptr) | |||
677 | { | |||
678 | QByteArray profile_rawdata; | |||
679 | profile_rawdata.resize(profile_size); | |||
680 | memcpy(profile_rawdata.data(), profile_data, profile_size); | |||
681 | imageSetIccProfile(IccProfile(profile_rawdata)); | |||
682 | } | |||
683 | else | |||
684 | { | |||
685 | // If ICC profile is null, check Exif metadata. | |||
686 | ||||
687 | checkExifWorkingColorSpace(); | |||
688 | } | |||
689 | } | |||
690 | ||||
691 | // ------------------------------------------------------------------- | |||
692 | // Get embedded text data. | |||
693 | ||||
694 | png_text* text_ptr = nullptr; | |||
695 | int num_comments = png_get_text(png_ptr, info_ptr, &text_ptr, nullptr); | |||
696 | ||||
697 | /* | |||
698 | Standard Embedded text includes in PNG : | |||
699 | ||||
700 | Title Short (one line) title or caption for image | |||
701 | Author Name of image's creator | |||
702 | Description Description of image (possibly long) | |||
703 | Copyright Copyright notice | |||
704 | Creation Time Time of original image creation | |||
705 | Software Software used to create the image | |||
706 | Disclaimer Legal disclaimer | |||
707 | Warning Warning of nature of content | |||
708 | Source Device used to create the image | |||
709 | Comment Miscellaneous comment; conversion from GIF comment | |||
710 | ||||
711 | Extra Raw profiles tag are used by ImageMagick and defines at this Url: | |||
712 | search.cpan.org/src/EXIFTOOL/Image-ExifTool-5.87/html/TagNames/PNG.html#TextualData | |||
713 | */ | |||
714 | ||||
715 | if (m_loadFlags & LoadICCData) | |||
716 | { | |||
717 | for (int i = 0 ; i < num_comments ; ++i) | |||
718 | { | |||
719 | // Check if we have a Raw profile embedded using ImageMagick technique. | |||
720 | ||||
721 | if ((memcmp(text_ptr[i].key, "Raw profile type exif", 21) != 0) || | |||
722 | (memcmp(text_ptr[i].key, "Raw profile type APP1", 21) != 0) || | |||
723 | (memcmp(text_ptr[i].key, "Raw profile type iptc", 21) != 0)) | |||
724 | { | |||
725 | imageSetEmbbededText(QLatin1String(text_ptr[i].key), QLatin1String(text_ptr[i].text)); | |||
726 | ||||
727 | qCDebug(DIGIKAM_DIMG_LOG_PNG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_DIMG_LOG_PNG)()); qt_category; qt_category.control = false) QMessageLogger(static_cast<const char *>("/home/gilles/devel/8.x/core/dplugins/dimg/png/dimgpngloader_load.cpp" ), 727, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "Reading PNG Embedded text: key=" << text_ptr[i].key | |||
728 | << "size=" << QLatin1String(text_ptr[i].text).size(); | |||
729 | } | |||
730 | } | |||
731 | } | |||
732 | ||||
733 | // ------------------------------------------------------------------- | |||
734 | ||||
735 | if (m_loadFlags & LoadImageData) | |||
736 | { | |||
737 | png_read_end(png_ptr, info_ptr); | |||
738 | } | |||
739 | ||||
740 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) nullptr); | |||
741 | cleanupData->takeData(); | |||
742 | delete cleanupData; | |||
743 | ||||
744 | if (observer) | |||
745 | { | |||
746 | observer->progressInfo(1.0F); | |||
747 | } | |||
748 | ||||
749 | imageWidth() = width; | |||
750 | imageHeight() = height; | |||
751 | imageData() = data; | |||
752 | imageSetAttribute(QLatin1String("format"), QLatin1String("PNG")); | |||
753 | imageSetAttribute(QLatin1String("originalColorModel"), colorModel); | |||
754 | imageSetAttribute(QLatin1String("originalBitDepth"), bit_depth); | |||
755 | imageSetAttribute(QLatin1String("originalSize"), QSize(width, height)); | |||
756 | ||||
757 | return true; | |||
758 | } | |||
759 | ||||
760 | } // namespace DigikamPNGDImgPlugin |