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 - save 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::save(const QString& filePath, DImgLoaderObserver* const observer)
0087 {
0088     m_observer = observer;
0089 
0090 #ifdef Q_OS_WIN
0091 
0092     HANDLE fd = CreateFileW((LPCWSTR)filePath.utf16(), GENERIC_READ | GENERIC_WRITE, 0,
0093                             NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
0094 
0095     if (fd == INVALID_HANDLE_VALUE)
0096     {
0097         qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Error: Could not open destination file.";
0098         qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Last error code:" << GetLastError();
0099 
0100         return false;
0101     }
0102 
0103 #else
0104 
0105     int fd = QT_OPEN(filePath.toUtf8().constData(),
0106                      O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
0107 
0108     if (fd == -1)
0109     {
0110         qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Error: Could not open destination file.";
0111 
0112         return false;
0113     }
0114 
0115 #endif
0116 
0117     try
0118     {
0119         QVariant losslessAttr = imageGetAttribute(QLatin1String("lossless"));
0120         bool lossless         = losslessAttr.isValid() ? losslessAttr.toBool() : false;
0121 
0122         // NOTE: if quality = 0 -> lossless compression.
0123 
0124         int quality           = 0;
0125 
0126         if (!lossless)
0127         {
0128             QVariant qualityAttr = imageGetAttribute(QLatin1String("quality"));
0129             quality              = qualityAttr.isValid() ? qualityAttr.toInt() : 3;
0130         }
0131 
0132         qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF quality: " << quality;
0133 
0134         CPGFFileStream stream(fd);
0135         CPGFImage      pgf;
0136         PGFHeader      header;
0137         header.width   = imageWidth();
0138         header.height  = imageHeight();
0139         header.quality = quality;
0140 
0141         if (imageHasAlpha())
0142         {
0143             if (imageSixteenBit())
0144             {
0145                 // NOTE : there is no PGF color mode in 16 bits with alpha.
0146 
0147                 header.channels = 3;
0148                 header.bpp      = 48;
0149                 header.mode     = ImageModeRGB48;
0150             }
0151             else
0152             {
0153                 header.channels = 4;
0154                 header.bpp      = 32;
0155                 header.mode     = ImageModeRGBA;
0156             }
0157         }
0158         else
0159         {
0160             if (imageSixteenBit())
0161             {
0162                 header.channels = 3;
0163                 header.bpp      = 48;
0164                 header.mode     = ImageModeRGB48;
0165             }
0166             else
0167             {
0168                 header.channels = 3;
0169                 header.bpp      = 24;
0170                 header.mode     = ImageModeRGBColor;
0171             }
0172         }
0173 
0174 #ifdef PGFCodecVersionID
0175 
0176 #   if PGFCodecVersionID < 0x061142
0177 
0178         header.background.rgbtBlue  = 0;
0179         header.background.rgbtGreen = 0;
0180         header.background.rgbtRed   = 0;
0181 
0182 #   endif
0183 
0184 #endif
0185 
0186         pgf.SetHeader(header);
0187 
0188         // NOTE: see bug #273765 : Loading PGF thumbs with OpenMP support through a separated thread do not work properly with libppgf 6.11.24
0189 
0190         pgf.ConfigureEncoder(false);
0191 
0192         pgf.ImportBitmap(4 * imageWidth() * (imageSixteenBit() ? 2 : 1),
0193                          (UINT8*)imageData(),
0194                          imageBitsDepth() * 4,
0195                          nullptr,
0196                          DImgPGFLoader::CallbackForLibPGF, this);
0197 
0198         UINT32 nWrittenBytes = 0;
0199 
0200 #ifdef PGFCodecVersionID
0201 
0202 #   if PGFCodecVersionID >= 0x061124
0203 
0204         pgf.Write(&stream, &nWrittenBytes, CallbackForLibPGF, this);
0205 
0206 #   endif
0207 
0208 #else
0209 
0210         pgf.Write(&stream, 0, CallbackForLibPGF, &nWrittenBytes, this);
0211 
0212 #endif
0213 
0214         qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF width     = " << header.width;
0215         qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF height    = " << header.height;
0216         qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF bbp       = " << header.bpp;
0217         qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF channels  = " << header.channels;
0218         qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF quality   = " << header.quality;
0219         qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF mode      = " << header.mode;
0220         qCDebug(DIGIKAM_DIMG_LOG_PGF) << "Bytes Written = " << nWrittenBytes;
0221 
0222 #ifdef Q_OS_WIN
0223 
0224         CloseHandle(fd);
0225 
0226 #else
0227 
0228         close(fd);
0229 
0230 #endif
0231 
0232         // TODO: Store ICC profile in an appropriate place in the image
0233 
0234         storeColorProfileInMetadata();
0235 
0236         if (observer)
0237         {
0238             observer->progressInfo(1.0F);
0239         }
0240 
0241         imageSetAttribute(QLatin1String("savedFormat"), QLatin1String("PGF"));
0242         saveMetadata(filePath);
0243 
0244         return true;
0245     }
0246     catch (IOException& e)
0247     {
0248         int err = e.error;
0249 
0250         if (err >= AppError)
0251         {
0252             err -= AppError;
0253         }
0254 
0255         qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Error: Opening and saving PGF image failed (" << err << ")!";
0256 
0257 #ifdef Q_OS_WIN
0258 
0259         CloseHandle(fd);
0260 
0261 #else
0262 
0263         close(fd);
0264 
0265 #endif
0266 
0267         return false;
0268     }
0269 
0270     return true;
0271 }
0272 
0273 } // namespace Digikam