Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit 5120b702 authored by Anna Medonosová's avatar Anna Medonosová

Gamut masking: mask rotation, advanced selector, tweaks

Summary:
This diff includes:
  - implementation for the advanced color selector (T9643),
  - common gamut masking toolbar for both advanced and artistic selectors (M135, M136),
  - mask rotation (as defined in T9642).

There are also some minor tweaks:
  - adaptive highlight of selected swatches - dark when the selected color is light, light when the selected color is dark;
  - gamut mask preview while editing is always on;
  - the blip in the artistic color selector is always on;
  - the text size of the comparative gray scale in the artistic selector is computed so it fits inside the rectangles.

Reviewers: #krita, rempt

Reviewed By: #krita, rempt

Subscribers: rempt, kamathraghavendra, lsegovia, scottpetrovic

Tags: #krita

Differential Revision: https://phabricator.kde.org/D15860
parent 0104ab76
......@@ -37,6 +37,11 @@ KisGamutMaskViewConverter::~KisGamutMaskViewConverter()
{
}
QSize KisGamutMaskViewConverter::viewSize() const
{
return QSize(m_viewSize, m_viewSize);
}
QPointF KisGamutMaskViewConverter::documentToView(const QPointF &documentPoint) const
{
return QPointF(documentToViewX(documentPoint.x()), documentToViewY(documentPoint.y()));
......
......@@ -36,7 +36,9 @@ public:
KisGamutMaskViewConverter();
~KisGamutMaskViewConverter();
QSize viewSize() const;
void setViewSize(QSize viewSize);
void setMaskSize(QSizeF maskSize);
QPointF documentToView(const QPointF &documentPoint) const override;
......
......@@ -54,26 +54,49 @@ KoShape* KoGamutMaskShape::koShape()
return m_maskShape;
}
bool KoGamutMaskShape::coordIsClear(const QPointF& coord, const KoViewConverter& viewConverter) const
bool KoGamutMaskShape::coordIsClear(const QPointF& coord, const KoViewConverter& viewConverter, int maskRotation) const
{
QPointF translatedPoint = viewConverter.viewToDocument(coord);
// apply mask rotation to coord
const KisGamutMaskViewConverter& converter = dynamic_cast<const KisGamutMaskViewConverter&>(viewConverter);
QPointF centerPoint(converter.viewSize().width()*0.5, converter.viewSize().height()*0.5);
QTransform rotationTransform;
rotationTransform.translate(centerPoint.x(), centerPoint.y());
rotationTransform.rotate(-maskRotation);
rotationTransform.translate(-centerPoint.x(), -centerPoint.y());
QPointF rotatedCoord = rotationTransform.map(coord);
QPointF translatedPoint = viewConverter.viewToDocument(rotatedCoord);
bool isClear = m_maskShape->hitTest(translatedPoint);
return isClear;
}
void KoGamutMaskShape::paint(QPainter &painter, const KoViewConverter& viewConverter)
void KoGamutMaskShape::paint(QPainter &painter, const KoViewConverter& viewConverter, int maskRotation)
{
painter.save();
// apply mask rotation before drawing
QPointF centerPoint(painter.viewport().width()*0.5, painter.viewport().height()*0.5);
painter.translate(centerPoint);
painter.rotate(maskRotation);
painter.translate(-centerPoint);
painter.setTransform(m_maskShape->absoluteTransformation(&viewConverter) * painter.transform());
m_maskShape->paint(painter, viewConverter, m_shapePaintingContext);
painter.restore();
}
void KoGamutMaskShape::paintStroke(QPainter &painter, const KoViewConverter &viewConverter)
void KoGamutMaskShape::paintStroke(QPainter &painter, const KoViewConverter &viewConverter, int maskRotation)
{
painter.save();
// apply mask rotation before drawing
QPointF centerPoint(painter.viewport().width()*0.5, painter.viewport().height()*0.5);
painter.translate(centerPoint);
painter.rotate(maskRotation);
painter.translate(-centerPoint);
painter.setTransform(m_maskShape->absoluteTransformation(&viewConverter) * painter.transform());
m_maskShape->paintStroke(painter, viewConverter, m_shapePaintingContext);
painter.restore();
......@@ -88,6 +111,7 @@ struct Q_DECL_HIDDEN KoGamutMask::Private {
QVector<KoGamutMaskShape*> maskShapes;
QVector<KoGamutMaskShape*> previewShapes;
QSizeF maskSize;
int rotation;
};
KoGamutMask::KoGamutMask(const QString& filename)
......@@ -95,6 +119,7 @@ KoGamutMask::KoGamutMask(const QString& filename)
, d(new Private())
{
d->maskSize = QSizeF(144.0,144.0);
setRotation(0);
}
KoGamutMask::KoGamutMask()
......@@ -102,6 +127,7 @@ KoGamutMask::KoGamutMask()
, d(new Private())
{
d->maskSize = QSizeF(144.0,144.0);
setRotation(0);
}
KoGamutMask::KoGamutMask(KoGamutMask* rhs)
......@@ -136,7 +162,7 @@ bool KoGamutMask::coordIsClear(const QPointF& coord, KoViewConverter &viewConver
}
for(KoGamutMaskShape* shape: *shapeVector) {
if (shape->coordIsClear(coord, viewConverter) == true) {
if (shape->coordIsClear(coord, viewConverter, rotation()) == true) {
return true;
}
}
......@@ -155,7 +181,7 @@ void KoGamutMask::paint(QPainter &painter, KoViewConverter& viewConverter, bool
}
for(KoGamutMaskShape* shape: *shapeVector) {
shape->paint(painter, viewConverter);
shape->paint(painter, viewConverter, rotation());
}
}
......@@ -170,7 +196,7 @@ void KoGamutMask::paintStroke(QPainter &painter, KoViewConverter &viewConverter,
}
for(KoGamutMaskShape* shape: *shapeVector) {
shape->paintStroke(painter, viewConverter);
shape->paintStroke(painter, viewConverter, rotation());
}
}
......@@ -364,6 +390,16 @@ void KoGamutMask::setDescription(QString description)
d->description = description;
}
int KoGamutMask::rotation()
{
return d->rotation;
}
void KoGamutMask::setRotation(int rotation)
{
d->rotation = rotation;
}
QSizeF KoGamutMask::maskSize()
{
return d->maskSize;
......
......@@ -38,10 +38,10 @@ public:
KoGamutMaskShape();
~KoGamutMaskShape();
bool coordIsClear(const QPointF& coord, const KoViewConverter& viewConverter) const;
bool coordIsClear(const QPointF& coord, const KoViewConverter& viewConverter, int maskRotation) const;
QPainterPath outline();
void paint(QPainter &painter, const KoViewConverter& viewConverter);
void paintStroke(QPainter &painter, const KoViewConverter& viewConverter);
void paint(QPainter &painter, const KoViewConverter& viewConverter, int maskRotation);
void paintStroke(QPainter &painter, const KoViewConverter& viewConverter, int maskRotation);
KoShape* koShape();
private:
......@@ -78,6 +78,9 @@ public:
QString description();
void setDescription(QString description);
int rotation();
void setRotation(int rotation);
QSizeF maskSize();
void setMaskShapes(QList<KoShape*> shapes);
......
......@@ -261,6 +261,7 @@ set(kritaui_LIB_SRCS
widgets/KoStrokeConfigWidget.cpp
widgets/KoFillConfigWidget.cpp
widgets/KisNewsWidget.cpp
widgets/KisGamutMaskToolbar.cpp
utils/kis_document_aware_spin_box_unit_manager.cpp
......@@ -491,6 +492,7 @@ ki18n_wrap_ui(kritaui_LIB_SRCS
forms/wdgnewwindowlayout.ui
forms/KisWelcomePage.ui
forms/KisNewsPage.ui
forms/wdgGamutMaskToolbar.ui
brushhud/kis_dlg_brush_hud_config.ui
dialogs/kis_delayed_save_dialog.ui
......
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>wdgGamutMaskToolbar</class>
<widget class="QWidget" name="wdgGamutMaskToolbar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>378</width>
<height>57</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<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>
<item>
<widget class="QPushButton" name="bnToggleMask">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="toolTip">
<string>Toggle gamut mask</string>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelMaskName">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Select a mask in &quot;Gamut Masks&quot; docker</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="KisSliderSpinBox" name="rotationSlider" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisSliderSpinBox</class>
<extends>QWidget</extends>
<header>kis_slider_spin_box.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
/*
* Copyright (c) 2018 Anna Medonosova <anna.medonosova@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <QWidget>
#include "KisGamutMaskToolbar.h"
#include <kis_icon_utils.h>
#include <kis_canvas_resource_provider.h>
KisGamutMaskToolbar::KisGamutMaskToolbar(QWidget* parent) : QWidget(parent)
, m_selectedMask(nullptr)
{
m_ui = new Ui_wdgGamutMaskToolbar();
m_ui->setupUi(this);
m_iconMaskOff = KisIconUtils::loadIcon("gamut-mask-off");
m_iconMaskOn = KisIconUtils::loadIcon("gamut-mask-on");
m_textNoMask = i18n("Select a mask in \"Gamut Masks\" docker");
m_textMaskDisabled = i18n("Mask is disabled");
m_ui->bnToggleMask->setChecked(false);
m_ui->bnToggleMask->setIcon(m_iconMaskOff);
m_ui->rotationSlider->setRange(0, 360);
m_ui->rotationSlider->setPrefix(i18n("Rotation: "));
m_ui->rotationSlider->setSuffix("°");
m_ui->rotationSlider->setFastSliderStep(30); // TODO: test for usability
m_ui->rotationSlider->hide();
// gamut mask connections
connect(m_ui->bnToggleMask, SIGNAL(toggled(bool)), SLOT(slotGamutMaskToggle(bool)));
connect(m_ui->rotationSlider, SIGNAL(valueChanged(int)), SLOT(slotGamutMaskRotate(int)));
}
void KisGamutMaskToolbar::connectMaskSignals(KisCanvasResourceProvider* resourceProvider)
{
connect(resourceProvider, SIGNAL(sigGamutMaskChanged(KoGamutMask*)),
this, SLOT(slotGamutMaskSet(KoGamutMask*)));
connect(resourceProvider, SIGNAL(sigGamutMaskUnset()),
this, SLOT(slotGamutMaskUnset()));
connect(this, SIGNAL(sigGamutMaskChanged(KoGamutMask*)),
resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMask*)));
}
void KisGamutMaskToolbar::slotGamutMaskSet(KoGamutMask *mask)
{
if (!mask) {
return;
}
m_selectedMask = mask;
if (m_selectedMask) {
slotGamutMaskToggle(true);
} else {
slotGamutMaskToggle(false);
}
}
void KisGamutMaskToolbar::slotGamutMaskUnset()
{
m_ui->rotationSlider->hide();
m_ui->labelMaskName->show();
m_ui->labelMaskName->setText(m_textNoMask);
}
void KisGamutMaskToolbar::slotGamutMaskToggle(bool state)
{
bool b = (!m_selectedMask) ? false : state;
m_ui->bnToggleMask->setChecked(b);
if (b == true) {
m_ui->bnToggleMask->setIcon(m_iconMaskOn);
m_ui->labelMaskName->hide();
m_ui->rotationSlider->show();
m_ui->rotationSlider->blockSignals(true);
m_ui->rotationSlider->setValue(m_selectedMask->rotation());
m_ui->rotationSlider->blockSignals(false);
} else {
m_ui->bnToggleMask->setIcon(m_iconMaskOff);
m_ui->rotationSlider->hide();
m_ui->labelMaskName->show();
m_ui->labelMaskName->setText(m_textMaskDisabled);
}
emit sigGamutMaskToggle(state);
}
void KisGamutMaskToolbar::slotGamutMaskRotate(int angle)
{
if (!m_selectedMask) {
return;
}
m_selectedMask->setRotation(angle);
emit sigGamutMaskChanged(m_selectedMask);
}
/*
* Copyright (c) 2018 Anna Medonosova <anna.medonosova@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KISGAMUTMASKTOOLBAR_H
#define KISGAMUTMASKTOOLBAR_H
#include <QWidget>
#include <QIcon>
#include <resources/KoGamutMask.h>
#include "kritaui_export.h"
#include "ui_wdgGamutMaskToolbar.h"
class KisCanvasResourceProvider;
class KRITAUI_EXPORT KisGamutMaskToolbar : public QWidget
{
Q_OBJECT
public:
KisGamutMaskToolbar(QWidget* parent = nullptr);
void connectMaskSignals(KisCanvasResourceProvider* resourceProvider);
Q_SIGNALS:
void sigGamutMaskToggle(bool state);
void sigGamutMaskChanged(KoGamutMask*);
public Q_SLOTS:
void slotGamutMaskSet(KoGamutMask* mask);
void slotGamutMaskUnset();
private Q_SLOTS:
void slotGamutMaskToggle(bool state);
void slotGamutMaskRotate(int angle);
private:
Ui_wdgGamutMaskToolbar* m_ui;
KoGamutMask* m_selectedMask;
QIcon m_iconMaskOff;
QIcon m_iconMaskOn;
QString m_textNoMask;
QString m_textMaskDisabled;
};
#endif // KISGAMUTMASKTOOLBAR_H
......@@ -149,9 +149,38 @@ void KisColorSelector::updateSettings()
{
KisColorSelectorBase::updateSettings();
KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
setConfiguration(KisColorSelectorConfiguration::fromString(cfg.readEntry("colorSelectorConfiguration", KisColorSelectorConfiguration().toString())));
}
void KisColorSelector::slotGamutMaskSet(KoGamutMask *gamutMask)
{
m_mainComponent->setGamutMask(gamutMask);
m_subComponent->setGamutMask(gamutMask);
slotGamutMaskToggle(true);
}
void KisColorSelector::slotGamutMaskUnset()
{
m_mainComponent->unsetGamutMask();
m_subComponent->unsetGamutMask();
slotGamutMaskToggle(false);
}
void KisColorSelector::slotGamutMaskPreviewUpdate()
{
m_mainComponent->updateGamutMaskPreview();
m_subComponent->updateGamutMaskPreview();
}
void KisColorSelector::slotGamutMaskToggle(bool state)
{
m_mainComponent->toggleGamutMask(state);
m_subComponent->toggleGamutMask(state);
}
void KisColorSelector::updateIcons() {
if (m_button) {
m_button->setIcon(KisIconUtils::loadIcon("configure"));
......
......@@ -50,7 +50,10 @@ public:
public Q_SLOTS:
void reset() override;
void updateSettings() override;
void slotGamutMaskSet(KoGamutMask* gamutMask);
void slotGamutMaskUnset();
void slotGamutMaskPreviewUpdate();
void slotGamutMaskToggle(bool state);
Q_SIGNALS:
void settingsButtonClicked();
......
......@@ -40,6 +40,7 @@
#include "kis_image.h"
#include "kis_display_color_converter.h"
#include <resources/KoGamutMask.h>
class KisColorPreviewPopup : public QWidget {
public:
......
......@@ -32,6 +32,7 @@ class KoColorSpace;
class KisCanvas2;
class KisColorPreviewPopup;
class KisDisplayColorConverter;
class KoGamutMask;
/// Base class for all color selectors, that should support color management and zooming.
......
......@@ -22,6 +22,7 @@
#include "KoColorSpace.h"
#include <QPainter>
#include <QMouseEvent>
#include <resources/KoGamutMask.h>
KisColorSelectorComponent::KisColorSelectorComponent(KisColorSelector* parent) :
......@@ -36,14 +37,17 @@ KisColorSelectorComponent::KisColorSelectorComponent(KisColorSelector* parent) :
m_hsySaturation(1),
m_luma(0.299),
m_parent(parent),
m_width(0),
m_height(0),
m_gamutMaskOn(false),
m_currentGamutMask(nullptr),
m_maskPreviewActive(true),
m_lastX(0),
m_lastY(0),
m_x(0),
m_y(0),
m_width(0),
m_height(0),
m_dirty(true),
m_lastColorSpace(0),
m_lastX(0),
m_lastY(0)
m_lastColorSpace(0)
{
Q_ASSERT(parent);
}
......@@ -90,6 +94,32 @@ void KisColorSelectorComponent::setDirty()
m_dirty = true;
}
void KisColorSelectorComponent::setGamutMask(KoGamutMask *gamutMask)
{
m_currentGamutMask = gamutMask;
m_gamutMaskOn = true;
}
void KisColorSelectorComponent::unsetGamutMask()
{
m_gamutMaskOn = false;
m_currentGamutMask = nullptr;
}
void KisColorSelectorComponent::updateGamutMaskPreview()
{
setDirty();
update();
}
void KisColorSelectorComponent::toggleGamutMask(bool state)
{
m_gamutMaskOn = state;
setDirty();
update();
}
bool KisColorSelectorComponent::isDirty() const
{
return m_dirty || m_lastColorSpace!=colorSpace();
......
......@@ -20,6 +20,9 @@
#include <QObject>
#include <QColor>
#include <resources/KoGamutMask.h>
#include "kis_color_selector.h"
class KoColorSpace;
......@@ -39,7 +42,7 @@ public:
void paintEvent(QPainter*);
/// saves the mouse position, so that a blip can be created.
void mouseEvent(int x, int y);
virtual void mouseEvent(int x, int y);
/// return the color, that was selected by calling mouseEvent
KoColor currentColor();
......@@ -59,6 +62,11 @@ public:
/// returns true, if this component wants to grab the mouse (normally true, if containsPoint returns true)
bool wantsGrab(int x, int y) {return containsPointInComponentCoords(x-m_x, y-m_y);}
void setGamutMask(KoGamutMask* gamutMask);
void unsetGamutMask();
void updateGamutMaskPreview();
void toggleGamutMask(bool state);
public Q_SLOTS:
/// set hue, saturation, value or/and lightness
/// unused parameters should be set to -1
......@@ -100,15 +108,18 @@ protected:
Parameter m_parameter;
Type m_type;
KisColorSelector* m_parent;
bool m_gamutMaskOn;
KoGamutMask* m_currentGamutMask;
bool m_maskPreviewActive;
qreal m_lastX;
qreal m_lastY;
int m_x;
int m_y;
private:
int m_width;
int m_height;
int m_x;
int m_y;
bool m_dirty;
const KoColorSpace* m_lastColorSpace;
qreal m_lastX;
qreal m_lastY;
};
#endif // KIS_COLOR_SELECTOR_COMPONENT_H
......@@ -33,6 +33,7 @@
#include <kactioncollection.h>
#include <KisDocument.h>
#include <KisGamutMaskToolbar.h>
#include "KisViewManager.h"
#include "kis_canvas2.h"
#include "kis_canvas_resource_provider.h"
......@@ -48,16 +49,25 @@ KisColorSelectorContainer::KisColorSelectorContainer(QWidget *parent) :
m_myPaintShadeSelector(new KisMyPaintShadeSelector(this)),
m_minimalShadeSelector(new KisMinimalShadeSelector(this)),
m_shadeSelector(m_myPaintShadeSelector),
m_gamutMaskToolbar(new KisGamutMaskToolbar(this)),
m_canvas(0)
{
m_widgetLayout = new QBoxLayout(QBoxLayout::TopToBottom, this);
m_widgetLayout-><