File indexing completed on 2024-05-19 04:21:29
0001 // vim: set tabstop=4 shiftwidth=4 expandtab: 0002 /* 0003 Gwenview: an image viewer 0004 Copyright 2010 Aurélien Gâteau <agateau@kde.org> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License 0008 as published by the Free Software Foundation; either version 2 0009 of the License, or (at your option) any later version. 0010 0011 This program is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 GNU General Public License for more details. 0015 0016 You should have received a copy of the GNU General Public License 0017 along with this program; if not, write to the Free Software 0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. 0019 0020 */ 0021 // Self 0022 #include "resizeimagedialog.h" 0023 0024 // Qt 0025 #include <QBuffer> 0026 #include <QDialogButtonBox> 0027 #include <QFileInfo> 0028 #include <QPushButton> 0029 #include <QTimer> 0030 0031 // KF 0032 #include <KGuiItem> 0033 #include <KIO/Global> 0034 #include <KLocalizedString> 0035 0036 // Local 0037 #include <lib/gwenviewconfig.h> 0038 #include <ui_resizeimagewidget.h> 0039 0040 namespace Gwenview 0041 { 0042 struct ResizeImageDialogPrivate : public Ui_ResizeImageWidget { 0043 bool mUpdateFromRatio; 0044 bool mUpdateFromSizeOrPercentage; 0045 QSize mOriginalSize; 0046 }; 0047 0048 ResizeImageDialog::ResizeImageDialog(QWidget *parent) 0049 : QDialog(parent) 0050 , d(new ResizeImageDialogPrivate) 0051 { 0052 d->mUpdateFromRatio = false; 0053 d->mUpdateFromSizeOrPercentage = false; 0054 0055 auto mainLayout = new QVBoxLayout; 0056 setLayout(mainLayout); 0057 mainLayout->setSizeConstraint(QLayout::SetFixedSize); 0058 0059 auto content = new QWidget(this); 0060 d->setupUi(content); 0061 mainLayout->addWidget(content); 0062 auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 0063 QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); 0064 okButton->setDefault(true); 0065 okButton->setShortcut(Qt::CTRL | Qt::Key_Return); 0066 connect(buttonBox, &QDialogButtonBox::accepted, this, &ResizeImageDialog::accept); 0067 connect(buttonBox, &QDialogButtonBox::rejected, this, &ResizeImageDialog::reject); 0068 mainLayout->addWidget(buttonBox); 0069 0070 content->layout()->setContentsMargins(0, 0, 0, 0); 0071 KGuiItem::assign(okButton, KGuiItem(i18n("Resize"), QStringLiteral("transform-scale"))); 0072 setWindowTitle(content->windowTitle()); 0073 d->mWidthSpinBox->setFocus(); 0074 0075 d->mEstimatedSizeLabel->setToolTip(i18nc("@action", 0076 "Assuming that the image format won't be changed and\nfor lossy images using the " 0077 "value set in 'Lossy image save quality'.")); 0078 0079 connect(d->mWidthSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &ResizeImageDialog::slotWidthChanged); 0080 connect(d->mHeightSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &ResizeImageDialog::slotHeightChanged); 0081 connect(d->mWidthPercentSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ResizeImageDialog::slotWidthPercentChanged); 0082 connect(d->mHeightPercentSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ResizeImageDialog::slotHeightPercentChanged); 0083 connect(d->mWidthSpinBox, &QSpinBox::editingFinished, this, &ResizeImageDialog::slotCalculateImageSize); 0084 connect(d->mHeightSpinBox, &QSpinBox::editingFinished, this, &ResizeImageDialog::slotCalculateImageSize); 0085 connect(d->mWidthPercentSpinBox, &QSpinBox::editingFinished, this, &ResizeImageDialog::slotCalculateImageSize); 0086 connect(d->mHeightPercentSpinBox, &QSpinBox::editingFinished, this, &ResizeImageDialog::slotCalculateImageSize); 0087 connect(d->mKeepAspectCheckBox, &QCheckBox::toggled, this, &ResizeImageDialog::slotKeepAspectChanged); 0088 0089 auto timer = new QTimer(this); 0090 connect(timer, &QTimer::timeout, this, QOverload<>::of(&ResizeImageDialog::slotCalculateImageSize)); 0091 timer->start(2000); 0092 } 0093 0094 ResizeImageDialog::~ResizeImageDialog() 0095 { 0096 delete d; 0097 } 0098 0099 void ResizeImageDialog::setOriginalSize(const QSize &size) 0100 { 0101 d->mOriginalSize = size; 0102 d->mOriginalWidthLabel->setText(QString::number(size.width()) + QStringLiteral(" px")); 0103 d->mOriginalHeightLabel->setText(QString::number(size.height()) + QStringLiteral(" px")); 0104 d->mWidthSpinBox->setValue(size.width()); 0105 d->mHeightSpinBox->setValue(size.height()); 0106 } 0107 0108 void ResizeImageDialog::setCurrentImageUrl(const QUrl &imageUrl) 0109 { 0110 mCurrentImageUrl = imageUrl; 0111 0112 // mCurrentSize->setText has to be set after the mCurrentImageUrl has been set, otherwise it's -1 0113 QFileInfo fileInfo(mCurrentImageUrl.toLocalFile()); 0114 d->mCurrentSize->setText(KIO::convertSize(fileInfo.size())); 0115 mValueChanged = false; 0116 } 0117 0118 QSize ResizeImageDialog::size() const 0119 { 0120 return QSize(d->mWidthSpinBox->value(), d->mHeightSpinBox->value()); 0121 } 0122 0123 void ResizeImageDialog::slotWidthChanged(int width) 0124 { 0125 mValueChanged = true; 0126 // Update width percentage to match width, only if this was a manual adjustment 0127 if (!d->mUpdateFromSizeOrPercentage && !d->mUpdateFromRatio) { 0128 d->mUpdateFromSizeOrPercentage = true; 0129 d->mWidthPercentSpinBox->setValue((double(width) / d->mOriginalSize.width()) * 100); 0130 d->mUpdateFromSizeOrPercentage = false; 0131 } 0132 0133 if (!d->mKeepAspectCheckBox->isChecked() || d->mUpdateFromRatio) { 0134 return; 0135 } 0136 0137 // Update height to keep ratio, only if ratio locked and this was a manual adjustment 0138 d->mUpdateFromRatio = true; 0139 d->mHeightSpinBox->setValue(d->mOriginalSize.height() * width / d->mOriginalSize.width()); 0140 d->mUpdateFromRatio = false; 0141 } 0142 0143 void ResizeImageDialog::slotHeightChanged(int height) 0144 { 0145 mValueChanged = true; 0146 // Update height percentage to match height, only if this was a manual adjustment 0147 if (!d->mUpdateFromSizeOrPercentage && !d->mUpdateFromRatio) { 0148 d->mUpdateFromSizeOrPercentage = true; 0149 d->mHeightPercentSpinBox->setValue((double(height) / d->mOriginalSize.height()) * 100); 0150 d->mUpdateFromSizeOrPercentage = false; 0151 } 0152 0153 if (!d->mKeepAspectCheckBox->isChecked() || d->mUpdateFromRatio) { 0154 return; 0155 } 0156 0157 // Update width to keep ratio, only if ratio locked and this was a manual adjustment 0158 d->mUpdateFromRatio = true; 0159 d->mWidthSpinBox->setValue(d->mOriginalSize.width() * height / d->mOriginalSize.height()); 0160 d->mUpdateFromRatio = false; 0161 } 0162 0163 void ResizeImageDialog::slotWidthPercentChanged(double widthPercent) 0164 { 0165 mValueChanged = true; 0166 // Update width to match width percentage, only if this was a manual adjustment 0167 if (!d->mUpdateFromSizeOrPercentage && !d->mUpdateFromRatio) { 0168 d->mUpdateFromSizeOrPercentage = true; 0169 d->mWidthSpinBox->setValue((widthPercent / 100) * d->mOriginalSize.width()); 0170 d->mUpdateFromSizeOrPercentage = false; 0171 } 0172 0173 if (!d->mKeepAspectCheckBox->isChecked() || d->mUpdateFromRatio) { 0174 return; 0175 } 0176 0177 // Keep height percentage in sync with width percentage, only if ratio locked and this was a manual adjustment 0178 d->mUpdateFromRatio = true; 0179 d->mHeightPercentSpinBox->setValue(d->mWidthPercentSpinBox->value()); 0180 d->mUpdateFromRatio = false; 0181 } 0182 0183 void ResizeImageDialog::slotHeightPercentChanged(double heightPercent) 0184 { 0185 mValueChanged = true; 0186 // Update height to match height percentage, only if this was a manual adjustment 0187 if (!d->mUpdateFromSizeOrPercentage && !d->mUpdateFromRatio) { 0188 d->mUpdateFromSizeOrPercentage = true; 0189 d->mHeightSpinBox->setValue((heightPercent / 100) * d->mOriginalSize.height()); 0190 d->mUpdateFromSizeOrPercentage = false; 0191 } 0192 0193 if (!d->mKeepAspectCheckBox->isChecked() || d->mUpdateFromRatio) { 0194 return; 0195 } 0196 0197 // Keep height percentage in sync with width percentage, only if ratio locked and this was a manual adjustment 0198 d->mUpdateFromRatio = true; 0199 d->mWidthPercentSpinBox->setValue(d->mHeightPercentSpinBox->value()); 0200 d->mUpdateFromRatio = false; 0201 } 0202 0203 void ResizeImageDialog::slotKeepAspectChanged(bool value) 0204 { 0205 if (value) { 0206 d->mUpdateFromSizeOrPercentage = true; 0207 slotWidthChanged(d->mWidthSpinBox->value()); 0208 slotWidthPercentChanged(d->mWidthPercentSpinBox->value()); 0209 d->mUpdateFromSizeOrPercentage = false; 0210 } 0211 } 0212 0213 void ResizeImageDialog::slotCalculateImageSize() 0214 { 0215 qint64 size = calculateEstimatedImageSize(); 0216 if (size == -1) { 0217 return; 0218 } else if (size == -2) { 0219 d->mEstimatedSize->setText(i18n("error")); 0220 } else { 0221 d->mEstimatedSize->setText(KIO::convertSize(size)); 0222 } 0223 } 0224 0225 qint64 ResizeImageDialog::calculateEstimatedImageSize() 0226 { 0227 if (mValueChanged) { 0228 QImage image(mCurrentImageUrl.toLocalFile()); 0229 if (image.isNull()) { 0230 return -2; 0231 } 0232 0233 QByteArray ba; 0234 QBuffer buffer(&ba); 0235 QFileInfo fileInfo(mCurrentImageUrl.toLocalFile()); 0236 QString suffix = fileInfo.suffix(); 0237 0238 buffer.open(QIODevice::ReadWrite); 0239 image = image.scaled(size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0240 0241 if (QString::compare(suffix, QStringLiteral("jpg"), Qt::CaseInsensitive) == 0 0242 || QString::compare(suffix, QStringLiteral("jpeg"), Qt::CaseInsensitive) == 0 0243 || QString::compare(suffix, QStringLiteral("avif"), Qt::CaseInsensitive) == 0 0244 || QString::compare(suffix, QStringLiteral("heic"), Qt::CaseInsensitive) == 0 0245 || QString::compare(suffix, QStringLiteral("webp"), Qt::CaseInsensitive) == 0) { 0246 image.save(&buffer, suffix.toStdString().c_str(), GwenviewConfig::jPEGQuality()); 0247 } else { 0248 image.save(&buffer, suffix.toStdString().c_str()); 0249 } 0250 0251 qint64 size = buffer.size(); 0252 buffer.close(); 0253 0254 mValueChanged = false; 0255 return size; 0256 } 0257 return -1; 0258 } 0259 0260 } // namespace 0261 0262 #include "moc_resizeimagedialog.cpp"