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"