Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_tool_select_similar.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 1999 Matthias Elter <me@kde.org>
3 * SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
4 * SPDX-FileCopyrightText: 2005 Boudewijn Rempt <boud@valdyas.org>
5 * SPDX-FileCopyrightText: 2015 Michael Abrahams <miabraha@gmail.com>
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
11
12#include <QApplication>
13#include <QHBoxLayout>
14#include <QVBoxLayout>
15
16#include <ksharedconfig.h>
17
18#include <KoColorSpace.h>
21
22#include "kis_canvas2.h"
23#include "kis_command_utils.h"
24#include "kis_image.h"
25#include "kis_iterator_ng.h"
27#include "kis_slider_spin_box.h"
28#include "krita_utils.h"
29#include <KoPointerEvent.h>
30#include <kis_cursor.h>
31#include <kis_paint_device.h>
32#include <kis_pixel_selection.h>
36#include <kis_default_bounds.h>
37#include <kis_fill_painter.h>
38
40 : KisToolSelect(canvas,
41 KisCursor::load("tool_similar_selection_cursor.png", 6, 6),
42 i18n("Similar Color Selection"))
43 , m_threshold(20)
44 , m_opacitySpread(100)
45 , m_previousTime(0)
46{
47}
48
49void KisToolSelectSimilar::activate(const QSet<KoShape*> &shapes)
50{
52 m_configGroup = KSharedConfig::openConfig()->group(toolId());
53}
54
61
63{
65 if (isMovingSelection()) {
66 return;
67 }
68
70
71 if (!currentNode() ||
72 !(currentNode()->projection()) ||
73 !selectionEditable()) {
74
75 event->ignore();
76 return;
77 }
78
79 KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
81
83
84 KisCursorOverrideLock cursorLock(KisCursor::waitCursor());
85
86 // Create the stroke
88 new KisStrokeStrategyUndoCommandBased(kundo2_i18n("Select Similar Color"), false, image().data());
89 strategy->setSupportsWrapAroundMode(false);
90 KisStrokeId strokeId = image()->startStroke(strategy);
91
92 // Construct the reference device
94 m_referencePaintDevice = currentNode()->projection();
95 } else if (sampleLayersMode() == SampleAllLayers) {
96 m_referencePaintDevice = currentImage()->projection();
99 m_referencePaintDevice = KisMergeLabeledLayersCommand::createRefPaintDevice(image(), "Similar Colors Selection Tool Reference Result Paint Device");
101 }
102 KisPaintDeviceSP newReferencePaintDevice = KisMergeLabeledLayersCommand::createRefPaintDevice(image(), "Similar Colors Selection Tool Reference Result Paint Device");
104 const int currentTime = image()->animationInterface()->currentTime();
105
106 image()->addJob(
107 strokeId,
110 image(),
112 newReferenceNodeList,
114 newReferencePaintDevice,
117 m_previousTime != currentTime
118 )),
119 false,
122 )
123 );
124
125 m_referencePaintDevice = newReferencePaintDevice;
126 m_referenceNodeList = newReferenceNodeList;
127 m_previousTime = currentTime;
128 }
129
130 // Get the color of the pixel where the user clicked
132 const QPoint pos = convertToImagePixelCoordFloored(event);
133 QSharedPointer<KoColor> referenceColor = QSharedPointer<KoColor>(new KoColor(sourceDevice->colorSpace()));
134 // We need to obtain the reference color from the reference paint
135 // device, but it is produced in a stroke, so we must get the color
136 // after the device is ready. So we get it in the stroke
137 image()->addJob(
138 strokeId,
141 [sourceDevice, referenceColor, pos]() -> KUndo2Command*
142 {
143 *referenceColor = sourceDevice->pixel(pos);
144 return 0;
145 }
146 )),
147 false,
150 )
151 );
152
153 // Get the similar colors selection
154 KisFillPainter painter;
155 QRect bounds = currentImage()->bounds();
157 progressHelper(new KisProcessingVisitor::ProgressHelper(currentNode()));
158 KisPixelSelectionSP tmpSel = new KisPixelSelection(new KisSelectionDefaultBounds(currentNode()->projection()));
159
163 painter.setSizemod(growSelection());
165 painter.setFeather(featherSelection());
166
169 tmpSel, referenceColor, sourceDevice,
170 bounds, nullptr, progressHelper
171 );
172
173 for (KisStrokeJobData *job : jobs) {
174 image()->addJob(strokeId, job);
175 }
176
177 image()->addJob(
178 strokeId,
181 [tmpSel]() mutable -> KUndo2Command*
182 {
183 tmpSel->invalidateOutlineCache();
184 return 0;
185 }
186 )),
187 false,
190 )
191 );
192
193 image()->endStroke(strokeId);
194
195 // Apply selection
196 KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select Similar Color"));
197 helper.selectPixelSelection(tmpSel, selectionAction());
198}
199
209
211{
212 m_threshold = threshold;
213 m_configGroup.writeEntry("threshold", threshold);
214}
215
217{
218 m_opacitySpread = opacitySpread;
219 m_configGroup.writeEntry("opacitySpread", opacitySpread);
220}
221
223{
225 KisSelectionOptions *selectionWidget = selectionOptionWidget();
226
227 selectionWidget->setStopGrowingAtDarkestPixelButtonVisible(true);
228
229 // Create widgets
230 KisSliderSpinBox *sliderThreshold = new KisSliderSpinBox;
231 sliderThreshold->setPrefix(i18nc(
232 "The 'threshold' spinbox prefix in similar selection tool options",
233 "Threshold: "));
234 sliderThreshold->setRange(1, 100);
235 sliderThreshold->setSingleStep(1);
236 sliderThreshold->setToolTip(
237 i18n("Set the color similarity tolerance of the selection. "
238 "Increasing threshold increases the range of similar colors to be selected."));
239
240 KisSliderSpinBox *sliderSpread = new KisSliderSpinBox;
241 sliderSpread->setRange(0, 100);
243 i18nc("The 'spread' spinbox in similar color selection tool options; {n} is the "
244 "number value, % is the percent sign",
245 "Spread: {n}%"));
246
247 // Set the tooltips
248 sliderThreshold->setToolTip(
249 i18n("Set the color similarity tolerance of the selection. "
250 "Increasing threshold increases the range of similar colors to be selected."));
251 sliderSpread->setToolTip(
252 i18n("Set the extent of the opaque portion of the selection. "
253 "Decreasing spread decreases opacity of selection areas depending on color similarity."));
254
255 // Construct the option widget
256 KisOptionCollectionWidgetWithHeader *sectionSelectionExtent =
258 i18nc("The 'selection extent' section label in similar selection "
259 "tool options",
260 "Selection extent"));
261 sectionSelectionExtent->appendWidget("sliderThreshold", sliderThreshold);
262 sectionSelectionExtent->appendWidget("sliderSpread", sliderSpread);
263 selectionWidget->insertWidget(3, "sectionSelectionExtent", sectionSelectionExtent);
264
265 // load setting from config
266 if (m_configGroup.hasKey("threshold")) {
267 m_threshold = m_configGroup.readEntry("threshold", 20);
268 } else {
269 m_threshold = m_configGroup.readEntry("fuzziness", 20);
270 }
271 sliderThreshold->setValue(m_threshold);
272
273 m_opacitySpread = m_configGroup.readEntry("opacitySpread", 100);
274 sliderSpread->setValue(m_opacitySpread);
275
276 // Make connections
277 connect(sliderThreshold,
278 SIGNAL(valueChanged(int)),
279 this,
280 SLOT(slotSetThreshold(int)));
281 connect(sliderSpread,
282 SIGNAL(valueChanged(int)),
283 this,
284 SLOT(slotSetOpacitySpread(int)));
285
286 return selectionWidget;
287}
288
290{
292 useCursor(KisCursor::load("tool_similar_selection_cursor_add.png", 6, 6));
293 } else if (selectionAction() == SELECTION_SUBTRACT) {
294 useCursor(KisCursor::load("tool_similar_selection_cursor_sub.png", 6, 6));
295 } else if (selectionAction() == SELECTION_INTERSECT) {
296 useCursor(KisCursor::load("tool_similar_selection_cursor_inter.png", 6, 6));
298 useCursor(KisCursor::load("tool_similar_selection_cursor_symdiff.png", 6, 6));
299 } else {
300 KisToolSelect::resetCursorStyle();
301 }
302}
@ SELECTION_INTERSECT
@ SELECTION_SYMMETRICDIFFERENCE
@ SELECTION_SUBTRACT
@ SELECTION_ADD
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
static QCursor load(const QString &cursorName, int hotspotX=-1, int hotspotY=-1)
static QCursor waitCursor()
Definition kis_cursor.cc:54
void setSizemod(int sizemod)
void setFillThreshold(int threshold)
void setFeather(int feather)
QVector< KisStrokeJobData * > createSimilarColorsSelectionJobs(KisPixelSelectionSP outSelection, const QSharedPointer< KoColor > referenceColor, KisPaintDeviceSP referenceDevice, const QRect &rect, KisPixelSelectionSP mask, QSharedPointer< KisProcessingVisitor::ProgressHelper > progressHelper=nullptr)
void setAntiAlias(bool antiAlias)
void setStopGrowingAtDarkestPixel(bool stopGrowingAtDarkestPixel)
void setOpacitySpread(int opacitySpread)
static KisPaintDeviceSP createRefPaintDevice(KisImageSP originalImage, QString name="Merge Labeled Layers Reference Paint Device")
@ GroupSelectionPolicy_SelectIfColorLabeled
Groups will be taken into account only if they have set an explicit color label. This ignores groups ...
Wrapper class around a KisOptionCollectionWidget that also provide a header with a title label and an...
void appendWidget(const QString &id, QWidget *widget)
Insert the given widget with the given id at the end of the list. The list widget takes ownership of ...
void insertWidget(int index, const QString &id, QWidget *widget)
Insert the given widget with the given id at the given position. The list widget takes ownership of t...
const KoColorSpace * colorSpace() const
bool pixel(qint32 x, qint32 y, QColor *c) const
void setStopGrowingAtDarkestPixelButtonVisible(bool visible)
void selectPixelSelection(KisProcessingApplicator &applicator, KisPixelSelectionSP selection, SelectionAction action)
This class is a spinbox in which you can click and drag to set the value. A slider like bar is displa...
void setValue(int newValue)
void setRange(int newMinimum, int newMaximum, bool computeNewFastSliderStep=true)
Set the minimum and the maximum values of the range, computing a new "fast slider step" based on the ...
KisSelectionOptions * selectionOptionWidget()
SampleLayersMode sampleLayersMode() const
SelectionAction selectionAction() const
void beginPrimaryAction(KoPointerEvent *event) override
QList< int > colorLabelsSelected() const
void activate(const QSet< KoShape * > &shapes) override
void endPrimaryAction(KoPointerEvent *event) override
bool stopGrowingAtDarkestPixel() const
void deactivate() override
QWidget * createOptionWidget() override
bool antiAliasSelection() const
void beginPrimaryAction(KoPointerEvent *event) override
KisToolSelectSimilar(KoCanvasBase *canvas)
QWidget * createOptionWidget() override
KisMergeLabeledLayersCommand::ReferenceNodeInfoListSP m_referenceNodeList
void activate(const QSet< KoShape * > &shapes) override
KisPaintDeviceSP m_referencePaintDevice
void endPrimaryAction(KoPointerEvent *event) override
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define bounds(x, a, b)
QSharedPointer< KUndo2Command > KUndo2CommandSP
Definition kis_types.h:262
KUndo2MagicString kundo2_i18n(const char *text)
void setText(QSpinBox *spinBox, const QStringView textTemplate)
The LambdaCommand struct is a shorthand for creation of AggregateCommand commands using C++ lambda fe...