| 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 |