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        : 2019-09-21
0007  * Description : Magick DImg plugin.
0008  *
0009  * SPDX-FileCopyrightText: 2020-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 "dimgimagemagickplugin.h"
0016 
0017 // Qt includes
0018 
0019 #include <QMimeDatabase>
0020 
0021 // KDE includes
0022 
0023 #include <klocalizedstring.h>
0024 
0025 // Image Magick includes
0026 
0027 // Pragma directives to reduce warnings from ImageMagick header files.
0028 #if !defined(Q_OS_DARWIN) && defined(Q_CC_GNU)
0029 #   pragma GCC diagnostic push
0030 #   pragma GCC diagnostic ignored "-Wignored-qualifiers"
0031 #   pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
0032 #endif
0033 
0034 #if defined(Q_CC_CLANG)
0035 #   pragma clang diagnostic push
0036 #   pragma clang diagnostic ignored "-Wignored-qualifiers"
0037 #   pragma clang diagnostic ignored "-Wkeyword-macro"
0038 #endif
0039 
0040 #include <Magick++.h>
0041 
0042 #if MagickLibVersion < 0x700
0043 #   include <magick/magick.h>
0044 #endif
0045 
0046 using namespace Magick;
0047 using namespace MagickCore;
0048 
0049 #if defined(Q_CC_CLANG)
0050 #   pragma clang diagnostic pop
0051 #endif
0052 
0053 #if defined(Q_CC_GNU)
0054 #   pragma GCC diagnostic pop
0055 #endif
0056 
0057 // Local includes
0058 
0059 #include "digikam_debug.h"
0060 #include "digikam_globals.h"
0061 #include "dimgimagemagickloader.h"
0062 
0063 namespace DigikamImageMagickDImgPlugin
0064 {
0065 
0066 DImgImageMagickPlugin::DImgImageMagickPlugin(QObject* const parent)
0067     : DPluginDImg(parent)
0068 {
0069     MagickCoreGenesis((char*)nullptr, MagickFalse);
0070 
0071     m_readFormats  = decoderFormats();
0072     m_writeFormats = encoderFormats();
0073 }
0074 
0075 DImgImageMagickPlugin::~DImgImageMagickPlugin()
0076 {
0077 }
0078 
0079 void DImgImageMagickPlugin::cleanUp()
0080 {
0081     MagickCoreTerminus();
0082 }
0083 
0084 QString DImgImageMagickPlugin::name() const
0085 {
0086     return i18nc("@title", "ImageMagick loader");
0087 }
0088 
0089 QString DImgImageMagickPlugin::iid() const
0090 {
0091     return QLatin1String(DPLUGIN_IID);
0092 }
0093 
0094 QIcon DImgImageMagickPlugin::icon() const
0095 {
0096     return QIcon::fromTheme(QLatin1String("image-x-generic"));
0097 }
0098 
0099 QString DImgImageMagickPlugin::description() const
0100 {
0101     return i18nc("@info", "An image loader based on ImageMagick coders");
0102 }
0103 
0104 QString DImgImageMagickPlugin::details() const
0105 {
0106     return xi18nc("@info", "<para>This plugin allows users to load and save image using ImageMagick coders.</para>"
0107                   "<para>ImageMagick is a free and open-source software suite for converting raster image and vector image files. "
0108                   "It can read and write over 200 image file formats.</para>"
0109                   "<para>See <a href='https://en.wikipedia.org/wiki/ImageMagick'>ImageMagick documentation</a> for details.</para>"
0110     );
0111 }
0112 
0113 QString DImgImageMagickPlugin::handbookSection() const
0114 {
0115     return QLatin1String("supported_materials");
0116 }
0117 
0118 QString DImgImageMagickPlugin::handbookChapter() const
0119 {
0120     return QLatin1String("image_formats");
0121 }
0122 
0123 QString DImgImageMagickPlugin::handbookReference() const
0124 {
0125     return QLatin1String("image-others");
0126 }
0127 
0128 QList<DPluginAuthor> DImgImageMagickPlugin::authors() const
0129 {
0130     return QList<DPluginAuthor>()
0131             << DPluginAuthor(QString::fromUtf8("Maik Qualmann"),
0132                              QString::fromUtf8("metzpinguin at gmail dot com"),
0133                              QString::fromUtf8("(C) 2019-2023"))
0134             << DPluginAuthor(QString::fromUtf8("Gilles Caulier"),
0135                              QString::fromUtf8("caulier dot gilles at gmail dot com"),
0136                              QString::fromUtf8("(C) 2006-2023"))
0137             ;
0138 }
0139 
0140 void DImgImageMagickPlugin::setup(QObject* const /*parent*/)
0141 {
0142     // Nothing to do
0143 }
0144 
0145 QMap<QString, QStringList> DImgImageMagickPlugin::extraAboutData() const
0146 {
0147     QString mimes = typeMimes();
0148     QMap<QString, QStringList> map;
0149 
0150     try
0151     {
0152         ExceptionInfo ex          = *AcquireExceptionInfo();
0153         size_t n                  = 0;
0154         const MagickInfo** inflst = GetMagickInfoList("*", &n, &ex);
0155 
0156         if (!inflst)
0157         {
0158             qCWarning(DIGIKAM_DIMG_LOG_MAGICK) << "ImageMagick coders list is null!";
0159             return QMap<QString, QStringList>();
0160         }
0161 
0162         for (uint i = 0 ; i < n ; ++i)
0163         {
0164             const MagickInfo* inf = inflst[i];
0165 
0166             if (inf)
0167             {
0168                 QString mod =
0169 
0170 #if (MagickLibVersion >= 0x69A && defined(magick_module))
0171 
0172                     QString::fromLatin1(inf->magick_module).toUpper();
0173 
0174 #else
0175 
0176                     QString::fromLatin1(inf->module).toUpper();
0177 
0178 #endif
0179 
0180                 if (mimes.contains(mod))
0181                 {
0182                     map.insert(mod,
0183                                QStringList() << QLatin1String(inf->description)
0184                                              << (inf->decoder ?
0185                                                  i18nc("@info: can read file format",     "yes") :
0186                                                  i18nc("@info: cannot read file format",  "no"))
0187                                              << (inf->encoder ?
0188                                                  i18nc("@info: can write file format",    "yes") :
0189                                                  i18nc("@info: cannot write file format", "no"))
0190 
0191                     );
0192                 }
0193             }
0194         }
0195 
0196         free(inflst);
0197     }
0198     catch (Exception& error)
0199     {
0200         qCWarning(DIGIKAM_DIMG_LOG) << "ImageMagickInfo exception:" << error.what();
0201         return QMap<QString, QStringList>();
0202     }
0203 
0204     return map;
0205 }
0206 
0207 QString DImgImageMagickPlugin::loaderName() const
0208 {
0209     return QLatin1String("IMAGEMAGICK");
0210 }
0211 
0212 QString DImgImageMagickPlugin::typeMimes() const
0213 {
0214     QStringList formats = m_readFormats;
0215     formats.sort();
0216 
0217     QString ret;
0218 
0219     Q_FOREACH (const QString& str, formats)
0220     {
0221         if (!ret.contains(str))
0222         {
0223             ret += QString::fromUtf8("%1 ").arg(str.toUpper());
0224         }
0225     }
0226 
0227     return ret;
0228 }
0229 
0230 int DImgImageMagickPlugin::canRead(const QFileInfo& fileInfo, bool magic) const
0231 {
0232     QString filePath = fileInfo.filePath();
0233     QString format   = fileInfo.suffix().toUpper();
0234 
0235     if (!magic)
0236     {
0237         QString mimeType(QMimeDatabase().mimeTypeForFile(filePath).name());
0238 
0239         // Ignore non image format.
0240 
0241         if (
0242             mimeType.startsWith(QLatin1String("video/")) ||
0243             mimeType.startsWith(QLatin1String("audio/"))
0244            )
0245         {
0246             return 0;
0247         }
0248 
0249         if (!format.isEmpty() && m_readFormats.contains(format))
0250         {
0251             return 90;
0252         }
0253     }
0254 
0255     return 0;
0256 }
0257 
0258 int DImgImageMagickPlugin::canWrite(const QString& format) const
0259 {
0260     if (m_writeFormats.contains(format.toUpper()))
0261     {
0262         return 90;
0263     }
0264 
0265     return 0;
0266 }
0267 
0268 DImgLoader* DImgImageMagickPlugin::loader(DImg* const image, const DRawDecoding&) const
0269 {
0270     return new DImgImageMagickLoader(image);
0271 }
0272 
0273 QStringList DImgImageMagickPlugin::decoderFormats() const
0274 {
0275     QStringList formats;
0276 
0277     try
0278     {
0279         ExceptionInfo ex          = *AcquireExceptionInfo();
0280         size_t n                  = 0;
0281         const MagickInfo** inflst = GetMagickInfoList("*", &n, &ex);
0282 
0283         if (!inflst)
0284         {
0285             qWarning() << "ImageMagick coders list is null!";
0286             return QStringList();
0287         }
0288 
0289         for (uint i = 0 ; i < n ; ++i)
0290         {
0291             const MagickInfo* inf = inflst[i];
0292 
0293             if (inf && inf->decoder)
0294             {
0295 
0296 #if (MagickLibVersion >= 0x69A && defined(magick_module))
0297 
0298             formats.append(QString::fromLatin1(inf->magick_module).toUpper());
0299 
0300 #else
0301 
0302             formats.append(QString::fromLatin1(inf->module).toUpper());
0303 
0304 #endif
0305 
0306             }
0307         }
0308 
0309         free(inflst);
0310     }
0311     catch (Exception& error)
0312     {
0313         qCWarning(DIGIKAM_DIMG_LOG) << "ImageMagickInfo exception:" << error.what();
0314         return QStringList();
0315     }
0316 
0317     if (formats.contains(QLatin1String("JPEG")))
0318     {
0319         formats.append(QLatin1String("JPG"));
0320         formats.append(QLatin1String("JPE"));
0321     }
0322 
0323     if (formats.contains(QLatin1String("FITS")))
0324     {
0325         formats.append(QLatin1String("FTS"));
0326         formats.append(QLatin1String("FIT"));
0327     }
0328 
0329     // Remove known formats that are not stable.
0330 
0331     formats.removeAll(QLatin1String("XCF"));
0332 
0333     return formats;
0334 }
0335 
0336 QStringList DImgImageMagickPlugin::encoderFormats() const
0337 {
0338     QStringList formats;
0339 
0340     try
0341     {
0342         ExceptionInfo ex          = *AcquireExceptionInfo();
0343         size_t n                  = 0;
0344         const MagickInfo** inflst = GetMagickInfoList("*", &n, &ex);
0345 
0346         if (!inflst)
0347         {
0348             qWarning() << "ImageMagick coders list is null!";
0349             return QStringList();
0350         }
0351 
0352         for (uint i = 0 ; i < n ; ++i)
0353         {
0354             const MagickInfo* inf = inflst[i];
0355 
0356             if (inf && inf->encoder)
0357             {
0358 
0359 #if (MagickLibVersion >= 0x69A && defined(magick_module))
0360 
0361                 formats.append(QString::fromLatin1(inf->magick_module).toUpper());
0362 
0363 #else
0364 
0365                 formats.append(QString::fromLatin1(inf->module).toUpper());
0366 
0367 #endif
0368 
0369             }
0370         }
0371 
0372         free(inflst);
0373     }
0374     catch (Exception& error)
0375     {
0376         qCWarning(DIGIKAM_DIMG_LOG) << "ImageMagickInfo exception:" << error.what();
0377         return QStringList();
0378     }
0379 
0380     if (formats.contains(QLatin1String("FITS")))
0381     {
0382         formats.append(QLatin1String("FTS"));
0383     }
0384 
0385     return formats;
0386 }
0387 
0388 DImgLoaderSettings* DImgImageMagickPlugin::exportWidget(const QString& format) const
0389 {
0390     Q_UNUSED(format);
0391 
0392     return nullptr;
0393 }
0394 
0395 } // namespace DigikamImageMagickDImgPlugin
0396 
0397 #include "moc_dimgimagemagickplugin.cpp"