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