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"