diff --git a/lib/abstractimageoperation.cpp b/lib/abstractimageoperation.cpp index 87cf2374..84d63ccc 100644 --- a/lib/abstractimageoperation.cpp +++ b/lib/abstractimageoperation.cpp @@ -1,134 +1,129 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2007 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Self #include "abstractimageoperation.h" // Qt #include #include // KDE #include // Local #include "document/documentfactory.h" #include "document/documentjob.h" namespace Gwenview { class ImageOperationCommand : public QUndoCommand { public: ImageOperationCommand(AbstractImageOperation* op) : mOp(op) {} ~ImageOperationCommand() { delete mOp; } void undo() Q_DECL_OVERRIDE { mOp->undo(); } void redo() override { mOp->redo(); } private: AbstractImageOperation* mOp; }; struct AbstractImageOperationPrivate { QString mText; QUrl mUrl; ImageOperationCommand* mCommand; }; AbstractImageOperation::AbstractImageOperation() : d(new AbstractImageOperationPrivate) { } AbstractImageOperation::~AbstractImageOperation() { delete d; } void AbstractImageOperation::applyToDocument(Document::Ptr doc) { d->mUrl = doc->url(); d->mCommand = new ImageOperationCommand(this); d->mCommand->setText(d->mText); // QUndoStack::push() executes command by calling its redo() function doc->undoStack()->push(d->mCommand); } Document::Ptr AbstractImageOperation::document() const { Document::Ptr doc = DocumentFactory::instance()->load(d->mUrl); doc->startLoadingFullImage(); return doc; } void AbstractImageOperation::finish(bool ok) { if (ok) { - finishUndoJob(); + // Give QUndoStack time to update in case the redo/undo is executed immediately + // (e.g. undo crop just sets the previous image) + QTimer::singleShot(0, document().data(), &Document::imageOperationCompleted); } else { #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) // Remove command from undo stack without executing undo() d->mCommand->setObsolete(true); #endif document()->undoStack()->undo(); } } void AbstractImageOperation::finishFromKJob(KJob* job) { finish(job->error() == KJob::NoError); } -void AbstractImageOperation::finishUndoJob() -{ - // Give QUndoStack time to update in case the undo is executed immediately - // (e.g. undo crop just sets the previous image) - QTimer::singleShot(0, document().data(), &Document::imageOperationCompleted); -} - void AbstractImageOperation::setText(const QString& text) { d->mText = text; } void AbstractImageOperation::redoAsDocumentJob(DocumentJob* job) { connect(job, SIGNAL(result(KJob*)), SLOT(finishFromKJob(KJob*))); document()->enqueueJob(job); } } // namespace diff --git a/lib/abstractimageoperation.h b/lib/abstractimageoperation.h index cad74c3d..51b09530 100644 --- a/lib/abstractimageoperation.h +++ b/lib/abstractimageoperation.h @@ -1,91 +1,89 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2007 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ABSTRACTIMAGEOPERATION_H #define ABSTRACTIMAGEOPERATION_H #include // Qt #include // KDE // Local #include class KJob; namespace Gwenview { struct AbstractImageOperationPrivate; /** * An operation to apply on a document. This class pushes internal instances of * QUndoCommand to the document undo stack, but it only does so if the * operation succeeded. * * Class inheriting from this class should: * - Implement redo() and call finish() or finishFromKJob() when done * - Implement undo() * - Define the operation/command text with setText() */ class GWENVIEWLIB_EXPORT AbstractImageOperation : public QObject { Q_OBJECT public: AbstractImageOperation(); virtual ~AbstractImageOperation(); void applyToDocument(Document::Ptr); Document::Ptr document() const; - void finishUndoJob(); - protected: virtual void redo() = 0; virtual void undo() {} void setText(const QString&); /** * Convenience method which can be called from redo() if the operation is * implemented as a job */ void redoAsDocumentJob(DocumentJob* job); protected Q_SLOTS: void finish(bool ok); /** * Convenience slot which call finish() correctly if job succeeded */ void finishFromKJob(KJob* job); private: AbstractImageOperationPrivate* const d; friend class ImageOperationCommand; }; } // namespace #endif /* ABSTRACTIMAGEOPERATION_H */ diff --git a/lib/crop/cropimageoperation.cpp b/lib/crop/cropimageoperation.cpp index 9618f3b2..128fd156 100644 --- a/lib/crop/cropimageoperation.cpp +++ b/lib/crop/cropimageoperation.cpp @@ -1,95 +1,95 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2007 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Self #include "cropimageoperation.h" // Qt #include // KDE #include #include // Local #include "document/document.h" #include "document/documentjob.h" #include "document/abstractdocumenteditor.h" namespace Gwenview { class CropJob : public ThreadedDocumentJob { public: CropJob(const QRect& rect) : mRect(rect) {} void threadedStart() Q_DECL_OVERRIDE { if (!checkDocumentEditor()) { return; } const QImage src = document()->image(); const QImage dst = src.copy(mRect); document()->editor()->setImage(dst); setError(NoError); } private: QRect mRect; }; struct CropImageOperationPrivate { QRect mRect; QImage mOriginalImage; }; CropImageOperation::CropImageOperation(const QRect& rect) : d(new CropImageOperationPrivate) { d->mRect = rect; setText(i18n("Crop")); } CropImageOperation::~CropImageOperation() { delete d; } void CropImageOperation::redo() { d->mOriginalImage = document()->image(); redoAsDocumentJob(new CropJob(d->mRect)); } void CropImageOperation::undo() { if (!document()->editor()) { qWarning() << "!document->editor()"; return; } document()->editor()->setImage(d->mOriginalImage); - finishUndoJob(); + finish(true); } } // namespace diff --git a/lib/redeyereduction/redeyereductionimageoperation.cpp b/lib/redeyereduction/redeyereductionimageoperation.cpp index 0667d554..76b84fc0 100644 --- a/lib/redeyereduction/redeyereductionimageoperation.cpp +++ b/lib/redeyereduction/redeyereductionimageoperation.cpp @@ -1,167 +1,167 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2007 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Self #include "redeyereductionimageoperation.h" // Stdc #include // Qt #include #include #include // KDE #include // Local #include "ramp.h" #include "document/document.h" #include "document/documentjob.h" #include "document/abstractdocumenteditor.h" #include "paintutils.h" namespace Gwenview { class RedEyeReductionJob : public ThreadedDocumentJob { public: RedEyeReductionJob(const QRectF& rectF) : mRectF(rectF) {} void threadedStart() Q_DECL_OVERRIDE { if (!checkDocumentEditor()) { return; } QImage img = document()->image(); RedEyeReductionImageOperation::apply(&img, mRectF); document()->editor()->setImage(img); setError(NoError); } private: QRectF mRectF; }; struct RedEyeReductionImageOperationPrivate { QRectF mRectF; QImage mOriginalImage; }; RedEyeReductionImageOperation::RedEyeReductionImageOperation(const QRectF& rectF) : d(new RedEyeReductionImageOperationPrivate) { d->mRectF = rectF; setText(i18n("RedEyeReduction")); } RedEyeReductionImageOperation::~RedEyeReductionImageOperation() { delete d; } void RedEyeReductionImageOperation::redo() { QImage img = document()->image(); QRect rect = PaintUtils::containingRect(d->mRectF); d->mOriginalImage = img.copy(rect); redoAsDocumentJob(new RedEyeReductionJob(d->mRectF)); } void RedEyeReductionImageOperation::undo() { if (!document()->editor()) { qWarning() << "!document->editor()"; return; } QImage img = document()->image(); { QPainter painter(&img); painter.setCompositionMode(QPainter::CompositionMode_Source); QRect rect = PaintUtils::containingRect(d->mRectF); painter.drawImage(rect.topLeft(), d->mOriginalImage); } document()->editor()->setImage(img); - finishUndoJob(); + finish(true); } /** * This code is inspired from code found in a Paint.net plugin: * http://paintdotnet.forumer.com/viewtopic.php?f=27&t=26193&p=205954&hilit=red+eye#p205954 */ inline qreal computeRedEyeAlpha(const QColor& src) { int hue, sat, value; src.getHsv(&hue, &sat, &value); qreal axs = 1.0; if (hue > 259) { static const Ramp ramp(30, 35, 0., 1.); axs = ramp(sat); } else { const Ramp ramp(hue * 2 + 29, hue * 2 + 40, 0., 1.); axs = ramp(sat); } return qBound(qreal(0.), src.alphaF() * axs, qreal(1.)); } void RedEyeReductionImageOperation::apply(QImage* img, const QRectF& rectF) { const QRect rect = PaintUtils::containingRect(rectF); const qreal radius = rectF.width() / 2; const qreal centerX = rectF.x() + radius; const qreal centerY = rectF.y() + radius; const Ramp radiusRamp( qMin(qreal(radius * 0.7), qreal(radius - 1)), radius, qreal(1.), qreal(0.)); uchar* line = img->scanLine(rect.top()) + rect.left() * 4; for (int y = rect.top(); y < rect.bottom(); ++y, line += img->bytesPerLine()) { QRgb* ptr = (QRgb*)line; for (int x = rect.left(); x < rect.right(); ++x, ++ptr) { const qreal currentRadius = sqrt(pow(y - centerY, 2) + pow(x - centerX, 2)); qreal alpha = radiusRamp(currentRadius); if (qFuzzyCompare(alpha, 0)) { continue; } const QColor src(*ptr); alpha *= computeRedEyeAlpha(src); int r = src.red(); int g = src.green(); int b = src.blue(); QColor dst; // Replace red with green, and blend according to alpha dst.setRed(int((1 - alpha) * r + alpha * g)); dst.setGreen(g); dst.setBlue(b); *ptr = dst.rgba(); } } } } // namespace diff --git a/lib/resize/resizeimageoperation.cpp b/lib/resize/resizeimageoperation.cpp index 9dbbd51c..0e23c093 100644 --- a/lib/resize/resizeimageoperation.cpp +++ b/lib/resize/resizeimageoperation.cpp @@ -1,95 +1,95 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2007 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Self #include "resizeimageoperation.h" // Qt #include #include // KDE #include // Local #include "document/abstractdocumenteditor.h" #include "document/document.h" #include "document/documentjob.h" namespace Gwenview { struct ResizeImageOperationPrivate { QSize mSize; QImage mOriginalImage; }; class ResizeJob : public ThreadedDocumentJob { public: ResizeJob(const QSize& size) : mSize(size) {} void threadedStart() Q_DECL_OVERRIDE { if (!checkDocumentEditor()) { return; } QImage image = document()->image(); image = image.scaled(mSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); document()->editor()->setImage(image); setError(NoError); } private: QSize mSize; }; ResizeImageOperation::ResizeImageOperation(const QSize& size) : d(new ResizeImageOperationPrivate) { d->mSize = size; setText(i18nc("(qtundo-format)", "Resize")); } ResizeImageOperation::~ResizeImageOperation() { delete d; } void ResizeImageOperation::redo() { d->mOriginalImage = document()->image(); redoAsDocumentJob(new ResizeJob(d->mSize)); } void ResizeImageOperation::undo() { if (!document()->editor()) { qWarning() << "!document->editor()"; return; } document()->editor()->setImage(d->mOriginalImage); - finishUndoJob(); + finish(true); } } // namespace diff --git a/lib/transformimageoperation.cpp b/lib/transformimageoperation.cpp index 3fc9710f..7f33b6f0 100644 --- a/lib/transformimageoperation.cpp +++ b/lib/transformimageoperation.cpp @@ -1,110 +1,108 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2007 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Self #include "transformimageoperation.h" // Qt // KDE #include // Local #include "document/abstractdocumenteditor.h" namespace Gwenview { struct TransformImageOperationPrivate { Orientation mOrientation; }; TransformJob::TransformJob(Orientation orientation) : mOrientation(orientation) { } void TransformJob::threadedStart() { if (!checkDocumentEditor()) { return; } document()->editor()->applyTransformation(mOrientation); setError(NoError); } TransformImageOperation::TransformImageOperation(Orientation orientation) : d(new TransformImageOperationPrivate) { d->mOrientation = orientation; switch (d->mOrientation) { case ROT_90: setText(i18nc("(qtundo-format)", "Rotate Right")); break; case ROT_270: setText(i18nc("(qtundo-format)", "Rotate Left")); break; case HFLIP: setText(i18nc("(qtundo-format)", "Mirror")); break; case VFLIP: setText(i18nc("(qtundo-format)", "Flip")); break; default: // We should not get there because the transformations listed above are // the only one available from the UI. Define a default text nevertheless. setText(i18nc("(qtundo-format)", "Transform")); break; } } TransformImageOperation::~TransformImageOperation() { delete d; } void TransformImageOperation::redo() { redoAsDocumentJob(new TransformJob(d->mOrientation)); } void TransformImageOperation::undo() { Orientation orientation; switch (d->mOrientation) { case ROT_90: orientation = ROT_270; break; case ROT_270: orientation = ROT_90; break; default: orientation = d->mOrientation; break; } - TransformJob* job = new TransformJob(orientation); - connect(job, &TransformJob::result, this, &AbstractImageOperation::finishUndoJob); - document()->enqueueJob(job); + redoAsDocumentJob(new TransformJob(orientation)); } } // namespace