diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt --- a/libs/image/CMakeLists.txt +++ b/libs/image/CMakeLists.txt @@ -244,7 +244,6 @@ kis_liquify_transform_worker.cpp kis_green_coordinates_math.cpp kis_transparency_mask.cc - kis_inpaint_mask.cpp kis_undo_adapter.cpp kis_macro_based_undo_store.cpp kis_surrogate_undo_adapter.cpp diff --git a/libs/image/kis_inpaint_mask.h b/libs/image/kis_inpaint_mask.h deleted file mode 100644 --- a/libs/image/kis_inpaint_mask.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2017 Eugene Ingerman - * - * 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 _KIS_INPAINT_MASK_ -#define _KIS_INPAINT_MASK_ - -#include "kis_types.h" -#include "kis_transparency_mask.h" - -class QRect; - -/** - * A inpaint mask is a single channel mask that works with inpaint operation to denote area affected by inpaint operation. - * - */ -class KRITAIMAGE_EXPORT KisInpaintMask : public KisTransparencyMask -{ - Q_OBJECT - -public: - - KisInpaintMask(); - KisInpaintMask(const KisInpaintMask& rhs); - virtual ~KisInpaintMask(); - - KisNodeSP clone() const - { - return KisNodeSP(new KisInpaintMask(*this)); - } - - QRect decorateRect(KisPaintDeviceSP &src, KisPaintDeviceSP &dst, - const QRect & rc, - PositionToFilthy maskPos) const; -}; - -#endif //_KIS_INPAINT_MASK_ diff --git a/libs/image/kis_inpaint_mask.cpp b/libs/image/kis_inpaint_mask.cpp deleted file mode 100644 --- a/libs/image/kis_inpaint_mask.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2017 Eugene Ingerman - * - * 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. - */ - -#include "kis_inpaint_mask.h" - -#include "kis_debug.h" - -#include -#include -#include -#include -#include -#include "kis_paint_device.h" -#include "kis_painter.h" -#include "kis_node_visitor.h" -#include "kis_processing_visitor.h" -#include "KoColorSpaceRegistry.h" - -KisInpaintMask::KisInpaintMask() - : KisTransparencyMask() -{ -} - -KisInpaintMask::KisInpaintMask(const KisInpaintMask& rhs) - : KisTransparencyMask(rhs) -{ -} - -KisInpaintMask::~KisInpaintMask() -{ -} - -QRect KisInpaintMask::decorateRect(KisPaintDeviceSP &src, - KisPaintDeviceSP &dst, - const QRect & rc, - PositionToFilthy maskPos) const -{ - Q_UNUSED(maskPos); - KIS_ASSERT(dst != src); - - if (src != dst) { - KisPainter::copyAreaOptimized(rc.topLeft(), src, dst, rc); - src->fill(rc, KoColor(Qt::magenta, src->colorSpace())); - } - - return rc; -} - - diff --git a/libs/ui/kis_mask_manager.cc b/libs/ui/kis_mask_manager.cc --- a/libs/ui/kis_mask_manager.cc +++ b/libs/ui/kis_mask_manager.cc @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include "dialogs/kis_dlg_adjustment_layer.h" diff --git a/libs/ui/kis_paintop_box.cc b/libs/ui/kis_paintop_box.cc --- a/libs/ui/kis_paintop_box.cc +++ b/libs/ui/kis_paintop_box.cc @@ -1097,6 +1097,13 @@ setWidgetState(DISABLE_PRESETS | DISABLE_SIZE | DISABLE_FLOW); m_presetsEnabled = false; } + + if (flags & KisTool::FLAG_USES_CUSTOM_SIZE) { + setWidgetState(ENABLE_SIZE | ENABLE_FLOW); + } else { + setWidgetState(DISABLE_SIZE | DISABLE_FLOW); + } + } else setWidgetState(DISABLE_ALL); } diff --git a/libs/ui/tool/kis_tool.h b/libs/ui/tool/kis_tool.h --- a/libs/ui/tool/kis_tool.h +++ b/libs/ui/tool/kis_tool.h @@ -85,7 +85,7 @@ Q_PROPERTY(bool isActive READ isActive NOTIFY isActiveChanged) public: - enum { FLAG_USES_CUSTOM_PRESET=0x01, FLAG_USES_CUSTOM_COMPOSITEOP=0x02 }; + enum { FLAG_USES_CUSTOM_PRESET=0x01, FLAG_USES_CUSTOM_COMPOSITEOP=0x02, FLAG_USES_CUSTOM_SIZE=0x04 }; KisTool(KoCanvasBase * canvas, const QCursor & cursor); virtual ~KisTool(); diff --git a/plugins/tools/tool_smart_patch/kis_inpaint.cpp b/plugins/tools/tool_smart_patch/kis_inpaint.cpp --- a/plugins/tools/tool_smart_patch/kis_inpaint.cpp +++ b/plugins/tools/tool_smart_patch/kis_inpaint.cpp @@ -53,10 +53,8 @@ #include "KoColorSpaceTraits.h" const int MAX_DIST = 65535; -const quint8 MASK_SET = 0; -const quint8 MASK_CLEAR = 255; - -void patchImage(KisPaintDeviceSP, KisPaintDeviceSP, int radius); +const quint8 MASK_SET = 255; +const quint8 MASK_CLEAR = 0; class MaskedImage; //forward decl for the forward decl below template float distance_impl(const MaskedImage& my, int x, int y, const MaskedImage& other, int xo, int yo); @@ -152,7 +150,6 @@ void saveToDevice(KisPaintDeviceSP outDev, QRect rect) { - Q_ASSERT(outDev->colorSpace()->pixelSize() == (quint32) m_pixelSize); outDev->writeBytes(m_data, rect); } @@ -226,31 +223,25 @@ ImageData imageData; - void cacheImageSize(KisPaintDeviceSP imageDev) + void cacheImage(KisPaintDeviceSP imageDev, QRect rect) { - imageSize = imageDev->exactBounds(); - } - - void cacheImage(KisPaintDeviceSP imageDev) - { - Q_ASSERT(!imageSize.isEmpty() && imageSize.isValid()); cs = imageDev->colorSpace(); nChannels = cs->channelCount(); - imageData.Init(imageDev, imageSize); + imageData.Init(imageDev, rect); + imageSize = rect; } - void cacheMask(KisPaintDeviceSP maskDev) + void cacheMask(KisPaintDeviceSP maskDev, QRect rect) { - Q_ASSERT(!imageSize.isEmpty() && imageSize.isValid()); Q_ASSERT(maskDev->colorSpace()->pixelSize() == 1); csMask = maskDev->colorSpace(); - maskData.Init(maskDev, imageSize); + maskData.Init(maskDev, rect); //hard threshold for the initial mask //may be optional. needs testing std::for_each(maskData.data(), maskData.data() + maskData.num_bytes(), [](quint8 & v) { - v = (v < MASK_CLEAR) ? MASK_SET : MASK_CLEAR; + v = (v > MASK_CLEAR) ? MASK_SET : MASK_CLEAR; }); } @@ -275,11 +266,10 @@ std::fill(maskData.data(), maskData.data() + maskData.num_bytes(), MASK_CLEAR); } - void initialize(KisPaintDeviceSP _imageDev, KisPaintDeviceSP _maskDev) + void initialize(KisPaintDeviceSP _imageDev, KisPaintDeviceSP _maskDev, QRect _maskRect) { - cacheImageSize(_imageDev); - cacheImage(_imageDev); - cacheMask(_maskDev); + cacheImage(_imageDev, _maskRect); + cacheMask(_maskDev, _maskRect); //distance function is the only that needs to know the type //For performance reasons we can't use functions provided by color space @@ -301,10 +291,9 @@ distance = &distance_impl; } - MaskedImage(KisPaintDeviceSP _imageDev, KisPaintDeviceSP _maskDev) + MaskedImage(KisPaintDeviceSP _imageDev, KisPaintDeviceSP _maskDev, QRect _maskRect) { - initialize(_imageDev, _maskDev); -// DebugDump("Initialize"); + initialize(_imageDev, _maskDev, _maskRect); } void downsample2x(void) @@ -402,14 +391,14 @@ int countMasked(void) { int count = std::count_if(maskData.data(), maskData.data() + maskData.num_elements(), [](quint8 v) { - return v < MASK_CLEAR; + return v > MASK_CLEAR; }); return count; } inline bool isMasked(int x, int y) { - return (*maskData(x, y) < MASK_CLEAR); + return (*maskData(x, y) > MASK_CLEAR); } //returns true if the patch contains a masked pixel @@ -771,9 +760,9 @@ public: - Inpaint(KisPaintDeviceSP dev, KisPaintDeviceSP devMask, int _radius) + Inpaint(KisPaintDeviceSP dev, KisPaintDeviceSP devMask, int _radius, QRect maskRect) { - initial = new MaskedImage(dev, devMask); + initial = new MaskedImage(dev, devMask, maskRect); radius = _radius; devCache = dev; } @@ -978,16 +967,12 @@ QRect getMaskBoundingBox(KisPaintDeviceSP maskDev) { - KoColor defaultMaskPixel = maskDev->defaultPixel(); - maskDev->setDefaultPixel(KoColor(Qt::white, maskDev->colorSpace())); QRect maskRect = maskDev->nonDefaultPixelArea(); - maskDev->setDefaultPixel(defaultMaskPixel); - return maskRect; } -QRect patchImage(KisPaintDeviceSP imageDev, KisPaintDeviceSP maskDev, int patchRadius, int accuracy) +QRect patchImage(const KisPaintDeviceSP imageDev, const KisPaintDeviceSP maskDev, int patchRadius, int accuracy) { QRect maskRect = getMaskBoundingBox(maskDev); QRect imageRect = imageDev->exactBounds(); @@ -998,13 +983,8 @@ maskRect.adjust(-dx, -dy, dx, dy); maskRect = maskRect.intersected(imageRect); - KisPaintDeviceSP tempImageDev = new KisPaintDevice(imageDev->colorSpace()); - KisPaintDeviceSP tempMaskDev = new KisPaintDevice(maskDev->colorSpace()); - tempImageDev->makeCloneFrom(imageDev, maskRect); - tempMaskDev->makeCloneFrom(maskDev, maskRect); - if (!maskRect.isEmpty()) { - Inpaint inpaint(tempImageDev, tempMaskDev, patchRadius); + Inpaint inpaint(imageDev, maskDev, patchRadius, maskRect); MaskedImageSP output = inpaint.patch(); output->toPaintDevice(imageDev, maskRect); } diff --git a/plugins/tools/tool_smart_patch/kis_tool_smart_patch.h b/plugins/tools/tool_smart_patch/kis_tool_smart_patch.h --- a/plugins/tools/tool_smart_patch/kis_tool_smart_patch.h +++ b/plugins/tools/tool_smart_patch/kis_tool_smart_patch.h @@ -20,7 +20,9 @@ #define KIS_TOOL_SMART_PATCH_H_ #include -#include "kis_tool_freehand.h" +#include + +#include "kis_tool_paint.h" #include "KoToolFactoryBase.h" @@ -34,42 +36,48 @@ class KActionCollection; class KoCanvasBase; +class KisPaintInformation; +class KisSpacingInfomation; + -class KisToolSmartPatch : public KisToolFreehand +class KisToolSmartPatch : public KisToolPaint { Q_OBJECT public: KisToolSmartPatch(KoCanvasBase * canvas); virtual ~KisToolSmartPatch(); - QWidget * createOptionWidget(); + QWidget * createOptionWidget() override; - void activatePrimaryAction(); - void deactivatePrimaryAction(); + void activatePrimaryAction() override; + void deactivatePrimaryAction() override; - void beginPrimaryAction(KoPointerEvent *event); - void continuePrimaryAction(KoPointerEvent *event); - void endPrimaryAction(KoPointerEvent *event); + void beginPrimaryAction(KoPointerEvent *event) override; + void continuePrimaryAction(KoPointerEvent *event) override; + void endPrimaryAction(KoPointerEvent *event) override; + + void paint(QPainter &painter, const KoViewConverter &converter) override; + int flags() const override { return KisTool::FLAG_USES_CUSTOM_SIZE; } protected Q_SLOTS: void resetCursorStyle(); public Q_SLOTS: - virtual void activate(ToolActivation toolActivation, const QSet &shapes); + void activate(ToolActivation toolActivation, const QSet &shapes); void deactivate(); -Q_SIGNALS: - private: - bool canCreateInpaintMask() const; - QRect inpaintImage(KisPaintDeviceSP maskDev, KisPaintDeviceSP imageDev); + //QRect inpaintImage(KisPaintDeviceSP maskDev, KisPaintDeviceSP imageDev); + QPainterPath getBrushOutlinePath(const QPointF &documentPos, const KoPointerEvent *event); + QPainterPath brushOutline(); + void requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event) override; private: struct Private; + class InpaintCommand; const QScopedPointer m_d; - void createInpaintMask(); - void deleteInpaintMask(); + void addMaskPath(KoPointerEvent *event); }; diff --git a/plugins/tools/tool_smart_patch/kis_tool_smart_patch.cpp b/plugins/tools/tool_smart_patch/kis_tool_smart_patch.cpp --- a/plugins/tools/tool_smart_patch/kis_tool_smart_patch.cpp +++ b/plugins/tools/tool_smart_patch/kis_tool_smart_patch.cpp @@ -19,209 +19,244 @@ #include "kis_tool_smart_patch.h" #include "QApplication" +#include "QPainterPath" #include -#include - +#include #include #include "kis_canvas2.h" #include "kis_cursor.h" -#include "kis_config.h" +#include "kis_painter.h" +#include "kis_paintop_preset.h" + #include "kundo2magicstring.h" +#include "kundo2stack.h" +#include "kis_transaction_based_command.h" +#include "kis_transaction.h" + +#include "kis_processing_applicator.h" +#include "kis_datamanager.h" -#include "KoProperties.h" #include "KoColorSpaceRegistry.h" -#include "KoShapeController.h" -#include "KoDocumentResourceManager.h" -#include "kis_node_manager.h" -#include "kis_cursor.h" #include "kis_tool_smart_patch_options_widget.h" #include "libs/image/kis_paint_device_debug_utils.h" -#include "kis_resources_snapshot.h" -#include "kis_layer.h" -#include "kis_transaction.h" #include "kis_paint_layer.h" -#include "kis_inpaint_mask.h" - QRect patchImage(KisPaintDeviceSP imageDev, KisPaintDeviceSP maskDev, int radius, int accuracy); +class KisToolSmartPatch::InpaintCommand : public KisTransactionBasedCommand { +public: + InpaintCommand( KisPaintDeviceSP maskDev, KisPaintDeviceSP imageDev, int accuracy, int patchRadius ) : + m_maskDev(maskDev), m_imageDev(imageDev), m_accuracy(accuracy), m_patchRadius(patchRadius) {} + + KUndo2Command* paint() override { + KisTransaction transaction(m_imageDev); + patchImage(m_imageDev, m_maskDev, m_patchRadius, m_accuracy); + return transaction.endAndTake(); + } + +private: + KisPaintDeviceSP m_maskDev, m_imageDev; + int m_accuracy, m_patchRadius; +}; + struct KisToolSmartPatch::Private { - KisMaskSP mask = nullptr; - KisNodeSP maskNode = nullptr; - KisNodeSP paintNode = nullptr; - KisPaintDeviceSP imageDev = nullptr; KisPaintDeviceSP maskDev = nullptr; - KisResourcesSnapshotSP resources = nullptr; - KoColor currentFgColor; + KisPainter maskDevPainter; + float brushRadius = 50.; //initial default. actually read from ui. KisToolSmartPatchOptionsWidget *optionsWidget = nullptr; + QRectF oldOutlineRect; + QPainterPath brushOutline; }; KisToolSmartPatch::KisToolSmartPatch(KoCanvasBase * canvas) - : KisToolFreehand(canvas, - KisCursor::load("tool_freehand_cursor.png", 5, 5), - kundo2_i18n("Smart Patch Stroke")), + : KisToolPaint(canvas, KisCursor::blankCursor()), m_d(new Private) { + setSupportOutline(true); setObjectName("tool_SmartPatch"); + m_d->maskDev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); + m_d->maskDevPainter.begin( m_d->maskDev ); + + m_d->maskDevPainter.setPaintColor(KoColor(Qt::magenta, m_d->maskDev->colorSpace())); + m_d->maskDevPainter.setBackgroundColor(KoColor(Qt::white, m_d->maskDev->colorSpace())); + m_d->maskDevPainter.setFillStyle( KisPainter::FillStyleForegroundColor ); } KisToolSmartPatch::~KisToolSmartPatch() { m_d->optionsWidget = nullptr; + m_d->maskDevPainter.end(); } void KisToolSmartPatch::activate(ToolActivation activation, const QSet &shapes) { - KisToolFreehand::activate(activation, shapes); + KisToolPaint::activate(activation, shapes); } void KisToolSmartPatch::deactivate() { - KisToolFreehand::deactivate(); + KisToolPaint::deactivate(); } void KisToolSmartPatch::resetCursorStyle() { - KisToolFreehand::resetCursorStyle(); -} - - -bool KisToolSmartPatch::canCreateInpaintMask() const -{ - KisNodeSP node = currentNode(); - return node && node->inherits("KisPaintLayer"); -} - -QRect KisToolSmartPatch::inpaintImage(KisPaintDeviceSP maskDev, KisPaintDeviceSP imageDev) -{ - int accuracy = 50; //default accuracy - middle value - int patchRadius = 4; //default radius, which works well for most cases tested - - if (!m_d.isNull() && m_d->optionsWidget) { - accuracy = m_d->optionsWidget->getAccuracy(); - patchRadius = m_d->optionsWidget->getPatchRadius(); - } - return patchImage(imageDev, maskDev, patchRadius, accuracy); + KisToolPaint::resetCursorStyle(); } void KisToolSmartPatch::activatePrimaryAction() { - KisToolFreehand::activatePrimaryAction(); + setOutlineEnabled(true); + KisToolPaint::activatePrimaryAction(); } void KisToolSmartPatch::deactivatePrimaryAction() { - KisToolFreehand::deactivatePrimaryAction(); + setOutlineEnabled(false); + KisToolPaint::deactivatePrimaryAction(); } -void KisToolSmartPatch::createInpaintMask(void) +void KisToolSmartPatch::addMaskPath( KoPointerEvent *event ) { - m_d->mask = new KisInpaintMask(); + QPointF imagePos = currentImage()->documentToPixel(event->point); + QPainterPath currentBrushOutline = brushOutline().translated(imagePos); + m_d->maskDevPainter.fillPainterPath(currentBrushOutline); - KisLayerSP parentLayer = qobject_cast(m_d->paintNode.data()); - m_d->mask->initSelection(parentLayer); - image()->addNode(m_d->mask, m_d->paintNode); -} - -void KisToolSmartPatch::deleteInpaintMask(void) -{ - KisCanvas2 * kiscanvas = static_cast(canvas()); - KisViewManager* viewManager = kiscanvas->viewManager(); - if (! m_d->paintNode.isNull()) - viewManager->nodeManager()->slotNonUiActivatedNode(m_d->paintNode); - - image()->removeNode(m_d->mask); - m_d->mask = nullptr; + canvas()->updateCanvas(currentImage()->pixelToDocument(m_d->maskDev->exactBounds())); } void KisToolSmartPatch::beginPrimaryAction(KoPointerEvent *event) { - m_d->paintNode = currentNode(); - - KisCanvas2 * kiscanvas = static_cast(canvas()); - KisViewManager* viewManager = kiscanvas->viewManager(); - //we can only apply inpaint operation to paint layer - if (!m_d->paintNode.isNull() && m_d->paintNode->inherits("KisPaintLayer")) { - - - if (!m_d->mask.isNull()) { - viewManager->nodeManager()->slotNonUiActivatedNode(m_d->mask); - } else { - - createInpaintMask(); - viewManager->nodeManager()->slotNonUiActivatedNode(m_d->mask); - - //Collapse freehand drawing of the mask followed by inpaint operation into a single undo node - canvas()->shapeController()->resourceManager()->undoStack()->beginMacro(kundo2_i18n("Smart Patch")); - - - //User will be drawing on an alpha mask. Show color matching inpaint mask color. - m_d->currentFgColor = canvas()->resourceManager()->foregroundColor(); - canvas()->resourceManager()->setForegroundColor(KoColor(Qt::magenta, image()->colorSpace())); - } - KisToolFreehand::beginPrimaryAction(event); - } else { - viewManager-> - showFloatingMessage( - i18n("Select a paint layer to use this tool"), - QIcon(), 2000, KisFloatingMessage::Medium, Qt::AlignCenter); + if ( currentNode().isNull() || !currentNode()->inherits("KisPaintLayer") || nodePaintAbility()!=NodePaintAbility::PAINT ) { + KisCanvas2 * kiscanvas = static_cast(canvas()); + kiscanvas->viewManager()-> + showFloatingMessage( + i18n("Select a paint layer to use this tool"), + QIcon(), 2000, KisFloatingMessage::Medium, Qt::AlignCenter); + event->ignore(); + return; } + + addMaskPath(event); + setMode(KisTool::PAINT_MODE); + KisToolPaint::beginPrimaryAction(event); } void KisToolSmartPatch::continuePrimaryAction(KoPointerEvent *event) { - if (!m_d->mask.isNull()) - KisToolFreehand::continuePrimaryAction(event); + CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); + addMaskPath(event); + KisToolPaint::continuePrimaryAction(event); } void KisToolSmartPatch::endPrimaryAction(KoPointerEvent *event) { - if (mode() != KisTool::PAINT_MODE) - return; + CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); + addMaskPath(event); + KisToolPaint::endPrimaryAction(event); + setMode(KisTool::HOVER_MODE); - if (m_d->mask.isNull()) - return; + QApplication::setOverrideCursor(KisCursor::waitCursor()); + + int accuracy = 50; //default accuracy - middle value + int patchRadius = 4; //default radius, which works well for most cases tested + + if (m_d->optionsWidget) { + accuracy = m_d->optionsWidget->getAccuracy(); + patchRadius = m_d->optionsWidget->getPatchRadius(); + } + + KisProcessingApplicator applicator( image(), currentNode(), KisProcessingApplicator::NONE, KisImageSignalVector() << ModifiedSignal, + kundo2_i18n("Smart Patch")); - KisToolFreehand::endPrimaryAction(event); + //actual inpaint operation. filling in areas masked by user + applicator.applyCommand( new InpaintCommand( KisPainter::convertToAlphaAsAlpha(m_d->maskDev), currentNode()->paintDevice(), accuracy, patchRadius ), + KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE ); - //Next line is important. We need to wait for the paint operation to finish otherwise - //mask will be incomplete. + applicator.end(); image()->waitForDone(); - //User drew a mask on the temporary inpaint mask layer. Get this mask to pass to the inpaint algorithm - m_d->maskDev = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); + QApplication::restoreOverrideCursor(); + m_d->maskDev->clear(); +} - if (!m_d->mask.isNull()) { - m_d->maskDev->makeCloneFrom(m_d->mask->paintDevice(), m_d->mask->paintDevice()->extent()); +QPainterPath KisToolSmartPatch::brushOutline( void ) +{ + const qreal diameter = m_d->brushRadius; + QPainterPath outline; + outline.addEllipse(QPointF(0,0), -0.5 * diameter, -0.5 * diameter ); + return outline; +} - //Once we get the mask we delete the temporary layer on which user painted it - deleteInpaintMask(); +QPainterPath KisToolSmartPatch::getBrushOutlinePath(const QPointF &documentPos, + const KoPointerEvent *event) +{ + Q_UNUSED(event); - image()->waitForDone(); - m_d->imageDev = currentNode()->paintDevice(); + QPointF imagePos = currentImage()->documentToPixel(documentPos); + QPainterPath path = brushOutline(); - KisTransaction inpaintTransaction(kundo2_i18n("Inpaint Operation"), m_d->imageDev); + return path.translated( imagePos.rx(), imagePos.ry() ); +} - QApplication::setOverrideCursor(KisCursor::waitCursor()); +void KisToolSmartPatch::requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event) +{ + static QPointF lastDocPoint = QPointF(0,0); + if( event ) + lastDocPoint=outlineDocPoint; + + m_d->brushRadius = currentPaintOpPreset()->settings()->paintOpSize(); + m_d->brushOutline = getBrushOutlinePath(lastDocPoint, event); + + QRectF outlinePixelRect = m_d->brushOutline.boundingRect(); + QRectF outlineDocRect = currentImage()->pixelToDocument(outlinePixelRect); + + // This adjusted call is needed as we paint with a 3 pixel wide brush and the pen is outside the bounds of the path + // Pen uses view coordinates so we have to zoom the document value to match 2 pixel in view coordiates + // See BUG 275829 + qreal zoomX; + qreal zoomY; + canvas()->viewConverter()->zoom(&zoomX, &zoomY); + qreal xoffset = 2.0/zoomX; + qreal yoffset = 2.0/zoomY; + + if (!outlineDocRect.isEmpty()) { + outlineDocRect.adjust(-xoffset,-yoffset,xoffset,yoffset); + } - //actual inpaint operation. filling in areas masked by user - QRect changedRect = inpaintImage(m_d->maskDev, m_d->imageDev); - currentNode()->setDirty(changedRect); - inpaintTransaction.commit(image()->undoAdapter()); + if (!m_d->oldOutlineRect.isEmpty()) { + canvas()->updateCanvas(m_d->oldOutlineRect); + } - //Matching endmacro for inpaint operation - canvas()->shapeController()->resourceManager()->undoStack()->endMacro(); + if (!outlineDocRect.isEmpty()) { + canvas()->updateCanvas(outlineDocRect); + } - QApplication::restoreOverrideCursor(); + m_d->oldOutlineRect = outlineDocRect; +} - canvas()->resourceManager()->setForegroundColor(m_d->currentFgColor); +void KisToolSmartPatch::paint(QPainter &painter, const KoViewConverter &converter) +{ + Q_UNUSED(converter); + + painter.save(); + painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination); + painter.setPen(QColor(128, 255, 128)); + painter.drawPath(pixelToView(m_d->brushOutline)); + painter.restore(); + + painter.save(); + painter.setBrush(Qt::magenta); + QImage img = m_d->maskDev->convertToQImage(0); + if( !img.size().isEmpty() ){ + painter.drawImage(pixelToView(m_d->maskDev->exactBounds()), img); } + painter.restore(); } QWidget * KisToolSmartPatch::createOptionWidget()