diff --git a/app/imageopscontextmanageritem.cpp b/app/imageopscontextmanageritem.cpp index 569fff2a..3901cb36 100644 --- a/app/imageopscontextmanageritem.cpp +++ b/app/imageopscontextmanageritem.cpp @@ -1,307 +1,307 @@ // 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 "imageopscontextmanageritem.h" #include "dialogguard.h" // Qt #include #include #include // KDE #include #include #include #include // Local #include "viewmainpage.h" #include "gvcore.h" #include "mainwindow.h" #include "sidebar.h" #include #include #include #include #include #include #include #include #include #include namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) qDebug() << x #else #define LOG(x) ; #endif struct ImageOpsContextManagerItem::Private { ImageOpsContextManagerItem* q; MainWindow* mMainWindow; SideBarGroup* mGroup; QAction * mRotateLeftAction; QAction * mRotateRightAction; QAction * mMirrorAction; QAction * mFlipAction; QAction * mResizeAction; QAction * mCropAction; QAction * mRedEyeReductionAction; QList mActionList; void setupActions() { KActionCollection* actionCollection = mMainWindow->actionCollection(); KActionCategory* edit = new KActionCategory(i18nc("@title actions category - means actions changing image", "Edit"), actionCollection); mRotateLeftAction = edit->addAction("rotate_left", q, SLOT(rotateLeft())); mRotateLeftAction->setText(i18n("Rotate Left")); mRotateLeftAction->setToolTip(i18nc("@info:tooltip", "Rotate image to the left")); mRotateLeftAction->setIcon(QIcon::fromTheme("object-rotate-left")); actionCollection->setDefaultShortcut(mRotateLeftAction, Qt::CTRL + Qt::Key_L); mRotateRightAction = edit->addAction("rotate_right", q, SLOT(rotateRight())); mRotateRightAction->setText(i18n("Rotate Right")); mRotateRightAction->setToolTip(i18nc("@info:tooltip", "Rotate image to the right")); mRotateRightAction->setIcon(QIcon::fromTheme("object-rotate-right")); actionCollection->setDefaultShortcut(mRotateRightAction, Qt::CTRL + Qt::Key_R); mMirrorAction = edit->addAction("mirror", q, SLOT(mirror())); mMirrorAction->setText(i18n("Mirror")); mMirrorAction->setIcon(QIcon::fromTheme("object-flip-horizontal")); mFlipAction = edit->addAction("flip", q, SLOT(flip())); mFlipAction->setText(i18n("Flip")); mFlipAction->setIcon(QIcon::fromTheme("object-flip-vertical")); mResizeAction = edit->addAction("resize", q, SLOT(resizeImage())); mResizeAction->setText(i18n("Resize")); mResizeAction->setIcon(QIcon::fromTheme("transform-scale")); actionCollection->setDefaultShortcut(mResizeAction, Qt::SHIFT + Qt::Key_R); mCropAction = edit->addAction("crop", q, SLOT(crop())); mCropAction->setText(i18n("Crop")); mCropAction->setIcon(QIcon::fromTheme("transform-crop-and-resize")); actionCollection->setDefaultShortcut(mCropAction, Qt::SHIFT + Qt::Key_C); mRedEyeReductionAction = edit->addAction("red_eye_reduction", q, SLOT(startRedEyeReduction())); - mRedEyeReductionAction->setText(i18n("Red Eye Reduction")); + mRedEyeReductionAction->setText(i18n("Reduce Red Eye")); mRedEyeReductionAction->setIcon(QIcon::fromTheme("redeyes")); mActionList << mRotateLeftAction << mRotateRightAction << mMirrorAction << mFlipAction << mResizeAction << mCropAction << mRedEyeReductionAction ; } bool ensureEditable() { QUrl url = q->contextManager()->currentUrl(); Document::Ptr doc = DocumentFactory::instance()->load(url); doc->waitUntilLoaded(); if (doc->isEditable()) { return true; } KMessageBox::sorry( QApplication::activeWindow(), i18nc("@info", "Gwenview cannot edit this kind of image.") ); return false; } }; ImageOpsContextManagerItem::ImageOpsContextManagerItem(ContextManager* manager, MainWindow* mainWindow) : AbstractContextManagerItem(manager) , d(new Private) { d->q = this; d->mMainWindow = mainWindow; d->mGroup = new SideBarGroup(i18n("Image Operations")); setWidget(d->mGroup); EventWatcher::install(d->mGroup, QEvent::Show, this, SLOT(updateSideBarContent())); d->setupActions(); updateActions(); connect(contextManager(), SIGNAL(selectionChanged()), SLOT(updateActions())); connect(mainWindow, SIGNAL(viewModeChanged()), SLOT(updateActions())); connect(mainWindow->viewMainPage(), SIGNAL(completed()), SLOT(updateActions())); } ImageOpsContextManagerItem::~ImageOpsContextManagerItem() { delete d; } void ImageOpsContextManagerItem::updateSideBarContent() { if (!d->mGroup->isVisible()) { return; } d->mGroup->clear(); Q_FOREACH(QAction * action, d->mActionList) { if (action->isEnabled() && action->priority() != QAction::LowPriority) { d->mGroup->addAction(action); } } } void ImageOpsContextManagerItem::updateActions() { bool canModify = contextManager()->currentUrlIsRasterImage(); bool viewMainPageIsVisible = d->mMainWindow->viewMainPage()->isVisible(); if (!viewMainPageIsVisible) { // Since we only support image operations on one image for now, // disable actions if several images are selected and the document // view is not visible. if (contextManager()->selectedFileItemList().count() != 1) { canModify = false; } } d->mRotateLeftAction->setEnabled(canModify); d->mRotateRightAction->setEnabled(canModify); d->mMirrorAction->setEnabled(canModify); d->mFlipAction->setEnabled(canModify); d->mResizeAction->setEnabled(canModify); d->mCropAction->setEnabled(canModify && viewMainPageIsVisible); d->mRedEyeReductionAction->setEnabled(canModify && viewMainPageIsVisible); updateSideBarContent(); } void ImageOpsContextManagerItem::rotateLeft() { TransformImageOperation* op = new TransformImageOperation(ROT_270); applyImageOperation(op); } void ImageOpsContextManagerItem::rotateRight() { TransformImageOperation* op = new TransformImageOperation(ROT_90); applyImageOperation(op); } void ImageOpsContextManagerItem::mirror() { TransformImageOperation* op = new TransformImageOperation(HFLIP); applyImageOperation(op); } void ImageOpsContextManagerItem::flip() { TransformImageOperation* op = new TransformImageOperation(VFLIP); applyImageOperation(op); } void ImageOpsContextManagerItem::resizeImage() { if (!d->ensureEditable()) { return; } Document::Ptr doc = DocumentFactory::instance()->load(contextManager()->currentUrl()); doc->startLoadingFullImage(); DialogGuard dialog(d->mMainWindow); dialog->setOriginalSize(doc->size()); if (!dialog->exec()) { return; } ResizeImageOperation* op = new ResizeImageOperation(dialog->size()); applyImageOperation(op); } void ImageOpsContextManagerItem::crop() { if (!d->ensureEditable()) { return; } RasterImageView* imageView = d->mMainWindow->viewMainPage()->imageView(); if (!imageView) { qCritical() << "No ImageView available!"; return; } CropTool* tool = new CropTool(imageView); connect(tool, &CropTool::imageOperationRequested, this, &ImageOpsContextManagerItem::applyImageOperation); connect(tool, &CropTool::done, this, &ImageOpsContextManagerItem::restoreDefaultImageViewTool); d->mMainWindow->setDistractionFreeMode(true); imageView->setCurrentTool(tool); } void ImageOpsContextManagerItem::startRedEyeReduction() { if (!d->ensureEditable()) { return; } RasterImageView* view = d->mMainWindow->viewMainPage()->imageView(); if (!view) { qCritical() << "No RasterImageView available!"; return; } RedEyeReductionTool* tool = new RedEyeReductionTool(view); connect(tool, &RedEyeReductionTool::imageOperationRequested, this, &ImageOpsContextManagerItem::applyImageOperation); connect(tool, &RedEyeReductionTool::done, this, &ImageOpsContextManagerItem::restoreDefaultImageViewTool); d->mMainWindow->setDistractionFreeMode(true); view->setCurrentTool(tool); } void ImageOpsContextManagerItem::applyImageOperation(AbstractImageOperation* op) { // For now, we only support operations on one image QUrl url = contextManager()->currentUrl(); Document::Ptr doc = DocumentFactory::instance()->load(url); op->applyToDocument(doc); } void ImageOpsContextManagerItem::restoreDefaultImageViewTool() { RasterImageView* imageView = d->mMainWindow->viewMainPage()->imageView(); if (!imageView) { qCritical() << "No RasterImageView available!"; return; } AbstractRasterImageViewTool* tool = imageView->currentTool(); imageView->setCurrentTool(nullptr); tool->deleteLater(); d->mMainWindow->setDistractionFreeMode(false); } } // namespace diff --git a/lib/redeyereduction/redeyereductionimageoperation.cpp b/lib/redeyereduction/redeyereductionimageoperation.cpp index 482af005..5eb9147e 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() 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")); + setText(i18n("Reduce Red Eye")); } 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); 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