diff --git a/krita/data/CMakeLists.txt b/krita/data/CMakeLists.txt --- a/krita/data/CMakeLists.txt +++ b/krita/data/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory( palettes ) add_subdirectory( symbols ) add_subdirectory( preset_icons ) +add_subdirectory( gamutmasks ) ########### install files ############### diff --git a/krita/data/gamutmasks/CMakeLists.txt b/krita/data/gamutmasks/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/krita/data/gamutmasks/CMakeLists.txt @@ -0,0 +1,9 @@ +########### install files ############### + +install( FILES + testing1.kgm + testing2.kgm + GamutMaskTemplate.kra + new_mask.kgm +DESTINATION ${DATA_INSTALL_DIR}/krita/gamutmasks) + diff --git a/krita/data/gamutmasks/GamutMaskTemplate.kra b/krita/data/gamutmasks/GamutMaskTemplate.kra new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ + + + + + + + + + + image/svg+xml + + + 2016 + + + Timothée Giet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/krita/pics/svg/dark_wheel-light.svg b/krita/pics/svg/dark_wheel-light.svg new file mode 100644 --- /dev/null +++ b/krita/pics/svg/dark_wheel-light.svg @@ -0,0 +1,1500 @@ + + + + + + + + + + + image/svg+xml + + + 2016 + + + Timothée Giet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/krita/pics/svg/dark_wheel-rings.svg b/krita/pics/svg/dark_wheel-rings.svg new file mode 100644 --- /dev/null +++ b/krita/pics/svg/dark_wheel-rings.svg @@ -0,0 +1,1489 @@ + + + + + + + + + + + image/svg+xml + + + 2016 + + + Timothée Giet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/krita/pics/svg/dark_wheel-sectors.svg b/krita/pics/svg/dark_wheel-sectors.svg new file mode 100644 --- /dev/null +++ b/krita/pics/svg/dark_wheel-sectors.svg @@ -0,0 +1,1499 @@ + + + + + + + + + + + image/svg+xml + + + 2016 + + + Timothée Giet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/krita/pics/svg/light_infinity.svg b/krita/pics/svg/light_infinity.svg new file mode 100644 --- /dev/null +++ b/krita/pics/svg/light_infinity.svg @@ -0,0 +1,1501 @@ + + + + + + + + + + + image/svg+xml + + + 2016 + + + Timothée Giet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/krita/pics/svg/light_wheel-light.svg b/krita/pics/svg/light_wheel-light.svg new file mode 100644 --- /dev/null +++ b/krita/pics/svg/light_wheel-light.svg @@ -0,0 +1,1500 @@ + + + + + + + + + + + image/svg+xml + + + 2016 + + + Timothée Giet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/krita/pics/svg/light_wheel-rings.svg b/krita/pics/svg/light_wheel-rings.svg new file mode 100644 --- /dev/null +++ b/krita/pics/svg/light_wheel-rings.svg @@ -0,0 +1,1507 @@ + + + + + + + + + + + image/svg+xml + + + 2016 + + + Timothée Giet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/krita/pics/svg/light_wheel-sectors.svg b/krita/pics/svg/light_wheel-sectors.svg new file mode 100644 --- /dev/null +++ b/krita/pics/svg/light_wheel-sectors.svg @@ -0,0 +1,1517 @@ + + + + + + + + + + + image/svg+xml + + + 2016 + + + Timothée Giet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/krita/pics/svg/svg-icons.qrc b/krita/pics/svg/svg-icons.qrc --- a/krita/pics/svg/svg-icons.qrc +++ b/krita/pics/svg/svg-icons.qrc @@ -1,6 +1,5 @@ - - - + + broken-preset.svgz dark_addblankframe.svg dark_addcolor.svg @@ -92,34 +91,28 @@ dark_color-to-alpha.svg light_preset-switcher.svg dark_preset-switcher.svg - dark_animation_play.svg dark_animation_stop.svg dark_dropframe.svg dark_droppedframes.svg - light_animation_play.svg light_animation_stop.svg light_dropframe.svg light_droppedframes.svg - dark_landscape.svg dark_portrait.svg light_landscape.svg light_portrait.svg - dark_interpolation_constant.svg dark_interpolation_linear.svg dark_interpolation_bezier.svg dark_interpolation_sharp.svg dark_interpolation_smooth.svg - light_interpolation_bezier.svg light_interpolation_constant.svg light_interpolation_linear.svg light_interpolation_sharp.svg light_interpolation_smooth.svg - dark_audio-none.svg dark_audio-volume-high.svg dark_audio-volume-mute.svg @@ -136,7 +129,6 @@ light_zoom-fit.svg light_zoom-horizontal.svg light_zoom-vertical.svg - dark_showColoring.svg dark_showMarks.svg dark_showColoringOff.svg @@ -147,6 +139,13 @@ light_showColoringOff.svg light_showMarksOff.svg light_updateColorize.svg - + light_wheel-light.svg + light_wheel-rings.svg + light_wheel-sectors.svg + dark_wheel-light.svg + dark_wheel-rings.svg + dark_wheel-sectors.svg + dark_infinity.svg + light_infinity.svg diff --git a/libs/flake/CMakeLists.txt b/libs/flake/CMakeLists.txt --- a/libs/flake/CMakeLists.txt +++ b/libs/flake/CMakeLists.txt @@ -119,6 +119,7 @@ KoClipMask.cpp KoClipMaskPainter.cpp KoCurveFit.cpp + KisGamutMaskViewConverter.cpp commands/KoShapeGroupCommand.cpp commands/KoShapeAlignCommand.cpp @@ -217,6 +218,7 @@ text/KoSvgTextShapeMarkupConverter.cpp resources/KoSvgSymbolCollectionResource.cpp + resources/KoGamutMask.cpp FlakeDebug.cpp tests/MockShapes.cpp diff --git a/libs/flake/KisGamutMaskViewConverter.h b/libs/flake/KisGamutMaskViewConverter.h new file mode 100644 --- /dev/null +++ b/libs/flake/KisGamutMaskViewConverter.h @@ -0,0 +1,51 @@ +#ifndef KISGAMUTMASKVIEWCONVERTER_H +#define KISGAMUTMASKVIEWCONVERTER_H + +#include "kritaflake_export.h" + +#include +#include +#include + +class QPointF; +class QRectF; + +/** + * @brief view convertor for gamut mask calculations and painting; 0,0 in the center + */ +class KRITAFLAKE_EXPORT KisGamutMaskViewConverter : public KoViewConverter +{ +public: + KisGamutMaskViewConverter(); + ~KisGamutMaskViewConverter(); + + void setViewSize(QSize viewSize); + void setMaskSize(QSizeF maskSize); + + QPointF documentToView(const QPointF &documentPoint) const override; + QPointF viewToDocument(const QPointF &viewPoint) const override; + + QRectF documentToView(const QRectF &documentRect) const override; + QRectF viewToDocument(const QRectF &viewRect) const override; + + QSizeF documentToView(const QSizeF& documentSize) const override; + QSizeF viewToDocument(const QSizeF& viewSize) const override; + + qreal documentToViewX(qreal documentX) const override; + qreal documentToViewY(qreal documentY) const override; + qreal viewToDocumentX(qreal viewX) const override; + qreal viewToDocumentY(qreal viewY) const override; + + void setZoom(qreal zoom) override; + void zoom(qreal *zoomX, qreal *zoomY) const override; + +private: + void computeAndSetZoom(); + + qreal m_zoomLevel; // 1.0 is 100% + int m_viewSize; + QSizeF m_maskSize; + qreal m_maskResolution; +}; + +#endif // KISGAMUTMASKVIEWCONVERTER_H diff --git a/libs/flake/KisGamutMaskViewConverter.cpp b/libs/flake/KisGamutMaskViewConverter.cpp new file mode 100644 --- /dev/null +++ b/libs/flake/KisGamutMaskViewConverter.cpp @@ -0,0 +1,152 @@ +#include "KisGamutMaskViewConverter.h" + +#include +#include +#include + +#include + +//#define DEBUG_GAMUT_MASK_CONVERTER + +KisGamutMaskViewConverter::KisGamutMaskViewConverter() + : m_viewSize(1.0) + , m_maskSize(QSizeF(1,1)) + , m_maskResolution(1) +{ + computeAndSetZoom(); +} + +KisGamutMaskViewConverter::~KisGamutMaskViewConverter() +{ +} + +QPointF KisGamutMaskViewConverter::documentToView(const QPointF &documentPoint) const +{ + return QPointF(documentToViewX(documentPoint.x()), documentToViewY(documentPoint.y())); +} + + +QPointF KisGamutMaskViewConverter::viewToDocument(const QPointF &viewPoint) const +{ + return QPointF(viewToDocumentX(viewPoint.x()), viewToDocumentY(viewPoint.y())); +} + +QRectF KisGamutMaskViewConverter::documentToView(const QRectF &documentRect) const +{ + return QRectF(documentToView(documentRect.topLeft()), documentToView(documentRect.size())); +} + +QRectF KisGamutMaskViewConverter::viewToDocument(const QRectF &viewRect) const +{ + return QRectF(viewToDocument(viewRect.topLeft()), viewToDocument(viewRect.size())); +} + +QSizeF KisGamutMaskViewConverter::documentToView(const QSizeF &documentSize) const +{ + return QSizeF(documentToViewX(documentSize.width()), documentToViewY(documentSize.height())); +} + +QSizeF KisGamutMaskViewConverter::viewToDocument(const QSizeF &viewSize) const +{ + return QSizeF(viewToDocumentX(viewSize.width()), viewToDocumentY(viewSize.height())); +} + +qreal KisGamutMaskViewConverter::documentToViewX(qreal documentX) const +{ + qreal translated = documentX * m_zoomLevel; + +#ifdef DEBUG_GAMUT_MASK_CONVERTER + debugFlake << "KisGamutMaskViewConverter::DocumentToViewX: " + << "documentX: " << documentX + << " -> translated: " << translated; +#endif + + return translated; +} + +qreal KisGamutMaskViewConverter::documentToViewY(qreal documentY) const +{ + qreal translated = documentY * m_zoomLevel; + +#ifdef DEBUG_GAMUT_MASK_CONVERTER + debugFlake << "KisGamutMaskViewConverter::DocumentToViewY: " + << "documentY: " << documentY + << " -> translated: " << translated; +#endif + + return translated; +} + +qreal KisGamutMaskViewConverter::viewToDocumentX(qreal viewX) const +{ + qreal translated = viewX / m_zoomLevel; + +#ifdef DEBUG_GAMUT_MASK_CONVERTER + debugFlake << "KisGamutMaskViewConverter::viewToDocumentX: " + << "viewX: " << viewX + << " -> translated: " << translated; +#endif + + return translated; +} + +qreal KisGamutMaskViewConverter::viewToDocumentY(qreal viewY) const +{ + qreal translated = viewY / m_zoomLevel; + +#ifdef DEBUG_GAMUT_MASK_CONVERTER + debugFlake << "KisGamutMaskViewConverter::viewToDocumentY: " + << "viewY: " << viewY + << " -> translated: " << translated; +#endif + + return translated; +} + + +void KisGamutMaskViewConverter::setZoom(qreal zoom) +{ + if (qFuzzyCompare(zoom, qreal(0.0)) || qFuzzyCompare(zoom, qreal(1.0))) { + zoom = 1; + } + +#ifdef DEBUG_GAMUT_MASK_CONVERTER + debugFlake << "KisGamutMaskViewConverter::setZoom: setting to " << zoom; +#endif + + m_zoomLevel = zoom; +} + +void KisGamutMaskViewConverter::zoom(qreal *zoomX, qreal *zoomY) const +{ + *zoomX = m_zoomLevel; + *zoomY = m_zoomLevel; +} + +void KisGamutMaskViewConverter::setViewSize(QSize viewSize) +{ + m_viewSize = viewSize.width(); + + computeAndSetZoom(); +} + +void KisGamutMaskViewConverter::setMaskSize(QSizeF maskSize) +{ + m_maskSize = maskSize; + m_maskResolution = maskSize.width(); + + computeAndSetZoom(); +} + +void KisGamutMaskViewConverter::computeAndSetZoom() +{ + qreal zoom = m_viewSize / m_maskResolution; + +#ifdef DEBUG_GAMUT_MASK_CONVERTER + debugFlake << "KisGamutMaskViewConverter::computeAndSetZoom: " + << "m_viewSize: " << m_viewSize + << " m_maskSize: " << m_maskResolution; +#endif + + setZoom(zoom); +} diff --git a/libs/flake/resources/KoGamutMask.h b/libs/flake/resources/KoGamutMask.h new file mode 100644 --- /dev/null +++ b/libs/flake/resources/KoGamutMask.h @@ -0,0 +1,88 @@ +#ifndef KOGAMUTMASK_H +#define KOGAMUTMASK_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +class KoViewConverter; + +class KoGamutMaskShape +{ +public: + KoGamutMaskShape(KoShape* shape); + KoGamutMaskShape(); + ~KoGamutMaskShape(); + + bool coordIsClear(const QPointF& coord, const KoViewConverter& viewConverter) const; + QPainterPath outline(); + void paint(QPainter &painter, const KoViewConverter& viewConverter); + KoShape* koShape(); + +private: + KoShape* m_maskShape; + KoShapePaintingContext m_shapePaintingContext; +}; + + +/** + * @brief The resource type for gamut masks used by the artistic color selector + */ +class KRITAFLAKE_EXPORT KoGamutMask : public QObject, public KoResource +{ + Q_OBJECT + +public: + /** + * @brief load a gamut mask from given file + * @param filename + */ + KoGamutMask(const QString &filename); + + /** + * @brief create KoGamutMask from polygons + * @param polygons + */ + KoGamutMask(); + + // TODO: copy constructor, for duplicating masks + KoGamutMask(KoGamutMask *rhs); + + bool coordIsClear(const QPointF& coord, KoViewConverter& viewConverter, bool preview); + + bool load() override __attribute__((optimize(0))); + bool loadFromDevice(QIODevice *dev) override; + bool save() override; + bool saveToDevice(QIODevice* dev) const override; + + void paint(QPainter &painter, KoViewConverter& viewConverter, bool preview); + + QString title(); + void setTitle(QString title); + + QString description(); + void setDescription(QString description); + + QSizeF maskSize(); + + void setMaskShapes(QList shapes); + void setPreviewMaskShapes(QList shapes); + void setMaskShapesToVector(QList shapes, QVector& targetVector); + + QList koShapes() const; + + // switch back to loaded shapes when ending mask preview + void clearPreview(); +private: + struct Private; + Private* const d; +}; + +#endif // KOGAMUTMASK_H diff --git a/libs/flake/resources/KoGamutMask.cpp b/libs/flake/resources/KoGamutMask.cpp new file mode 100644 --- /dev/null +++ b/libs/flake/resources/KoGamutMask.cpp @@ -0,0 +1,350 @@ +#include "KoGamutMask.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +KoGamutMaskShape::KoGamutMaskShape(KoShape* shape) + : m_maskShape(shape) + , m_shapePaintingContext(KoShapePaintingContext()) +{ +} + +KoGamutMaskShape::KoGamutMaskShape() {}; +KoGamutMaskShape::~KoGamutMaskShape() {}; + +KoShape* KoGamutMaskShape::koShape() +{ + return m_maskShape; +} + +bool KoGamutMaskShape::coordIsClear(const QPointF& coord, const KoViewConverter& viewConverter) const +{ + QPointF translatedPoint = viewConverter.viewToDocument(coord); + + bool isClear = m_maskShape->hitTest(translatedPoint); + + return isClear; +} + +void KoGamutMaskShape::paint(QPainter &painter, const KoViewConverter& viewConverter) +{ + painter.save(); + painter.setTransform(m_maskShape->absoluteTransformation(&viewConverter) * painter.transform()); + m_maskShape->paint(painter, viewConverter, m_shapePaintingContext); + painter.restore(); +} + + +struct Q_DECL_HIDDEN KoGamutMask::Private { + QString name; + QString title; + QString description; + QByteArray data; + QVector maskShapes; + QVector previewShapes; + QSizeF maskSize; // at 100DPI +}; + +KoGamutMask::KoGamutMask(const QString& filename) + : KoResource(filename) + , d(new Private()) +{ + +} + +KoGamutMask::KoGamutMask() + : KoResource(QString()) + , d(new Private()) +{ +} + +KoGamutMask::KoGamutMask(KoGamutMask* rhs) + : QObject(0) + , KoResource(QString()) + , d(new Private()) +{ + setFilename(rhs->filename()); + setTitle(rhs->title()); + setDescription(rhs->description()); + d->maskSize = rhs->d->maskSize; + + QList newShapes; + for(KoGamutMaskShape* sh: d->maskShapes) { + KoShape* shape = sh->koShape(); + newShapes.append(shape); + } + + setMaskShapes(newShapes); + + setValid(true); +} + + +bool KoGamutMask::coordIsClear(const QPointF& coord, KoViewConverter &viewConverter, bool preview) +{ + QVector* shapeVector; + + if (preview && !d->previewShapes.isEmpty()) { + shapeVector = &d->previewShapes; + } else { + shapeVector = &d->maskShapes; + } + + for(KoGamutMaskShape* shape: *shapeVector) { + if (shape->coordIsClear(coord, viewConverter) == true) { + return true; + } + } + + return false; +} + +void KoGamutMask::paint(QPainter &painter, KoViewConverter& viewConverter, bool preview) +{ + QVector* shapeVector; + + if (preview && !d->previewShapes.isEmpty()) { + shapeVector = &d->previewShapes; + } else { + shapeVector = &d->maskShapes; + } + + for(KoGamutMaskShape* shape: *shapeVector) { + shape->paint(painter, viewConverter); + } +} + +bool KoGamutMask::load() +{ + QFile file(filename()); + if (file.size() == 0) return false; + if (!file.open(QIODevice::ReadOnly)) { + warnFlake << "Can't open file " << filename(); + return false; + } + bool res = loadFromDevice(&file); + setValid(res); + file.close(); + return res; +} + +bool KoGamutMask::loadFromDevice(QIODevice *dev) +{ + if (!dev->isOpen()) dev->open(QIODevice::ReadOnly); + + d->data = dev->readAll(); + + Q_ASSERT(d->data.size() != 0); + + if (filename().isNull()) { + warnFlake << "Cannot load gamut mask" << name() << "there is no filename set"; + return false; + } + + if (d->data.isNull()) { + QFile file(filename()); + if (file.size() == 0) { + warnFlake << "Cannot load gamut mask" << name() << "there is no data available"; + return false; + } + + file.open(QIODevice::ReadOnly); + d->data = file.readAll(); + file.close(); + } + + QBuffer buf(&d->data); + buf.open(QBuffer::ReadOnly); + + KoStore* store(KoStore::createStore(&buf, KoStore::Read, "application/x-krita-gamutmask", KoStore::Zip)); + if (!store || store->bad()) return false; + + bool storeOpened = store->open("gamutmask.svg"); + if (!storeOpened) { return false; } + + QByteArray data; + data.resize(store->size()); + QByteArray ba = store->read(store->size()); + store->close(); + + KoXmlDocument xmlDocument; + QString errorMsg; + int errorLine = 0; + int errorColumn; + + bool ok = xmlDocument.setContent(ba, false, &errorMsg, &errorLine, &errorColumn); + if (!ok) { + + errorFlake << "Parsing error in " << filename() << "! Aborting!" << endl + << " In line: " << errorLine << ", column: " << errorColumn << endl + << " Error message: " << errorMsg << endl; + errorFlake << "Parsing error in the main document at line" << errorLine + << ", column" << errorColumn << endl + << "Error message: " << errorMsg; + + return false; + } + + KoDocumentResourceManager manager; + SvgParser parser(&manager); + parser.setResolution(QRectF(0,0,100,100), 72); // initialize with default values + QSizeF fragmentSize; + + QList shapes = parser.parseSvg(xmlDocument.documentElement(), &fragmentSize); + + d->maskSize = fragmentSize; + + d->title = parser.documentTitle(); + setName(d->title); + d->description = parser.documentDescription(); + + setMaskShapes(shapes); + + if (store->open("preview.png")) { + KoStoreDevice previewDev(store); + previewDev.open(QIODevice::ReadOnly); + + QImage preview = QImage(); + preview.load(&previewDev, "PNG"); + setImage(preview); + + (void)store->close(); + } + + buf.close(); + + return true; +} + +void KoGamutMask::setMaskShapes(QList shapes) +{ + setMaskShapesToVector(shapes, d->maskShapes); +} + +bool KoGamutMask::save() +{ + QFile file(filename()); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + return false; + } + saveToDevice(&file); + file.close(); + + return true; +} + +QList KoGamutMask::koShapes() const +{ + QList shapes; + for(KoGamutMaskShape* maskShape: d->maskShapes) { + shapes.append(maskShape->koShape()); + } + + return shapes; +} + +bool KoGamutMask::saveToDevice(QIODevice *dev) const +{ + KoStore* store(KoStore::createStore(dev, KoStore::Write, "application/x-krita-gamutmask", KoStore::Zip)); + if (!store || store->bad()) return false; + + QList shapes = koShapes(); + + std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex); + + if (!store->open("gamutmask.svg")) { + return false; + } + + KoStoreDevice storeDev(store); + storeDev.open(QIODevice::WriteOnly); + + SvgWriter writer(shapes); + writer.setDocumentTitle(d->title); + writer.setDocumentDescription(d->description); + + writer.save(storeDev, d->maskSize); + + if (!store->close()) { return false; } + + + if (!store->open("preview.png")) { + return false; + } + + KoStoreDevice previewDev(store); + previewDev.open(QIODevice::WriteOnly); + + image().save(&previewDev, "PNG"); + if (!store->close()) { return false; } + + return store->finalize(); +} + +QString KoGamutMask::title() +{ + return d->title; +} + +void KoGamutMask::setTitle(QString title) +{ + d->title = title; + setName(title); +} + +QString KoGamutMask::description() +{ + return d->description; +} + +void KoGamutMask::setDescription(QString description) +{ + d->description = description; +} + +QSizeF KoGamutMask::maskSize() +{ + return d->maskSize; +} + +// TODO: rethink preview +void KoGamutMask::setPreviewMaskShapes(QList shapes) +{ + setMaskShapesToVector(shapes, d->previewShapes); +} + +void KoGamutMask::setMaskShapesToVector(QList shapes, QVector &targetVector) +{ + targetVector.clear(); + + for(KoShape* sh: shapes) { + KoGamutMaskShape* maskShape = new KoGamutMaskShape(sh); + targetVector.append(maskShape); + } +} + +// clean up when ending mask preview +void KoGamutMask::clearPreview() +{ + d->previewShapes.clear(); +} diff --git a/libs/flake/svg/SvgWriter.h b/libs/flake/svg/SvgWriter.h --- a/libs/flake/svg/SvgWriter.h +++ b/libs/flake/svg/SvgWriter.h @@ -64,6 +64,9 @@ bool saveDetached(SvgSavingContext &savingContext); + void setDocumentTitle(QString title); + void setDocumentDescription(QString description); + private: void saveShapes(const QList shapes, SvgSavingContext &savingContext); @@ -75,6 +78,8 @@ QList m_toplevelShapes; bool m_writeInlineImages; + QString m_documentTitle; + QString m_documentDescription; }; #endif // SVGWRITER_H diff --git a/libs/flake/svg/SvgWriter.cpp b/libs/flake/svg/SvgWriter.cpp --- a/libs/flake/svg/SvgWriter.cpp +++ b/libs/flake/svg/SvgWriter.cpp @@ -119,6 +119,14 @@ << "\""; svgStream << ">" << endl; + if (!m_documentTitle.isNull() && !m_documentTitle.isEmpty()) { + svgStream << "" << m_documentTitle << "" << endl; + } + + if (!m_documentDescription.isNull() && !m_documentDescription.isEmpty()) { + svgStream << "" << m_documentDescription << "" << endl; + } + { SvgSavingContext savingContext(outputDevice, m_writeInlineImages); saveShapes(m_toplevelShapes, savingContext); @@ -300,3 +308,14 @@ // TODO: once we support saving single (flat) odf files // we can embed these here to have full support for generic shapes } + +void SvgWriter::setDocumentTitle(QString title) +{ + m_documentTitle = title; +} + +void SvgWriter::setDocumentDescription(QString description) +{ + m_documentDescription = description; +} + diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp --- a/libs/ui/KisApplication.cpp +++ b/libs/ui/KisApplication.cpp @@ -266,6 +266,7 @@ KoResourcePaths::addResourceType("pythonscripts", "data", "/pykrita"); KoResourcePaths::addResourceType("symbols", "data", "/symbols"); KoResourcePaths::addResourceType("preset_icons", "data", "/preset_icons"); + KoResourcePaths::addResourceType("ko_gamutmasks", "data", "/gamutmasks/", true); // // Extra directories to look for create resources. (Does anyone actually use that anymore?) // KoResourcePaths::addResourceDir("ko_gradients", "/usr/share/create/gradients/gimp"); @@ -296,6 +297,7 @@ d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/tool_icons/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/emblem_icons/"); + d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gamutmasks/"); // Indicate that it is now safe for users of KoResourcePaths to load resources KoResourcePaths::setReady(); diff --git a/libs/ui/kis_canvas_resource_provider.h b/libs/ui/kis_canvas_resource_provider.h --- a/libs/ui/kis_canvas_resource_provider.h +++ b/libs/ui/kis_canvas_resource_provider.h @@ -36,6 +36,7 @@ class KoCanvasBase; class KisViewManager; class KoPattern; +class KoGamutMask; class KisFilterConfiguration; #include @@ -53,6 +54,7 @@ enum Resources { HdrExposure = KoCanvasResourceManager::KritaStart + 1, CurrentPattern, + CurrentGamutMask, CurrentGradient, CurrentDisplayProfile, CurrentKritaNode, @@ -115,6 +117,8 @@ KisNodeSP currentNode() const; + KoGamutMask* currentGamutMask() const; + KisPaintOpPresetSP currentPreset() const; void setPaintOpPreset(const KisPaintOpPresetSP preset); @@ -186,6 +190,10 @@ void slotNodeActivated(const KisNodeSP node); void slotPainting(); + void slotGamutMaskActivated(KoGamutMask* mask); + void slotGamutMaskUnset(); + void slotGamutMaskPreviewUpdate(); + /** * Set the image size in pixels. The resource provider will store * the image size in postscript points. @@ -225,6 +233,9 @@ void moveMirrorVerticalCenter(); void moveMirrorHorizontalCenter(); + void sigGamutMaskChanged(KoGamutMask* mask); + void sigGamutMaskUnset(); + void sigGamutMaskPreviewUpdate(); private: diff --git a/libs/ui/kis_canvas_resource_provider.cpp b/libs/ui/kis_canvas_resource_provider.cpp --- a/libs/ui/kis_canvas_resource_provider.cpp +++ b/libs/ui/kis_canvas_resource_provider.cpp @@ -153,6 +153,16 @@ return m_view->activeNode(); } +KoGamutMask *KisCanvasResourceProvider::currentGamutMask() const +{ + if (m_resourceManager->hasResource(CurrentGamutMask)) { + return m_resourceManager->resource(CurrentGamutMask).value(); + } + else { + return nullptr; + } +} + KisPaintOpPresetSP KisCanvasResourceProvider::currentPreset() const { KisPaintOpPresetSP preset = m_resourceManager->resource(CurrentPaintOpPreset).value(); @@ -354,6 +364,25 @@ } } +void KisCanvasResourceProvider::slotGamutMaskActivated(KoGamutMask *mask) +{ + QVariant v; + v.setValue(mask); + m_resourceManager->setResource(CurrentGamutMask, v); + emit sigGamutMaskChanged(mask); +} + +void KisCanvasResourceProvider::slotGamutMaskUnset() +{ + m_resourceManager->clearResource(CurrentGamutMask); + emit sigGamutMaskUnset(); +} + +void KisCanvasResourceProvider::slotGamutMaskPreviewUpdate() +{ + emit sigGamutMaskPreviewUpdate(); +} + void KisCanvasResourceProvider::slotResetEnableFGChange(bool b) { m_enablefGChange = b; diff --git a/libs/widgets/KoResourceServerProvider.h b/libs/widgets/KoResourceServerProvider.h --- a/libs/widgets/KoResourceServerProvider.h +++ b/libs/widgets/KoResourceServerProvider.h @@ -34,6 +34,7 @@ #include #include #include +#include /** @@ -62,6 +63,7 @@ KoResourceServer* gradientServer(); KoResourceServer* paletteServer(); KoResourceServer* svgSymbolCollectionServer(); + KoResourceServer* gamutMaskServer(); private: KoResourceServerProvider(const KoResourceServerProvider&); diff --git a/libs/widgets/KoResourceServerProvider.cpp b/libs/widgets/KoResourceServerProvider.cpp --- a/libs/widgets/KoResourceServerProvider.cpp +++ b/libs/widgets/KoResourceServerProvider.cpp @@ -121,6 +121,7 @@ KoResourceServer* gradientServer; KoResourceServer* paletteServer; KoResourceServer *svgSymbolCollectionServer; + KoResourceServer* gamutMaskServer; }; KoResourceServerProvider::KoResourceServerProvider() : d(new Private) @@ -136,14 +137,18 @@ d->svgSymbolCollectionServer = new KoResourceServerSimpleConstruction("symbols", "*.svg"); d->svgSymbolCollectionServer->loadResources(blacklistFileNames(d->svgSymbolCollectionServer->fileNames(), d->svgSymbolCollectionServer->blackListedFiles())); + + d->gamutMaskServer = new KoResourceServerSimpleConstruction("ko_gamutmasks", "*.kgm"); + d->gamutMaskServer->loadResources(blacklistFileNames(d->gamutMaskServer->fileNames(), d->gamutMaskServer->blackListedFiles())); } KoResourceServerProvider::~KoResourceServerProvider() { delete d->patternServer; delete d->gradientServer; delete d->paletteServer; delete d->svgSymbolCollectionServer; + delete d->gamutMaskServer; delete d; } @@ -185,3 +190,9 @@ return d->svgSymbolCollectionServer; } +KoResourceServer* KoResourceServerProvider::gamutMaskServer() +{ + return d->gamutMaskServer; +} + + diff --git a/plugins/dockers/CMakeLists.txt b/plugins/dockers/CMakeLists.txt --- a/plugins/dockers/CMakeLists.txt +++ b/plugins/dockers/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory(presethistory) add_subdirectory(svgcollectiondocker) add_subdirectory(histogram) +add_subdirectory(gamutmask) if(NOT APPLE AND HAVE_QT_QUICK) add_subdirectory(touchdocker) option(ENABLE_CPU_THROTTLE "Build the CPU Throttle Docker" OFF) diff --git a/plugins/dockers/artisticcolorselector/CMakeLists.txt b/plugins/dockers/artisticcolorselector/CMakeLists.txt --- a/plugins/dockers/artisticcolorselector/CMakeLists.txt +++ b/plugins/dockers/artisticcolorselector/CMakeLists.txt @@ -3,13 +3,14 @@ artisticcolorselector_dock.cpp kis_color.cpp kis_color_selector.cpp -) + ) ki18n_wrap_ui(kritaartisticcolorselector_SOURCES forms/wdgArtisticColorSelector.ui - forms/wdgColorPreferencesPopup.ui + forms/wdgARCSSettings.ui + forms/wdgWheelPreferencesPopup.ui ) add_library(kritaartisticcolorselector MODULE ${kritaartisticcolorselector_SOURCES}) -target_link_libraries(kritaartisticcolorselector kritaui) +target_link_libraries(kritaartisticcolorselector kritalibkis kritaui) install(TARGETS kritaartisticcolorselector DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.h b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.h --- a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.h +++ b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.h @@ -19,14 +19,29 @@ #define H_ARTISTIC_COLOR_SELECTOR_DOCK_H #include +#include +#include + +#include +#include +#include +#include +#include +#include +//#include +#include +#include + #include class KisCanvasResourceProvider; class KisColor; class QButtonGroup; class QMenu; + struct ArtisticColorSelectorUI; -struct ColorPreferencesPopupUI; +struct ARCSSettingsUI; +struct WheelPreferencesPopupUI; class ArtisticColorSelectorDock: public QDockWidget, public KisMainwindowObserver { @@ -40,23 +55,35 @@ void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; - +//Q_SIGNALS: +// void sigGamutMaskChanged(); + private Q_SLOTS: void slotCanvasResourceChanged(int key, const QVariant& value); void slotFgColorChanged(const KisColor& color); void slotBgColorChanged(const KisColor& color); void slotColorSpaceSelected(int type); void slotPreferenceChanged(); - void slotMenuActionTriggered(QAction* action); + void slotResetRingPositions(); void slotResetDefaultSettings(); void slotLightModeChanged(bool setToAbsolute); - + void slotGamutMaskToggle(bool value); + void slotGamutMaskActivatePreview(bool value); + void slotGamutMaskSet(KoGamutMask* mask); + void slotGamutMaskUnset(); + void slotGamutMaskPreviewUpdate(); + private: KisCanvasResourceProvider* m_resourceProvider; QButtonGroup* m_hsxButtons; - QMenu* m_resetMenu; ArtisticColorSelectorUI* m_selectorUI; - ColorPreferencesPopupUI* m_preferencesUI; + ARCSSettingsUI* m_preferencesUI; + WheelPreferencesPopupUI* m_wheelPrefsUI; + KoGamutMask* m_selectedMask; + + QPixmap m_infinityPixmap; + + void updateWheelInfoStrip(); }; diff --git a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp --- a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp +++ b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp @@ -15,97 +15,211 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include + #include #include +#include +#include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include #include #include #include +#include +#include +#include #include "artisticcolorselector_dock.h" #include #include +#include #include "ui_wdgArtisticColorSelector.h" -#include "ui_wdgColorPreferencesPopup.h" +#include "ui_wdgARCSSettings.h" +#include "ui_wdgWheelPreferencesPopup.h" -enum { ACTION_RESET_EVERYTHING, ACTION_RESET_SELECTED_RING, ACTION_RESET_EVERY_RING, ACTION_RESET_LIGHT }; +class KisMainWindow; struct ArtisticColorSelectorUI: public QWidget, public Ui_wdgArtisticColorSelector { ArtisticColorSelectorUI() { setupUi(this); } }; -struct ColorPreferencesPopupUI: public QWidget, public Ui_wdgColorPreferencesPopup +struct ARCSSettingsUI: public QWidget, public Ui_wdgARCSSettings { - ColorPreferencesPopupUI() { + ARCSSettingsUI() { setupUi(this); } }; -ArtisticColorSelectorDock::ArtisticColorSelectorDock(): - QDockWidget(i18n("Artistic Color Selector")), - m_resourceProvider(0) +struct WheelPreferencesPopupUI: public QWidget, public Ui_wdgWheelPreferencesPopup +{ + WheelPreferencesPopupUI() { + setupUi(this); + } +}; + + +ArtisticColorSelectorDock::ArtisticColorSelectorDock() + : QDockWidget(i18n("Artistic Color Selector")) + , m_resourceProvider(0) + , m_selectedMask(nullptr) { m_hsxButtons = new QButtonGroup(); - m_resetMenu = new QMenu(); - m_preferencesUI = new ColorPreferencesPopupUI(); + m_preferencesUI = new ARCSSettingsUI(); + m_wheelPrefsUI = new WheelPreferencesPopupUI(); m_selectorUI = new ArtisticColorSelectorUI(); - m_resetMenu->addAction(i18n("Reset All Rings"))->setData(ACTION_RESET_EVERY_RING); - m_resetMenu->addAction(i18n("Reset Selected Ring"))->setData(ACTION_RESET_SELECTED_RING); - m_resetMenu->addAction(i18n("Reset Light"))->setData(ACTION_RESET_LIGHT); - m_resetMenu->addAction(i18n("Reset Everything"))->setData(ACTION_RESET_EVERYTHING); + QPixmap hueStepsPixmap = KisIconUtils::loadIcon("wheel-sectors").pixmap(16,16); + QPixmap saturationStepsPixmap = KisIconUtils::loadIcon("wheel-rings").pixmap(16,16); + QPixmap valueScaleStepsPixmap = KisIconUtils::loadIcon("wheel-light").pixmap(16,16); + QIcon infinityIcon = KisIconUtils::loadIcon("infinity"); + m_infinityPixmap = infinityIcon.pixmap(16,16); m_selectorUI->colorSelector->loadSettings(); - m_selectorUI->bnColorPrefs->setPopupWidget(m_preferencesUI); - m_selectorUI->bnReset->setMenu(m_resetMenu); - m_selectorUI->bnAbsLight->setChecked(!m_selectorUI->colorSelector->islightRelative()); + m_selectorUI->bnWheelPrefs->setPopupWidget(m_wheelPrefsUI); + + // TODO: make it into separate window + m_selectorUI->bnDockerPrefs->setPopupWidget(m_preferencesUI); + m_selectorUI->bnDockerPrefs->setIcon(KisIconUtils::loadIcon("configure")); + + m_selectorUI->bnToggleMask->setChecked(false); + m_selectorUI->bnToggleMask->setIcon(KisIconUtils::loadIcon("novisible")); + m_selectorUI->labelMaskToolbar->setPixmap(KisIconUtils::loadIcon("media-playback-stop").pixmap(16,16)); + + m_selectorUI->labelHueStepsIcon->setPixmap(hueStepsPixmap); + m_selectorUI->labelSaturationStepsIcon->setPixmap(saturationStepsPixmap); + m_selectorUI->labelValueScaleStepsIcon->setPixmap(valueScaleStepsPixmap); + + m_selectorUI->labelHueSteps->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + m_selectorUI->labelSaturationSteps->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + m_selectorUI->labelValueScaleSteps->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + //preferences m_hsxButtons->addButton(m_preferencesUI->bnHsy, KisColor::HSY); m_hsxButtons->addButton(m_preferencesUI->bnHsi, KisColor::HSI); m_hsxButtons->addButton(m_preferencesUI->bnHsl, KisColor::HSL); m_hsxButtons->addButton(m_preferencesUI->bnHsv, KisColor::HSV); - m_preferencesUI->numPiecesSlider->setRange(1, 48); - m_preferencesUI->numRingsSlider->setRange(1, 20); - m_preferencesUI->numLightPiecesSlider->setRange(1, 30); - m_preferencesUI->numPiecesSlider->setValue(m_selectorUI->colorSelector->getNumPieces()); - m_preferencesUI->numRingsSlider->setValue(m_selectorUI->colorSelector->getNumRings()); - m_preferencesUI->numLightPiecesSlider->setValue(m_selectorUI->colorSelector->getNumLightPieces()); - m_preferencesUI->bnInverseSat->setChecked(m_selectorUI->colorSelector->isSaturationInverted()); - + m_wheelPrefsUI->bnInverseSat->setChecked(m_selectorUI->colorSelector->isSaturationInverted()); + + m_wheelPrefsUI->labelHueSteps->setPixmap(hueStepsPixmap); + m_wheelPrefsUI->labelSaturationSteps->setPixmap(saturationStepsPixmap); + m_wheelPrefsUI->labelValueScaleSteps->setPixmap(valueScaleStepsPixmap); + + m_wheelPrefsUI->numHueSteps->setRange(MIN_NUM_UI_HUE_PIECES, MAX_NUM_HUE_PIECES); + m_wheelPrefsUI->numSaturationSteps->setRange(MIN_NUM_SATURATION_RINGS, MAX_NUM_SATURATION_RINGS); + m_wheelPrefsUI->numValueScaleSteps->setRange(MIN_NUM_UI_LIGHT_PIECES, MAX_NUM_LIGHT_PIECES); + + m_wheelPrefsUI->bnInfHueSteps->setIcon(infinityIcon); + m_wheelPrefsUI->bnInfValueScaleSteps->setIcon(infinityIcon); + + int selectorHueSteps = m_selectorUI->colorSelector->getNumPieces(); + if (selectorHueSteps == 1) { + m_wheelPrefsUI->bnInfHueSteps->setChecked(true); + } else { + m_wheelPrefsUI->bnInfHueSteps->setChecked(false); + } + m_wheelPrefsUI->numHueSteps->setValue(selectorHueSteps); + + m_wheelPrefsUI->numSaturationSteps->setValue(m_selectorUI->colorSelector->getNumRings()); + + int selectorValueScaleSteps = m_selectorUI->colorSelector->getNumLightPieces(); + if (selectorValueScaleSteps == 1) { + m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(true); + } else { + m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(false); + } + m_wheelPrefsUI->numValueScaleSteps->setValue(m_selectorUI->colorSelector->getNumLightPieces()); + + m_preferencesUI->bnDefInfHueSteps->setIcon(infinityIcon); + m_preferencesUI->bnDefInfValueScaleSteps->setIcon(infinityIcon); + + m_preferencesUI->labelDefHueSteps->setPixmap(hueStepsPixmap); + m_preferencesUI->labelDefSaturationSteps->setPixmap(saturationStepsPixmap); + m_preferencesUI->labelDefValueScaleSteps->setPixmap(valueScaleStepsPixmap); + + m_preferencesUI->defaultHueSteps->setRange(MIN_NUM_HUE_PIECES, MAX_NUM_HUE_PIECES); + m_preferencesUI->defaultSaturationSteps->setRange(MIN_NUM_SATURATION_RINGS, MAX_NUM_SATURATION_RINGS); + m_preferencesUI->defaultValueScaleSteps->setRange(MIN_NUM_LIGHT_PIECES, MAX_NUM_LIGHT_PIECES); + + m_preferencesUI->defaultHueSteps->setValue(m_selectorUI->colorSelector->getDefaultHueSteps()); + m_preferencesUI->defaultSaturationSteps->setValue(m_selectorUI->colorSelector->getDefaultSaturationSteps()); + m_preferencesUI->defaultValueScaleSteps->setValue(m_selectorUI->colorSelector->getDefaultValueScaleSteps()); + + m_preferencesUI->showColorBlip->setChecked(m_selectorUI->colorSelector->getShowColorBlip()); + m_preferencesUI->showBgColor->setChecked(m_selectorUI->colorSelector->getShowBgColor()); + m_preferencesUI->showValueScaleNumbers->setChecked(m_selectorUI->colorSelector->getShowValueScaleNumbers()); + m_preferencesUI->bnAbsLight->setChecked(m_selectorUI->colorSelector->islightRelative()); + + m_preferencesUI->enforceGamutMask->setChecked(m_selectorUI->colorSelector->enforceGamutMask()); + m_preferencesUI->permissiveGamutMask->setChecked(!m_selectorUI->colorSelector->enforceGamutMask()); + m_preferencesUI->showMaskPreview->setChecked(m_selectorUI->colorSelector->maskPreviewActive()); + switch(m_selectorUI->colorSelector->getColorSpace()) { case KisColor::HSV: { m_preferencesUI->bnHsv->setChecked(true); } break; case KisColor::HSI: { m_preferencesUI->bnHsi->setChecked(true); } break; case KisColor::HSL: { m_preferencesUI->bnHsl->setChecked(true); } break; case KisColor::HSY: { m_preferencesUI->bnHsy->setChecked(true); } break; } - - connect(m_preferencesUI->numLightPiecesSlider, SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); - connect(m_preferencesUI->numPiecesSlider , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); - connect(m_preferencesUI->numRingsSlider , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); - connect(m_preferencesUI->bnInverseSat , SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); - connect(m_selectorUI->colorSelector , SIGNAL(sigFgColorChanged(const KisColor&)) , SLOT(slotFgColorChanged(const KisColor&))); - connect(m_selectorUI->colorSelector , SIGNAL(sigBgColorChanged(const KisColor&)) , SLOT(slotBgColorChanged(const KisColor&))); - connect(m_hsxButtons , SIGNAL(buttonClicked(int)) , SLOT(slotColorSpaceSelected(int))); - connect(m_preferencesUI->bnDefault , SIGNAL(clicked(bool)) , SLOT(slotResetDefaultSettings())); - connect(m_selectorUI->bnAbsLight , SIGNAL(toggled(bool)) , SLOT(slotLightModeChanged(bool))); - connect(m_resetMenu , SIGNAL(triggered(QAction*)) , SLOT(slotMenuActionTriggered(QAction*))); - + + connect(m_wheelPrefsUI->numValueScaleSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); + connect(m_wheelPrefsUI->numHueSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); + connect(m_wheelPrefsUI->numSaturationSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); + connect(m_wheelPrefsUI->bnInverseSat , SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); + connect(m_wheelPrefsUI->bnInfHueSteps , SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); + connect(m_wheelPrefsUI->bnInfValueScaleSteps, SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); + connect(m_wheelPrefsUI->bnDefault , SIGNAL(clicked(bool)) , SLOT(slotResetDefaultSettings())); + connect(m_wheelPrefsUI->bnResetPosition , SIGNAL(clicked(bool)) , SLOT(slotResetRingPositions())); + + connect(m_preferencesUI->defaultHueSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); + connect(m_preferencesUI->defaultSaturationSteps, SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); + connect(m_preferencesUI->defaultValueScaleSteps, SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); + connect(m_preferencesUI->bnDefInfHueSteps , SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); + connect(m_preferencesUI->bnDefInfValueScaleSteps, SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); + + connect(m_preferencesUI->showColorBlip , SIGNAL(toggled(bool)) , SLOT(slotPreferenceChanged())); + connect(m_preferencesUI->showBgColor , SIGNAL(toggled(bool)) , SLOT(slotPreferenceChanged())); + connect(m_preferencesUI->showValueScaleNumbers, SIGNAL(toggled(bool)) , SLOT(slotPreferenceChanged())); + connect(m_preferencesUI->enforceGamutMask , SIGNAL(toggled(bool)) , SLOT(slotPreferenceChanged())); + connect(m_preferencesUI->bnAbsLight , SIGNAL(toggled(bool)) , SLOT(slotLightModeChanged(bool))); + connect(m_preferencesUI->showMaskPreview , SIGNAL(toggled(bool)), SLOT(slotGamutMaskActivatePreview(bool))); + + connect(m_selectorUI->colorSelector , SIGNAL(sigFgColorChanged(const KisColor&)) , SLOT(slotFgColorChanged(const KisColor&))); + connect(m_selectorUI->colorSelector , SIGNAL(sigBgColorChanged(const KisColor&)) , SLOT(slotBgColorChanged(const KisColor&))); + + // gamut mask connections + connect(m_selectorUI->bnToggleMask , SIGNAL(toggled(bool)) , SLOT(slotGamutMaskToggle(bool))); + + connect(m_hsxButtons , SIGNAL(buttonClicked(int)) , SLOT(slotColorSpaceSelected(int))); + + updateWheelInfoStrip(); + setWidget(m_selectorUI); } ArtisticColorSelectorDock::~ArtisticColorSelectorDock() { m_selectorUI->colorSelector->saveSettings(); delete m_hsxButtons; - delete m_resetMenu; } void ArtisticColorSelectorDock::setMainWindow(KisViewManager* kisview) @@ -115,6 +229,17 @@ m_selectorUI->colorSelector->setBgColor(m_resourceProvider->resourceManager()->backgroundColor().toQColor()); connect(m_resourceProvider->resourceManager(), SIGNAL(canvasResourceChanged(int, const QVariant&)), SLOT(slotCanvasResourceChanged(int, const QVariant&))); + + connect(m_resourceProvider, SIGNAL(sigGamutMaskChanged(KoGamutMask*)), + this, SLOT(slotGamutMaskSet(KoGamutMask*))); + + connect(m_resourceProvider, SIGNAL(sigGamutMaskUnset()), + this, SLOT(slotGamutMaskUnset())); + + if (m_selectorUI->colorSelector->maskPreviewActive()) { + connect(m_resourceProvider, SIGNAL(sigGamutMaskPreviewUpdate()), + this, SLOT(slotGamutMaskPreviewUpdate())); + } } void ArtisticColorSelectorDock::slotCanvasResourceChanged(int key, const QVariant& value) @@ -147,59 +272,181 @@ void ArtisticColorSelectorDock::slotPreferenceChanged() { - m_selectorUI->colorSelector->setNumPieces(m_preferencesUI->numPiecesSlider->value()); - m_selectorUI->colorSelector->setNumRings(m_preferencesUI->numRingsSlider->value()); - m_selectorUI->colorSelector->setNumLightPieces(m_preferencesUI->numLightPiecesSlider->value()); - m_selectorUI->colorSelector->setInverseSaturation(m_preferencesUI->bnInverseSat->isChecked()); -} + int hueSteps; + if (m_wheelPrefsUI->bnInfHueSteps->isChecked()) { + m_wheelPrefsUI->numHueSteps->setEnabled(false); + hueSteps = 1; + } else { + m_wheelPrefsUI->numHueSteps->setEnabled(true); + hueSteps = m_wheelPrefsUI->numHueSteps->value(); + } + m_selectorUI->colorSelector->setNumPieces(hueSteps); -void ArtisticColorSelectorDock::slotMenuActionTriggered(QAction* action) -{ - switch(action->data().toInt()) - { - case ACTION_RESET_SELECTED_RING: - m_selectorUI->colorSelector->resetSelectedRing(); - break; + m_selectorUI->colorSelector->setNumRings(m_wheelPrefsUI->numSaturationSteps->value()); - case ACTION_RESET_EVERY_RING: - m_selectorUI->colorSelector->resetRings(); - break; + int valueScaleSteps; + if (m_wheelPrefsUI->bnInfValueScaleSteps->isChecked()) { + m_wheelPrefsUI->numValueScaleSteps->setEnabled(false); + valueScaleSteps = 1; + } else { + valueScaleSteps = m_wheelPrefsUI->numValueScaleSteps->value(); + m_wheelPrefsUI->numValueScaleSteps->setEnabled(true); + } + m_selectorUI->colorSelector->setNumLightPieces(valueScaleSteps); + + int defHueSteps; + if (m_preferencesUI->bnDefInfHueSteps->isChecked()) { + m_preferencesUI->defaultHueSteps->setEnabled(false); + defHueSteps = 1; + } else { + m_preferencesUI->defaultHueSteps->setEnabled(true); + defHueSteps = m_preferencesUI->defaultHueSteps->value(); + } + m_selectorUI->colorSelector->setDefaultHueSteps(defHueSteps); - case ACTION_RESET_LIGHT: - m_selectorUI->colorSelector->resetLight(); - break; + m_selectorUI->colorSelector->setDefaultSaturationSteps(m_preferencesUI->defaultSaturationSteps->value()); - case ACTION_RESET_EVERYTHING: - m_selectorUI->colorSelector->resetLight(); - m_selectorUI->colorSelector->resetRings(); - break; + int defValueScaleSteps; + if (m_preferencesUI->bnDefInfValueScaleSteps->isChecked()) { + m_preferencesUI->defaultValueScaleSteps->setEnabled(false); + defValueScaleSteps = 1; + } else { + m_preferencesUI->defaultValueScaleSteps->setEnabled(true); + defValueScaleSteps = m_preferencesUI->defaultValueScaleSteps->value(); } + m_selectorUI->colorSelector->setDefaultValueScaleSteps(defValueScaleSteps); + + m_selectorUI->colorSelector->setShowColorBlip(m_preferencesUI->showColorBlip->isChecked()); + m_selectorUI->colorSelector->setShowBgColor(m_preferencesUI->showBgColor->isChecked()); + m_selectorUI->colorSelector->setShowValueScaleNumbers(m_preferencesUI->showValueScaleNumbers->isChecked()); + m_selectorUI->colorSelector->setEnforceGamutMask(m_preferencesUI->enforceGamutMask->isChecked()); + + // the selector wheel forbids saturation inversion in some cases, + // reflecting that in the ui + if (m_selectorUI->colorSelector->saturationIsInvertible()) { + m_wheelPrefsUI->bnInverseSat->setEnabled(true); + m_selectorUI->colorSelector->setInverseSaturation(m_wheelPrefsUI->bnInverseSat->isChecked()); + } else { + m_wheelPrefsUI->bnInverseSat->setEnabled(false); + m_wheelPrefsUI->bnInverseSat->setChecked(false); + m_selectorUI->colorSelector->setInverseSaturation(false); + } + + updateWheelInfoStrip(); +} + +void ArtisticColorSelectorDock::slotResetRingPositions() +{ + m_selectorUI->colorSelector->resetRings(); } void ArtisticColorSelectorDock::slotResetDefaultSettings() { - m_selectorUI->colorSelector->setNumRings(7); - m_preferencesUI->numRingsSlider->blockSignals(true); - m_preferencesUI->numRingsSlider->setValue(7); - m_preferencesUI->numRingsSlider->blockSignals(false); + quint32 hueSteps = m_selectorUI->colorSelector->getDefaultHueSteps(); + quint32 saturationSteps = m_selectorUI->colorSelector->getDefaultSaturationSteps(); + quint32 valueScaleSteps = m_selectorUI->colorSelector->getDefaultValueScaleSteps(); + + m_selectorUI->colorSelector->setNumRings(saturationSteps); + m_wheelPrefsUI->numSaturationSteps->blockSignals(true); + m_wheelPrefsUI->numSaturationSteps->setValue(saturationSteps); + m_wheelPrefsUI->numSaturationSteps->blockSignals(false); + + m_selectorUI->colorSelector->setNumPieces(hueSteps); + m_wheelPrefsUI->numHueSteps->blockSignals(true); + m_wheelPrefsUI->numHueSteps->setValue(hueSteps); + m_wheelPrefsUI->numHueSteps->blockSignals(false); + + if (hueSteps == 1) { + m_wheelPrefsUI->numHueSteps->setEnabled(false); + m_wheelPrefsUI->bnInfHueSteps->setChecked(true); + } else { + m_wheelPrefsUI->numHueSteps->setEnabled(true); + m_wheelPrefsUI->bnInfHueSteps->setChecked(false); + } - m_selectorUI->colorSelector->setNumPieces(12); - m_preferencesUI->numPiecesSlider->blockSignals(true); - m_preferencesUI->numPiecesSlider->setValue(12); - m_preferencesUI->numPiecesSlider->blockSignals(false); + m_selectorUI->colorSelector->setNumLightPieces(valueScaleSteps); + m_wheelPrefsUI->numValueScaleSteps->blockSignals(true); + m_wheelPrefsUI->numValueScaleSteps->setValue(valueScaleSteps); + m_wheelPrefsUI->numValueScaleSteps->blockSignals(false); + + if (valueScaleSteps == 1) { + m_wheelPrefsUI->numValueScaleSteps->setEnabled(false); + m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(true); + } else { + m_wheelPrefsUI->numValueScaleSteps->setEnabled(true); + m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(false); + } - m_selectorUI->colorSelector->setNumLightPieces(9); - m_preferencesUI->numLightPiecesSlider->blockSignals(true); - m_preferencesUI->numLightPiecesSlider->setValue(9); - m_preferencesUI->numLightPiecesSlider->blockSignals(false); + updateWheelInfoStrip(); } void ArtisticColorSelectorDock::slotLightModeChanged(bool setToAbsolute) { - m_selectorUI->colorSelector->setLight(m_selectorUI->colorSelector->getLight(), !setToAbsolute); + m_selectorUI->colorSelector->setLight(m_selectorUI->colorSelector->getLight(), setToAbsolute); } +void ArtisticColorSelectorDock::updateWheelInfoStrip() +{ + int hueSteps = m_selectorUI->colorSelector->getNumPieces(); + if (hueSteps == 1) { + m_selectorUI->labelHueSteps->setPixmap(m_infinityPixmap); + } else { + m_selectorUI->labelHueSteps->setNum(hueSteps); + } + + m_selectorUI->labelSaturationSteps->setNum(m_wheelPrefsUI->numSaturationSteps->value()); + int valueScaleSteps = m_selectorUI->colorSelector->getNumLightPieces(); + if (valueScaleSteps == 1) { + m_selectorUI->labelValueScaleSteps->setPixmap(m_infinityPixmap); + } else { + m_selectorUI->labelValueScaleSteps->setNum(valueScaleSteps); + } +} + +void ArtisticColorSelectorDock::slotGamutMaskActivatePreview(bool value) +{ + m_selectorUI->colorSelector->setMaskPreviewActive(value); + + if (value) { + connect(m_resourceProvider, SIGNAL(sigGamutMaskPreviewUpdate()), + this, SLOT(slotGamutMaskPreviewUpdate())); + } else { + disconnect(m_resourceProvider, SIGNAL(sigGamutMaskPreviewUpdate()), + this, SLOT(slotGamutMaskPreviewUpdate())); + } + + m_selectorUI->colorSelector->update(); +} + +void ArtisticColorSelectorDock::slotGamutMaskToggle(bool checked) +{ + bool b = (!m_selectedMask) ? false : checked; + + m_selectorUI->bnToggleMask->setChecked(b); + + if (b == true) { + m_selectorUI->colorSelector->setGamutMask(m_selectedMask); + m_selectorUI->bnToggleMask->setIcon(KisIconUtils::loadIcon("visible")); + } else { + m_selectorUI->bnToggleMask->setIcon(KisIconUtils::loadIcon("novisible")); + } + + m_selectorUI->colorSelector->setGamutMaskOn(b); + + // TODO: HACK + // the selector wheel forbids saturation inversion in some cases, + // reflecting that in the ui + if (m_selectorUI->colorSelector->saturationIsInvertible()) { + m_wheelPrefsUI->bnInverseSat->setEnabled(true); + m_selectorUI->colorSelector->setInverseSaturation(m_wheelPrefsUI->bnInverseSat->isChecked()); + } else { + m_wheelPrefsUI->bnInverseSat->setEnabled(false); + m_wheelPrefsUI->bnInverseSat->setChecked(false); + m_selectorUI->colorSelector->setInverseSaturation(false); + } + +} void ArtisticColorSelectorDock::setCanvas(KoCanvasBase *canvas) { @@ -210,3 +457,35 @@ { setEnabled(false); } + +void ArtisticColorSelectorDock::slotGamutMaskSet(KoGamutMask *mask) +{ + m_selectedMask = mask; + + if (m_selectedMask) { + m_selectorUI->colorSelector->setGamutMask(m_selectedMask); + m_selectorUI->labelMaskName->setText(m_selectedMask->title()); + slotGamutMaskToggle(true); + } else { + slotGamutMaskToggle(false); + m_selectorUI->labelMaskName->setText(i18n("Select a mask in \"Gamut Masks\" docker")); + } +} + +void ArtisticColorSelectorDock::slotGamutMaskUnset() +{ + if (!m_selectedMask) { + return; + } + + m_selectedMask = nullptr; + + slotGamutMaskToggle(false); + m_selectorUI->labelMaskName->setText(i18n("Select a mask in \"Gamut Masks\" docker")); + m_selectorUI->colorSelector->setGamutMask(m_selectedMask); +} + +void ArtisticColorSelectorDock::slotGamutMaskPreviewUpdate() +{ + m_selectorUI->colorSelector->update(); +} diff --git a/plugins/dockers/artisticcolorselector/forms/wdgARCSSettings.ui b/plugins/dockers/artisticcolorselector/forms/wdgARCSSettings.ui new file mode 100644 --- /dev/null +++ b/plugins/dockers/artisticcolorselector/forms/wdgARCSSettings.ui @@ -0,0 +1,263 @@ + + + wdgARCSSettings + + + + 0 + 0 + 358 + 520 + + + + + + + Selector Appearance + + + + + + Show color blip + + + + + + + Show background color indicator + + + + + + + Show lightness numbers on the value scale + + + + + + + + + + Color Space + + + + + + + + HS&Y + + + true + + + true + + + + + + + HS&V + + + true + + + + + + + HSL + + + true + + + + + + + HSI + + + true + + + + + + + + + Use HSV saturation component + + + + + + + + + + Gamut Mask Behavior + + + + + + + + Enforce gamut &mask + + + + + + + &Just show the shapes + + + + + + + + + Show preview while editing a mask + + + false + + + + + + + + + + Default Selector Steps Settings + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Hue Steps + + + + + + + + + + + 0 + 0 + + + + Saturation Rings + + + + + + + + + + + + + true + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Value Scale Steps + + + + + + + + + + + 0 + 0 + + + + + + + + + + + true + + + + + + + + + + + KisSliderSpinBox + QWidget +
kis_slider_spin_box.h
+ 1 +
+
+ + +
diff --git a/plugins/dockers/artisticcolorselector/forms/wdgArtisticColorSelector.ui b/plugins/dockers/artisticcolorselector/forms/wdgArtisticColorSelector.ui --- a/plugins/dockers/artisticcolorselector/forms/wdgArtisticColorSelector.ui +++ b/plugins/dockers/artisticcolorselector/forms/wdgArtisticColorSelector.ui @@ -6,68 +6,222 @@ 0 0 - 337 - 294 + 360 + 279 100 100 - + - + + + + 1 + 0 + + - Pref. + V - + + + + 2 + 0 + + + + QFrame::StyledPanel + - Reset + 0 - + + + + 1 + 0 + + - Abs. + H - - true + + + + + + + 2 + 0 + - - true + + QFrame::StyledPanel + + + 0 + + + + + + + + 1 + 0 + + + + S + + + + + + + + 2 + 0 + + + + QFrame::StyledPanel + + + 0 + + + + + + + + 0 + 0 + + + + + 20 + 16777215 + + + + Text-align:left + + + - true + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 30 + 0 + + + + + + + false - + - + 0 1 + + + + + + M + + + + + + + + + + true + + + + + + + + 0 + 0 + + + + Select a mask in "Gamut Masks" docker + + + true + + + + + KisPopupButton QPushButton -
kis_popup_button.h
+
kis_popup_button.h
KisColorSelector diff --git a/plugins/dockers/artisticcolorselector/forms/wdgColorPreferencesPopup.ui b/plugins/dockers/artisticcolorselector/forms/wdgWheelPreferencesPopup.ui rename from plugins/dockers/artisticcolorselector/forms/wdgColorPreferencesPopup.ui rename to plugins/dockers/artisticcolorselector/forms/wdgWheelPreferencesPopup.ui --- a/plugins/dockers/artisticcolorselector/forms/wdgColorPreferencesPopup.ui +++ b/plugins/dockers/artisticcolorselector/forms/wdgWheelPreferencesPopup.ui @@ -1,89 +1,60 @@ - wdgColorPreferencesPopup - + wdgWheelPreferencesPopup + 0 0 - 358 - 139 + 325 + 127 - - - - - HSY - - - true - - - true - - - true + + + + + + 0 + 0 + - - + + - HSI + true - - true - - - - - HSL - - - true - - - true + + + + + 0 + 0 + - - + + - HSV + true - - true - - - - - - - - - Hue Pieces: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - + + 0 @@ -93,69 +64,88 @@ - + + + Saturation Rings + - Saturation Rings: + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 0 - 0 - + + + + Hue Steps - - - - - Light Pieces + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 0 - 0 - + + + + Value Scale Steps + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + Invert Saturation true + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Reset Steps + - true + false - + - Reset to Default + Reset Rings - true + false diff --git a/plugins/dockers/artisticcolorselector/kis_arcs_constants.h b/plugins/dockers/artisticcolorselector/kis_arcs_constants.h new file mode 100644 --- /dev/null +++ b/plugins/dockers/artisticcolorselector/kis_arcs_constants.h @@ -0,0 +1,21 @@ +#ifndef KIS_ARCS_CONSTANTS_H +#define KIS_ARCS_CONSTANTS_H + +#include + +static const int MIN_NUM_HUE_PIECES = 1; +static const int MIN_NUM_UI_HUE_PIECES = 2; +static const int MAX_NUM_HUE_PIECES = 48; +static const int MIN_NUM_LIGHT_PIECES = 1; +static const int MIN_NUM_UI_LIGHT_PIECES = 2; +static const int MAX_NUM_LIGHT_PIECES = 30; +static const int MIN_NUM_SATURATION_RINGS = 1; +static const int MAX_NUM_SATURATION_RINGS = 20; + +static const int DEFAULT_HUE_STEPS = 12; +static const int DEFAULT_SATURATION_STEPS = 7; +static const int DEFAULT_VALUE_SCALE_STEPS = 11; + +static const QString UTF_INF_CHAR = "\u221E"; + +#endif // KIS_ARCS_CONSTANTS_H diff --git a/plugins/dockers/artisticcolorselector/kis_color_selector.h b/plugins/dockers/artisticcolorselector/kis_color_selector.h --- a/plugins/dockers/artisticcolorselector/kis_color_selector.h +++ b/plugins/dockers/artisticcolorselector/kis_color_selector.h @@ -29,43 +29,46 @@ #include "kis_acs_types.h" #include "kis_signal_compressor_with_param.h" +#include +#include + class QPainter; class QPainter; class KisColorSelector: public QWidget { Q_OBJECT - + typedef KisRadian Radian; - + struct ColorRing { ColorRing(): angle(0) { } - + Radian getPieceAngle() const { return RAD_360 / float(pieced.size()); } Radian getShift () const { return angle % getPieceAngle(); } Radian getMovedAngel() const { return angle - tmpAngle; } - + void setTemporaries(const KisColor& color) { tmpAngle = angle; tmpColor = color; } - + KisColor tmpColor; Radian tmpAngle; Radian angle; float saturation; float outerRadius; float innerRadius; QVector pieced; }; - + public: KisColorSelector(QWidget* parent, KisColor::Type type=KisColor::HSL); - + void setColorSpace(KisColor::Type type); void setNumPieces(int num); - void setNumLightPieces(int num); + void setNumLightPieces(int num) __attribute__((optimize(0))); void setNumRings(int num); void resetRings(); void resetSelectedRing(); @@ -75,38 +78,68 @@ void selectColor(const KisColor& color); void setFgColor(const KisColor& fgColor); void setBgColor(const KisColor& bgColor); - + + void setDefaultHueSteps(int num); + void setDefaultSaturationSteps(int num); + void setDefaultValueScaleSteps(int num); + void setShowColorBlip(bool value); + void setShowBgColor(bool value); + void setShowValueScaleNumbers(bool value); + void setGamutMask(KoGamutMask* gamutMask); + bool gamutMaskOn(); + void setGamutMaskOn(bool gamutMaskOn); + void setEnforceGamutMask(bool enforce); + KoGamutMask* gamutMask(); + + bool maskPreviewActive(); + void setMaskPreviewActive(bool value); + + bool saturationIsInvertible(); + void saveSettings(); void loadSettings(); - + KisColor::Type getColorSpace () const { return m_colorSpace; } qint32 getNumRings () const { return m_colorRings.size(); } qint32 getNumPieces () const { return m_numPieces; } qint32 getNumLightPieces () const { return m_numLightPieces; } qreal getLight () const { return m_light; } bool isSaturationInverted() const { return m_inverseSaturation; } bool islightRelative () const { return m_relativeLight; } + quint32 getDefaultHueSteps () const { return m_defaultHueSteps; } + quint32 getDefaultSaturationSteps () const { return m_defaultSaturationSteps; } + quint32 getDefaultValueScaleSteps () const { return m_defaultValueScaleSteps; } + bool getShowColorBlip () const { return m_showColorBlip; } + bool getShowBgColor () const { return m_showBgColor; } + bool getShowValueScaleNumbers () const { return m_showValueScaleNumbers; } + bool enforceGamutMask () const { return m_enforceGamutMask; } + Q_SIGNALS: void sigFgColorChanged(const KisColor& color); void sigBgColorChanged(const KisColor& color); - + private: void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void resizeEvent(QResizeEvent* event) override; void paintEvent(QPaintEvent* event) override; + void leaveEvent(QEvent* e) override; + bool colorIsClear(const KisColor &color); + bool colorIsClear(const QPointF &colorPoint); void requestUpdateColorAndPreview(const KisColor &color, Acs::ColorRole role); void recalculateAreas(quint8 numLightPieces); void recalculateRings(quint8 numRings, quint8 numPieces); void createRing(ColorRing& wheel, quint8 numPieces, qreal innerRadius, qreal outerRadius); - void drawRing(QPainter& painter, ColorRing& wheel, const QRect& rect); + void drawRing(QPainter& painter, int ringIndex, ColorRing& wheel, const QRect& rect); void drawOutline(QPainter& painter, const QRect& rect); + void drawBlip(QPainter& painter, const QRect& rect); void drawLightStrip(QPainter& painter, const QRect& rect); + void drawGamutMaskShape(QPainter& painter, const QRect& rect); qint8 getHueIndex(Radian hue, Radian shift=0.0f) const; qreal getHue(int hueIdx, Radian shift=0.0f) const; @@ -117,8 +150,12 @@ qint8 getSaturationIndex(const QPointF& pt) const; qint8 getSaturationIndex(qreal saturation) const; qreal getSaturation(int saturationIdx) const; - - QPointF mapCoord(const QPointF& pt, const QRectF& rect) const; + +// QPointF mapCoord(const QPointF& pt, const QRectF& rect) const; + + QPointF mapCoordToView(const QPointF& pt, const QRectF& viewRect) const; + QPointF mapCoordToUnit(const QPointF& pt, const QRectF& viewRect) const; + QPointF mapColorToUnit(const KisColor& color, bool invertSaturation = true) const; public: // This is a private interface for signal compressor, don't use it. @@ -139,14 +176,34 @@ KisColor m_fgColor; KisColor m_bgColor; QImage m_renderBuffer; + QImage m_maskBuffer; QRect m_renderArea; QRect m_lightStripArea; bool m_mouseMoved; Acs::ColorRole m_selectedColorRole; QPointF m_clickPos; qint8 m_clickedRing; QVector m_colorRings; Qt::MouseButtons m_pressedButtons; + const QColor m_grayColor; + const QColor m_gamutMaskColor; + + // docker settings + quint8 m_defaultHueSteps; + quint8 m_defaultSaturationSteps; + quint8 m_defaultValueScaleSteps; + bool m_showColorBlip; + bool m_showValueScaleNumbers; + bool m_showBgColor; + + bool m_gamutMaskOn; + KoGamutMask* m_currentGamutMask; + bool m_enforceGamutMask; + QSize m_renderAreaSize; + bool m_maskPreviewActive; + KisGamutMaskViewConverter* m_viewConverter; + + bool m_widgetUpdatesSelf; typedef KisSignalCompressorWithParam> ColorCompressorType; QScopedPointer m_updateColorCompressor; diff --git a/plugins/dockers/artisticcolorselector/kis_color_selector.cpp b/plugins/dockers/artisticcolorselector/kis_color_selector.cpp --- a/plugins/dockers/artisticcolorselector/kis_color_selector.cpp +++ b/plugins/dockers/artisticcolorselector/kis_color_selector.cpp @@ -30,25 +30,29 @@ #include #include +#include +#include +#include #include "kis_color_selector.h" -static const int MIN_NUM_HUE_PIECES = 1; -static const int MAX_NUM_HUE_PIECES = 48; -static const int MIN_NUM_LIGHT_PIECES = 1; -static const int MAX_NUM_LIGHT_PIECES = 30; -static const int MIN_NUM_SATURATION_RINGS = 1; -static const int MAX_NUM_SATURATION_RINGS = 20; - -KisColorSelector::KisColorSelector(QWidget* parent, KisColor::Type type): - QWidget(parent), - m_colorSpace(type), - m_inverseSaturation(false), - m_relativeLight(false), - m_light(0.5f), - m_selectedColorRole(Acs::Foreground), - m_clickedRing(-1) -{ +KisColorSelector::KisColorSelector(QWidget* parent, KisColor::Type type) + : QWidget(parent) + , m_colorSpace(type) + , m_inverseSaturation(false) + , m_relativeLight(true) + , m_light(0.5f) + , m_selectedColorRole(Acs::Foreground) + , m_clickedRing(-1) + , m_gamutMaskOn(false) + , m_widgetUpdatesSelf(false) + , m_grayColor(QColor(128,128,128,255)) + , m_gamutMaskColor(QColor(128,128,128,255)) + , m_currentGamutMask(nullptr) + , m_maskPreviewActive(true) +{ + m_viewConverter = new KisGamutMaskViewConverter(); + recalculateRings(9, 12); recalculateAreas(9); selectColor(KisColor(Qt::red, KisColor::HSY)); @@ -112,14 +116,36 @@ void KisColorSelector::setFgColor(const KisColor& fgColor) { - m_fgColor = KisColor(fgColor, m_colorSpace); - update(); + if (!m_widgetUpdatesSelf) { + m_fgColor = KisColor(fgColor, m_colorSpace); + dbgPlugins << "KisColorSelector::setFgColor: m_fgColor set to:" + << "H:" << m_fgColor.getH() + << "S:" << m_fgColor.getS() + << "X:" << m_fgColor.getX(); + + m_selectedColor = KisColor(fgColor, m_colorSpace); + dbgPlugins << "KisColorSelector::setFgColor: m_selectedColor set to:" + << "H:" << m_selectedColor.getH() + << "S:" << m_selectedColor.getS() + << "X:" << m_selectedColor.getX(); + update(); + } else { + dbgPlugins << "KisColorSelector::setFgColor: m_widgetUpdatesSelf == true, not updating color"; + } } void KisColorSelector::setBgColor(const KisColor& bgColor) { - m_bgColor = KisColor(bgColor, m_colorSpace); - update(); + if (!m_widgetUpdatesSelf) { + m_bgColor = KisColor(bgColor, m_colorSpace); + dbgPlugins << "KisColorSelector::setBgColor: m_bgColor set to:" + << "H:" << m_bgColor.getH() + << "S:" << m_bgColor.getS() + << "X:" << m_bgColor.getX(); + update(); + } else { + dbgPlugins << "KisColorSelector::setBgColor: m_widgetUpdatesSelf == true, not updating color"; + } } void KisColorSelector::resetRings() @@ -165,15 +191,92 @@ } } -QPointF KisColorSelector::mapCoord(const QPointF& pt, const QRectF& rect) const +void KisColorSelector::setGamutMask(KoGamutMask* gamutMask) +{ + if (!gamutMask) { + return; + } + + m_currentGamutMask = gamutMask; + m_viewConverter->setViewSize(m_renderAreaSize); + m_viewConverter->setMaskSize(m_currentGamutMask->maskSize()); + update(); +} + +KoGamutMask* KisColorSelector::gamutMask() +{ + return m_currentGamutMask; +} + +bool KisColorSelector::maskPreviewActive() +{ + return m_maskPreviewActive; +} + +void KisColorSelector::setMaskPreviewActive(bool value) +{ + m_maskPreviewActive = value; +} + +bool KisColorSelector::gamutMaskOn() +{ + return m_gamutMaskOn; +} + + +void KisColorSelector::setGamutMaskOn(bool gamutMaskOn) +{ + // do not allow the mask on, if it is a nullptr + if (m_currentGamutMask) { + m_gamutMaskOn = gamutMaskOn; + update(); + } +} + +void KisColorSelector::setEnforceGamutMask(bool enforce) { - qreal w = rect.width() / 2.0; - qreal h = rect.height() / 2.0; - qreal x = pt.x() - (rect.x() + w); - qreal y = pt.y() - (rect.y() + h); + m_enforceGamutMask = enforce; + update(); +} + +QPointF KisColorSelector::mapCoordToView(const QPointF& pt, const QRectF& viewRect) const +{ + qreal w = viewRect.width() / 2.0; + qreal h = viewRect.height() / 2.0; + + qreal x = pt.x() + 1.0; + qreal y = (pt.y()) + 1.0; + + return QPointF(x*w, y*h); +} + +QPointF KisColorSelector::mapCoordToUnit(const QPointF& pt, const QRectF& viewRect) const +{ + qreal w = viewRect.width() / 2.0; + qreal h = viewRect.height() / 2.0; + qreal x = pt.x() - (viewRect.x() + w); + qreal y = pt.y() - (viewRect.y() + h); return QPointF(x/w, y/h); } +QPointF KisColorSelector::mapColorToUnit(const KisColor& color, bool invertSaturation) const +{ + qreal angle = color.getH() * 2.0 * M_PI; + + qreal radius; + if (invertSaturation && m_inverseSaturation) { + radius = 1.0 - color.getS(); + } else { + radius = color.getS(); + } + + qreal x = std::cos(angle)*radius; + qreal y = std::sin(-angle)*radius; + + return QPointF(x,y); +} + + qint8 KisColorSelector::getLightIndex(const QPointF& pt) const { if (m_lightStripArea.contains(pt.toPoint(), true)) { @@ -273,10 +376,14 @@ int x = (width - size) / 2; int y = (height - size) / 2; + m_renderAreaSize = QSize(size,size); + m_viewConverter->setViewSize(m_renderAreaSize); + m_renderArea = QRect(x+stripThick, y, size, size); m_lightStripArea = QRect(0, 0, stripThick, QWidget::height()); - m_renderBuffer = QImage(size, size, QImage::Format_ARGB32); + m_renderBuffer = QImage(size, size, QImage::Format_ARGB32_Premultiplied); + m_maskBuffer = QImage(size, size, QImage::Format_ARGB32_Premultiplied); m_numLightPieces = numLightPieces; } @@ -321,8 +428,34 @@ } } +bool KisColorSelector::colorIsClear(const KisColor &color) +{ + if (m_gamutMaskOn && m_currentGamutMask) { + + QPointF colorCoord = mapCoordToView(mapColorToUnit(color, false), m_renderArea); + bool isClear = m_currentGamutMask->coordIsClear(colorCoord, *m_viewConverter, m_maskPreviewActive); + + if (isClear) { + return true; + } else { + return false; + } + + } else { + return true; + } + + return false; +} + + void KisColorSelector::requestUpdateColorAndPreview(const KisColor &color, Acs::ColorRole role) { + dbgPlugins << "KisColorSelector::requestUpdateColorAndPreview: requesting update to: " + << "H:" << color.getH() + << "S:" << color.getS() + << "X:" << color.getX(); + m_updateColorCompressor->start(qMakePair(color, role)); } @@ -336,21 +469,30 @@ m_selectedColor = color.first; m_selectedColorRole = color.second; + dbgPlugins << "KisColorSelector::slotUpdateColorAndPreview: m_selectedColor set to:" + << "H:" << m_selectedColor.getH() + << "S:" << m_selectedColor.getS() + << "X:" << m_selectedColor.getX() + << "role:" << m_selectedColorRole; + + if (selectAsFgColor) { emit sigFgColorChanged(m_selectedColor); } else { emit sigBgColorChanged(m_selectedColor); } } -void KisColorSelector::drawRing(QPainter& painter, KisColorSelector::ColorRing& ring, const QRect& rect) +void KisColorSelector::drawRing(QPainter& painter, int ringIndex, KisColorSelector::ColorRing& ring, const QRect& rect) { - painter.setRenderHint(QPainter::Antialiasing, false); + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, true); painter.resetTransform(); painter.translate(rect.width()/2, rect.height()/2); if (ring.pieced.size() > 1) { painter.rotate(-ring.getShift().degrees()); painter.scale(rect.width()/2, rect.height()/2); - painter.setPen(Qt::NoPen); - +// painter.setPen(Qt::NoPen); + QPen normalPen = QPen(QBrush(m_grayColor), 0.005); + QPen clearMaskPen = QPen(QBrush(Qt::white), 0.005); QBrush brush(Qt::SolidPattern); for(int i=0; i 1) { - for(int i=0; i= 0 && m_selectedPiece >= 0) { painter.resetTransform(); painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2); painter.rotate(-m_colorRings[m_selectedRing].getShift().degrees()); painter.scale(rect.width()/2, rect.height()/2); - painter.setPen(QPen(QBrush(Qt::red), 0.01)); + painter.setPen(selectedPen); painter.drawPath(m_colorRings[m_selectedRing].pieced[m_selectedPiece]); } } else { for(int i=0; i= 0) { - qreal iRad = m_colorRings[m_selectedRing].innerRadius; - qreal oRad = m_colorRings[m_selectedRing].outerRadius; + if (m_selectedRing >= 0) { + qreal iRad = m_colorRings[m_selectedRing].innerRadius; + qreal oRad = m_colorRings[m_selectedRing].outerRadius; - painter.setPen(QPen(QBrush(Qt::red), 0.005)); - painter.drawEllipse(QRectF(-iRad, -iRad, iRad*2.0, iRad*2.0)); - painter.drawEllipse(QRectF(-oRad, -oRad, oRad*2.0, oRad*2.0)); + painter.setPen(selectedPen); + painter.drawEllipse(QRectF(-iRad, -iRad, iRad*2.0, iRad*2.0)); + painter.drawEllipse(QRectF(-oRad, -oRad, oRad*2.0, oRad*2.0)); - if (getNumPieces() <= 1) { float c = std::cos(-m_selectedColor.getH() * PI2); float s = std::sin(-m_selectedColor.getH() * PI2); painter.drawLine(QPointF(c*iRad, s*iRad), QPointF(c*oRad, s*oRad)); @@ -449,7 +606,7 @@ { bool isVertical = true; qreal penSize = qreal(qMin(QWidget::width(), QWidget::height())) / 200.0; - KisColor color(m_selectedColor); + KisColor color(m_fgColor); painter.resetTransform(); @@ -475,6 +632,11 @@ if (i == m_selectedLightPiece) painter.drawRect(r); + + if (m_showValueScaleNumbers) { + int valueNumber = light*100; + painter.drawText(r, Qt::AlignCenter, QString::number(valueNumber)); + } } } else { @@ -515,6 +677,51 @@ } } +void KisColorSelector::drawBlip(QPainter& painter, const QRect& rect) +{ + painter.setRenderHint(QPainter::Antialiasing, false); + painter.resetTransform(); + painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2); + painter.scale(rect.width()/2, rect.height()/2); + + QPointF fgColorPos = mapColorToUnit(m_fgColor); + + dbgPlugins << "KisColorSelector::drawBlip: " + << "colorPoint H:" << m_fgColor.getH() << " S:" << m_fgColor.getS() + << "-> coord X:" << fgColorPos.x() << " Y:" << fgColorPos.y(); + + + painter.setPen(QPen(QBrush(Qt::white), 0.01)); + painter.drawEllipse(fgColorPos, 0.05, 0.05); + + painter.setPen(QPen(QBrush(Qt::black), 0.01)); + painter.setBrush(m_fgColor.getQColor()); + painter.drawEllipse(fgColorPos, 0.04, 0.04); +} + +void KisColorSelector::drawGamutMaskShape(QPainter &painter, const QRect &rect) +{ + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, true); + + painter.resetTransform(); + painter.translate(rect.width()/2, rect.height()/2); + painter.scale(rect.width()/2, rect.height()/2); + + painter.setPen(Qt::NoPen); + painter.setBrush(m_gamutMaskColor); + + painter.drawEllipse(QPointF(0,0), 1.0, 1.0); + + painter.resetTransform(); +// painter.setCompositionMode(QPainter::CompositionMode_Xor); + painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + + m_currentGamutMask->paint(painter, *m_viewConverter, m_maskPreviewActive); + + painter.restore(); +} + void KisColorSelector::paintEvent(QPaintEvent* /*event*/) { // 0 red -> (1,0,0) @@ -530,23 +737,64 @@ QPainter imgPainter(&m_renderBuffer); QPainter wdgPainter(this); - QRect fgRect(0, 0 , QWidget::width(), QWidget::height()/2); - QRect bgRect(0, QWidget::height()/2, QWidget::width(), QWidget::height()/2); + // draw the fg and bg color previews + // QPainter settings must be saved and restored afterwards, otherwise the wheel drawing is totally broken + wdgPainter.save(); + wdgPainter.setRenderHint(QPainter::Antialiasing, true); + + QRect fgRect(0, 0, QWidget::width(), QWidget::height()); wdgPainter.fillRect(fgRect, m_fgColor.getQColor()); - wdgPainter.fillRect(bgRect, m_bgColor.getQColor()); + + int bgSide = qMin(QWidget::width()*0.15,QWidget::height()*0.15); + + if (m_showBgColor) { + QPointF bgPolyPoints[3] = { + QPointF(QWidget::width(), QWidget::height()), + QPointF(QWidget::width()-bgSide, QWidget::height()), + QPointF(QWidget::width(), QWidget::height()-bgSide) + }; + + wdgPainter.setBrush(m_bgColor.getQColor()); + wdgPainter.setPen(m_bgColor.getQColor()); + wdgPainter.drawPolygon(bgPolyPoints, 3); + } + + wdgPainter.restore(); for(int i=0; ilocalPos(), m_renderArea); + m_widgetUpdatesSelf = true; + dbgPlugins << "KisColorSelector::mousePressEvent: m_widgetUpdatesSelf = true"; + + m_clickPos = mapCoordToUnit(event->localPos(), m_renderArea); m_mouseMoved = false; m_pressedButtons = event->buttons(); m_clickedRing = getSaturationIndex(m_clickPos); @@ -566,20 +814,26 @@ } else { Radian angle = std::atan2(m_clickPos.x(), m_clickPos.y()) - RAD_90; - m_selectedColor.setH(angle.scaled(0.0f, 1.0f)); - m_selectedColor.setS(getSaturation(m_clickedRing)); - m_selectedColor.setX(getLight(m_light, m_selectedColor.getH(), m_relativeLight)); - requestUpdateColorAndPreview(m_selectedColor, Acs::buttonsToRole(Qt::NoButton, m_pressedButtons)); - m_selectedRing = m_clickedRing; - m_mouseMoved = true; - update(); + + KisColor color; + color.setH(angle.scaled(0.0f, 1.0f)); + color.setS(getSaturation(m_clickedRing)); + color.setX(getLight(m_light, color.getH(), m_relativeLight)); + + if ((!m_enforceGamutMask) || colorIsClear(color)) { + m_selectedColor.setHSX(color.getH(), color.getS(), color.getX()); + requestUpdateColorAndPreview(m_selectedColor, Acs::buttonsToRole(Qt::NoButton, m_pressedButtons)); + m_selectedRing = m_clickedRing; + m_mouseMoved = true; + update(); + } } } } void KisColorSelector::mouseMoveEvent(QMouseEvent* event) { - QPointF dragPos = mapCoord(event->localPos(), m_renderArea); + QPointF dragPos = mapCoordToUnit(event->localPos(), m_renderArea); qint8 clickedLightPiece = getLightIndex(event->localPos()); if (clickedLightPiece >= 0) { @@ -612,18 +866,26 @@ color.setH(angle.scaled(0.0f, 1.0f)); color.setX(getLight(m_light, color.getH(), m_relativeLight)); - m_selectedPiece = getHueIndex(angle, m_colorRings[m_clickedRing].getShift()); - requestUpdateColorAndPreview(color, m_selectedColorRole); + if ((!m_enforceGamutMask) || colorIsClear(color)) { + m_selectedPiece = getHueIndex(angle, m_colorRings[m_clickedRing].getShift()); + requestUpdateColorAndPreview(color, m_selectedColorRole); + } } m_mouseMoved = true; } } else { Radian angle = std::atan2(dragPos.x(), dragPos.y()) - RAD_90; - m_selectedColor.setH(angle.scaled(0.0f, 1.0f)); - m_selectedColor.setX(getLight(m_light, m_selectedColor.getH(), m_relativeLight)); - requestUpdateColorAndPreview(m_selectedColor, m_selectedColorRole); + KisColor color; + color.setH(angle.scaled(0.0f, 1.0f)); + color.setS(getSaturation(m_clickedRing)); + color.setX(getLight(m_light, color.getH(), m_relativeLight)); + + if ((!m_enforceGamutMask) || colorIsClear(color)) { + m_selectedColor.setHSX(color.getH(), color.getS(), color.getX()); + requestUpdateColorAndPreview(m_selectedColor, m_selectedColorRole); + } } update(); @@ -633,32 +895,48 @@ { if (!m_mouseMoved && m_clickedRing >= 0) { Radian angle = std::atan2(m_clickPos.x(), m_clickPos.y()) - RAD_90; + KisColor color; - m_selectedRing = m_clickedRing; - m_selectedPiece = getHueIndex(angle, m_colorRings[m_clickedRing].getShift()); + qint8 hueIndex = getHueIndex(angle, m_colorRings[m_clickedRing].getShift()); - if (getNumPieces() > 1) - m_selectedColor.setH(getHue(m_selectedPiece, m_colorRings[m_clickedRing].getShift())); - else - m_selectedColor.setH(angle.scaled(0.0f, 1.0f)); + if (getNumPieces() > 1) { + color.setH(getHue(hueIndex, m_colorRings[m_clickedRing].getShift())); + } else { + color.setH(angle.scaled(0.0f, 1.0f)); + } - m_selectedColor.setS(getSaturation(m_selectedRing)); - m_selectedColor.setX(getLight(m_light, m_selectedColor.getH(), m_relativeLight)); + color.setS(getSaturation(m_clickedRing)); + color.setX(getLight(m_light, color.getH(), m_relativeLight)); - requestUpdateColorAndPreview(m_selectedColor, Acs::buttonsToRole(Qt::NoButton, m_pressedButtons)); + if ((!m_enforceGamutMask) || colorIsClear(color)) { + m_selectedColor.setHSX(color.getH(), color.getS(), color.getX()); + m_selectedPiece = hueIndex; + m_selectedRing = m_clickedRing; + requestUpdateColorAndPreview(m_selectedColor, Acs::buttonsToRole(Qt::NoButton, m_pressedButtons)); + } } else if (m_mouseMoved) requestUpdateColorAndPreview(m_selectedColor, m_selectedColorRole); m_clickedRing = -1; + + m_widgetUpdatesSelf = false; + dbgPlugins << "KisColorSelector::ReleasePressEvent: m_widgetUpdatesSelf = false"; + update(); } void KisColorSelector::resizeEvent(QResizeEvent* /*event*/) { recalculateAreas(quint8(getNumLightPieces())); } +void KisColorSelector::leaveEvent(QEvent* /*e*/) +{ + m_widgetUpdatesSelf = false; + dbgPlugins << "KisColorSelector::leaveEvent: m_widgetUpdatesSelf = false"; +} + void KisColorSelector::saveSettings() { KisConfig cfg; @@ -676,6 +954,17 @@ cfg.writeEntry("ArtColorSel.SelColorX", m_selectedColor.getX()); cfg.writeEntry("ArtColorSel.SelColorA", m_selectedColor.getA()); + cfg.writeEntry("ArtColorSel.defaultHueSteps", quint32(m_defaultHueSteps)); + cfg.writeEntry("ArtColorSel.defaultSaturationSteps", quint32(m_defaultSaturationSteps)); + cfg.writeEntry("ArtColorSel.defaultValueScaleSteps", quint32(m_defaultValueScaleSteps)); + + cfg.writeEntry("ArtColorSel.showBgColor", m_showBgColor); + cfg.writeEntry("ArtColorSel.showColorBlip", m_showColorBlip); + cfg.writeEntry("ArtColorSel.showValueScaleNumber", m_showValueScaleNumbers); + cfg.writeEntry("ArtColorSel.enforceGamutMask", m_enforceGamutMask); + + cfg.writeEntry("ArtColorSel.maskPreviewActive", m_maskPreviewActive); + QList angles; for(int i=0; i("ArtColorSel.ColorSpace" , KisColor::HSY))); - setNumLightPieces(cfg.readEntry("ArtColorSel.LightPieces", 19)); + m_defaultHueSteps = cfg.readEntry("ArtColorSel.defaultHueSteps", DEFAULT_HUE_STEPS); + m_defaultSaturationSteps = cfg.readEntry("ArtColorSel.defaultSaturationSteps", DEFAULT_SATURATION_STEPS); + m_defaultValueScaleSteps = cfg.readEntry("ArtColorSel.defaultValueScaleSteps", DEFAULT_VALUE_SCALE_STEPS); + + setNumLightPieces(cfg.readEntry("ArtColorSel.LightPieces", DEFAULT_VALUE_SCALE_STEPS)); m_selectedColor.setH(cfg.readEntry("ArtColorSel.SelColorH", 0.0f)); m_selectedColor.setS(cfg.readEntry("ArtColorSel.SelColorS", 0.0f)); m_selectedColor.setX(cfg.readEntry("ArtColorSel.SelColorX", 0.0f)); m_selectedColor.setA(1.0f); setInverseSaturation(cfg.readEntry("ArtColorSel.InversedSaturation", false)); - setLight(cfg.readEntry("ArtColorSel.Light", 0.5f), cfg.readEntry("ArtColorSel.RelativeLight", false)); + setLight(cfg.readEntry("ArtColorSel.Light", 0.5f), cfg.readEntry("ArtColorSel.RelativeLight", true)); - recalculateRings( - cfg.readEntry("ArtColorSel.NumRings" , 11), - cfg.readEntry("ArtColorSel.RingPieces", 12) - ); + setNumRings(cfg.readEntry("ArtColorSel.NumRings" , DEFAULT_SATURATION_STEPS)); + setNumPieces(cfg.readEntry("ArtColorSel.RingPieces", DEFAULT_HUE_STEPS)); QList angles = cfg.readList("ArtColorSel.RingAngles"); @@ -712,6 +1003,56 @@ } } + m_showBgColor = cfg.readEntry("ArtColorSel.showBgColor", true); + m_showColorBlip = cfg.readEntry("ArtColorSel.showColorBlip", true); + m_showValueScaleNumbers = cfg.readEntry("ArtColorSel.showValueScaleNumber", false); + m_enforceGamutMask = cfg.readEntry("ArtColorSel.enforceGamutMask", true); + + m_maskPreviewActive = cfg.readEntry("ArtColorSel.maskPreviewActive", true); + selectColor(m_selectedColor); update(); } + +void KisColorSelector::setDefaultHueSteps(int num) +{ + num = qBound(MIN_NUM_HUE_PIECES, num, MAX_NUM_HUE_PIECES); + m_defaultHueSteps = num; +} + +void KisColorSelector::setDefaultSaturationSteps(int num) +{ + num = qBound(MIN_NUM_SATURATION_RINGS, num, MAX_NUM_SATURATION_RINGS); + m_defaultSaturationSteps = num; +} + +void KisColorSelector::setDefaultValueScaleSteps(int num) +{ + num = qBound(MIN_NUM_LIGHT_PIECES, num, MAX_NUM_LIGHT_PIECES); + m_defaultValueScaleSteps = num; +} + +void KisColorSelector::setShowColorBlip(bool value) { + m_showColorBlip = value; + update(); +} + +void KisColorSelector::setShowBgColor(bool value) +{ + m_showBgColor = value; + update(); +} + +void KisColorSelector::setShowValueScaleNumbers(bool value) +{ + m_showValueScaleNumbers = value; + update(); +} + +bool KisColorSelector::saturationIsInvertible() +{ + if (!m_gamutMaskOn) return true; + bool b = (m_enforceGamutMask && getNumPieces() == 1) ? false : true; + return b; +} + diff --git a/plugins/dockers/gamutmask/CMakeLists.txt b/plugins/dockers/gamutmask/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/plugins/dockers/gamutmask/CMakeLists.txt @@ -0,0 +1,13 @@ +set(kritagamutmask_SOURCES + gamutmask_plugin.cpp + gamutmask_dock.cpp + KisGamutMaskChooser.cpp +) + +ki18n_wrap_ui(kritagamutmask_SOURCES + forms/wdgGamutMaskChooser.ui +) + +add_library(kritagamutmask MODULE ${kritagamutmask_SOURCES}) +target_link_libraries(kritagamutmask kritaui) +install(TARGETS kritagamutmask DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/dockers/gamutmask/KisGamutMaskChooser.h b/plugins/dockers/gamutmask/KisGamutMaskChooser.h new file mode 100644 --- /dev/null +++ b/plugins/dockers/gamutmask/KisGamutMaskChooser.h @@ -0,0 +1,27 @@ +#ifndef KISGAMUTMASKCHOOSER_H +#define KISGAMUTMASKCHOOSER_H + +#include + +class KoResourceItemChooser; +class KoResource; +class KoGamutMask; + +class KisGamutMaskChooser : public QWidget +{ + Q_OBJECT +public: + explicit KisGamutMaskChooser(QWidget *parent = nullptr); + ~KisGamutMaskChooser() override; + +Q_SIGNALS: + void sigGamutMaskSelected(KoGamutMask* mask); + +private Q_SLOTS: + void resourceSelected(KoResource* resource); + +private: + KoResourceItemChooser* m_itemChooser; +}; + +#endif // KISGAMUTMASKCHOOSER_H diff --git a/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp b/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp new file mode 100644 --- /dev/null +++ b/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp @@ -0,0 +1,81 @@ +#include "KisGamutMaskChooser.h" + +#include +#include +#include + +#include +#include +#include +#include + + +/// The resource item delegate for rendering the resource preview +class KisGamutMaskDelegate: public QAbstractItemDelegate +{ +public: + KisGamutMaskDelegate(QObject * parent = 0) : QAbstractItemDelegate(parent) {} + ~KisGamutMaskDelegate() override {} + /// reimplemented + void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override; + /// reimplemented + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const override { + return option.decorationSize; + } +}; + +void KisGamutMaskDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const +{ + painter->save(); + painter->setRenderHint(QPainter::SmoothPixmapTransform, true); + + if (!index.isValid()) + return; + + KoResource* resource = static_cast(index.internalPointer()); + KoGamutMask* mask = static_cast(resource); + + if (!mask) { + return; + } + + QImage preview = mask->image(); + + if(preview.isNull()) { + return; + } + + QRect paintRect = option.rect.adjusted(1, 1, -1, -1); + painter->drawImage(paintRect.x(), paintRect.y(), + preview.scaled(paintRect.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + painter->restore(); +} + + +KisGamutMaskChooser::KisGamutMaskChooser(QWidget *parent) : QWidget(parent) +{ + KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); + QSharedPointer adapter(new KoResourceServerAdapter(rServer)); + m_itemChooser = new KoResourceItemChooser(adapter, this); + m_itemChooser->setItemDelegate(new KisGamutMaskDelegate(this)); + m_itemChooser->showTaggingBar(true); + m_itemChooser->showButtons(false); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(0,0,0,0); + + layout->addWidget(m_itemChooser); + setLayout(layout); + + connect(m_itemChooser, SIGNAL(resourceSelected(KoResource*)), this, SLOT(resourceSelected(KoResource*))); +} + +KisGamutMaskChooser::~KisGamutMaskChooser() +{ + +} + +void KisGamutMaskChooser::resourceSelected(KoResource* resource) +{ + emit sigGamutMaskSelected(static_cast(resource)); +} diff --git a/plugins/dockers/gamutmask/forms/wdgGamutMaskChooser.ui b/plugins/dockers/gamutmask/forms/wdgGamutMaskChooser.ui new file mode 100644 --- /dev/null +++ b/plugins/dockers/gamutmask/forms/wdgGamutMaskChooser.ui @@ -0,0 +1,214 @@ + + + wdgGamutMaskChooser + + + + 0 + 0 + 358 + 336 + + + + + + + + 0 + 0 + + + + + + + + + + + 0 + 0 + + + + Select a mask + + + + + + + Apply To Selector + + + + + + + + + + Edit + + + + + + + + + + Delete + + + + + + + + + + + + + 0 + 0 + + + + Edit the gamut mask + + + + + + 6 + + + + + Title + + + + + + + 30 + + + + + + + Description + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 45 + + + + + 0 + 0 + + + + + + + + + + + + Cancel + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + true + + + Preview + + + + + + + + + + Save + + + + + + + + + + Save As New + + + + + + + + + + + + + + + + KisGamutMaskChooser + QWidget +
KisGamutMaskChooser.h
+ 1 +
+
+ + +
diff --git a/plugins/dockers/gamutmask/gamutmask_dock.h b/plugins/dockers/gamutmask/gamutmask_dock.h new file mode 100644 --- /dev/null +++ b/plugins/dockers/gamutmask/gamutmask_dock.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018 Anna Medonosova + * + * 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 H_GAMUT_MASK_DOCK_H +#define H_GAMUT_MASK_DOCK_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class KisCanvasResourceProvider; +class KisColor; +class QButtonGroup; +class QMenu; + +struct GamutMaskChooserUI; + +class GamutMaskDock: public QDockWidget, public KisMainwindowObserver, public KoResourceServerObserver +{ + Q_OBJECT + +public: + GamutMaskDock(); + ~GamutMaskDock() override; + QString observerName() override { return "GamutMaskDock"; } + void setMainWindow(KisViewManager* kisview) override; + void setCanvas(KoCanvasBase *canvas) override; + void unsetCanvas() override; + +public: // KoResourceServerObserver + void unsetResourceServer() override; + void resourceAdded(KoGamutMask* resource) override {} + void removingResource(KoGamutMask* resource) override; + void resourceChanged(KoGamutMask* resource) override; + void syncTaggedResourceView() override {} + void syncTagAddition(const QString&) override {} + void syncTagRemoval(const QString&) override {} + +Q_SIGNALS: + void sigGamutMaskSet(KoGamutMask* mask); + void sigGamutMaskChanged(KoGamutMask* mask); + void sigGamutMaskUnset(); + void sigGamutMaskPreviewUpdate(); + +private Q_SLOTS: + void slotGamutMaskEdit(); + void slotGamutMaskSave(); + void slotGamutMaskCancelEdit(); + void slotGamutMaskSelected(KoGamutMask* mask); + void slotGamutMaskPreview(); + void slotGamutMaskSaveNew(); + void slotGamutMaskDelete(); + void slotGamutMaskSet(); + + void slotDocumentRemoved(QString filename); + +private: + KisCanvasResourceProvider* m_resourceProvider; + + void closeMaskDocument(); + void openMaskEditor(); + void cancelMaskEdit(); + void saveSelectedMaskResource(); + void finalizeMaskSave(); + + bool m_selfClosingMaskFile; + + KoGamutMask *addDuplicateResource(QString newTitle); + QPair resolveMaskTitle(QString suggestedTitle); + + QList getShapesFromLayer(); + KisShapeLayerSP getShapeLayer(); + + GamutMaskChooserUI* m_dockerUI; + KoResourceItemChooser* m_maskChooser; + + KoGamutMask* m_selectedMask; + QRegExpValidator* m_maskTitleValidator; + + KisDocument* m_maskDocument; + KisView* m_view; +}; + + +#endif // H_GAMUT_MASK_DOCK_H diff --git a/plugins/dockers/gamutmask/gamutmask_dock.cpp b/plugins/dockers/gamutmask/gamutmask_dock.cpp new file mode 100644 --- /dev/null +++ b/plugins/dockers/gamutmask/gamutmask_dock.cpp @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2018 Anna Medonosova + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "gamutmask_dock.h" +#include +#include + +#include "ui_wdgGamutMaskChooser.h" + +class KisMainWindow; + +struct GamutMaskChooserUI: public QWidget, public Ui_wdgGamutMaskChooser +{ + GamutMaskChooserUI() { + setupUi(this); + } +}; + + +GamutMaskDock::GamutMaskDock() + : QDockWidget(i18n("Gamut Masks")) + , m_resourceProvider(0) + , m_maskDocument(nullptr) + , m_view(nullptr) + , m_selectedMask(nullptr) +{ + m_dockerUI = new GamutMaskChooserUI(); + + m_dockerUI->bnMaskEditor->setIcon(KisIconUtils::loadIcon("dirty-preset")); + m_dockerUI->bnMaskSet->setIcon(KisIconUtils::loadIcon("dialog-ok")); + m_dockerUI->bnMaskDelete->setIcon(KisIconUtils::loadIcon("deletelayer")); + m_dockerUI->maskPropertiesBox->setVisible(false); + + m_dockerUI->bnSaveMaskNew->setIcon(KisIconUtils::loadIcon("list-add")); + m_dockerUI->bnSaveMask->setIcon(KisIconUtils::loadIcon("document-save")); + m_dockerUI->bnCancelMaskEdit->setIcon(KisIconUtils::loadIcon("dialog-cancel")); + m_dockerUI->bnPreviewMask->setIcon(KisIconUtils::loadIcon("visible")); + + QRegularExpression maskTitleRegex("^[-_\\sA-Za-z0-9]+$"); + QRegularExpressionValidator* m_maskTitleValidator = new QRegularExpressionValidator(maskTitleRegex, this); + m_dockerUI->maskTitleEdit->setValidator(m_maskTitleValidator); + + KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); + rServer->addObserver(this); + + // gamut mask connections + connect(m_dockerUI->bnSaveMask , SIGNAL(clicked()) , SLOT(slotGamutMaskSave())); + connect(m_dockerUI->bnCancelMaskEdit , SIGNAL(clicked()) , SLOT(slotGamutMaskCancelEdit())); + connect(m_dockerUI->bnPreviewMask , SIGNAL(clicked()) , SLOT(slotGamutMaskPreview())); + + connect(m_dockerUI->bnMaskEditor , SIGNAL(clicked()) , SLOT(slotGamutMaskEdit())); + connect(m_dockerUI->maskChooser, SIGNAL(sigGamutMaskSelected(KoGamutMask*)), SLOT(slotGamutMaskSelected(KoGamutMask*))); + connect(m_dockerUI->bnSaveMaskNew , SIGNAL(clicked()) , SLOT(slotGamutMaskSaveNew())); + connect(m_dockerUI->bnMaskSet , SIGNAL(clicked()) , SLOT(slotGamutMaskSet())); + connect(m_dockerUI->bnMaskDelete , SIGNAL(clicked()) , SLOT(slotGamutMaskDelete())); + + setWidget(m_dockerUI); +} + +GamutMaskDock::~GamutMaskDock() +{ + KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); + rServer->removeObserver(this); +} + +void GamutMaskDock::setMainWindow(KisViewManager* kisview) +{ + m_resourceProvider = kisview->resourceProvider(); + + m_selectedMask = m_resourceProvider->currentGamutMask(); + + connect(this, SIGNAL(sigGamutMaskSet(KoGamutMask*)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMask*))); + connect(this, SIGNAL(sigGamutMaskChanged(KoGamutMask*)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMask*))); + connect(this, SIGNAL(sigGamutMaskUnset()), m_resourceProvider, SLOT(slotGamutMaskUnset())); + connect(this, SIGNAL(sigGamutMaskPreviewUpdate()), m_resourceProvider, SLOT(slotGamutMaskPreviewUpdate())); + connect(KisPart::instance(), SIGNAL(sigDocumentRemoved(QString)), this, SLOT(slotDocumentRemoved(QString))); +} + +void GamutMaskDock::slotGamutMaskEdit() +{ + if (!m_selectedMask) { + return; + } + openMaskEditor(); +} + +void GamutMaskDock::openMaskEditor() +{ + m_dockerUI->maskPropertiesBox->setVisible(true); + + //shouldnt be nullptr here + if (!m_selectedMask) { + return; + } + + m_dockerUI->maskTitleEdit->setText(m_selectedMask->title()); + m_dockerUI->maskDescriptionEdit->setPlainText(m_selectedMask->description()); + + // open gamut mask template in the application + QString maskTemplateFile = KoResourcePaths::findResource("data", "gamutmasks/GamutMaskTemplate.kra"); + dbgPlugins << "GamutMaskDock::slotGamutMaskEdit: maskTemplateFile" + << maskTemplateFile; + + // open template document + m_maskDocument = KisPart::instance()->createDocument(); + KisPart::instance()->addDocument(m_maskDocument); + m_maskDocument->openUrl(QUrl::fromLocalFile(maskTemplateFile), KisDocument::DontAddToRecent); + m_maskDocument->resetURL(); + + KisShapeLayerSP shapeLayer = getShapeLayer(); + + // pass only copies of shapes to the layer, + // so the originals don't disappear from the mask later + for (KoShape *shape: m_selectedMask->koShapes()) { + shapeLayer->addShape(shape->cloneShape()); + } + + m_maskDocument->setPreActivatedNode(shapeLayer); + + // set document as active + KisMainWindow* mainWindow = KisPart::instance()->currentMainwindow(); + Q_ASSERT(mainWindow); + + m_view = mainWindow->addViewAndNotifyLoadingCompleted(m_maskDocument); + Q_ASSERT(m_view); + + for(KisView *view: KisPart::instance()->views()) { + if (view->document() == m_maskDocument) { + view->activateWindow(); + break; + } + } +} + +void GamutMaskDock::cancelMaskEdit() +{ + if (m_selectedMask) { + m_selectedMask->clearPreview(); + + m_dockerUI->maskPropertiesBox->setVisible(false); + + if (m_resourceProvider->currentGamutMask() == m_selectedMask) { + emit sigGamutMaskChanged(m_selectedMask); + } + } +} + +void GamutMaskDock::saveSelectedMaskResource() +{ + if (m_selectedMask) { + m_selectedMask->setDescription(m_dockerUI->maskDescriptionEdit->toPlainText()); + + m_selectedMask->setMaskShapes(getShapesFromLayer()); + + m_selectedMask->setImage( + m_maskDocument->image()->convertToQImage(m_maskDocument->image()->bounds() + , m_maskDocument->image()->profile() + ) + ); + + m_selectedMask->clearPreview(); + m_selectedMask->save(); + } +} + +void GamutMaskDock::finalizeMaskSave() +{ + closeMaskDocument(); + m_dockerUI->maskPropertiesBox->setVisible(false); +} + +KoGamutMask* GamutMaskDock::addDuplicateResource(QString newTitle) +{ + // construct a copy + KoGamutMask* newMask = new KoGamutMask(m_selectedMask); + + QPair maskFile = resolveMaskTitle(newTitle); + QString maskTitle = maskFile.first; + QFileInfo fileInfo = maskFile.second; + + newMask->setTitle(maskTitle); + newMask->setFilename(fileInfo.filePath()); + + newMask->setValid(true); + + KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); + rServer->addResource(newMask, false); + + + + return newMask; +} + +QPair GamutMaskDock::resolveMaskTitle(QString suggestedTitle) +{ + KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); + QString saveLocation = rServer->saveLocation(); + + QString processedTitle = suggestedTitle.trimmed(); + QString newFilename = saveLocation + processedTitle.replace(QRegularExpression("\\s+"), "_") + ".kgm"; + + QFileInfo fileInfo(newFilename); + + QStringList resourceBlacklist = rServer->blackListedFiles(); + + int i = 1; + int lastIndex = 0; + while (fileInfo.exists()) { + if (resourceBlacklist.contains(fileInfo.filePath())) { + // allow to overwrite blacklisted masks + break; + } else { + fileInfo.setFile(saveLocation + processedTitle + QString("%1").arg(i) + ".kgm"); + lastIndex = i; + i++; + } + } + + QString maskTitle = (lastIndex > 0) ? (processedTitle + QString("%1").arg(lastIndex)) : processedTitle; + + return QPair(maskTitle, fileInfo); +} + +void GamutMaskDock::closeMaskDocument() +{ + if (m_maskDocument) { + // HACK: set the document to not modified to bypass confirmation dialog + // the close is already confirmed + m_maskDocument->setModified(false); + + m_maskDocument->closeUrl(); + m_view->closeView(); + m_view->deleteLater(); + + // HACK: set a flag that we are doing it ourselves, so the docker does not react to + // removing signal from KisPart + m_selfClosingMaskFile = true; + KisPart::instance()->removeView(m_view); + KisPart::instance()->removeDocument(m_maskDocument); + m_selfClosingMaskFile = false; + + m_maskDocument = nullptr; + m_view = nullptr; + } +} + +QList GamutMaskDock::getShapesFromLayer() +{ + KisShapeLayerSP shapeLayer = getShapeLayer(); + + // create a deep copy of the shapes to save in the mask, + // otherwise they vanish when the template closes + QList newShapes; + for (KoShape* sh: shapeLayer->shapes()) { + KoShape* newShape = sh->cloneShape(); + newShapes.append(newShape); + } + + return newShapes; +} + +KisShapeLayerSP GamutMaskDock::getShapeLayer() +{ + KisNodeSP node = m_maskDocument->image()->rootLayer()->findChildByName("maskShapesLayer"); + return KisShapeLayerSP(dynamic_cast(node.data())); +} + +void GamutMaskDock::slotGamutMaskSave() +{ + QString newTitle = m_dockerUI->maskTitleEdit->text(); + // was this mask previously set to selectors? + bool setToSelectors = (m_resourceProvider->currentGamutMask() == m_selectedMask); + + if (m_selectedMask->title() != newTitle) { + // title has changed, rename + if (!m_selectedMask) { + return; + } + + // construct a copy + KoGamutMask* newMask = addDuplicateResource(newTitle); + + // delete old mask and select new + slotGamutMaskDelete(); + m_selectedMask = newMask; + } + + saveSelectedMaskResource(); + finalizeMaskSave(); + + if (setToSelectors) { + emit sigGamutMaskSet(m_selectedMask); + } +} + +void GamutMaskDock::slotGamutMaskCancelEdit() +{ + if (!m_selectedMask) { + return; + } + + bool cancelApproved = false; + if (m_maskDocument->isModified()) { + int res = QMessageBox::warning(this, + i18nc("@title:window", "Krita"), + i18n("

Gamut mask '%1' has been modified.

Discard changes?

" + , m_selectedMask->title()), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + switch (res) { + case QMessageBox::Yes : { + cancelApproved = true; + break; + } + case QMessageBox::No : { + cancelApproved = false; + break; + } + } + } else { + cancelApproved = true; + } + + if (cancelApproved) { + cancelMaskEdit(); + closeMaskDocument(); + } +} + +void GamutMaskDock::slotGamutMaskPreview() +{ + if (!m_selectedMask) { + return; + } + + m_selectedMask->setPreviewMaskShapes(getShapesFromLayer()); + emit sigGamutMaskPreviewUpdate(); +} + +void GamutMaskDock::slotGamutMaskSelected(KoGamutMask *mask) +{ + m_selectedMask = mask; + + if (m_selectedMask) { + m_dockerUI->labelMaskName->setText(m_selectedMask->title()); + } else { + m_dockerUI->labelMaskName->setText(i18n("Select a mask")); + } +} + +void GamutMaskDock::setCanvas(KoCanvasBase *canvas) +{ + setEnabled(canvas != 0); +} + +void GamutMaskDock::unsetCanvas() +{ + setEnabled(false); +} + + +void GamutMaskDock::unsetResourceServer() +{ + KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); + rServer->removeObserver(this); +} + +void GamutMaskDock::removingResource(KoGamutMask *resource) +{ + // if deleting previously set mask, notify selectors to unset their mask + if (resource == m_resourceProvider->currentGamutMask()) { + emit sigGamutMaskUnset(); + m_selectedMask = nullptr; + m_dockerUI->labelMaskName->setText(i18n("Select a mask")); + } +} + +void GamutMaskDock::resourceChanged(KoGamutMask *resource) +{ + // if currently set mask has been changed, notify selectors + // TODO: is m_resourceProvider->currentGamutMask() == m_selectedMask? + if (resource == m_resourceProvider->currentGamutMask()) { + m_selectedMask = resource; + emit sigGamutMaskChanged(resource); + } +} + +void GamutMaskDock::slotGamutMaskSaveNew() +{ + if (!m_selectedMask) { + return; + } + + KoGamutMask* newMask = addDuplicateResource(m_dockerUI->maskTitleEdit->text()); + m_selectedMask = newMask; + + saveSelectedMaskResource(); + finalizeMaskSave(); +} + +void GamutMaskDock::slotGamutMaskDelete() +{ + if (!m_selectedMask) { + return; + } + + int res = QMessageBox::warning(this, + i18nc("@title:window", "Krita"), + i18n("Are you sure you want to delete mask '%1'?" + , m_selectedMask->title()), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + + // delete only if user confirms + if (res == QMessageBox::Yes) { + KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); + // TODO: other resources do it this way, but the files stay on disk + rServer->removeResourceAndBlacklist(m_selectedMask); + + m_selectedMask = nullptr; + m_dockerUI->labelMaskName->setText(i18n("Select a mask")); + } +} + +void GamutMaskDock::slotGamutMaskSet() +{ + if (!m_selectedMask) { + return; + } + + emit sigGamutMaskSet(m_selectedMask); +} + +void GamutMaskDock::slotDocumentRemoved(QString filename) +{ + if (!m_maskDocument) { + return; + } + + // HACK: we do not want to run this if it is we who close the file + if (!m_selfClosingMaskFile) { + // KisPart called, that a document will be removed + // if it's ours, cancel the mask edit operation + if (m_maskDocument->url().toLocalFile() == filename) { + cancelMaskEdit(); + } + } +} diff --git a/plugins/dockers/gamutmask/gamutmask_plugin.h b/plugins/dockers/gamutmask/gamutmask_plugin.h new file mode 100644 --- /dev/null +++ b/plugins/dockers/gamutmask/gamutmask_plugin.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Anna Medonosova + * + * 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 H_GAMUT_MASK_PLUGIN_H +#define H_GAMUT_MASK_PLUGIN_H + +#include +#include + +class GamutMaskPlugin: public QObject +{ +public: + GamutMaskPlugin(QObject *parent, const QVariantList &); +}; + +#endif // H_GAMUT_MASK_PLUGIN_H diff --git a/plugins/dockers/gamutmask/gamutmask_plugin.cpp b/plugins/dockers/gamutmask/gamutmask_plugin.cpp new file mode 100644 --- /dev/null +++ b/plugins/dockers/gamutmask/gamutmask_plugin.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Anna Medonosova + * + * 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 "gamutmask_plugin.h" +#include "gamutmask_dock.h" + +#include +#include +#include +#include + +K_PLUGIN_FACTORY_WITH_JSON(PaletteDockPluginFactory, "krita_gamutmask.json", registerPlugin();) + +class GamutMaskDockFactory: public KoDockFactoryBase +{ +public: + QString id() const override { + return QString("GamutMask"); + } + + virtual Qt::DockWidgetArea defaultDockWidgetArea() const { + return Qt::RightDockWidgetArea; + } + + QDockWidget* createDockWidget() override { + GamutMaskDock* dockWidget = new GamutMaskDock(); + dockWidget->setObjectName(id()); + return dockWidget; + } + + DockPosition defaultDockPosition() const override { + return DockMinimized; + } +}; + + +GamutMaskPlugin::GamutMaskPlugin(QObject* parent, const QVariantList &): + QObject(parent) +{ + KoDockRegistry::instance()->add(new GamutMaskDockFactory()); +} + +#include "gamutmask_plugin.moc" diff --git a/plugins/dockers/gamutmask/krita_gamutmask.json b/plugins/dockers/gamutmask/krita_gamutmask.json new file mode 100644 --- /dev/null +++ b/plugins/dockers/gamutmask/krita_gamutmask.json @@ -0,0 +1,9 @@ +{ + "Id": "Gamut masks", + "Type": "Service", + "X-KDE-Library": "kritagamutmask", + "X-KDE-ServiceTypes": [ + "Krita/Dock" + ], + "X-Krita-Version": "28" +}