File indexing completed on 2025-01-19 03:51:01

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-06-03
0007  * Description : A PGF IO file for DImg framework - load operations
0008  *
0009  * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "digikam_config.h"
0016 #include "dimgpgfloader.h"       // krazy:exclude=includes
0017 
0018 // C Ansi includes
0019 
0020 extern "C"
0021 {
0022 #ifndef Q_CC_MSVC
0023 #   include <unistd.h>
0024 #endif
0025 #include <sys/types.h>
0026 #include <sys/stat.h>
0027 #include <fcntl.h>
0028 }
0029 
0030 // C++ includes
0031 
0032 #include <iostream>
0033 #include <cmath>
0034 #include <cstdio>
0035 
0036 // Qt includes
0037 
0038 #include <QFile>
0039 #include <QVariant>
0040 #include <QByteArray>
0041 #include <QTextStream>
0042 #include <QDataStream>
0043 #include <qplatformdefs.h>
0044 
0045 // Windows includes
0046 
0047 #ifdef Q_OS_WIN
0048 #   include <windows.h>
0049 #endif
0050 
0051 // Libpgf includes
0052 
0053 // Pragma directives to reduce warnings from Libpgf header files.
0054 #if defined(Q_CC_GNU)
0055 #   pragma GCC diagnostic push
0056 #   pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
0057 #endif
0058 
0059 #if defined(Q_CC_CLANG)
0060 #   pragma clang diagnostic push
0061 #   pragma clang diagnostic ignored "-Wkeyword-macro"
0062 #endif
0063 
0064 #include "PGFimage.h"
0065 
0066 // Restore warnings
0067 #if defined(Q_CC_CLANG)
0068 #   pragma clang diagnostic pop
0069 #endif
0070 
0071 #if defined(Q_CC_GNU)
0072 #   pragma GCC diagnostic pop
0073 #endif
0074 
0075 // Local includes
0076 
0077 #include "digikam_debug.h"
0078 #include "dimg.h"
0079 #include "dimgloaderobserver.h"
0080 #include "pgfutils.h"
0081 #include "metaengine.h"
0082 
0083 namespace Digikam
0084 {
0085 
0086 bool DImgPGFLoader::load(const QString& filePath, DImgLoaderObserver* const observer)
0087 {
0088     m_observer = observer;
0089     readMetadata(filePath);
0090 
0091 #ifdef Q_OS_WIN
0092 
0093     FILE* const file = _wfopen((const wchar_t*)filePath.utf16(), L"rb");
0094 
0095 #else
0096 
0097     FILE* const file = fopen(filePath.toUtf8().constData(), "rb");
0098 
0099 #endif
0100 
0101     if (!file)
0102     {
0103         qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Error: Could not open source file.";
0104 
0105         loadingFailed();
0106 
0107         return false;
0108     }
0109 
0110     unsigned char header[3];
0111 
0112     if (fread(&header, 3, 1, file) != 1)
0113     {
0114         loadingFailed();
0115         fclose(file);
0116 
0117         return false;
0118     }
0119 
0120     unsigned char pgfID[3] = { 0x50, 0x47, 0x46 };
0121 
0122     if (memcmp(&header[0], &pgfID, 3) != 0)
0123     {
0124         // not a PGF file
0125         loadingFailed();
0126         fclose(file);
0127 
0128         return false;
0129     }
0130 
0131     fclose(file);
0132 
0133     // -------------------------------------------------------------------
0134     // Initialize PGF API.
0135 
0136 #ifdef Q_OS_WIN
0137 
0138     HANDLE fd = CreateFileW((LPCWSTR)filePath.utf16(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
0139 
0140     if (fd == INVALID_HANDLE_VALUE)
0141     {
0142         qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Error: Could not open source file.";
0143         qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Last error code:" << GetLastError();
0144         loadingFailed();
0145 
0146         return false;
0147     }
0148 
0149 #else
0150 
0151     int fd = QT_OPEN(filePath.toUtf8().constData(), O_RDONLY);
0152 
0153     if (fd == -1)
0154     {
0155         loadingFailed();
0156 
0157         return false;
0158     }
0159 
0160 #endif
0161 
0162     CPGFFileStream stream(fd);
0163     CPGFImage      pgf;
0164     int            colorModel = DImg::COLORMODELUNKNOWN;
0165 
0166     try
0167     {
0168         // open pgf image
0169         pgf.Open(&stream);
0170 
0171         switch (pgf.Mode())
0172         {
0173             case ImageModeRGBColor:
0174             case ImageModeRGB48:
0175                 m_hasAlpha = false;
0176                 colorModel = DImg::RGB;
0177                 break;
0178 
0179             case ImageModeRGBA:
0180                 m_hasAlpha = true;
0181                 colorModel = DImg::RGB;
0182                 break;
0183 
0184             default:
0185                 qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Cannot load PGF image: color mode not supported ("
0186                                                 << pgf.Mode() << ")";
0187 
0188 #ifdef Q_OS_WIN
0189 
0190                 CloseHandle(fd);
0191 
0192 #else
0193 
0194                 close(fd);
0195 
0196 #endif
0197 
0198                 loadingFailed();
0199 
0200                 return false;
0201                 break;
0202         }
0203 
0204         switch (pgf.Channels())
0205         {
0206             case 3:
0207             case 4:
0208                 break;
0209 
0210             default:
0211                 qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Cannot load PGF image: color channels number not supported ("
0212                                                 << pgf.Channels() << ")";
0213 
0214 #ifdef Q_OS_WIN
0215 
0216                 CloseHandle(fd);
0217 
0218 #else
0219 
0220                 close(fd);
0221 
0222 #endif
0223 
0224                 loadingFailed();
0225 
0226                 return false;
0227                 break;
0228         }
0229 
0230         int bitDepth = pgf.BPP();
0231 
0232         switch (bitDepth)
0233         {
0234             case 24:    // RGB 8 bits.
0235             case 32:    // RGBA 8 bits.
0236                 m_sixteenBit = false;
0237                 break;
0238 
0239             case 48:    // RGB 16 bits.
0240             case 64:    // RGBA 16 bits.
0241                 m_sixteenBit = true;
0242                 break;
0243 
0244             default:
0245                 qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Cannot load PGF image: color bits depth not supported ("
0246                                                 << bitDepth << ")";
0247 
0248 #ifdef Q_OS_WIN
0249 
0250                 CloseHandle(fd);
0251 
0252 #else
0253 
0254                 close(fd);
0255 
0256 #endif
0257 
0258                 loadingFailed();
0259 
0260                 return false;
0261                 break;
0262         }
0263 
0264         if (DIGIKAM_DIMG_LOG_PGF().isDebugEnabled())
0265         {
0266             const PGFHeader* const pgfHeader = pgf.GetHeader();
0267             qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF width    = " << pgfHeader->width;
0268             qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF height   = " << pgfHeader->height;
0269             qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF bbp      = " << pgfHeader->bpp;
0270             qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF channels = " << pgfHeader->channels;
0271             qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF quality  = " << pgfHeader->quality;
0272             qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF mode     = " << pgfHeader->mode;
0273             qCDebug(DIGIKAM_DIMG_LOG_PGF) << "Has Alpha    = " << m_hasAlpha;
0274             qCDebug(DIGIKAM_DIMG_LOG_PGF) << "Is 16 bits   = " << m_sixteenBit;
0275         }
0276 
0277         // NOTE: see bug #273765 : Loading PGF thumbs with OpenMP support through a separated thread do not work properly with libppgf 6.11.24
0278         pgf.ConfigureDecoder(false);
0279 
0280         int width   = pgf.Width();
0281         int height  = pgf.Height();
0282         uchar* data = nullptr;
0283 
0284         QSize originalSize(width, height);
0285 
0286         if (m_loadFlags & LoadImageData)
0287         {
0288             // -------------------------------------------------------------------
0289             // Find out if we do the fast-track loading with reduced size. PGF specific.
0290             int level          = 0;
0291             QVariant attribute = imageGetAttribute(QLatin1String("scaledLoadingSize"));
0292 
0293             if (attribute.isValid() && pgf.Levels() > 0)
0294             {
0295                 int scaledLoadingSize = attribute.toInt();
0296                 int i, w, h;
0297 
0298                 for (i = pgf.Levels() - 1 ; i >= 0 ; --i)
0299                 {
0300                     w = pgf.Width(i);
0301                     h = pgf.Height(i);
0302 
0303                     if (qMin(w, h) >= scaledLoadingSize)
0304                     {
0305                         break;
0306                     }
0307                 }
0308 
0309                 if (i >= 0)
0310                 {
0311                     width  = w;
0312                     height = h;
0313                     level  = i;
0314 
0315                     qCDebug(DIGIKAM_DIMG_LOG_PGF) << "Loading PGF scaled version at level " << i
0316                                                   << " (" << w << " x " << h << ") for size "
0317                                                   << scaledLoadingSize;
0318                 }
0319             }
0320 
0321             if (m_sixteenBit)
0322             {
0323                 data = new_failureTolerant(width, height, 8); // 16 bits/color/pixel
0324             }
0325             else
0326             {
0327                 data = new_failureTolerant(width, height, 4); // 8 bits/color/pixel
0328             }
0329 
0330             // Fill all with 255 including alpha channel.
0331             memset(data, 0xFF, width * height * (m_sixteenBit ? 8 : 4));
0332 
0333             pgf.Read(level, DImgPGFLoader::CallbackForLibPGF, this);
0334             pgf.GetBitmap(m_sixteenBit ? width * 8 : width * 4,
0335                           (UINT8*)data,
0336                           m_sixteenBit ? 64 : 32,
0337                           nullptr,
0338                           CallbackForLibPGF, this);
0339 
0340             if (observer)
0341             {
0342                 observer->progressInfo(1.0F);
0343             }
0344         }
0345 
0346         // -------------------------------------------------------------------
0347 
0348 #ifdef Q_OS_WIN
0349 
0350         CloseHandle(fd);
0351 
0352 #else
0353 
0354         close(fd);
0355 
0356 #endif
0357 
0358         // Get ICC color profile.
0359 
0360         if (m_loadFlags & LoadICCData)
0361         {
0362             // TODO: Implement proper storage in PGF for color profiles
0363             checkExifWorkingColorSpace();
0364         }
0365 
0366         imageWidth()  = width;
0367         imageHeight() = height;
0368         imageData()   = data;
0369         imageSetAttribute(QLatin1String("format"),             QLatin1String("PGF"));
0370         imageSetAttribute(QLatin1String("originalColorModel"), colorModel);
0371         imageSetAttribute(QLatin1String("originalBitDepth"),   bitDepth);
0372         imageSetAttribute(QLatin1String("originalSize"),       originalSize);
0373 
0374         return true;
0375     }
0376     catch (IOException& e)
0377     {
0378         int err = e.error;
0379 
0380         if (err >= AppError)
0381         {
0382             err -= AppError;
0383         }
0384 
0385         qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Error: Opening and reading PGF image failed (" << err << ")!";
0386 
0387 #ifdef Q_OS_WIN
0388 
0389         CloseHandle(fd);
0390 
0391 #else
0392 
0393         close(fd);
0394 
0395 #endif
0396 
0397         loadingFailed();
0398 
0399         return false;
0400     }
0401     catch (std::bad_alloc& e)
0402     {
0403         qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Failed to allocate memory for loading" << filePath << e.what();
0404 
0405 #ifdef Q_OS_WIN
0406 
0407         CloseHandle(fd);
0408 
0409 #else
0410 
0411         close(fd);
0412 
0413 #endif
0414 
0415         loadingFailed();
0416 
0417         return false;
0418     }
0419 
0420     return true;
0421 }
0422 
0423 } // namespace Digikam