diff --git a/krita/krita.qrc b/krita/krita.qrc
--- a/krita/krita.qrc
+++ b/krita/krita.qrc
@@ -53,7 +53,7 @@
pics/select_pixel.png
pics/select_shape.png
pics/selection_add.png
- pics/selection_exclude.png
+ pics/selection_symmetric_difference.png
pics/selection_intersect.png
pics/selection_replace.png
pics/selection_subtract.png
diff --git a/krita/pics/selection_exclude.png b/krita/pics/selection_symmetric_difference.png
rename from krita/pics/selection_exclude.png
rename to krita/pics/selection_symmetric_difference.png
diff --git a/libs/image/KisSelectionTags.h b/libs/image/KisSelectionTags.h
--- a/libs/image/KisSelectionTags.h
+++ b/libs/image/KisSelectionTags.h
@@ -30,6 +30,7 @@
SELECTION_ADD,
SELECTION_SUBTRACT,
SELECTION_INTERSECT,
+ SELECTION_SYMMETRICDIFFERENCE,
SELECTION_DEFAULT
};
diff --git a/libs/image/kis_pixel_selection.h b/libs/image/kis_pixel_selection.h
--- a/libs/image/kis_pixel_selection.h
+++ b/libs/image/kis_pixel_selection.h
@@ -158,6 +158,11 @@
*/
void intersectSelection(KisPixelSelectionSP selection);
+ /**
+ * Invert a selection or intersect with the inverse of a selection
+ */
+ void symmetricdifferenceSelection(KisPixelSelectionSP selection);
+
private:
// We don't want these methods to be used on selections:
using KisPaintDevice::extent;
diff --git a/libs/image/kis_pixel_selection.cpp b/libs/image/kis_pixel_selection.cpp
--- a/libs/image/kis_pixel_selection.cpp
+++ b/libs/image/kis_pixel_selection.cpp
@@ -162,6 +162,9 @@
case SELECTION_INTERSECT:
intersectSelection(selection);
break;
+ case SELECTION_SYMMETRICDIFFERENCE:
+ symmetricdifferenceSelection(selection);
+ break;
default:
break;
}
@@ -267,6 +270,32 @@
m_d->invalidateThumbnailImage();
}
+void KisPixelSelection::symmetricdifferenceSelection(KisPixelSelectionSP selection)
+{
+ QRect r = selection->selectedRect().united(selectedRect());
+ if (r.isEmpty()) return;
+
+ KisHLineIteratorSP dst = createHLineIteratorNG(r.x(), r.y(), r.width());
+ KisHLineConstIteratorSP src = selection->createHLineConstIteratorNG(r.x(), r.y(), r.width());
+ for (int i = 0; i < r.height(); ++i) {
+
+ do {
+ *dst->rawData() = abs(*dst->rawData() - *src->oldRawData());
+ } while (src->nextPixel() && dst->nextPixel());
+
+ dst->nextRow();
+ src->nextRow();
+ }
+
+ m_d->outlineCacheValid &= selection->outlineCacheValid();
+
+ if (m_d->outlineCacheValid) {
+ m_d->outlineCache = (m_d->outlineCache | selection->outlineCache()) - (m_d->outlineCache & selection->outlineCache());
+ }
+
+ m_d->invalidateThumbnailImage();
+}
+
void KisPixelSelection::clear(const QRect & r)
{
if (*defaultPixel().data() != MIN_SELECTED) {
diff --git a/libs/libkis/Selection.h b/libs/libkis/Selection.h
--- a/libs/libkis/Selection.h
+++ b/libs/libkis/Selection.h
@@ -196,6 +196,11 @@
*/
void intersect(Selection *selection);
+ /**
+ * Intersect with the inverse of the given selection with this selection.
+ */
+ void symmetricdifference(Selection *selection);
+
/**
* @brief pixelData reads the given rectangle from the Selection's mask and returns it as a
* byte array. The pixel data starts top-left, and is ordered row-first.
diff --git a/libs/libkis/Selection.cpp b/libs/libkis/Selection.cpp
--- a/libs/libkis/Selection.cpp
+++ b/libs/libkis/Selection.cpp
@@ -293,6 +293,12 @@
d->selection->pixelSelection()->applySelection(selection->selection()->pixelSelection(), SELECTION_INTERSECT);
}
+void Selection::symmetricdifference(Selection *selection)
+{
+ if (!d->selection) return;
+ d->selection->pixelSelection()->applySelection(selection->selection()->pixelSelection(), SELECTION_SYMMETRICDIFFERENCE);
+}
+
QByteArray Selection::pixelData(int x, int y, int w, int h) const
{
diff --git a/libs/libqml/qml/panels/SelectPanel.qml b/libs/libqml/qml/panels/SelectPanel.qml
--- a/libs/libqml/qml/panels/SelectPanel.qml
+++ b/libs/libqml/qml/panels/SelectPanel.qml
@@ -145,6 +145,15 @@
checked: (toolManager.currentTool && toolManager.currentTool.selectionAction === 2) ? true : false;
onClicked: if (toolManager.currentTool && toolManager.currentTool.selectionAction !== undefined) toolManager.currentTool.selectionAction = 2;
}
+ Button {
+ id: selectSymmetricDifference;
+ anchors.left: selectSub.right;
+ image: Settings.theme.icon("select-symmetric-difference");
+ width: Constants.ToolbarButtonSize * 0.8;
+ height: width;
+ checked: (toolManager.currentTool && toolManager.currentTool.selectionAction === 4) ? true : false;
+ onClicked: if (toolManager.currentTool && toolManager.currentTool.selectionAction !== undefined) toolManager.currentTool.selectionAction = 4;
+ }
}
Item {
width: childrenRect.width;
diff --git a/libs/ui/forms/wdgselectionoptions.ui b/libs/ui/forms/wdgselectionoptions.ui
--- a/libs/ui/forms/wdgselectionoptions.ui
+++ b/libs/ui/forms/wdgselectionoptions.ui
@@ -7,25 +7,10 @@
0
0
271
- 106
+ 110
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
+
-
@@ -156,9 +141,28 @@
+ -
+
+
+ Symmetric Difference
+
+
+ -1
+
+
+ ...
+
+
+ true
+
+
+ false
+
+
+
- -
+
-
Qt::Horizontal
diff --git a/libs/ui/kis_selection_manager.cc b/libs/ui/kis_selection_manager.cc
--- a/libs/ui/kis_selection_manager.cc
+++ b/libs/ui/kis_selection_manager.cc
@@ -715,6 +715,9 @@
case SELECTION_INTERSECT:
actionName = kundo2_i18n("Select Opaque (Intersect)");
break;
+ case SELECTION_SYMMETRICDIFFERENCE:
+ actionName = kundo2_i18n("Select Opaque (Symmetric Difference)");
+ break;
default:
actionName = kundo2_i18n("Select Opaque");
break;
diff --git a/libs/ui/tool/kis_selection_tool_config_widget_helper.h b/libs/ui/tool/kis_selection_tool_config_widget_helper.h
--- a/libs/ui/tool/kis_selection_tool_config_widget_helper.h
+++ b/libs/ui/tool/kis_selection_tool_config_widget_helper.h
@@ -59,6 +59,7 @@
void slotAddModeRequested();
void slotSubtractModeRequested();
void slotIntersectModeRequested();
+ void slotSymmetricDifferenceModeRequested();
private:
KisSelectionOptions* m_optionsWidget;
diff --git a/libs/ui/tool/kis_selection_tool_config_widget_helper.cpp b/libs/ui/tool/kis_selection_tool_config_widget_helper.cpp
--- a/libs/ui/tool/kis_selection_tool_config_widget_helper.cpp
+++ b/libs/ui/tool/kis_selection_tool_config_widget_helper.cpp
@@ -85,7 +85,7 @@
void KisSelectionToolConfigWidgetHelper::slotWidgetActionChanged(int action)
{
- if (action >= SELECTION_REPLACE && action <= SELECTION_INTERSECT) {
+ if (action >= SELECTION_REPLACE && action <= SELECTION_SYMMETRICDIFFERENCE) {
m_selectionAction = (SelectionAction)action;
KConfigGroup cfg = KSharedConfig::openConfig()->group("KisToolSelectBase");
@@ -135,6 +135,12 @@
slotWidgetActionChanged(m_optionsWidget->action());
}
+void KisSelectionToolConfigWidgetHelper::slotSymmetricDifferenceModeRequested()
+{
+ m_optionsWidget->setAction(SELECTION_SYMMETRICDIFFERENCE);
+ slotWidgetActionChanged(m_optionsWidget->action());
+}
+
void KisSelectionToolConfigWidgetHelper::slotToolActivatedChanged(bool isActivated)
{
if (!isActivated) return;
diff --git a/libs/ui/tool/kis_selection_tool_helper.cpp b/libs/ui/tool/kis_selection_tool_helper.cpp
--- a/libs/ui/tool/kis_selection_tool_helper.cpp
+++ b/libs/ui/tool/kis_selection_tool_helper.cpp
@@ -105,14 +105,14 @@
KisSelectionTransaction transaction(pixelSelection);
- if (!hasSelection && m_action == SELECTION_SUBTRACT) {
+ if (!hasSelection && m_action == SELECTION_SYMMETRICDIFFERENCE) {
pixelSelection->invert();
}
pixelSelection->applySelection(m_selection, m_action);
QRect dirtyRect = m_view->image()->bounds();
- if (hasSelection && m_action != SELECTION_REPLACE && m_action != SELECTION_INTERSECT) {
+ if (hasSelection && m_action != SELECTION_REPLACE && m_action != SELECTION_SYMMETRICDIFFERENCE) {
dirtyRect = m_selection->selectedRect();
}
m_view->selection()->updateProjection(dirtyRect);
@@ -226,6 +226,9 @@
case SELECTION_SUBTRACT:
path = path1 - path2;
break;
+ case SELECTION_SYMMETRICDIFFERENCE:
+ path = (path1 | path2) - (path1 & path2);
+ break;
}
KoShape *newShape = KoPathShape::createShapeFromPainterPath(path);
@@ -279,7 +282,7 @@
bool result = false;
if (KisAlgebra2D::maxDimension(selectionViewRect) < KisConfig(true).selectionViewSizeMinimum() &&
- (action == SELECTION_INTERSECT || action == SELECTION_REPLACE)) {
+ (action == SELECTION_SYMMETRICDIFFERENCE || action == SELECTION_REPLACE)) {
// Queueing this action to ensure we avoid a race condition when unlocking the node system
QTimer::singleShot(0, m_canvas->viewManager()->selectionManager(), SLOT(deselect()));
diff --git a/libs/ui/widgets/kis_selection_options.cc b/libs/ui/widgets/kis_selection_options.cc
--- a/libs/ui/widgets/kis_selection_options.cc
+++ b/libs/ui/widgets/kis_selection_options.cc
@@ -53,6 +53,7 @@
m_action->addButton(m_page->subtract, SELECTION_SUBTRACT);
m_action->addButton(m_page->replace, SELECTION_REPLACE);
m_action->addButton(m_page->intersect, SELECTION_INTERSECT);
+ m_action->addButton(m_page->symmetricdifference, SELECTION_SYMMETRICDIFFERENCE);
m_page->pixel->setGroupPosition(KoGroupButton::GroupLeft);
m_page->shape->setGroupPosition(KoGroupButton::GroupRight);
@@ -63,10 +64,12 @@
m_page->subtract->setGroupPosition(KoGroupButton::GroupRight);
m_page->replace->setGroupPosition(KoGroupButton::GroupLeft);
m_page->intersect->setGroupPosition(KoGroupButton::GroupCenter);
+ m_page->symmetricdifference->setGroupPosition(KoGroupButton::GroupRight);
m_page->add->setIcon(KisIconUtils::loadIcon("selection_add"));
m_page->subtract->setIcon(KisIconUtils::loadIcon("selection_subtract"));
m_page->replace->setIcon(KisIconUtils::loadIcon("selection_replace"));
m_page->intersect->setIcon(KisIconUtils::loadIcon("selection_intersect"));
+ m_page->symmetricdifference->setIcon(KisIconUtils::loadIcon("selection_symmetric_difference"));
connect(m_mode, SIGNAL(buttonClicked(int)), this, SIGNAL(modeChanged(int)));
connect(m_action, SIGNAL(buttonClicked(int)), this, SIGNAL(actionChanged(int)));
@@ -138,6 +141,15 @@
m_action->button(SELECTION_INTERSECT)->setToolTip(toolTipText);
+ break;
+
+ case SELECTION_SYMMETRICDIFFERENCE:
+ toolTipText = shortcutString.isEmpty() ?
+ i18nc("@info:tooltip", "Symmetric Difference") :
+ i18nc("@info:tooltip", "Symmetric Difference (%1)", shortcutString);
+
+ m_action->button(SELECTION_SYMMETRICDIFFERENCE)->setToolTip(toolTipText);
+
break;
}
}
diff --git a/plugins/extensions/pykrita/sip/krita/Selection.sip b/plugins/extensions/pykrita/sip/krita/Selection.sip
--- a/plugins/extensions/pykrita/sip/krita/Selection.sip
+++ b/plugins/extensions/pykrita/sip/krita/Selection.sip
@@ -35,6 +35,7 @@
void add(Selection *selection);
void subtract(Selection *selection);
void intersect(Selection *selection);
+ void symmetricdifference(Selection *selection);
QByteArray pixelData(int x, int y, int w, int h) const;
void setPixelData(QByteArray value, int x, int y, int w, int h);
private:
diff --git a/plugins/tools/selectiontools/kis_selection_modifier_mapper.cc b/plugins/tools/selectiontools/kis_selection_modifier_mapper.cc
--- a/plugins/tools/selectiontools/kis_selection_modifier_mapper.cc
+++ b/plugins/tools/selectiontools/kis_selection_modifier_mapper.cc
@@ -59,6 +59,7 @@
Qt::KeyboardModifiers intersectModifiers;
Qt::KeyboardModifiers addModifiers;
Qt::KeyboardModifiers subtractModifiers;
+ Qt::KeyboardModifiers symmetricdifferenceModifiers;
};
@@ -94,10 +95,12 @@
replaceModifiers = Qt::ControlModifier;
intersectModifiers = (Qt::KeyboardModifiers)(Qt::AltModifier | Qt::ShiftModifier);
subtractModifiers = Qt::AltModifier;
+ symmetricdifferenceModifiers = (Qt::KeyboardModifiers)(Qt::ControlModifier | Qt::AltModifier);
} else {
replaceModifiers = Qt::AltModifier;
intersectModifiers = (Qt::KeyboardModifiers)(Qt::ControlModifier | Qt::ShiftModifier);
subtractModifiers = Qt::ControlModifier;
+ symmetricdifferenceModifiers = (Qt::KeyboardModifiers)(Qt::ShiftModifier | Qt::ControlModifier);
}
addModifiers = Qt::ShiftModifier;
@@ -119,6 +122,9 @@
newAction = SELECTION_ADD;
} else if (m == subtractModifiers) {
newAction = SELECTION_SUBTRACT;
+ } else if (m == symmetricdifferenceModifiers) {
+ newAction = SELECTION_SYMMETRICDIFFERENCE;
}
+
return newAction;
}