File indexing completed on 2025-01-19 03:50:59

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-02-10
0007  * Description : rotate image batch tool.
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 "rotate.h"
0016 
0017 // Qt includes
0018 
0019 #include <QCheckBox>
0020 #include <QLabel>
0021 #include <QWidget>
0022 
0023 // KDE includes
0024 
0025 #include <klocalizedstring.h>
0026 
0027 // Local includes
0028 
0029 #include "dlayoutbox.h"
0030 #include "digikam_debug.h"
0031 #include "dcombobox.h"
0032 #include "dimg.h"
0033 #include "dimgbuiltinfilter.h"
0034 #include "dmetadata.h"
0035 #include "jpegutils.h"
0036 #include "freerotationfilter.h"
0037 #include "freerotationsettings.h"
0038 
0039 namespace DigikamBqmRotatePlugin
0040 {
0041 
0042 class Q_DECL_HIDDEN Rotate::Private
0043 {
0044 public:
0045 
0046     explicit Private()
0047       : CUSTOM_ANGLE(DImg::ROT270 + 1),
0048         label       (nullptr),
0049         useExif     (nullptr),
0050         comboBox    (nullptr),
0051         frSettings  (nullptr)
0052     {
0053     }
0054 
0055     const int             CUSTOM_ANGLE;
0056 
0057     QLabel*               label;
0058 
0059     QCheckBox*            useExif;
0060 
0061     DComboBox*            comboBox;
0062 
0063     FreeRotationSettings* frSettings;
0064 };
0065 
0066 Rotate::Rotate(QObject* const parent)
0067     : BatchTool(QLatin1String("Rotate"), TransformTool, parent),
0068       d        (new Private)
0069 {
0070 }
0071 
0072 Rotate::~Rotate()
0073 {
0074     delete d;
0075 }
0076 
0077 BatchTool* Rotate::clone(QObject* const parent) const
0078 {
0079     return new Rotate(parent);
0080 }
0081 
0082 void Rotate::registerSettingsWidget()
0083 {
0084 
0085     DVBox* const vbox = new DVBox;
0086     d->useExif        = new QCheckBox(i18nc("@title", "Use Exif Orientation"), vbox);
0087 
0088     d->label     = new QLabel(vbox);
0089     d->comboBox  = new DComboBox(vbox);
0090     d->comboBox->insertItem(DImg::ROT90,     i18nc("@item: angle", "90 degrees"));
0091     d->comboBox->insertItem(DImg::ROT180,    i18nc("@item: angle", "180 degrees"));
0092     d->comboBox->insertItem(DImg::ROT270,    i18nc("@item: angle", "270 degrees"));
0093     d->comboBox->insertItem(d->CUSTOM_ANGLE, i18nc("@item: angle", "Custom"));
0094     d->comboBox->setDefaultIndex(DImg::ROT90);
0095     d->label->setText(i18nc("@label", "Angle:"));
0096 
0097     d->frSettings       = new FreeRotationSettings(vbox);
0098 
0099     QLabel* const space = new QLabel(vbox);
0100     vbox->setStretchFactor(space, 10);
0101 
0102     m_settingsWidget    = vbox;
0103 
0104     setNeedResetExifOrientation(true);
0105 
0106     connect(d->comboBox, SIGNAL(activated(int)),
0107             this, SLOT(slotSettingsChanged()));
0108 
0109     connect(d->useExif, SIGNAL(toggled(bool)),
0110             this, SLOT(slotSettingsChanged()));
0111 
0112     connect(d->frSettings, SIGNAL(signalSettingsChanged()),
0113             this, SLOT(slotSettingsChanged()));
0114 
0115     slotSettingsChanged();
0116 
0117     BatchTool::registerSettingsWidget();
0118 }
0119 
0120 BatchToolSettings Rotate::defaultSettings()
0121 {
0122     BatchToolSettings settings;
0123     FreeRotationContainer defaultPrm = d->frSettings->defaultSettings();
0124 
0125     settings.insert(QLatin1String("useExif"),         true);
0126     settings.insert(QLatin1String("rotation"),        d->comboBox->defaultIndex());
0127     settings.insert(QLatin1String("angle"),           defaultPrm.angle);
0128     settings.insert(QLatin1String("antiAlias"),       defaultPrm.antiAlias);
0129     settings.insert(QLatin1String("autoCrop"),        defaultPrm.autoCrop);
0130     settings.insert(QLatin1String("backgroundColor"), defaultPrm.backgroundColor);
0131 
0132     return settings;
0133 }
0134 
0135 void Rotate::slotAssignSettings2Widget()
0136 {
0137     d->useExif->setChecked(settings()[QLatin1String("useExif")].toBool());
0138     d->comboBox->setCurrentIndex(settings()[QLatin1String("rotation")].toInt());
0139     FreeRotationContainer prm;
0140     prm.angle           = settings()[QLatin1String("angle")].toDouble();
0141     prm.antiAlias       = settings()[QLatin1String("antiAlias")].toBool();
0142     prm.autoCrop        = settings()[QLatin1String("autoCrop")].toInt();
0143     prm.backgroundColor = settings()[QLatin1String("backgroundColor")].value<QColor>();
0144     d->frSettings->setSettings(prm);
0145 }
0146 
0147 void Rotate::slotSettingsChanged()
0148 {
0149     d->label->setEnabled(!d->useExif->isChecked());
0150     d->comboBox->setEnabled(!d->useExif->isChecked());
0151     d->frSettings->setEnabled(d->comboBox->isEnabled() && d->comboBox->currentIndex() == d->CUSTOM_ANGLE);
0152 
0153     BatchToolSettings settings;
0154     FreeRotationContainer currentPrm = d->frSettings->settings();
0155 
0156     settings.insert(QLatin1String("useExif"),         d->useExif->isChecked());
0157     settings.insert(QLatin1String("rotation"),        d->comboBox->currentIndex());
0158     settings.insert(QLatin1String("angle"),           currentPrm.angle);
0159     settings.insert(QLatin1String("antiAlias"),       currentPrm.antiAlias);
0160     settings.insert(QLatin1String("autoCrop"),        currentPrm.autoCrop);
0161     settings.insert(QLatin1String("backgroundColor"), currentPrm.backgroundColor);
0162 
0163     BatchTool::slotSettingsChanged(settings);
0164 }
0165 
0166 bool Rotate::toolOperations()
0167 {
0168     FreeRotationContainer prm;
0169     bool useExif        = settings()[QLatin1String("useExif")].toBool();
0170     int rotation        = settings()[QLatin1String("rotation")].toInt();
0171     prm.angle           = settings()[QLatin1String("angle")].toDouble();
0172     prm.antiAlias       = settings()[QLatin1String("antiAlias")].toBool();
0173     prm.autoCrop        = settings()[QLatin1String("autoCrop")].toInt();
0174     prm.backgroundColor = settings()[QLatin1String("backgroundColor")].value<QColor>();
0175 
0176     // JPEG image : lossless method if non-custom rotation angle.
0177 
0178     if (JPEGUtils::isJpegImage(inputUrl().toLocalFile()) && image().isNull())
0179     {
0180         JPEGUtils::JpegRotator rotator(inputUrl().toLocalFile());
0181         rotator.setDestinationFile(outputUrl().toLocalFile());
0182 
0183         if (useExif)
0184         {
0185             return rotator.autoExifTransform();
0186         }
0187         else
0188         {
0189             switch (rotation)
0190             {
0191                 case DImg::ROT90:
0192                 {
0193                     return rotator.exifTransform(MetaEngineRotation::Rotate90);
0194                 }
0195 
0196                 case DImg::ROT180:
0197                 {
0198                     return rotator.exifTransform(MetaEngineRotation::Rotate180);
0199                 }
0200 
0201                 case DImg::ROT270:
0202                 {
0203                     return rotator.exifTransform(MetaEngineRotation::Rotate270);
0204                 }
0205 
0206                 default:
0207                 {
0208                     // there is no lossless method to turn JPEG image with a custom angle.
0209                     // fall through
0210                     break;
0211                 }
0212             }
0213         }
0214     }
0215 
0216     // Non-JPEG image: DImg
0217 
0218     if (!loadToDImg())
0219     {
0220         return false;
0221     }
0222 
0223     if (useExif)
0224     {
0225         // Exif rotation is currently not recorded to image history
0226 
0227         image().rotateAndFlip(image().exifOrientation(inputUrl().toLocalFile()));
0228     }
0229     else
0230     {
0231         switch (rotation)
0232         {
0233             case DImg::ROT90:
0234             {
0235                 DImgBuiltinFilter filter(DImgBuiltinFilter::Rotate90);
0236                 applyFilter(&filter);
0237                 break;
0238             }
0239 
0240             case DImg::ROT180:
0241             {
0242                 DImgBuiltinFilter filter(DImgBuiltinFilter::Rotate180);
0243                 applyFilter(&filter);
0244                 break;
0245             }
0246 
0247             case DImg::ROT270:
0248             {
0249                 DImgBuiltinFilter filter(DImgBuiltinFilter::Rotate270);
0250                 applyFilter(&filter);
0251                 break;
0252             }
0253 
0254             default:      // Custom value
0255             {
0256                 FreeRotationFilter fr(&image(), nullptr, prm);
0257                 applyFilterChangedProperties(&fr);
0258                 break;
0259             }
0260         }
0261     }
0262 
0263     return (savefromDImg());
0264 }
0265 
0266 } // namespace DigikamBqmRotatePlugin
0267 
0268 #include "moc_rotate.cpp"