Commit d0efb113 authored by Agata Cacko's avatar Agata Cacko

Port Contiguous Selection Tool to strokes

Before this commit, Contiguous Selection Tool would work in GUI thread
just keeping the image under barrierLock().
This commit ensures that all the operations happen in a working thread.
parent 7748d8d1
......@@ -245,7 +245,11 @@ void KisFillPainter::genericFillStart(int startX, int startY, KisPaintDeviceSP s
Q_ASSERT(m_height > 0);
// Create a selection from the surrounding area
m_fillSelection = createFloodSelection(startX, startY, sourceDevice);
KisPixelSelectionSP pixelSelection = createFloodSelection(startX, startY, sourceDevice);
KisSelectionSP newSelection = new KisSelection(pixelSelection->defaultBounds());
newSelection->pixelSelection()->applySelection(pixelSelection, SELECTION_REPLACE);
m_fillSelection = newSelection;
}
void KisFillPainter::genericFillEnd(KisPaintDeviceSP filled)
......@@ -280,8 +284,15 @@ void KisFillPainter::genericFillEnd(KisPaintDeviceSP filled)
m_width = m_height = -1;
}
KisSelectionSP KisFillPainter::createFloodSelection(int startX, int startY, KisPaintDeviceSP sourceDevice)
KisPixelSelectionSP KisFillPainter::createFloodSelection(int startX, int startY, KisPaintDeviceSP sourceDevice)
{
KisPixelSelectionSP newSelection = new KisPixelSelection(new KisSelectionDefaultBounds(device()));
return createFloodSelection(newSelection, startX, startY, sourceDevice);
}
KisPixelSelectionSP KisFillPainter::createFloodSelection(KisPixelSelectionSP pixelSelection, int startX, int startY, KisPaintDeviceSP sourceDevice)
{
if (m_width < 0 || m_height < 0) {
if (selection() && m_careForSelection) {
QRect rc = selection()->selectedExactRect();
......@@ -296,11 +307,8 @@ KisSelectionSP KisFillPainter::createFloodSelection(int startX, int startY, KisP
QRect fillBoundsRect(0, 0, m_width, m_height);
QPoint startPoint(startX, startY);
KisSelectionSP selection = new KisSelection(new KisSelectionDefaultBounds(device()));
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
if (!fillBoundsRect.contains(startPoint)) {
return selection;
return pixelSelection;
}
KisScanlineFill gc(sourceDevice, startPoint, fillBoundsRect);
......@@ -309,16 +317,16 @@ KisSelectionSP KisFillPainter::createFloodSelection(int startX, int startY, KisP
if (m_sizemod > 0) {
KisGrowSelectionFilter biggy(m_sizemod, m_sizemod);
biggy.process(pixelSelection, selection->selectedRect().adjusted(-m_sizemod, -m_sizemod, m_sizemod, m_sizemod));
biggy.process(pixelSelection, pixelSelection->selectedRect().adjusted(-m_sizemod, -m_sizemod, m_sizemod, m_sizemod));
}
else if (m_sizemod < 0) {
KisShrinkSelectionFilter tiny(-m_sizemod, -m_sizemod, false);
tiny.process(pixelSelection, selection->selectedRect());
tiny.process(pixelSelection, pixelSelection->selectedRect());
}
if (m_feather > 0) {
KisFeatherSelectionFilter feathery(m_feather);
feathery.process(pixelSelection, selection->selectedRect().adjusted(-m_feather, -m_feather, m_feather, m_feather));
feathery.process(pixelSelection, pixelSelection->selectedRect().adjusted(-m_feather, -m_feather, m_feather, m_feather));
}
return selection;
return pixelSelection;
}
......@@ -150,13 +150,29 @@ public:
/**
* Returns a selection mask for the floodfill starting at the specified position.
* This variant basically creates a new selection object and passes it down
* to the other variant of the function.
*
* @param startX the X position where the floodfill starts
* @param startY the Y position where the floodfill starts
* @param sourceDevice the sourceDevice that determines the area that
* is floodfilled if sampleMerged is on
*/
KisSelectionSP createFloodSelection(int startX, int startY, KisPaintDeviceSP sourceDevice);
KisPixelSelectionSP createFloodSelection(int startX, int startY, KisPaintDeviceSP sourceDevice);
/**
* Returns a selection mask for the floodfill starting at the specified position.
* This variant requires an empty selection object. It is used in cases where the pointer
* to the selection must be known beforehand, for example when the selection is filled
* in a stroke and then the pointer to the pixel selection is needed later.
*
* @param selection empty new selection object
* @param startX the X position where the floodfill starts
* @param startY the Y position where the floodfill starts
* @param sourceDevice the sourceDevice that determines the area that
* is floodfilled if sampleMerged is on
*/
KisPixelSelectionSP createFloodSelection(KisPixelSelectionSP newSelection, int startX, int startY, KisPaintDeviceSP sourceDevice);
/**
* Set the threshold for floodfill. The range is 0-255: 0 means the fill will only
......
......@@ -70,81 +70,102 @@ struct LazyInitGlobalSelection : public KisTransactionBasedCommand {
}
};
void KisSelectionToolHelper::selectPixelSelection(KisPixelSelectionSP selection, SelectionAction action)
{
KisView* view = m_canvas->imageView();
if (selection->selectedExactRect().isEmpty()) {
m_canvas->viewManager()->selectionManager()->deselect();
return;
}
KisProcessingApplicator applicator(view->image(),
0 /* we need no automatic updates */,
KisProcessingApplicator::SUPPORTS_WRAPAROUND_MODE,
KisImageSignalVector() << ModifiedSignal,
m_name);
applicator.applyCommand(new LazyInitGlobalSelection(view));
selectPixelSelection(applicator, selection, action);
applicator.end();
}
void KisSelectionToolHelper::selectPixelSelection(KisProcessingApplicator& applicator, KisPixelSelectionSP selection, SelectionAction action)
{
KisView* view = m_canvas->imageView();
QPointer<KisCanvas2> canvas = m_canvas;
applicator.applyCommand(new LazyInitGlobalSelection(view), KisStrokeJobData::SEQUENTIAL);
struct ApplyToPixelSelection : public KisTransactionBasedCommand {
ApplyToPixelSelection(KisView *view,
KisPixelSelectionSP selection,
SelectionAction action) : m_view(view),
m_selection(selection),
m_action(action) {}
SelectionAction action,
QPointer<KisCanvas2> canvas) : m_view(view),
m_selection(selection),
m_action(action),
m_canvas(canvas) {}
KisView *m_view;
KisPixelSelectionSP m_selection;
SelectionAction m_action;
QPointer<KisCanvas2> m_canvas;
KUndo2Command* paint() override {
KisSelectionSP selection = m_view->selection();
KIS_SAFE_ASSERT_RECOVER(selection) { return 0; }
KUndo2Command *savedCommand = 0;
if (!m_selection->selectedExactRect().isEmpty()) {
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
KIS_SAFE_ASSERT_RECOVER(pixelSelection) { return 0; }
KisSelectionSP selection = m_view->selection();
KIS_SAFE_ASSERT_RECOVER(selection) { return 0; }
bool hasSelection = !pixelSelection->isEmpty();
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
KIS_SAFE_ASSERT_RECOVER(pixelSelection) { return 0; }
KisSelectionTransaction transaction(pixelSelection);
bool hasSelection = !pixelSelection->isEmpty();
if (!hasSelection && m_action == SELECTION_SYMMETRICDIFFERENCE) {
m_action = SELECTION_REPLACE;
}
KisSelectionTransaction transaction(pixelSelection);
if (!hasSelection && m_action == SELECTION_SUBTRACT) {
pixelSelection->invert();
}
if (!hasSelection && m_action == SELECTION_SYMMETRICDIFFERENCE) {
m_action = SELECTION_REPLACE;
}
pixelSelection->applySelection(m_selection, m_action);
if (!hasSelection && m_action == SELECTION_SUBTRACT) {
pixelSelection->invert();
}
QRect dirtyRect = m_view->image()->bounds();
if (hasSelection &&
m_action != SELECTION_REPLACE &&
m_action != SELECTION_INTERSECT &&
m_action != SELECTION_SYMMETRICDIFFERENCE) {
pixelSelection->applySelection(m_selection, m_action);
dirtyRect = m_selection->selectedRect();
}
m_view->selection()->updateProjection(dirtyRect);
QRect dirtyRect = m_view->image()->bounds();
if (hasSelection &&
m_action != SELECTION_REPLACE &&
m_action != SELECTION_INTERSECT &&
m_action != SELECTION_SYMMETRICDIFFERENCE) {
KUndo2Command *savedCommand = transaction.endAndTake();
pixelSelection->setDirty(dirtyRect);
dirtyRect = m_selection->selectedRect();
}
m_view->selection()->updateProjection(dirtyRect);
savedCommand = transaction.endAndTake();
pixelSelection->setDirty(dirtyRect);
}
if (m_view->selection()->selectedExactRect().isEmpty()) {
KisCommandUtils::CompositeCommand *cmd = new KisCommandUtils::CompositeCommand();
cmd->addCommand(savedCommand);
cmd->addCommand(new KisDeselectActiveSelectionCommand(m_view->selection(), m_view->image()));
savedCommand = cmd;
KUndo2Command *deselectCommand = new KisDeselectActiveSelectionCommand(m_view->selection(), m_view->image());
if (savedCommand) {
KisCommandUtils::CompositeCommand *cmd = new KisCommandUtils::CompositeCommand();
cmd->addCommand(savedCommand);
cmd->addCommand(deselectCommand);
savedCommand = cmd;
} else {
savedCommand = deselectCommand;
}
}
return savedCommand;
}
};
applicator.applyCommand(new ApplyToPixelSelection(view, selection, action));
applicator.end();
applicator.applyCommand(new ApplyToPixelSelection(view, selection, action, canvas), KisStrokeJobData::SEQUENTIAL);
}
void KisSelectionToolHelper::addSelectionShape(KoShape* shape, SelectionAction action)
......
......@@ -26,6 +26,7 @@
#include "kis_layer.h"
#include "kis_selection.h"
#include "kis_canvas2.h"
#include "kis_processing_applicator.h"
class KoShape;
......@@ -39,7 +40,9 @@ public:
KisSelectionToolHelper(KisCanvas2* canvas, const KUndo2MagicString& name);
virtual ~KisSelectionToolHelper();
void selectPixelSelection(KisProcessingApplicator& applicator, KisPixelSelectionSP selection, SelectionAction action);
void selectPixelSelection(KisPixelSelectionSP selection, SelectionAction action);
void addSelectionShape(KoShape* shape, SelectionAction action = SELECTION_DEFAULT);
void addSelectionShapes(QList<KoShape*> shapes, SelectionAction action = SELECTION_DEFAULT);
......
......@@ -52,6 +52,11 @@
#include "commands_new/KisMergeLabeledLayersCommand.h"
#include "kis_image.h"
#include "kis_undo_stores.h"
#include "kis_resources_snapshot.h"
#include "kis_processing_applicator.h"
#include <processing/fill_processing_visitor.h>
#include "kis_command_utils.h"
KisToolSelectContiguous::KisToolSelectContiguous(KoCanvasBase *canvas)
......@@ -81,7 +86,7 @@ void KisToolSelectContiguous::beginPrimaryAction(KoPointerEvent *event)
KisPaintDeviceSP dev;
if (!currentNode() ||
!(dev = currentNode()->projection()) ||
!(dev = currentNode()->paintDevice()) ||
!currentNode()->visible() ||
!selectionEditable()) {
event->ignore();
......@@ -94,15 +99,16 @@ void KisToolSelectContiguous::beginPrimaryAction(KoPointerEvent *event)
QApplication::setOverrideCursor(KisCursor::waitCursor());
// -------------------------------
KisProcessingApplicator applicator(currentImage(), currentNode(),
KisProcessingApplicator::NONE,
KisImageSignalVector() << ModifiedSignal,
kundo2_i18n("Select Contiguous Area"));
QPoint pos = convertToImagePixelCoordFloored(event);
QRect rc = currentImage()->bounds();
KisFillPainter fillpainter(dev);
fillpainter.setHeight(rc.height());
fillpainter.setWidth(rc.width());
fillpainter.setFillThreshold(m_fuzziness);
fillpainter.setFeather(m_feather);
fillpainter.setSizemod(m_sizemod);
KisImageSP image = currentImage();
KisPaintDeviceSP sourceDevice;
......@@ -114,44 +120,67 @@ void KisToolSelectContiguous::beginPrimaryAction(KoPointerEvent *event)
image, "Contiguous Selection Tool Reference Result Paint Device");
KisMergeLabeledLayersCommand* command = new KisMergeLabeledLayersCommand(refImage, sourceDevice, image->root(), colorLabelsSelected());
image->barrierLock();
command->redo();
image->unlock();
delete command;
applicator.applyCommand(command,
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
} else { // Sample Current Layer
sourceDevice = dev;
}
image->barrierLock();
KisSelectionSP selection = fillpainter.createFloodSelection(pos.x(), pos.y(), sourceDevice);
image->unlock();
// If we're not antialiasing, threshold the entire selection
if (!antiAliasSelection()) {
QRect r = selection->selectedExactRect();
if (r.isValid()) {
KisHLineIteratorSP selectionIt = selection->pixelSelection()->createHLineIteratorNG(r.x(), r.y(), r.width());
for (qint32 y = 0; y < r.height(); y++) {
do {
if (selectionIt->rawData()[0] > 0) {
selection->pixelSelection()->colorSpace()->setOpacity(selectionIt->rawData(), OPACITY_OPAQUE_U8, 1);
KisPixelSelectionSP selection = KisPixelSelectionSP(new KisPixelSelection(new KisSelectionDefaultBounds(dev)));
bool antiAlias = antiAliasSelection();
int fuzziness = m_fuzziness;
int feather = m_feather;
int sizemod = m_sizemod;
KUndo2Command* cmd = new KisCommandUtils::LambdaCommand(
[dev, rc, fuzziness, feather, sizemod, selection, pos, sourceDevice, antiAlias] () mutable -> KUndo2Command* {
KisFillPainter fillpainter(dev);
fillpainter.setHeight(rc.height());
fillpainter.setWidth(rc.width());
fillpainter.setFillThreshold(fuzziness);
fillpainter.setFeather(feather);
fillpainter.setSizemod(sizemod);
fillpainter.createFloodSelection(selection, pos.x(), pos.y(), sourceDevice);
// If we're not antialiasing, threshold the entire selection
if (!antiAlias) {
QRect r = selection->selectedExactRect();
if (r.isValid()) {
KisHLineIteratorSP selectionIt = selection->createHLineIteratorNG(r.x(), r.y(), r.width());
for (qint32 y = 0; y < r.height(); y++) {
do {
if (selectionIt->rawData()[0] > 0) {
selection->colorSpace()->setOpacity(selectionIt->rawData(), OPACITY_OPAQUE_U8, 1);
}
} while (selectionIt->nextPixel());
selectionIt->nextRow();
}
}
}
} while (selectionIt->nextPixel());
selectionIt->nextRow();
}
}
}
selection->invalidateOutlineCache();
return 0;
});
applicator.applyCommand(cmd, KisStrokeJobData::SEQUENTIAL);
KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
if (!kisCanvas || !selection->pixelSelection()) {
KIS_SAFE_ASSERT_RECOVER(kisCanvas) {
applicator.cancel();
QApplication::restoreOverrideCursor();
return;
}
};
selection->pixelSelection()->invalidateOutlineCache();
KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select Contiguous Area"));
helper.selectPixelSelection(selection->pixelSelection(), selectionAction());
helper.selectPixelSelection(applicator, selection, selectionAction());
applicator.end();
QApplication::restoreOverrideCursor();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment