Commit f0fbdfd0 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Symmetric Difference Selection Patch for Krita

Summary:
I have attempted to add a exclude selection for Krita, and while it does compile nicely and smoothly, there is a issue where Krita crash from the very beginning. Everything seem to check out.

The idea is just to finish the basic selection of Krita

Test Plan:
1. Fix the crash issue
2. Change the math operation of exclude operation
3. See if it works as expected
4. If all goes all, this can be used to finish basic operations of selection of Krita

Patch by Reptorian (Miguel Lopez)

Reviewers: #krita, dkazakov
Subscribers: langkamp, woltherav, rempt, dkazakov
Tags: #krita
Differential Revision: https://phabricator.kde.org/D15085
parent 7f6e6add
......@@ -53,7 +53,7 @@
<file>pics/select_pixel.png</file>
<file>pics/select_shape.png</file>
<file>pics/selection_add.png</file>
<file>pics/selection_exclude.png</file>
<file>pics/selection_symmetric_difference.png</file>
<file>pics/selection_intersect.png</file>
<file>pics/selection_replace.png</file>
<file>pics/selection_subtract.png</file>
......
......@@ -30,6 +30,7 @@ enum SelectionAction {
SELECTION_ADD,
SELECTION_SUBTRACT,
SELECTION_INTERSECT,
SELECTION_SYMMETRICDIFFERENCE,
SELECTION_DEFAULT
};
......
......@@ -162,6 +162,9 @@ void KisPixelSelection::applySelection(KisPixelSelectionSP selection, SelectionA
case SELECTION_INTERSECT:
intersectSelection(selection);
break;
case SELECTION_SYMMETRICDIFFERENCE:
symmetricdifferenceSelection(selection);
break;
default:
break;
}
......@@ -267,6 +270,32 @@ void KisPixelSelection::intersectSelection(KisPixelSelectionSP selection)
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) {
......
......@@ -158,6 +158,11 @@ private:
*/
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;
......
......@@ -293,6 +293,12 @@ void Selection::intersect(Selection *selection)
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
{
......
......@@ -196,6 +196,11 @@ public Q_SLOTS:
*/
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.
......
......@@ -145,6 +145,15 @@ Panel {
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;
......
......@@ -7,25 +7,10 @@
<x>0</x>
<y>0</y>
<width>271</width>
<height>106</height>
<height>110</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout">
<property name="spacing">
......@@ -156,9 +141,28 @@
</property>
</widget>
</item>
<item row="1" column="8">
<widget class="KoGroupButton" name="symmetricdifference">
<property name="toolTip">
<string>Symmetric Difference</string>
</property>
<property name="toolTipDuration">
<number>-1</number>
</property>
<property name="text">
<string>...</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="2">
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
......
......@@ -715,6 +715,9 @@ void KisSelectionManager::selectOpaqueOnNode(KisNodeSP node, SelectionAction act
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;
......
......@@ -85,7 +85,7 @@ bool KisSelectionToolConfigWidgetHelper::antiAliasSelection() const
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 @@ void KisSelectionToolConfigWidgetHelper::slotIntersectModeRequested()
slotWidgetActionChanged(m_optionsWidget->action());
}
void KisSelectionToolConfigWidgetHelper::slotSymmetricDifferenceModeRequested()
{
m_optionsWidget->setAction(SELECTION_SYMMETRICDIFFERENCE);
slotWidgetActionChanged(m_optionsWidget->action());
}
void KisSelectionToolConfigWidgetHelper::slotToolActivatedChanged(bool isActivated)
{
if (!isActivated) return;
......
......@@ -59,6 +59,7 @@ public Q_SLOTS:
void slotAddModeRequested();
void slotSubtractModeRequested();
void slotIntersectModeRequested();
void slotSymmetricDifferenceModeRequested();
private:
KisSelectionOptions* m_optionsWidget;
......
......@@ -105,14 +105,14 @@ void KisSelectionToolHelper::selectPixelSelection(KisPixelSelectionSP selection,
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 @@ void KisSelectionToolHelper::addSelectionShapes(QList< KoShape* > shapes, Select
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 KisSelectionToolHelper::tryDeselectCurrentSelection(const QRectF selectionV
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()));
......
......@@ -53,6 +53,7 @@ KisSelectionOptions::KisSelectionOptions(KisCanvas2 * /*canvas*/)
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 @@ KisSelectionOptions::KisSelectionOptions(KisCanvas2 * /*canvas*/)
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 @@ void KisSelectionOptions::updateActionButtonToolTip(int action, const QKeySequen
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;
}
}
......
......@@ -35,6 +35,7 @@ public Q_SLOTS:
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:
......
......@@ -59,6 +59,7 @@ struct Q_DECL_HIDDEN KisSelectionModifierMapper::Private
Qt::KeyboardModifiers intersectModifiers;
Qt::KeyboardModifiers addModifiers;
Qt::KeyboardModifiers subtractModifiers;
Qt::KeyboardModifiers symmetricdifferenceModifiers;
};
......@@ -94,10 +95,12 @@ void KisSelectionModifierMapper::Private::slotConfigChanged()
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 @@ SelectionAction KisSelectionModifierMapper::Private::map(Qt::KeyboardModifiers m
newAction = SELECTION_ADD;
} else if (m == subtractModifiers) {
newAction = SELECTION_SUBTRACT;
} else if (m == symmetricdifferenceModifiers) {
newAction = SELECTION_SYMMETRICDIFFERENCE;
}
return newAction;
}
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