File indexing completed on 2024-12-22 04:16:38

0001 /*
0002  *  SPDX-FileCopyrightText: 2007 Sven Langkamp <sven.langkamp@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_tool_select_path.h"
0008 
0009 #include <KoPathShape.h>
0010 
0011 #include "kis_canvas2.h"
0012 #include "kis_canvas_resource_provider.h"
0013 #include "kis_cursor.h"
0014 #include "kis_image.h"
0015 #include "kis_painter.h"
0016 #include "kis_pixel_selection.h"
0017 #include "kis_selection_options.h"
0018 #include "kis_selection_tool_helper.h"
0019 #include <KisView.h>
0020 #include <kis_command_utils.h>
0021 #include <kis_selection_filters.h>
0022 #include <KisOptimizedBrushOutline.h>
0023 #include <kis_default_bounds.h>
0024 
0025 KisToolSelectPath::KisToolSelectPath(KoCanvasBase * canvas)
0026     : KisToolSelectBase<KisDelegatedSelectPathWrapper>(canvas,
0027                                                        KisCursor::load("tool_polygonal_selection_cursor.png", 6, 6),
0028                                                        i18n("Select path"),
0029                                                        new __KisToolSelectPathLocalTool(canvas, this))
0030 {}
0031 
0032 void KisToolSelectPath::requestStrokeEnd()
0033 {
0034     localTool()->endPathWithoutLastPoint();
0035 }
0036 
0037 void KisToolSelectPath::requestStrokeCancellation()
0038 {
0039     localTool()->cancelPath();
0040 }
0041 
0042 // Install an event filter to catch right-click events.
0043 // This code is duplicated in kis_tool_path.cc
0044 bool KisToolSelectPath::eventFilter(QObject *obj, QEvent *event)
0045 {
0046     Q_UNUSED(obj);
0047     if (!localTool()->pathStarted()) {
0048         return false;
0049     }
0050     if (event->type() == QEvent::MouseButtonPress ||
0051             event->type() == QEvent::MouseButtonDblClick) {
0052         QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
0053         if (mouseEvent->button() == Qt::RightButton && isSelecting()) {
0054             localTool()->removeLastPoint();
0055             return true;
0056         }
0057     } else if (event->type() == QEvent::TabletPress) {
0058         QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
0059         if (tabletEvent->button() == Qt::RightButton && isSelecting()) {
0060             localTool()->removeLastPoint();
0061             return true;
0062         }
0063     }
0064     return false;
0065 }
0066 
0067 QList<QPointer<QWidget> > KisToolSelectPath::createOptionWidgets()
0068 {
0069     QList<QPointer<QWidget> > widgetsList =
0070             DelegatedSelectPathTool::createOptionWidgets();
0071     QList<QPointer<QWidget> > filteredWidgets;
0072     Q_FOREACH (QWidget* widget, widgetsList) {
0073         if (widget->objectName() != "Stroke widget") {
0074             filteredWidgets.push_back(widget);
0075         }
0076     }
0077     return filteredWidgets;
0078 }
0079 
0080 void KisDelegatedSelectPathWrapper::beginPrimaryAction(KoPointerEvent *event) {
0081     DelegatedSelectPathTool::mousePressEvent(event);
0082 }
0083 
0084 void KisDelegatedSelectPathWrapper::continuePrimaryAction(KoPointerEvent *event){
0085     DelegatedSelectPathTool::mouseMoveEvent(event);
0086 }
0087 
0088 void KisDelegatedSelectPathWrapper::endPrimaryAction(KoPointerEvent *event) {
0089     DelegatedSelectPathTool::mouseReleaseEvent(event);
0090 }
0091 
0092 void KisDelegatedSelectPathWrapper::beginPrimaryDoubleClickAction(KoPointerEvent *event)
0093 {
0094     DelegatedSelectPathTool::mouseDoubleClickEvent(event);
0095 }
0096 
0097 void KisDelegatedSelectPathWrapper::mousePressEvent(KoPointerEvent *event)
0098 {
0099     // this event will be forwarded using beginPrimaryAction
0100     Q_UNUSED(event);
0101 }
0102 
0103 void KisDelegatedSelectPathWrapper::mouseMoveEvent(KoPointerEvent *event)
0104 {
0105     DelegatedSelectPathTool::mouseMoveEvent(event);
0106 
0107     // WARNING: the code is duplicated from KisToolPaint::requestUpdateOutline
0108     KisCanvas2 *kiscanvas = qobject_cast<KisCanvas2*>(canvas());
0109     KisPaintingAssistantsDecorationSP decoration = kiscanvas->paintingAssistantsDecoration();
0110     if (decoration && decoration->visible() && decoration->hasPaintableAssistants()) {
0111         kiscanvas->updateCanvasDecorations();
0112     }
0113 }
0114 
0115 void KisDelegatedSelectPathWrapper::mouseReleaseEvent(KoPointerEvent *event)
0116 {
0117     // this event will be forwarded using continuePrimaryAction
0118     Q_UNUSED(event);
0119 }
0120 
0121 void KisDelegatedSelectPathWrapper::mouseDoubleClickEvent(KoPointerEvent *event)
0122 {
0123     // this event will be forwarded using endPrimaryAction
0124     Q_UNUSED(event);
0125 }
0126 
0127 __KisToolSelectPathLocalTool::__KisToolSelectPathLocalTool(KoCanvasBase * canvas, KisToolSelectPath* parentTool)
0128     : KoCreatePathTool(canvas), m_selectionTool(parentTool)
0129 {
0130     setEnableClosePathShortcut(false);
0131 }
0132 
0133 void __KisToolSelectPathLocalTool::paintPath(KoPathShape &pathShape, QPainter &painter, const KoViewConverter &converter)
0134 {
0135     Q_UNUSED(converter);
0136     KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
0137     if (!kisCanvas)
0138         return;
0139 
0140     QTransform matrix;
0141     matrix.scale(kisCanvas->image()->xRes(), kisCanvas->image()->yRes());
0142     matrix.translate(pathShape.position().x(), pathShape.position().y());
0143     m_selectionTool->paintToolOutline(&painter, m_selectionTool->pixelToView(matrix.map(pathShape.outline())));
0144 }
0145 
0146 void __KisToolSelectPathLocalTool::addPathShape(KoPathShape* pathShape)
0147 {
0148     pathShape->normalize();
0149     pathShape->close();
0150 
0151     KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
0152     if (!kisCanvas)
0153         return;
0154 
0155     KisImageWSP image = kisCanvas->image();
0156 
0157     KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select by Bezier Curve"));
0158 
0159     const SelectionMode mode =
0160         helper.tryOverrideSelectionMode(kisCanvas->viewManager()->selection(),
0161                                         m_selectionTool->selectionMode(),
0162                                         m_selectionTool->selectionAction());
0163 
0164     if (mode == PIXEL_SELECTION) {
0165         KisProcessingApplicator applicator(
0166             m_selectionTool->currentImage(),
0167             m_selectionTool->currentNode(),
0168             KisProcessingApplicator::NONE,
0169             KisImageSignalVector(),
0170             kundo2_i18n("Select by Bezier Curve"));
0171 
0172         KisPixelSelectionSP tmpSel = new KisPixelSelection(
0173             new KisDefaultBounds(m_selectionTool->currentImage()));
0174 
0175         const bool antiAlias = m_selectionTool->antiAliasSelection();
0176         const int grow = m_selectionTool->growSelection();
0177         const int feather = m_selectionTool->featherSelection();
0178 
0179         QTransform matrix;
0180         matrix.scale(image->xRes(), image->yRes());
0181         matrix.translate(pathShape->position().x(), pathShape->position().y());
0182 
0183         QPainterPath path = matrix.map(pathShape->outline());
0184 
0185         KUndo2Command *cmd = new KisCommandUtils::LambdaCommand(
0186             [tmpSel, antiAlias, grow, feather, path]() mutable
0187             -> KUndo2Command * {
0188                 KisPainter painter(tmpSel);
0189                 painter.setPaintColor(KoColor(Qt::black, tmpSel->colorSpace()));
0190                 // Since the feathering already smooths the selection, the
0191                 // antiAlias is not applied if we must feather
0192                 painter.setAntiAliasPolygonFill(antiAlias && feather == 0);
0193                 painter.setFillStyle(KisPainter::FillStyleForegroundColor);
0194                 painter.setStrokeStyle(KisPainter::StrokeStyleNone);
0195 
0196                 painter.fillPainterPath(path);
0197 
0198                 if (grow > 0) {
0199                     KisGrowSelectionFilter biggy(grow, grow);
0200                     biggy.process(tmpSel,
0201                                   tmpSel->selectedRect().adjusted(-grow,
0202                                                                   -grow,
0203                                                                   grow,
0204                                                                   grow));
0205                 } else if (grow < 0) {
0206                     KisShrinkSelectionFilter tiny(-grow, -grow, false);
0207                     tiny.process(tmpSel, tmpSel->selectedRect());
0208                 }
0209                 if (feather > 0) {
0210                     KisFeatherSelectionFilter feathery(feather);
0211                     feathery.process(tmpSel,
0212                                      tmpSel->selectedRect().adjusted(-feather,
0213                                                                      -feather,
0214                                                                      feather,
0215                                                                      feather));
0216                 }
0217 
0218                 if (grow == 0 && feather == 0) {
0219                     tmpSel->setOutlineCache(path);
0220                 } else {
0221                     tmpSel->invalidateOutlineCache();
0222                 }
0223 
0224                 return 0;
0225             });
0226 
0227         applicator.applyCommand(cmd, KisStrokeJobData::SEQUENTIAL);
0228         helper.selectPixelSelection(applicator,
0229                                     tmpSel,
0230                                     m_selectionTool->selectionAction());
0231         applicator.end();
0232 
0233         delete pathShape;
0234 
0235     } else {
0236         helper.addSelectionShape(pathShape, m_selectionTool->selectionAction());
0237     }
0238 }
0239 
0240 void __KisToolSelectPathLocalTool::beginShape()
0241 {
0242     KisToolSelectPath* selectPathTool = dynamic_cast<KisToolSelectPath*>(m_selectionTool);
0243     KIS_ASSERT(selectPathTool);
0244     selectPathTool->beginSelectInteraction();
0245 }
0246 
0247 void __KisToolSelectPathLocalTool::endShape()
0248 {
0249     KisToolSelectPath* selectPathTool = dynamic_cast<KisToolSelectPath*>(m_selectionTool);
0250     KIS_ASSERT(selectPathTool);
0251     selectPathTool->endSelectInteraction();
0252 }
0253 
0254 void KisToolSelectPath::resetCursorStyle()
0255 {
0256     if (selectionAction() == SELECTION_ADD) {
0257         useCursor(KisCursor::load("tool_polygonal_selection_cursor_add.png", 6, 6));
0258     } else if (selectionAction() == SELECTION_SUBTRACT) {
0259         useCursor(KisCursor::load("tool_polygonal_selection_cursor_sub.png", 6, 6));
0260     } else if (selectionAction() == SELECTION_INTERSECT) {
0261         useCursor(KisCursor::load("tool_polygonal_selection_cursor_inter.png", 6, 6));
0262     } else if (selectionAction() == SELECTION_SYMMETRICDIFFERENCE) {
0263         useCursor(KisCursor::load("tool_polygonal_selection_cursor_symdiff.png", 6, 6));
0264     } else {
0265         KisToolSelectBase<KisDelegatedSelectPathWrapper>::resetCursorStyle();
0266     }
0267 }
0268 
0269