File indexing completed on 2025-01-05 03:51:41
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2017-06-15 0007 * Description : a tool to replace part of the image using another 0008 * 0009 * SPDX-FileCopyrightText: 2004-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2017 by Shaza Ismail Kaoud <shaza dot ismail dot k at gmail dot com> 0011 * SPDX-FileCopyrightText: 2019 by Ahmed Fathi <ahmed dot fathi dot abdelmageed at gmail dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "healingclonetool.h" 0018 0019 // C++ includes 0020 0021 #include <stack> 0022 0023 // Qt includes 0024 0025 #include <QGridLayout> 0026 #include <QPushButton> 0027 #include <QGroupBox> 0028 #include <QLabel> 0029 #include <QPoint> 0030 #include <QIcon> 0031 0032 // KDE includes 0033 0034 #include <kconfiggroup.h> 0035 #include <ksharedconfig.h> 0036 #include <klocalizedstring.h> 0037 0038 // Local includes 0039 0040 #include "dexpanderbox.h" 0041 #include "dnuminput.h" 0042 #include "editortoolsettings.h" 0043 #include "imageiface.h" 0044 #include "itempropertiestxtlabel.h" 0045 #include "healingclonetoolwidget.h" 0046 0047 namespace DigikamEditorHealingCloneToolPlugin 0048 { 0049 0050 class Q_DECL_HIDDEN HealingCloneTool::Private 0051 { 0052 0053 public: 0054 0055 explicit Private() 0056 : btnSize (QSize(50, 50)), 0057 iconSize (QSize(30, 30)), 0058 radiusInput (nullptr), 0059 blurPercent (nullptr), 0060 previewWidget (nullptr), 0061 gboxSettings (nullptr), 0062 srcButton (nullptr), 0063 lassoButton (nullptr), 0064 moveButton (nullptr), 0065 undoCloneButton (nullptr), 0066 redoCloneButton (nullptr), 0067 resetLassoPoint (true), 0068 insideLassoOperation(false) 0069 { 0070 } 0071 0072 static const QString configGroupName; 0073 static const QString configRadiusAdjustmentEntry; 0074 static const QString configBlurAdjustmentEntry; 0075 0076 const QSize btnSize; 0077 const QSize iconSize; 0078 0079 DIntNumInput* radiusInput; 0080 DDoubleNumInput* blurPercent; 0081 HealingCloneToolWidget* previewWidget; 0082 EditorToolSettings* gboxSettings; 0083 QPushButton* srcButton; 0084 QPushButton* lassoButton; 0085 QPushButton* moveButton; 0086 QPushButton* undoCloneButton; 0087 QPushButton* redoCloneButton; 0088 0089 DImg cloneImg; 0090 0091 std::stack<DImg> undoStack; 0092 std::stack<DImg> redoStack; 0093 0094 bool resetLassoPoint; 0095 bool insideLassoOperation; 0096 0097 QPoint previousLassoPoint; 0098 QPoint startLassoPoint; 0099 0100 std::vector<DColor> lassoColors; 0101 std::vector<QPoint> lassoPoints; 0102 QPolygon lassoPolygon; 0103 0104 std::vector<std::vector<bool>> lassoFlags; 0105 std::map<std::pair<int,int>, DColor> lassoColorsMap; 0106 }; 0107 0108 const QString HealingCloneTool::Private::configGroupName(QLatin1String("Healing Clone Tool")); 0109 const QString HealingCloneTool::Private::configRadiusAdjustmentEntry(QLatin1String("RadiusAdjustment")); 0110 const QString HealingCloneTool::Private::configBlurAdjustmentEntry(QLatin1String("BlurAdjustment")); 0111 0112 // -------------------------------------------------------- 0113 0114 HealingCloneTool::HealingCloneTool(QObject* const parent) 0115 : EditorTool(parent), 0116 d (new Private) 0117 { 0118 setObjectName(QLatin1String("healing clone")); 0119 setToolHelp(QLatin1String("healingclonetool.anchor")); 0120 0121 d->gboxSettings = new EditorToolSettings(nullptr); 0122 d->previewWidget = new HealingCloneToolWidget; 0123 refreshImage(); 0124 0125 d->previewWidget->setFocusPolicy(Qt::StrongFocus); 0126 setToolView(d->previewWidget); 0127 setPreviewModeMask(PreviewToolBar::NoPreviewMode); 0128 0129 // -------------------------------------------------------- 0130 0131 QLabel* const label = new QLabel(i18n("Brush Radius:")); 0132 d->radiusInput = new DIntNumInput(); 0133 d->radiusInput->setRange(0, 200, 1); 0134 d->radiusInput->setDefaultValue(50); 0135 d->radiusInput->setWhatsThis(i18n("A radius of 0 has no effect, " 0136 "1 and above determine the brush radius " 0137 "that determines the size of parts copied in the image. \nShortcut :: [ and ]")); 0138 d->radiusInput->setToolTip(i18n("A radius of 0 has no effect, " 0139 "1 and above determine the brush radius " 0140 "that determines the size of parts copied in the image. \nShortcut :: [ and ]")); 0141 0142 d->previewWidget->setBrushValue(d->radiusInput->value()); 0143 0144 // -------------------------------------------------------- 0145 0146 QLabel* const label2 = new QLabel(i18n("Radial Blur Percent:")); 0147 d->blurPercent = new DDoubleNumInput(); 0148 d->blurPercent->setRange(0, 100, 0.1); 0149 d->blurPercent->setDefaultValue(0); 0150 d->blurPercent->setWhatsThis(i18n("A percent of 0 has no effect, values " 0151 "above 0 represent a factor for mixing " 0152 "the destination color with source color " 0153 "this is done radially i.e. the inner part of " 0154 "the brush radius is totally from source and mixing " 0155 "with destination is done gradually till the outer part " 0156 "of the circle.")); 0157 0158 // -------------------------------------------------------- 0159 0160 d->srcButton = new QPushButton(); 0161 d->srcButton->setFixedSize(d->btnSize); 0162 d->srcButton->setIcon(QIcon::fromTheme(QLatin1String("crosshairs"))); 0163 d->srcButton->setIconSize(d->iconSize); 0164 d->srcButton->setWhatsThis(i18n("Select Source Point.\nShortcut: S")); 0165 d->srcButton->setToolTip(i18n("Select Source Point.\nShortcut: S")); 0166 0167 // -------------------------------------------------------- 0168 0169 d->lassoButton = new QPushButton(); 0170 d->lassoButton->setFixedSize(d->btnSize); 0171 d->lassoButton->setIcon(QIcon::fromTheme(QLatin1String("edit-select-lasso"))); 0172 d->lassoButton->setIconSize(d->iconSize); 0173 d->lassoButton->setWhatsThis(i18n("Polygon Selection With Lasso.\nShortcut: L\n" 0174 "To Continue polygon, either press L or double click\n" 0175 "To Cancel, press ESC")); 0176 d->lassoButton->setToolTip(i18n("Polygon Selection With Lasso.\nShortcut: L\n" 0177 "To Continue polygon, either press L or double click\n" 0178 "To Cancel, press ESC")); 0179 0180 // -------------------------------------------------------- 0181 0182 d->moveButton = new QPushButton(); 0183 d->moveButton->setFixedSize(d->btnSize); 0184 d->moveButton->setIcon(QIcon::fromTheme(QLatin1String("transform-browse"))); 0185 d->moveButton->setIconSize(d->iconSize); 0186 d->moveButton->setWhatsThis(i18n("Move Image.\nShortcut: M")); 0187 d->moveButton->setToolTip(i18n("Move Image.\nShortcut: M")); 0188 0189 // -------------------------------------------------------- 0190 0191 d->undoCloneButton = new QPushButton(); 0192 d->undoCloneButton->setFixedSize(d->btnSize); 0193 d->undoCloneButton->setIcon(QIcon::fromTheme(QLatin1String("edit-undo"))); 0194 d->undoCloneButton->setIconSize(d->iconSize); 0195 d->undoCloneButton->setWhatsThis(i18n("Undo clone operation.\nShortcut: CTRL+Z")); 0196 d->undoCloneButton->setToolTip(i18n("Undo clone operation.\nShortcut: CTRL+Z")); 0197 0198 // -------------------------------------------------------- 0199 0200 d->redoCloneButton = new QPushButton(); 0201 d->redoCloneButton->setFixedSize(d->btnSize); 0202 d->redoCloneButton->setIcon(QIcon::fromTheme(QLatin1String("edit-redo"))); 0203 d->redoCloneButton->setIconSize(d->iconSize); 0204 d->redoCloneButton->setWhatsThis(i18n("Redo clone operation.\nShortcut: CTRL+Y")); 0205 d->redoCloneButton->setToolTip(i18n("Redo clone operation.\nShortcut: CTRL+Y")); 0206 0207 // -------------------------------------------------------- 0208 0209 QString help = i18n("<p>How To Use:<br/><br/>" 0210 "* Press <b>s</b> to switch to source-selection mode, and select source point on image.<br/>" 0211 "* Press <b>s</b> again and start cloning.<br/>" 0212 "* Press <b>[</b> and <b>]</b> to change brush size.<br/>" 0213 "* Press <b>CTRL+Mousewheel</b> to zoom in/out.<br/>" 0214 "* Press <b>m</b> to pan the image if image is larger than viewport.<br/>" 0215 "* Press <b>l</b> to start lasso mode. Start drawing lasso boundary either " 0216 "continuously or discretely, then double-click or press l again to close the boundary.<br/>" 0217 "* Inside lasso mode, you can clone only inside the lasso region.</p>"); 0218 0219 DTextBrowser* const label3 = new DTextBrowser(help); 0220 label3->setLinesNumber(20); 0221 0222 // Tool Buttons 0223 0224 const int spacing = d->gboxSettings->spacingHint(); 0225 QGridLayout* const grid = new QGridLayout(); 0226 QGroupBox* const iconsGroupBox = new QGroupBox(); 0227 QHBoxLayout* const iconsHBox = new QHBoxLayout(); 0228 iconsHBox->setSpacing(0); 0229 iconsHBox->addWidget(d->srcButton); 0230 iconsHBox->addWidget(d->lassoButton); 0231 iconsHBox->addWidget(d->moveButton); 0232 iconsHBox->addWidget(d->undoCloneButton); 0233 iconsHBox->addWidget(d->redoCloneButton); 0234 iconsGroupBox->setLayout(iconsHBox); 0235 0236 // --- 0237 0238 grid->addWidget(iconsGroupBox); 0239 grid->addWidget(new DLineWidget(Qt::Horizontal, d->gboxSettings->plainPage()), 3, 0, 1, 2); 0240 grid->addWidget(label, 4, 0, 1, 2); 0241 grid->addWidget(d->radiusInput, 5, 0, 1, 2); 0242 grid->addWidget(label2, 6, 0, 1, 2); 0243 grid->addWidget(d->blurPercent, 7, 0, 1, 2); 0244 grid->addWidget(new DLineWidget(Qt::Horizontal, d->gboxSettings->plainPage()), 8, 0, 1, 2); 0245 grid->addWidget(label3, 9, 0, 1, 2); 0246 grid->setRowStretch(10, 10); 0247 grid->setContentsMargins(spacing, spacing, spacing, spacing); 0248 grid->setSpacing(spacing); 0249 d->gboxSettings->plainPage()->setLayout(grid); 0250 0251 // -------------------------------------------------------- 0252 0253 setToolSettings(d->gboxSettings); 0254 0255 // -------------------------------------------------------- 0256 0257 d->lassoColors.push_back(DColor(Qt::red)); 0258 d->lassoColors.push_back(DColor(Qt::white)); 0259 d->lassoColors.push_back(DColor(Qt::black)); 0260 d->lassoColors.push_back(DColor(Qt::yellow)); 0261 d->lassoColors.push_back(DColor(Qt::blue)); 0262 d->lassoColors.push_back(DColor(Qt::yellow)); 0263 0264 // -------------------------------------------------------- 0265 0266 connect(d->radiusInput, SIGNAL(valueChanged(int)), 0267 this, SLOT(slotRadiusChanged(int))); 0268 0269 connect(d->srcButton, SIGNAL(clicked(bool)), 0270 d->previewWidget, SLOT(slotSetSourcePoint())); 0271 0272 connect(d->moveButton, SIGNAL(clicked(bool)), 0273 d->previewWidget, SLOT(slotMoveImage())); 0274 0275 connect(d->lassoButton, SIGNAL(clicked(bool)), 0276 d->previewWidget, SLOT(slotLassoSelect())); 0277 0278 connect(d->undoCloneButton, SIGNAL(clicked(bool)), 0279 this, SLOT(slotUndoClone())); 0280 0281 connect(d->redoCloneButton, SIGNAL(clicked(bool)), 0282 this, SLOT(slotRedoClone())); 0283 0284 connect(d->previewWidget, SIGNAL(signalClone(QPoint,QPoint)), 0285 this, SLOT(slotReplace(QPoint,QPoint))); 0286 0287 connect(d->previewWidget, SIGNAL(signalLasso(QPoint)), 0288 this, SLOT(slotLasso(QPoint))); 0289 0290 connect(d->previewWidget, SIGNAL(signalResetLassoPoint()), 0291 this, SLOT(slotResetLassoPoints())); 0292 0293 connect(d->previewWidget, SIGNAL(signalContinuePolygon()), 0294 this, SLOT(slotContinuePolygon())); 0295 0296 connect(d->previewWidget, SIGNAL(signalIncreaseBrushRadius()), 0297 this, SLOT(slotIncreaseBrushRadius())); 0298 0299 connect(d->previewWidget, SIGNAL(signalDecreaseBrushRadius()), 0300 this, SLOT(slotDecreaseBrushRadius())); 0301 0302 // Undo - redo 0303 0304 connect(d->previewWidget, SIGNAL(signalPushToUndoStack()), 0305 this, SLOT(slotPushToUndoStack())); 0306 0307 connect(d->previewWidget, SIGNAL(signalUndoClone()), 0308 this, SLOT(slotUndoClone())); 0309 0310 connect(d->previewWidget, SIGNAL(signalRedoClone()), 0311 this, SLOT(slotRedoClone())); 0312 0313 d->cloneImg = d->previewWidget->getOriginalImage(); 0314 } 0315 0316 HealingCloneTool::~HealingCloneTool() 0317 { 0318 delete d; 0319 } 0320 0321 void HealingCloneTool::readSettings() 0322 { 0323 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0324 KConfigGroup group = config->group(d->configGroupName); 0325 d->radiusInput->setValue(group.readEntry(d->configRadiusAdjustmentEntry, d->radiusInput->defaultValue())); 0326 d->blurPercent->setValue(group.readEntry(d->configBlurAdjustmentEntry, d->blurPercent->defaultValue())); 0327 } 0328 0329 void HealingCloneTool::writeSettings() 0330 { 0331 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0332 KConfigGroup group = config->group(d->configGroupName); 0333 group.writeEntry(d->configRadiusAdjustmentEntry, d->radiusInput->value()); 0334 group.writeEntry(d->configBlurAdjustmentEntry, d->blurPercent->value()); 0335 config->sync(); 0336 } 0337 0338 void HealingCloneTool::finalRendering() 0339 { 0340 ImageIface iface; 0341 FilterAction action(QLatin1String("digikam:healingCloneTool"), 1); 0342 iface.setOriginal(i18n("healingClone"), action, d->cloneImg); 0343 } 0344 0345 void HealingCloneTool::slotResetSettings() 0346 { 0347 d->radiusInput->blockSignals(true); 0348 d->radiusInput->slotReset(); 0349 d->radiusInput->blockSignals(false); 0350 } 0351 0352 void HealingCloneTool::slotResized() 0353 { 0354 toolView()->update(); 0355 } 0356 0357 void HealingCloneTool::slotReplace(const QPoint& srcPoint, const QPoint& dstPoint) 0358 { 0359 clone(&d->cloneImg, srcPoint, dstPoint); 0360 } 0361 0362 void HealingCloneTool::slotRadiusChanged(int r) 0363 { 0364 d->previewWidget->setBrushValue(r); 0365 } 0366 0367 void HealingCloneTool::clone(DImg* const img, 0368 const QPoint& srcPoint, 0369 const QPoint& dstPoint) 0370 { 0371 double blurPercent = d->blurPercent->value() / 100; 0372 int radius = d->radiusInput->value(); 0373 0374 for (int i = -1 * radius ; i < radius ; ++i) 0375 { 0376 for (int j = -1 * radius ; j < radius ; ++j) 0377 { 0378 int rPercent = (i * i) + (j * j); 0379 0380 if (rPercent < (radius * radius)) // Check for inside the circle 0381 { 0382 if ((srcPoint.x() + i < 0) || (srcPoint.x() + i >= (int)img->width()) || 0383 (srcPoint.y() + j < 0) || (srcPoint.y() + j >= (int)img->height()) || 0384 (dstPoint.x() + i < 0) || (dstPoint.x() + i >= (int)img->width()) || 0385 (dstPoint.y() + j < 0) || (dstPoint.y() + j >= (int)img->height())) 0386 { 0387 continue; 0388 } 0389 0390 DColor cSrc = img->getPixelColor(srcPoint.x() + i, srcPoint.y() + j); 0391 0392 if (d->insideLassoOperation && !d->lassoPoints.empty()) 0393 { 0394 if (d->lassoFlags.at(dstPoint.x() + i).at(dstPoint.y() + j)) 0395 { 0396 continue; 0397 } 0398 0399 bool isInside = d->lassoPolygon.containsPoint(QPoint(dstPoint.x() + i, 0400 dstPoint.y() + j), 0401 Qt::OddEvenFill); 0402 0403 if (!isInside) 0404 { 0405 continue; 0406 } 0407 0408 if (d->lassoFlags.at(srcPoint.x() + i).at(srcPoint.y() + j)) 0409 { 0410 cSrc = d->lassoColorsMap[std::make_pair(srcPoint.x() + i, 0411 srcPoint.y() + j)]; 0412 } 0413 } 0414 0415 double rP = blurPercent * rPercent / (radius * radius); 0416 0417 DColor cDst = img->getPixelColor(dstPoint.x() + i, dstPoint.y() + j); 0418 cSrc.multiply(1 - rP); 0419 cDst.multiply(rP); 0420 cSrc.blendAdd(cDst); 0421 img->setPixelColor(dstPoint.x() + i, dstPoint.y() + j, cSrc); 0422 d->previewWidget->setCloneVectorChanged(true); 0423 } 0424 } 0425 } 0426 0427 d->previewWidget->updateImage(*img); 0428 } 0429 0430 void HealingCloneTool::updateLasso(const std::vector<QPoint>& points) 0431 { 0432 uint radius = 5; 0433 static uint colorCounter = 0; 0434 0435 Q_FOREACH (const QPoint& p, points) 0436 { 0437 for (uint i = 0 ; i < radius ; ++i) 0438 { 0439 for (uint j = 0 ; j < radius ; ++j) 0440 { 0441 uint x_shifted = p.x() + i; 0442 uint y_shifted = p.y() + j; 0443 DColor c = d->cloneImg.getPixelColor(x_shifted, y_shifted); 0444 0445 d->lassoColorsMap.insert(std::make_pair(std::make_pair(x_shifted, y_shifted), c)); 0446 d->cloneImg.setPixelColor(x_shifted, y_shifted, d->lassoColors[(colorCounter) % d->lassoColors.size()]); 0447 d->lassoFlags.at(x_shifted).at(y_shifted) = true; 0448 colorCounter++; 0449 } 0450 } 0451 } 0452 0453 d->previewWidget->updateImage(d->cloneImg); 0454 } 0455 0456 void HealingCloneTool::slotLasso(const QPoint& dst) 0457 { 0458 if (d->resetLassoPoint) 0459 { 0460 d->previousLassoPoint = dst; 0461 d->resetLassoPoint = false; 0462 d->startLassoPoint = dst; 0463 } 0464 0465 std::vector<QPoint> points = interpolate(d->previousLassoPoint, dst); 0466 d->lassoPoints.push_back(dst); 0467 d->previousLassoPoint = dst; 0468 updateLasso(points); 0469 d->previewWidget->setIsLassoPointsVectorEmpty(d->lassoPoints.empty()); 0470 } 0471 0472 std::vector<QPoint> HealingCloneTool::interpolate(const QPoint& start, const QPoint& end) 0473 { 0474 std::vector<QPoint> points; 0475 points.push_back(start); 0476 QPointF distanceVec = QPoint(end.x()-start.x(), end.y() - start.y()); 0477 double distance = sqrt(distanceVec.x() * distanceVec.x() + distanceVec.y() * distanceVec.y()); 0478 0479 // Creating a unit vector 0480 0481 distanceVec.setX(distanceVec.x() / distance); 0482 distanceVec.setY(distanceVec.y() / distance); 0483 int steps = (int) distance; 0484 0485 for (int i = 0 ; i < steps ; ++i) 0486 { 0487 points.push_back(QPoint(start.x() + i * distanceVec.x(), 0488 start.y() + i * distanceVec.y())); 0489 } 0490 0491 points.push_back(end); 0492 0493 return points; 0494 } 0495 0496 void HealingCloneTool::removeLassoPixels() 0497 { 0498 std::map<std::pair<int,int>, DColor>::iterator it; 0499 0500 for (it = d->lassoColorsMap.begin() ; it != d->lassoColorsMap.end() ; ++it) 0501 { 0502 std::pair<int,int> xy = it->first; 0503 DColor color = it->second; 0504 d->cloneImg.setPixelColor(xy.first, xy.second, color); 0505 } 0506 0507 d->previewWidget->updateImage(d->cloneImg); 0508 } 0509 0510 void HealingCloneTool::redrawLassoPixels() 0511 { 0512 int colorCounter = 0; 0513 std::map<std::pair<int,int>, DColor>::iterator it; 0514 0515 for (it = d->lassoColorsMap.begin() ; it != d->lassoColorsMap.end() ; ++it) 0516 { 0517 colorCounter++; 0518 DColor color = d->lassoColors[(colorCounter) % d->lassoColors.size()]; 0519 std::pair<int,int> xy = it->first; 0520 d->cloneImg.setPixelColor(xy.first, xy.second, color); 0521 } 0522 0523 d->previewWidget->updateImage(d->cloneImg); 0524 } 0525 0526 void HealingCloneTool::slotResetLassoPoints() 0527 { 0528 removeLassoPixels(); 0529 d->resetLassoPoint = true; 0530 d->lassoPoints.clear(); 0531 d->insideLassoOperation = true; 0532 d->lassoPolygon.clear(); 0533 d->lassoColorsMap.clear(); 0534 initializeLassoFlags(); 0535 d->previewWidget->setIsLassoPointsVectorEmpty(d->lassoPoints.empty()); 0536 } 0537 0538 void HealingCloneTool::slotContinuePolygon() 0539 { 0540 if (d->lassoPoints.empty()) 0541 { 0542 return; 0543 } 0544 0545 const QPoint& start = d->previousLassoPoint; 0546 const QPoint& end = d->startLassoPoint; 0547 std::vector<QPoint> points = interpolate(start, end); 0548 updateLasso(points); 0549 0550 d->lassoPoints.push_back(start); 0551 0552 QVector<QPoint> polygon; 0553 0554 Q_FOREACH (const QPoint& point, d->lassoPoints) 0555 { 0556 polygon.append(point); 0557 } 0558 0559 d->lassoPolygon = QPolygon(polygon); 0560 } 0561 0562 void HealingCloneTool::slotIncreaseBrushRadius() 0563 { 0564 int size = d->radiusInput->value(); 0565 d->radiusInput->setValue(size + 1); 0566 } 0567 0568 void HealingCloneTool::slotDecreaseBrushRadius() 0569 { 0570 int size = d->radiusInput->value(); 0571 d->radiusInput->setValue(size - 1); 0572 } 0573 0574 void HealingCloneTool::initializeLassoFlags() 0575 { 0576 int w = d->cloneImg.width(); 0577 int h = d->cloneImg.height(); 0578 d->lassoFlags.resize(w); 0579 0580 for (int i = 0 ; i < w ; ++i) 0581 { 0582 d->lassoFlags.at(i).resize(h); 0583 } 0584 0585 for (int i = 0 ; i < w ; ++i) 0586 { 0587 for (int j = 0 ; j < h ; ++j) 0588 { 0589 d->lassoFlags.at(i).at(j) = false; 0590 } 0591 } 0592 } 0593 0594 void HealingCloneTool::slotPushToUndoStack() 0595 { 0596 d->redoStack = std::stack<DImg>(); 0597 removeLassoPixels(); 0598 d->undoStack.push(d->previewWidget->getOriginalImage()); 0599 redrawLassoPixels(); 0600 } 0601 0602 void HealingCloneTool::slotUndoClone() 0603 { 0604 if (d->undoStack.empty()) 0605 { 0606 return; 0607 } 0608 0609 removeLassoPixels(); 0610 d->redoStack.push(d->previewWidget->getOriginalImage()); 0611 d->cloneImg = d->undoStack.top(); 0612 d->undoStack.pop(); 0613 d->previewWidget->updateImage(d->cloneImg); 0614 redrawLassoPixels(); 0615 } 0616 0617 void HealingCloneTool::slotRedoClone() 0618 { 0619 /* 0620 slotResetLassoPoints(); 0621 */ 0622 if (d->redoStack.empty()) 0623 { 0624 return; 0625 } 0626 0627 removeLassoPixels(); 0628 d->undoStack.push(d->previewWidget->getOriginalImage()); 0629 0630 d->cloneImg = d->redoStack.top(); 0631 d->redoStack.pop(); 0632 d->previewWidget->updateImage(d->cloneImg); 0633 redrawLassoPixels(); 0634 } 0635 0636 void HealingCloneTool::refreshImage() 0637 { 0638 ImageRegionWidget* const wgt = dynamic_cast<ImageRegionWidget*>(d->previewWidget); 0639 0640 if (wgt) 0641 { 0642 QRectF test = wgt->sceneRect(); 0643 ImageRegionItem* const item = dynamic_cast<ImageRegionItem*>(wgt->item()); 0644 0645 if (item) 0646 { 0647 int w = item->boundingRect().width(); 0648 int h = item->boundingRect().height(); 0649 0650 test.setWidth(10); 0651 test.setHeight(10); 0652 wgt->fitInView(test, Qt::KeepAspectRatio); 0653 0654 test.setWidth(w); 0655 test.setHeight(h); 0656 wgt->fitInView(test, Qt::KeepAspectRatio); 0657 } 0658 } 0659 } 0660 0661 } // namespace DigikamEditorHealingCloneToolPlugin 0662 0663 #include "moc_healingclonetool.cpp"