diff --git a/libs/ui/dialogs/kis_dlg_preferences.cc b/libs/ui/dialogs/kis_dlg_preferences.cc --- a/libs/ui/dialogs/kis_dlg_preferences.cc +++ b/libs/ui/dialogs/kis_dlg_preferences.cc @@ -156,17 +156,21 @@ chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas()); chkEnableTranformToolAfterPaste->setChecked(cfg.activateTransformToolAfterPaste()); - m_groupBoxKineticScrollingSettings->setChecked(cfg.kineticScrollingEnabled()); + m_chkCtrlPickerUsesDedicatedPickerConfig->setChecked(cfg.ctrlPickerUsesDedicatedPickerConfig()); - m_cmbKineticScrollingGesture->addItem(i18n("On Touch Drag")); - m_cmbKineticScrollingGesture->addItem(i18n("On Click Drag")); - m_cmbKineticScrollingGesture->addItem(i18n("On Middle-Click Drag")); - //m_cmbKineticScrollingGesture->addItem(i18n("On Right Click Drag")); + { // Kinetic Scrolling + m_groupBoxKineticScrollingSettings->setChecked(cfg.kineticScrollingEnabled()); - m_cmbKineticScrollingGesture->setCurrentIndex(cfg.kineticScrollingGesture()); - m_kineticScrollingSensitivitySlider->setRange(0, 100); - m_kineticScrollingSensitivitySlider->setValue(cfg.kineticScrollingSensitivity()); - m_chkKineticScrollingHideScrollbars->setChecked(cfg.kineticScrollingHiddenScrollbars()); + m_cmbKineticScrollingGesture->addItem(i18n("On Touch Drag")); + m_cmbKineticScrollingGesture->addItem(i18n("On Click Drag")); + m_cmbKineticScrollingGesture->addItem(i18n("On Middle-Click Drag")); + //m_cmbKineticScrollingGesture->addItem(i18n("On Right Click Drag")); + + m_cmbKineticScrollingGesture->setCurrentIndex(cfg.kineticScrollingGesture()); + m_kineticScrollingSensitivitySlider->setRange(0, 100); + m_kineticScrollingSensitivitySlider->setValue(cfg.kineticScrollingSensitivity()); + m_chkKineticScrollingHideScrollbars->setChecked(cfg.kineticScrollingHiddenScrollbars()); + } // @@ -243,10 +247,14 @@ m_chkHiDPI->setChecked(true); m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker(true)); + + m_chkCtrlPickerUsesDedicatedPickerConfig->setChecked(cfg.ctrlPickerUsesDedicatedPickerConfig(true)); + m_groupBoxKineticScrollingSettings->setChecked(cfg.kineticScrollingEnabled(true)); m_cmbKineticScrollingGesture->setCurrentIndex(cfg.kineticScrollingGesture(true)); m_kineticScrollingSensitivitySlider->setValue(cfg.kineticScrollingSensitivity(true)); m_chkKineticScrollingHideScrollbars->setChecked(cfg.kineticScrollingHiddenScrollbars(true)); + m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt(true)); chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas(true)); chkEnableTranformToolAfterPaste->setChecked(cfg.activateTransformToolAfterPaste(true)); @@ -255,9 +263,6 @@ KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8()); cursorColor.fromQColor(cfg.getCursorMainColor(true)); cursorColorBtutton->setColor(cursorColor); - - - } CursorStyle GeneralTab::cursorStyle() @@ -1345,6 +1350,8 @@ cfg.setToolOptionsInDocker(dialog->m_general->toolOptionsInDocker()); + cfg.setCtrlPickerUsesDedicatedPickerConfig(dialog->m_general->m_chkCtrlPickerUsesDedicatedPickerConfig->isChecked()); + cfg.setKineticScrollingEnabled(dialog->m_general->kineticScrollingEnabled()); cfg.setKineticScrollingGesture(dialog->m_general->kineticScrollingGesture()); cfg.setKineticScrollingSensitivity(dialog->m_general->kineticScrollingSensitivity()); diff --git a/libs/ui/forms/wdggeneralsettings.ui b/libs/ui/forms/wdggeneralsettings.ui --- a/libs/ui/forms/wdggeneralsettings.ui +++ b/libs/ui/forms/wdggeneralsettings.ui @@ -26,7 +26,7 @@ - 0 + 2 @@ -430,6 +430,16 @@ + + + + <html><head/><body><p>Enabling this option will allow the Paint Tool's Ctrl-Picker to use the current settings from the dedicated Color Picker's <span style=" font-style:italic;">Tool Options</span>. This allows features like radius and mixing to be used from both color pickers using the same settings.</p></body></html> + + + Use Color Picker's tool options for Ctrl-Picker + + + @@ -455,7 +465,7 @@ - + @@ -782,17 +792,17 @@ QSpinBox
kis_int_parse_spin_box.h
- - KisColorButton - QPushButton -
kis_color_button.h
-
KisSliderSpinBox - QWidget + QFrame
kis_slider_spin_box.h
1
+ + KisColorButton + QPushButton +
kis_color_button.h
+
diff --git a/libs/ui/kis_config.h b/libs/ui/kis_config.h --- a/libs/ui/kis_config.h +++ b/libs/ui/kis_config.h @@ -513,6 +513,9 @@ bool toolOptionsInDocker(bool defaultValue = false) const; void setToolOptionsInDocker(bool inDocker); + bool ctrlPickerUsesDedicatedPickerConfig(bool defaultValue = false) const; + void setCtrlPickerUsesDedicatedPickerConfig(bool enabled); + bool kineticScrollingEnabled(bool defaultValue = false) const; void setKineticScrollingEnabled(bool enabled); diff --git a/libs/ui/kis_config.cc b/libs/ui/kis_config.cc --- a/libs/ui/kis_config.cc +++ b/libs/ui/kis_config.cc @@ -1733,14 +1733,24 @@ m_cfg.writeEntry("ToolOptionsInDocker", inDocker); } +bool KisConfig::ctrlPickerUsesDedicatedPickerConfig(bool defaultValue) const +{ + return (defaultValue ? false : m_cfg.readEntry("CtrlPickerUsesDedicatedPickerConfig", false)); +} + +void KisConfig::setCtrlPickerUsesDedicatedPickerConfig(bool enabled) +{ + m_cfg.writeEntry("CtrlPickerUsesDedicatedPickerConfig", enabled); +} + bool KisConfig::kineticScrollingEnabled(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("KineticScrollingEnabled", true)); } -void KisConfig::setKineticScrollingEnabled(bool value) +void KisConfig::setKineticScrollingEnabled(bool enabled) { - m_cfg.writeEntry("KineticScrollingEnabled", value); + m_cfg.writeEntry("KineticScrollingEnabled", enabled); } int KisConfig::kineticScrollingGesture(bool defaultValue) const diff --git a/libs/ui/tool/kis_tool_paint.h b/libs/ui/tool/kis_tool_paint.h --- a/libs/ui/tool/kis_tool_paint.h +++ b/libs/ui/tool/kis_tool_paint.h @@ -36,6 +36,7 @@ #include "kis_signal_compressor_with_param.h" #include #include +#include "kis_tool_utils.h" class QGridLayout; class KoCompositeOp; @@ -43,7 +44,6 @@ class KRITAUI_EXPORT KisToolPaint : public KisTool { - Q_OBJECT public: @@ -56,7 +56,6 @@ void mouseMoveEvent(KoPointerEvent *event) override; protected: - void setMode(ToolMode mode) override; void canvasResourceChanged(int key, const QVariant &v) override; @@ -86,12 +85,9 @@ const KoPointerEvent *event, KisPaintOpSettings::OutlineMode outlineMode); -protected: bool isOutlineEnabled() const; void setOutlineEnabled(bool enabled); - bool pickColor(const QPointF &documentPixel, AlternateAction action); - /// Add the tool-specific layout to the default option widget layout. void addOptionWidgetLayout(QLayout *layout); @@ -122,22 +118,6 @@ const KoCompositeOp* compositeOp(); -public Q_SLOTS: - void activate(ToolActivation toolActivation, const QSet &shapes) override; - void deactivate() override; - -private Q_SLOTS: - - void slotPopupQuickHelp(); - - void increaseBrushSize(); - void decreaseBrushSize(); - - void activatePickColorDelayed(); - - void slotColorPickingFinished(const KoColor &color); - -protected: quint8 m_opacity; bool m_paintOutline; QPointF m_outlineDocPoint; @@ -162,6 +142,7 @@ int colorPreviewResourceId(AlternateAction action); QRectF colorPreviewDocRect(const QPointF &outlineDocPoint); + bool pickColor(const QPointF &documentPixel, AlternateAction action); bool isPickingAction(AlternateAction action); struct PickingJob { @@ -176,17 +157,11 @@ }; void addPickerJob(const PickingJob &pickingJob); -private: - bool m_specialHoverModifier; QGridLayout *m_optionsWidgetLayout; bool m_supportOutline; - /** - * Used as a switch for pickColor - */ - // used to skip some of the tablet events and don't update the colour that often QTimer m_colorPickerDelayTimer; AlternateAction delayedAction; @@ -196,15 +171,30 @@ KisStrokeId m_pickerStrokeId; int m_pickingResource; + KisPickerUtils::ColorPickerConfig m_pickerSettings; typedef KisSignalCompressorWithParam PickingCompressor; QScopedPointer m_colorPickingCompressor; qreal m_localOpacity {1.0}; qreal m_oldOpacity {1.0}; + bool m_useDedicatePickerConfig; + +public Q_SLOTS: + void activate(ToolActivation toolActivation, const QSet &shapes) override; + void deactivate() override; + +private Q_SLOTS: + void slotPopupQuickHelp(); + + void increaseBrushSize(); + void decreaseBrushSize(); + + void activatePickColorDelayed(); + void slotColorPickingFinished(const KoColor &color); + Q_SIGNALS: void sigPaintingFinished(); }; #endif // KIS_TOOL_PAINT_H_ - diff --git a/libs/ui/tool/kis_tool_paint.cc b/libs/ui/tool/kis_tool_paint.cc --- a/libs/ui/tool/kis_tool_paint.cc +++ b/libs/ui/tool/kis_tool_paint.cc @@ -83,11 +83,9 @@ m_colorPickerDelayTimer(), m_isOutlineEnabled(true) { + m_opacity = OPACITY_OPAQUE_U8; m_specialHoverModifier = false; m_optionsWidgetLayout = 0; - - m_opacity = OPACITY_OPAQUE_U8; - m_supportOutline = false; { @@ -110,14 +108,17 @@ m_colorPickerDelayTimer.setSingleShot(true); connect(&m_colorPickerDelayTimer, SIGNAL(timeout()), this, SLOT(activatePickColorDelayed())); + m_pickerSettings.load(); + using namespace std::placeholders; // For _1 placeholder std::function callback = std::bind(&KisToolPaint::addPickerJob, this, _1); m_colorPickingCompressor.reset( - new PickingCompressor(100, callback, KisSignalCompressor::FIRST_ACTIVE)); + // "m_pickerSettings.resampleRateMS / 1.5" is a bit of magic number. + // It's a small hacky way to keep ctrl-picker speed roughly the same as dedicated kis_tool_colorpicker. + new PickingCompressor(m_pickerSettings.resampleRateMS / 1.5, callback, KisSignalCompressor::FIRST_ACTIVE)); } - KisToolPaint::~KisToolPaint() { } @@ -140,7 +141,6 @@ } connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle()), Qt::UniqueConnection); - } @@ -160,6 +160,9 @@ KisCanvasResourceProvider *provider = qobject_cast(canvas())->viewManager()->resourceProvider(); m_oldOpacity = provider->opacity(); provider->setOpacity(m_localOpacity); + + m_pickerSettings.load(); //Reload picker settings. + m_useDedicatePickerConfig = KSharedConfig::openConfig()->group("").readEntry("CtrlPickerUsesDedicatedPickerConfig", false); } void KisToolPaint::deactivate() @@ -244,63 +247,46 @@ KisTool::setMode(mode); } -void KisToolPaint::activatePickColor(AlternateAction action) -{ - m_showColorPreview = true; - - requestUpdateOutline(m_outlineDocPoint, 0); - - int resource = colorPreviewResourceId(action); - KoColor color = canvas()->resourceManager()->koColorResource(resource); - - KisCanvas2 * kisCanvas = dynamic_cast(canvas()); - KIS_ASSERT_RECOVER_RETURN(kisCanvas); - - m_colorPreviewCurrentColor = kisCanvas->displayColorConverter()->toQColor(color); - - if (!m_colorPreviewBaseColor.isValid()) { - m_colorPreviewBaseColor = m_colorPreviewCurrentColor; - } -} - -void KisToolPaint::deactivatePickColor(AlternateAction action) -{ - Q_UNUSED(action); - - m_showColorPreview = false; - m_oldColorPreviewRect = QRect(); - m_oldColorPreviewUpdateRect = QRect(); - m_colorPreviewCurrentColor = QColor(); -} - -void KisToolPaint::pickColorWasOverridden() -{ - m_colorPreviewShowComparePlate = false; - m_colorPreviewBaseColor = QColor(); -} - void KisToolPaint::activateAlternateAction(AlternateAction action) { switch (action) { + case Secondary: + // Falls through case PickFgNode: - /* Falls through */ + // Falls through case PickBgNode: - /* Falls through */ + // Falls through case PickFgImage: - /* Falls through */ + // Falls through case PickBgImage: delayedAction = action; m_colorPickerDelayTimer.start(100); - /* Falls through */ + // Falls through default: pickColorWasOverridden(); KisTool::activateAlternateAction(action); }; } +void KisToolPaint::deactivateAlternateAction(AlternateAction action) +{ + if (!isPickingAction(action)) { + KisTool::deactivateAlternateAction(action); + return; + } + + delayedAction = KisTool::NONE; + m_colorPickerDelayTimer.stop(); + + resetCursorStyle(); + deactivatePickColor(action); +} + void KisToolPaint::activatePickColorDelayed() { switch (delayedAction) { + case Secondary: + //Falls through, for now. case PickFgNode: useCursor(KisCursor::pickerLayerForegroundCursor()); activatePickColor(delayedAction); @@ -324,25 +310,47 @@ repaintDecorations(); } -bool KisToolPaint::isPickingAction(AlternateAction action) { - return action == PickFgNode || - action == PickBgNode || - action == PickFgImage || - action == PickBgImage; +void KisToolPaint::activatePickColor(AlternateAction action) +{ + m_showColorPreview = true; + + requestUpdateOutline(m_outlineDocPoint, 0); + + int resource = colorPreviewResourceId(action); + KoColor color = canvas()->resourceManager()->koColorResource(resource); + + KisCanvas2 *kisCanvas = dynamic_cast(canvas()); + KIS_ASSERT_RECOVER_RETURN(kisCanvas); + + m_colorPreviewCurrentColor = kisCanvas->displayColorConverter()->toQColor(color); + + if (!m_colorPreviewBaseColor.isValid()) { + m_colorPreviewBaseColor = m_colorPreviewCurrentColor; + } } -void KisToolPaint::deactivateAlternateAction(AlternateAction action) +void KisToolPaint::deactivatePickColor(AlternateAction action) { - if (!isPickingAction(action)) { - KisTool::deactivateAlternateAction(action); - return; - } + Q_UNUSED(action); - delayedAction = KisTool::NONE; - m_colorPickerDelayTimer.stop(); + m_showColorPreview = false; + m_oldColorPreviewRect = QRect(); + m_oldColorPreviewUpdateRect = QRect(); + m_colorPreviewCurrentColor = QColor(); +} - resetCursorStyle(); - deactivatePickColor(action); +void KisToolPaint::pickColorWasOverridden() +{ + m_colorPreviewShowComparePlate = false; + m_colorPreviewBaseColor = QColor(); +} + +bool KisToolPaint::isPickingAction(AlternateAction action) { + return action == PickFgNode + || action == PickBgNode + || action == PickFgImage + || action == PickBgImage + || action == Secondary; } void KisToolPaint::addPickerJob(const PickingJob &pickingJob) @@ -357,6 +365,7 @@ const QPoint imagePoint = image()->documentToImagePixelFloored(pickingJob.documentPixel); const bool fromCurrentNode = pickingJob.action == PickFgNode || pickingJob.action == PickBgNode; + m_pickingResource = colorPreviewResourceId(pickingJob.action); if (!fromCurrentNode) { @@ -375,14 +384,20 @@ KisPaintDeviceSP device = fromCurrentNode ? currentNode()->colorPickSourceDevice() : image()->projection(); - // Used for color picker blending. + bool pure = !m_useDedicatePickerConfig; + + // Used for color picker mixing. KoColor currentColor = canvas()->resourceManager()->foregroundColor(); if( pickingJob.action == PickBgNode || pickingJob.action == PickBgImage ){ currentColor = canvas()->resourceManager()->backgroundColor(); } + auto *canv = dynamic_cast(canvas()); + KIS_SAFE_ASSERT_RECOVER_RETURN(canv); + image()->addJob(m_pickerStrokeId, - new KisColorPickerStrokeStrategy::Data(device, imagePoint, currentColor)); + new KisColorPickerStrokeStrategy::Data(device, canv, imagePoint, currentColor, + m_pickerSettings.radius, m_pickerSettings.mix, pure)); } void KisToolPaint::beginAlternateAction(KoPointerEvent *event, AlternateAction action) @@ -429,11 +444,8 @@ int KisToolPaint::colorPreviewResourceId(AlternateAction action) { - bool toForegroundColor = action == PickFgNode || action == PickFgImage; - int resource = toForegroundColor ? - KoCanvasResourceProvider::ForegroundColor : KoCanvasResourceProvider::BackgroundColor; - - return resource; + bool toBackgroundColor = action == PickBgNode || action == PickBgImage; + return toBackgroundColor ? KoCanvasResourceProvider::BackgroundColor : KoCanvasResourceProvider::ForegroundColor; } void KisToolPaint::slotColorPickingFinished(const KoColor &color) @@ -442,7 +454,7 @@ if (!m_showColorPreview) return; - KisCanvas2 * kisCanvas = dynamic_cast(canvas()); + KisCanvas2 *kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas); QColor previewColor = kisCanvas->displayColorConverter()->toQColor(color); @@ -561,7 +573,6 @@ m_optionsWidgetLayout->addLayout(layout, rowCount, 0, 1, 2); } - void KisToolPaint::addOptionWidgetOption(QWidget *control, QWidget *label) { Q_ASSERT(m_optionsWidgetLayout != 0); @@ -574,7 +585,6 @@ } } - void KisToolPaint::setOpacity(qreal opacity) { m_opacity = quint8(opacity * OPACITY_OPAQUE_U8); @@ -738,8 +748,7 @@ // DIRTY HACK ALERT: we should fetch the assistant's dirty rect when requesting // the update, instead of just dumbly update the entire canvas! - - KisCanvas2 * kiscanvas = dynamic_cast(canvas()); + KisCanvas2 *kiscanvas = dynamic_cast(canvas()); KisPaintingAssistantsDecorationSP decoration = kiscanvas->paintingAssistantsDecoration(); if (decoration && decoration->visible()) { kiscanvas->updateCanvas(); @@ -779,4 +788,3 @@ return path; } - diff --git a/libs/ui/tool/kis_tool_utils.h b/libs/ui/tool/kis_tool_utils.h --- a/libs/ui/tool/kis_tool_utils.h +++ b/libs/ui/tool/kis_tool_utils.h @@ -23,21 +23,48 @@ #include #include -class QPoint; +class KisCanvas2; +class KoCanvasResourceProvider; class KoColor; +class QPoint; + +namespace KisToolUtils { // General tool utilities... +/** + * Recursively search a node with a non-transparent pixel + */ +KisNodeSP KRITAUI_EXPORT findNode(KisNodeSP node, const QPoint &point, bool wholeGroup, bool editableOnly = true); + +/** + * return true if success + * Clears the image. Selection is optional, use 0 to clear everything. + */ +bool KRITAUI_EXPORT clearImage(KisImageSP image, KisNodeSP node, KisSelectionSP selection); +} -namespace KisToolUtils { +//============================================================================= + +namespace KisPickerUtils { // Color picker utilities... +enum SamplingSource { + MERGED_VISIBLE, + CURRENT_LAYER +}; + +enum UpdateTarget { + FOREGROUND, + BACKGROUND, + BOTH, + SHIFT, // Shift FG to BG and update FG. +}; struct KRITAUI_EXPORT ColorPickerConfig { ColorPickerConfig(); - bool toForegroundColor; - bool updateColor; - bool addPalette; - bool normaliseValues; - bool sampleMerged; + SamplingSource source; + int resampleRateMS; + bool update; int radius; - int blend; + int mix; + bool normalizeColorData; void save(bool defaultActivation = true) const; void load(bool defaultActivation = true); @@ -51,27 +78,19 @@ * out_color - Output parameter returning newly picked color. * dev - Paint device to pick from. * pos - Position to pick from. - * blendColor - Optional color to be blended with. + * mixColor - Optional color to be mixed with. * radius - Picking area radius in pixels. - * blend - Blend percentage. 100% all picked, 0% all blendColor. - * pure - Whether to bypass radius, blending, and active layer settings for pure picking. + * mix - Mix percentage. 100% all picked, 0% all mixColor. + * pure - Whether to bypass radius, mixing, and active layer settings for pure picking. * * RETURN - Returns TRUE whenever a valid color is picked. */ -bool KRITAUI_EXPORT pickColor(KoColor &out_color, KisPaintDeviceSP dev, const QPoint &pos, - KoColor const *const blendColor = nullptr, int radius = 1, - int blend = 100, bool pure = false); +bool KRITAUI_EXPORT pickColor(KoColor &out_color, KisPaintDeviceSP sourceDevice, KisCanvas2 *canvas, const QPoint &pos, + KoColor const *const mixColor = nullptr, int radius = 1, int mix = 100, bool pure = false); -/** - * Recursively search a node with a non-transparent pixel - */ -KisNodeSP KRITAUI_EXPORT findNode(KisNodeSP node, const QPoint &point, bool wholeGroup, bool editableOnly = true); +void sampleRadius(KoColor &out_color, const KisPaintDeviceSP device, const QPoint position, const int radius); -/** - * return true if success - * Clears the image. Selection is optional, use 0 to clear everything. - */ -bool KRITAUI_EXPORT clearImage(KisImageSP image, KisNodeSP node, KisSelectionSP selection); +void KRITAUI_EXPORT submitColor(KoCanvasResourceProvider &resourceProvider, UpdateTarget target, KoColor &color); } #endif // KIS_TOOL_UTILS_H diff --git a/libs/ui/tool/kis_tool_utils.cpp b/libs/ui/tool/kis_tool_utils.cpp --- a/libs/ui/tool/kis_tool_utils.cpp +++ b/libs/ui/tool/kis_tool_utils.cpp @@ -20,83 +20,18 @@ #include #include +#include +#include #include #include #include #include #include #include +#include +#include namespace KisToolUtils { - - bool pickColor(KoColor &out_color, KisPaintDeviceSP dev, const QPoint &pos, - KoColor const *const blendColor, int radius, int blend, bool pure) - { - KIS_ASSERT(dev); - - // Bugfix hack forcing pure on first sample to avoid wrong - // format blendColor on newly initialized Krita. - static bool firstTime = true; - if (firstTime == true) { - pure = true; - firstTime = false; - } - - const KoColorSpace *cs = dev->colorSpace(); - KoColor pickedColor(Qt::transparent, cs); - - // Sampling radius. - if (!pure && radius > 1) { - QVector pixels; - const int effectiveRadius = radius - 1; - - const QRect pickRect(pos.x() - effectiveRadius, pos.y() - effectiveRadius, - 2 * effectiveRadius + 1, 2 * effectiveRadius + 1); - KisSequentialConstIterator it(dev, pickRect); - - const int radiusSq = pow2(effectiveRadius); - - while (it.nextPixel()) { - const QPoint realPos(it.x(), it.y()); - const QPoint pt = realPos - pos; - if (pow2(pt.x()) + pow2(pt.y()) < radiusSq) { - pixels << it.oldRawData(); - } - } - - const quint8 **cpixels = const_cast(pixels.constData()); - cs->mixColorsOp()->mixColors(cpixels, pixels.size(), pickedColor.data()); - } else { - dev->pixel(pos.x(), pos.y(), &pickedColor); - } - - // Color blending. - if (!pure && blendColor && blend < 100) { - //Scale from 0..100% to 0..255 range for mixOp weights. - quint8 blendScaled = static_cast(blend * 2.55f); - - const quint8 *colors[2]; - colors[0] = blendColor->data(); - colors[1] = pickedColor.data(); - qint16 weights[2]; - weights[0] = 255 - blendScaled; - weights[1] = blendScaled; - - const KoMixColorsOp *mixOp = dev->colorSpace()->mixColorsOp(); - mixOp->mixColors(colors, weights, 2, pickedColor.data()); - } - - pickedColor.convertTo(dev->compositionSourceColorSpace()); - - bool validColorPicked = pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8; - - if (validColorPicked) { - out_color = pickedColor; - } - - return validColorPicked; - } - KisNodeSP findNode(KisNodeSP node, const QPoint &point, bool wholeGroup, bool editableOnly) { KisNodeSP foundNode = 0; @@ -157,54 +92,163 @@ } return false; } +} - const QString ColorPickerConfig::CONFIG_GROUP_NAME = "tool_color_picker"; +//============================================================================= - ColorPickerConfig::ColorPickerConfig() - : toForegroundColor(true) - , updateColor(true) - , addPalette(false) - , normaliseValues(false) - , sampleMerged(true) - , radius(1) - , blend(100) - { +namespace KisPickerUtils { // Color picker utility functions. + +bool pickColor(KoColor &out_color, KisPaintDeviceSP sourceDevice, KisCanvas2 *canvas, + const QPoint &pos, KoColor const *const mixColor, int radius, int mix, bool pure) +{ + KIS_ASSERT(sourceDevice); + + // Bugfix hack forcing pure on first sample to avoid wrong + // format mixColor on newly initialized Krita. + static bool firstTime = true; + if (firstTime == true) { + pure = true; + firstTime = false; } - inline QString getConfigKey(bool defaultActivation) { - return defaultActivation ? - "ColorPickerDefaultActivation" : "ColorPickerTemporaryActivation"; + KoColor pickedColor(Qt::transparent, sourceDevice->colorSpace()); + + if (!pure && radius > 1) { + if (canvas && !canvas->image()->bounds().contains(pos)) { + //TODO: Implement proper radius sampling for reference images! + KisSharedPtr referenceImages = canvas->imageView()->document()->referenceImagesLayer(); + if (referenceImages && canvas->referenceImagesDecoration()->visible()) { + pickedColor.fromQColor(referenceImages->getPixel(pos)); + } + } else { + sampleRadius(pickedColor, sourceDevice, pos, radius); + } + } else { + if (canvas && !canvas->image()->bounds().contains(pos)) { + KisSharedPtr referenceImages = canvas->imageView()->document()->referenceImagesLayer(); + if (referenceImages && canvas->referenceImagesDecoration()->visible()) { + pickedColor.fromQColor(referenceImages->getPixel(pos)); + } + } else { + sourceDevice->pixel(pos.x(), pos.y(), &pickedColor); + } } - void ColorPickerConfig::save(bool defaultActivation) const - { - KisPropertiesConfiguration props; - props.setProperty("toForegroundColor", toForegroundColor); - props.setProperty("updateColor", updateColor); - props.setProperty("addPalette", addPalette); - props.setProperty("normaliseValues", normaliseValues); - props.setProperty("sampleMerged", sampleMerged); - props.setProperty("radius", radius); - props.setProperty("blend", blend); - - KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); - - config.writeEntry(getConfigKey(defaultActivation), props.toXML()); + // Color picker mixing. + if (!pure && mixColor && mix < 100) { + //Scale from 0..100% to 0..255 range for mixOp weights. + quint8 scaledMix = static_cast(mix * 2.55f); + + const quint8 *colors[2]; + colors[0] = mixColor->data(); + colors[1] = pickedColor.data(); + qint16 weights[2]; + weights[0] = 255 - scaledMix; + weights[1] = scaledMix; + + const KoMixColorsOp *mixOp = sourceDevice->colorSpace()->mixColorsOp(); + mixOp->mixColors(colors, weights, 2, pickedColor.data()); } - void ColorPickerConfig::load(bool defaultActivation) - { - KisPropertiesConfiguration props; - - KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); - props.fromXML(config.readEntry(getConfigKey(defaultActivation))); - - toForegroundColor = props.getBool("toForegroundColor", true); - updateColor = props.getBool("updateColor", true); - addPalette = props.getBool("addPalette", false); - normaliseValues = props.getBool("normaliseValues", false); - sampleMerged = props.getBool("sampleMerged", !defaultActivation ? false : true); - radius = props.getInt("radius", 1); - blend = props.getInt("blend", 100); + pickedColor.convertTo(sourceDevice->compositionSourceColorSpace()); + + bool validColorPicked = pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8; + + if (validColorPicked) { + out_color = pickedColor; + } + + return validColorPicked; +} + +void sampleRadius(KoColor &out_color, const KisPaintDeviceSP device, const QPoint position, const int radius) { + QVector pixels; + const int effectiveRadius = radius - 1; + + const QRect pickRect(position.x() - effectiveRadius, position.y() - effectiveRadius, + 2 * effectiveRadius + 1, 2 * effectiveRadius + 1); + + KisSequentialConstIterator it(device, pickRect); + + const int radiusSq = pow2(effectiveRadius); + + while (it.nextPixel()) { + const QPoint realPos(it.x(), it.y()); + const QPoint pt = realPos - position; + if (pow2(pt.x()) + pow2(pt.y()) < radiusSq) { + pixels << it.oldRawData(); + } + } + + const quint8 **cpixels = const_cast(pixels.constData()); + device->colorSpace()->mixColorsOp()->mixColors(cpixels, pixels.size(), out_color.data()); +} + +void submitColor(KoCanvasResourceProvider &resourceProvider, UpdateTarget target, KoColor &color) +{ + color.setOpacity(OPACITY_OPAQUE_U8); //Strip unwanted alpha component. + + switch (target) { + case FOREGROUND: + resourceProvider.setForegroundColor(color); + break; + case BACKGROUND: + resourceProvider.setBackgroundColor(color); + break; + case BOTH: + resourceProvider.setForegroundColor(color); + resourceProvider.setBackgroundColor(color); + break; + case SHIFT: + KoColor hold = resourceProvider.foregroundColor(); + resourceProvider.setForegroundColor(color); + resourceProvider.setBackgroundColor(hold); + break; } } + +const QString ColorPickerConfig::CONFIG_GROUP_NAME = "tool_color_picker"; + +ColorPickerConfig::ColorPickerConfig() + : source(MERGED_VISIBLE) + , resampleRateMS(200) + , update(true) + , radius(1) + , mix(100) + , normalizeColorData(false) +{ +} + +inline QString getConfigKey(bool defaultActivation) { + return defaultActivation ? + "ColorPickerDefaultActivation" : "ColorPickerTemporaryActivation"; +} + +void ColorPickerConfig::save(bool defaultActivation) const +{ + KisPropertiesConfiguration props; + props.setProperty("update", update); + props.setProperty("samplingSource", source); + props.setProperty("radius", radius); + props.setProperty("mix", mix); + props.setProperty("normaliseValues", normalizeColorData); + + KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); + + config.writeEntry(getConfigKey(defaultActivation), props.toXML()); +} + +void ColorPickerConfig::load(bool defaultActivation) +{ + KisPropertiesConfiguration props; + + KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); + props.fromXML(config.readEntry(getConfigKey(defaultActivation))); + + update = props.getBool("update"); + source = static_cast(props.getInt("samplingSource")); + radius = props.getInt("radius", 1); + mix = props.getInt("mix", 100); + normalizeColorData = props.getBool("normaliseValues"); +} +} diff --git a/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.h b/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.h --- a/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.h +++ b/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.h @@ -23,6 +23,7 @@ #include "kis_simple_stroke_strategy.h" #include "kis_lod_transform.h" #include "KoColor.h" +#include "kis_tool_utils.h" class KisColorPickerStrokeStrategy : public QObject, public KisSimpleStrokeStrategy { @@ -30,23 +31,33 @@ public: class Data : public KisStrokeJobData { public: - Data(KisPaintDeviceSP _dev, const QPoint _pt, KoColor _currentColor) - : dev(_dev), pt(_pt), currentColor(_currentColor) + Data(KisPaintDeviceSP _dev, KisCanvas2 *_canvas, const QPoint _pt, KoColor _currentColor, int _radius, int _mix, bool _pure) + : dev(_dev) + , canvas(_canvas) + , pt(_pt) + , currentColor(_currentColor) + , radius(_radius) + , mix(_mix) + , pure(_pure) {} KisStrokeJobData* createLodClone(int levelOfDetail) override { KisLodTransform t(levelOfDetail); const QPoint realPoint = t.map(pt); - return new Data(dev, realPoint, currentColor); + return new Data(dev, canvas, realPoint, currentColor, radius, mix, pure); } KisPaintDeviceSP dev; - QPoint pt; - KoColor currentColor; // Used for color picker blending. + KisCanvas2 *canvas; + QPoint pt; + KoColor currentColor; // Used for color picker mixing. + int radius; + int mix; + bool pure; // Used for temporary bypass of radius/mixing. }; public: - KisColorPickerStrokeStrategy(int lod = 0); + KisColorPickerStrokeStrategy(); ~KisColorPickerStrokeStrategy() override; void doStrokeCallback(KisStrokeJobData *data) override; diff --git a/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp b/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp --- a/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp +++ b/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp @@ -26,21 +26,13 @@ Private() : shouldSkipWork(false) {} bool shouldSkipWork; - int radius = 1; - int blend = 100; }; -KisColorPickerStrokeStrategy::KisColorPickerStrokeStrategy(int lod) +KisColorPickerStrokeStrategy::KisColorPickerStrokeStrategy() : m_d(new Private) { setSupportsWrapAroundMode(true); enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE); - - KisToolUtils::ColorPickerConfig config; - config.load(); - - m_d->radius = qMax(1, qRound(config.radius * KisLodTransform::lodToScale(lod))); - m_d->blend = config.blend; } KisColorPickerStrokeStrategy::~KisColorPickerStrokeStrategy() @@ -56,16 +48,18 @@ KoColor color; KoColor previous = d->currentColor; - if (KisToolUtils::pickColor(color, d->dev, d->pt, &previous, m_d->radius, m_d->blend) == true) { + if (KisPickerUtils::pickColor(color, d->dev, d->canvas, d->pt, &previous, d->radius, d->mix, d->pure) == true) { emit sigColorUpdated(color); } } KisStrokeStrategy* KisColorPickerStrokeStrategy::createLodClone(int levelOfDetail) { + Q_UNUSED(levelOfDetail); + m_d->shouldSkipWork = true; - KisColorPickerStrokeStrategy *lodStrategy = new KisColorPickerStrokeStrategy(levelOfDetail); + KisColorPickerStrokeStrategy *lodStrategy = new KisColorPickerStrokeStrategy(); connect(lodStrategy, &KisColorPickerStrokeStrategy::sigColorUpdated, this, &KisColorPickerStrokeStrategy::sigColorUpdated, Qt::DirectConnection); diff --git a/libs/ui/widgets/kis_scratch_pad.cpp b/libs/ui/widgets/kis_scratch_pad.cpp --- a/libs/ui/widgets/kis_scratch_pad.cpp +++ b/libs/ui/widgets/kis_scratch_pad.cpp @@ -257,7 +257,7 @@ void KisScratchPad::pick(KoPointerEvent *event) { KoColor color; - if (KisToolUtils::pickColor(color, m_paintLayer->projection(), event->point.toPoint())) { + if (KisPickerUtils::pickColor(color, m_paintLayer->projection(), nullptr, event->point.toPoint())) { emit colorSelected(color); } } diff --git a/plugins/tools/basictools/kis_tool_colorpicker.h b/plugins/tools/basictools/kis_tool_colorpicker.h --- a/plugins/tools/basictools/kis_tool_colorpicker.h +++ b/plugins/tools/basictools/kis_tool_colorpicker.h @@ -27,6 +27,7 @@ #include "ui_wdgcolorpicker.h" #include "kis_tool.h" #include +#include "kis_tool_utils.h" class KoResource; class KoColorSet; @@ -48,74 +49,47 @@ class KisToolColorPicker : public KisTool { Q_OBJECT - Q_PROPERTY(bool toForeground READ toForeground WRITE setToForeground NOTIFY toForegroundChanged) public: KisToolColorPicker(KoCanvasBase *canvas); ~KisToolColorPicker() override; -public: - struct Configuration { - Configuration(); - - bool toForegroundColor; - bool updateColor; - bool addPalette; - bool normaliseValues; - bool sampleMerged; - int radius; - int blend; - - void save(ToolActivation activation) const; - void load(ToolActivation activation); - }; - -public: QWidget* createOptionWidget() override; void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; - void paint(QPainter &gc, const KoViewConverter &converter) override; + void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override; - bool toForeground() const; + void paint(QPainter &gc, const KoViewConverter &converter) override; Q_SIGNALS: void toForegroundChanged(); +public Q_SLOTS: + void slotSetSamplingSource(int); + void slotSetUpdate(bool); + void slotNormalizeColorData(bool); + void slotChangeRadius(int); + void slotChangeMix(int); + protected: void activate(ToolActivation activation, const QSet &) override; void deactivate() override; -public Q_SLOTS: - void setToForeground(bool newValue); - void slotSetUpdateColor(bool); - void slotSetNormaliseValues(bool); - void slotSetAddPalette(bool); - void slotChangeRadius(int); - void slotChangeBlend(int); - void slotAddPalette(KoResource* resource); - void slotSetColorSource(int value); - private: void displayPickedColor(); - bool pickColor(const QPointF& pos); - void updateOptionWidget(); - - // Configuration - QScopedPointer m_config; + bool pickColor(const QPointF& pos, bool pure = false); + void updateCursor(KisPickerUtils::SamplingSource source); + void updateOptionsWidget(); + QScopedPointer m_config; ToolActivation m_toolActivationSource; bool m_isActivated; - KoColor m_pickedColor; - - // Used to skip some tablet events and update color less often - QTimer m_colorPickerDelayTimer; - + QTimer m_resampleDelayTimer; ColorPickerOptionsWidget *m_optionsWidget; - QList m_palettes; }; @@ -124,7 +98,7 @@ public: KisToolColorPickerFactory() : KoToolFactoryBase("KritaSelected/KisToolColorPicker") { - setToolTip(i18n("Color Selector Tool")); + setToolTip(i18n("Color Picker Tool")); setSection(TOOL_TYPE_FILL); setPriority(2); setIconName(koIconNameCStr("krita_tool_color_picker")); diff --git a/plugins/tools/basictools/kis_tool_colorpicker.cc b/plugins/tools/basictools/kis_tool_colorpicker.cc --- a/plugins/tools/basictools/kis_tool_colorpicker.cc +++ b/plugins/tools/basictools/kis_tool_colorpicker.cc @@ -34,20 +34,15 @@ #include "kis_wrapped_rect.h" #include "kis_tool_utils.h" -namespace -{ -// GUI ComboBox index constants -const int SAMPLE_MERGED = 0; -} - KisToolColorPicker::KisToolColorPicker(KoCanvasBase *canvas) : KisTool(canvas, KisCursor::pickerCursor()), - m_config(new KisToolUtils::ColorPickerConfig) + m_config(new KisPickerUtils::ColorPickerConfig) { setObjectName("tool_colorpicker"); m_isActivated = false; m_optionsWidget = 0; m_pickedColor = KoColor(); + m_resampleDelayTimer.setSingleShot(true); } KisToolColorPicker::~KisToolColorPicker() @@ -57,10 +52,73 @@ } } +QWidget* KisToolColorPicker::createOptionWidget() +{ + m_optionsWidget = new ColorPickerOptionsWidget(0); + m_optionsWidget->setObjectName(toolId() + " Options Widget"); + m_optionsWidget->tableColorData->setSortingEnabled(false); + + { // See https://bugs.kde.org/show_bug.cgi?id=316896 + QWidget *specialSpacer = new QWidget(m_optionsWidget); + specialSpacer->setObjectName("SpecialSpacer"); + specialSpacer->setFixedSize(0, 0); + m_optionsWidget->layout()->addWidget(specialSpacer); + } + + // Initialize mix KisSliderSpinBox. + m_optionsWidget->mix->setRange(1,100); + m_optionsWidget->mix->setSuffix("%"); + + { // Initialize palette selector. + KoResourceServer *srv = KoResourceServerProvider::instance()->paletteServer(); + + if (!srv) { + return m_optionsWidget; + } + + QList palettes = srv->resources(); + Q_FOREACH (KoColorSet *palette, palettes) { + if (palette) { + m_optionsWidget->cmbPalettes->addSqueezedItem(palette->name()); + m_palettes.append(palette); + } + } + } + + connect(m_optionsWidget->cbUpdate, SIGNAL(toggled(bool)), SLOT(slotSetUpdate(bool))); + connect(m_optionsWidget->cmbSamplingSources, SIGNAL(currentIndexChanged(int)), SLOT(slotSetSamplingSource(int))); + connect(m_optionsWidget->radius, SIGNAL(valueChanged(int)), SLOT(slotChangeRadius(int))); + connect(m_optionsWidget->mix, SIGNAL(valueChanged(int)), SLOT(slotChangeMix(int))); + connect(m_optionsWidget->cbNormaliseValues, SIGNAL(toggled(bool)), SLOT(slotNormalizeColorData(bool))); + + updateCursor(m_config->source); + updateOptionsWidget(); + return m_optionsWidget; +} + +void KisToolColorPicker::updateCursor(KisPickerUtils::SamplingSource source) +{ + if (source == KisPickerUtils::CURRENT_LAYER) { + useCursor(KisCursor::pickerLayerForegroundCursor()); + } else { + useCursor(KisCursor::pickerImageForegroundCursor()); + } +} + +void KisToolColorPicker::updateOptionsWidget() +{ + if (!m_optionsWidget) return; + + m_optionsWidget->cbUpdate->setChecked(m_config->update); + m_optionsWidget->cmbSamplingSources->setCurrentIndex(m_config->source); + m_optionsWidget->radius->setValue(m_config->radius); + m_optionsWidget->mix->setValue(m_config->mix); + m_optionsWidget->cbNormaliseValues->setChecked(m_config->normalizeColorData); +} + void KisToolColorPicker::paint(QPainter &gc, const KoViewConverter &converter) { - Q_UNUSED(gc); - Q_UNUSED(converter); + Q_UNUSED(gc); Q_UNUSED(converter); } void KisToolColorPicker::activate(ToolActivation activation, const QSet &shapes) @@ -68,9 +126,10 @@ m_isActivated = true; m_toolActivationSource = activation; m_config->load(m_toolActivationSource == KisTool::DefaultActivation); - updateOptionWidget(); - KisTool::activate(activation, shapes); + + updateCursor(m_config->source); + updateOptionsWidget(); } void KisToolColorPicker::deactivate() @@ -80,78 +139,75 @@ KisTool::deactivate(); } -bool KisToolColorPicker::pickColor(const QPointF &pos) +void KisToolColorPicker::beginPrimaryAction(KoPointerEvent *event) //Mouse down. { - // Timer check. - if (m_colorPickerDelayTimer.isActive()) { - return false; - } - else { - m_colorPickerDelayTimer.setSingleShot(true); - m_colorPickerDelayTimer.start(100); + bool sampleMerged = m_optionsWidget->cmbSamplingSources->currentIndex() == KisPickerUtils::MERGED_VISIBLE; + if (!sampleMerged) { + if (!currentNode()) { + QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Cannot pick a color as no layer is active.")); + event->ignore(); + return; + } + if (!currentNode()->visible()) { + QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Cannot pick a color as the active layer is not visible.")); + event->ignore(); + return; + } } - QScopedPointer> imageLocker; + setMode(KisTool::PAINT_MODE); - m_pickedColor.setOpacity(0.0); + QPoint pos = convertToImagePixelCoordFloored(event); - // Pick from reference images. - if (m_optionsWidget->cmbSources->currentIndex() == SAMPLE_MERGED) { - auto *kisCanvas = dynamic_cast(canvas()); - KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(kisCanvas, false); - KisSharedPtr referenceImageLayer = - kisCanvas->imageView()->document()->referenceImagesLayer(); - - if (referenceImageLayer && kisCanvas->referenceImagesDecoration()->visible()) { - QColor color = referenceImageLayer->getPixel(pos); - if (color.isValid()) { - m_pickedColor.fromQColor(color); - } - } + bool picked = pickColor(pos); + if (!picked) { // Color picking has to start in the visible part of the layer + event->ignore(); + return; } - if (m_pickedColor.opacityU8() == OPACITY_TRANSPARENT_U8) { - if (!currentImage()->bounds().contains(pos.toPoint()) && - !currentImage()->wrapAroundModePermitted()) { - return false; - } - - KisPaintDeviceSP dev; + displayPickedColor(); - if (m_optionsWidget->cmbSources->currentIndex() != SAMPLE_MERGED && - currentNode() && currentNode()->colorPickSourceDevice()) { - dev = currentNode()->colorPickSourceDevice(); - } - else { - imageLocker.reset(new boost::lock_guard(*currentImage())); - dev = currentImage()->projection(); - } + m_resampleDelayTimer.start(m_config->resampleRateMS); +} - KoColor previousColor = canvas()->resourceManager()->foregroundColor(); +void KisToolColorPicker::continuePrimaryAction(KoPointerEvent *event) //Mouse drag. +{ + CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); - KisToolUtils::pickColor(m_pickedColor, dev, pos.toPoint(), &previousColor, m_config->radius, m_config->blend); + if (m_resampleDelayTimer.isActive()) { + return; + } + else { + m_resampleDelayTimer.start(m_config->resampleRateMS); } - if (m_config->updateColor && - m_pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8) { + QPoint pos = convertToImagePixelCoordFloored(event); - KoColor publicColor = m_pickedColor; - publicColor.setOpacity(OPACITY_OPAQUE_U8); // Alpha is unwanted for FG and BG colors. + pickColor(pos); + displayPickedColor(); +} - if (m_config->toForegroundColor) { - canvas()->resourceManager()->setResource(KoCanvasResourceProvider::ForegroundColor, publicColor); - } - else { - canvas()->resourceManager()->setResource(KoCanvasResourceProvider::BackgroundColor, publicColor); - } - } +void KisToolColorPicker::endPrimaryAction(KoPointerEvent *event) //Mouse up. +{ + Q_UNUSED(event); + CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); - return true; + // Add picked colors to selected palette. + if (m_optionsWidget->cbAddToPalette->isChecked()) { + KisSwatch entry; + entry.setColor(m_pickedColor); + entry.setName("Picked"); // Placeholder - better than nothing! + + KoColorSet *palette = m_palettes.at(m_optionsWidget->cmbPalettes->currentIndex()); + palette->add(entry); + } } -void KisToolColorPicker::beginPrimaryAction(KoPointerEvent *event) -{ - bool sampleMerged = m_optionsWidget->cmbSources->currentIndex() == SAMPLE_MERGED; +void KisToolColorPicker::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { + Q_UNUSED(action); + + // ***WARNING: Modified copy-paste from 'beginPrimaryAction'; it'd be nice to unify these later.*** + bool sampleMerged = m_optionsWidget->cmbSamplingSources->currentIndex() == KisPickerUtils::MERGED_VISIBLE; if (!sampleMerged) { if (!currentNode()) { QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Cannot pick a color as no layer is active.")); @@ -165,59 +221,66 @@ } } - QPoint pos = convertToImagePixelCoordFloored(event); - setMode(KisTool::PAINT_MODE); - bool picked = pickColor(pos); - if (!picked) { - // Color picking has to start in the visible part of the layer + QPoint pos = convertToImagePixelCoordFloored(event); + + bool picked = pickColor(pos, false); + if (!picked) { // Color picking has to start in the visible part of the layer event->ignore(); return; } displayPickedColor(); -} -void KisToolColorPicker::continuePrimaryAction(KoPointerEvent *event) -{ - CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); - - QPoint pos = convertToImagePixelCoordFloored(event); - pickColor(pos); - displayPickedColor(); + m_resampleDelayTimer.start(m_config->resampleRateMS); } -void KisToolColorPicker::endPrimaryAction(KoPointerEvent *event) +bool KisToolColorPicker::pickColor(const QPointF &pos, bool pure) { - Q_UNUSED(event); - CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); + QScopedPointer> imageLocker; + + m_pickedColor.setOpacity(0.0); - if (m_config->addPalette) { - KisSwatch ent; - ent.setColor(m_pickedColor); - // We don't ask for a name, too intrusive here + auto *canv = dynamic_cast(canvas()); + KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(canv, false); - KoColorSet *palette = m_palettes.at(m_optionsWidget->cmbPalette->currentIndex()); - palette->add(ent); + if (m_pickedColor.opacityU8() == OPACITY_TRANSPARENT_U8) { + KisPaintDeviceSP activeDevice; - if (!palette->save()) { - QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Cannot write to palette file %1. Maybe it is read-only.", palette->filename())); + if (m_config->source != KisPickerUtils::MERGED_VISIBLE + && currentNode() && currentNode()->colorPickSourceDevice()) { + activeDevice = currentNode()->colorPickSourceDevice(); + } + else { + imageLocker.reset(new boost::lock_guard(*currentImage())); + activeDevice = currentImage()->projection(); } + + KoColor previousColor = canvas()->resourceManager()->foregroundColor(); + + KisPickerUtils::pickColor(m_pickedColor, activeDevice, canv, pos.toPoint(), &previousColor, m_config->radius, m_config->mix, pure); + } + + if (m_config->update && m_pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8) { + KisPickerUtils::UpdateTarget target = KisPickerUtils::FOREGROUND; + KisPickerUtils::submitColor(*canvas()->resourceManager(), target, m_pickedColor); } -} -struct PickedChannel { - QString name; - QString valueText; -}; + return true; +} void KisToolColorPicker::displayPickedColor() { + struct PickedChannel { + QString name; + QString valueText; + }; + if (m_pickedColor.data() && m_optionsWidget) { QList channels = m_pickedColor.colorSpace()->channels(); - m_optionsWidget->listViewChannels->clear(); + m_optionsWidget->tableColorData->clear(); QVector pickedChannels; for (int i = 0; i < channels.count(); ++i) { @@ -229,7 +292,7 @@ PickedChannel pc; pc.name = channels[i]->name(); - if (m_config->normaliseValues) { + if (m_config->normalizeColorData) { pc.valueText = m_pickedColor.colorSpace()->normalisedChannelValueText(m_pickedColor.data(), i); } else { pc.valueText = m_pickedColor.colorSpace()->channelValueText(m_pickedColor.data(), i); @@ -240,119 +303,37 @@ } Q_FOREACH (const PickedChannel &pc, pickedChannels) { - QTreeWidgetItem *item = new QTreeWidgetItem(m_optionsWidget->listViewChannels); + QTreeWidgetItem *item = new QTreeWidgetItem(m_optionsWidget->tableColorData); item->setText(0, pc.name); item->setText(1, pc.valueText); } } } -QWidget* KisToolColorPicker::createOptionWidget() -{ - m_optionsWidget = new ColorPickerOptionsWidget(0); - m_optionsWidget->setObjectName(toolId() + " option widget"); - m_optionsWidget->listViewChannels->setSortingEnabled(false); - - // See https://bugs.kde.org/show_bug.cgi?id=316896 - QWidget *specialSpacer = new QWidget(m_optionsWidget); - specialSpacer->setObjectName("SpecialSpacer"); - specialSpacer->setFixedSize(0, 0); - m_optionsWidget->layout()->addWidget(specialSpacer); - - // Initialize blend KisSliderSpinBox - m_optionsWidget->blend->setRange(0,100); - m_optionsWidget->blend->setSuffix("%"); - - updateOptionWidget(); - - connect(m_optionsWidget->cbUpdateCurrentColor, SIGNAL(toggled(bool)), SLOT(slotSetUpdateColor(bool))); - connect(m_optionsWidget->cbNormaliseValues, SIGNAL(toggled(bool)), SLOT(slotSetNormaliseValues(bool))); - connect(m_optionsWidget->cbPalette, SIGNAL(toggled(bool)), - SLOT(slotSetAddPalette(bool))); - connect(m_optionsWidget->radius, SIGNAL(valueChanged(int)), - SLOT(slotChangeRadius(int))); - connect(m_optionsWidget->blend, SIGNAL(valueChanged(int)), - SLOT(slotChangeBlend(int))); - connect(m_optionsWidget->cmbSources, SIGNAL(currentIndexChanged(int)), - SLOT(slotSetColorSource(int))); - - KoResourceServer *srv = KoResourceServerProvider::instance()->paletteServer(); - - if (!srv) { - return m_optionsWidget; - } - - QList palettes = srv->resources(); - - Q_FOREACH (KoColorSet *palette, palettes) { - if (palette) { - m_optionsWidget->cmbPalette->addSqueezedItem(palette->name()); - m_palettes.append(palette); - } - } - - return m_optionsWidget; -} - -void KisToolColorPicker::updateOptionWidget() -{ - if (!m_optionsWidget) return; - - m_optionsWidget->cbNormaliseValues->setChecked(m_config->normaliseValues); - m_optionsWidget->cbUpdateCurrentColor->setChecked(m_config->updateColor); - m_optionsWidget->cmbSources->setCurrentIndex(SAMPLE_MERGED + !m_config->sampleMerged); - m_optionsWidget->cbPalette->setChecked(m_config->addPalette); - m_optionsWidget->radius->setValue(m_config->radius); - m_optionsWidget->blend->setValue(m_config->blend); -} - -void KisToolColorPicker::setToForeground(bool newValue) -{ - m_config->toForegroundColor = newValue; - emit toForegroundChanged(); -} - -bool KisToolColorPicker::toForeground() const +void KisToolColorPicker::slotSetSamplingSource(int source) { - return m_config->toForegroundColor; + auto src = static_cast(source); + updateCursor(src); + m_config->source = src; } -void KisToolColorPicker::slotSetUpdateColor(bool state) +void KisToolColorPicker::slotSetUpdate(bool state) { - m_config->updateColor = state; + m_config->update = state; } -void KisToolColorPicker::slotSetNormaliseValues(bool state) +void KisToolColorPicker::slotNormalizeColorData(bool state) { - m_config->normaliseValues = state; + m_config->normalizeColorData = state; displayPickedColor(); } -void KisToolColorPicker::slotSetAddPalette(bool state) -{ - m_config->addPalette = state; -} - void KisToolColorPicker::slotChangeRadius(int value) { m_config->radius = value; } -void KisToolColorPicker::slotChangeBlend(int value) -{ - m_config->blend = value; -} - -void KisToolColorPicker::slotSetColorSource(int value) +void KisToolColorPicker::slotChangeMix(int value) { - m_config->sampleMerged = value == SAMPLE_MERGED; -} - -void KisToolColorPicker::slotAddPalette(KoResource *resource) -{ - KoColorSet *palette = dynamic_cast(resource); - if (palette) { - m_optionsWidget->cmbPalette->addSqueezedItem(palette->name()); - m_palettes.append(palette); - } + m_config->mix = value; } diff --git a/plugins/tools/basictools/wdgcolorpicker.ui b/plugins/tools/basictools/wdgcolorpicker.ui --- a/plugins/tools/basictools/wdgcolorpicker.ui +++ b/plugins/tools/basictools/wdgcolorpicker.ui @@ -6,101 +6,26 @@ 0 0 - 263 - 323 + 274 + 498 Color Picker - - - - 1 - - - - - Blend: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 1 - - - - - - - - 0 - 0 - - - - <nobr>Blending controls the percentage of color that is picked</nobr> and mixed with your current brush color. A full blending value picks colors completely, ignoring brush color. - - - - - - - + + - + 0 0 - - - 200 - 0 - - - - - 200 - 32767 - - - - <nobr>Determines whether the color picker will sample</nobr> colors from all visible layers or only the currently selected layer. - - - 0 - - - - Sample All Visible Layers - - - - - Sample Current Layer - - - - - - - - Displays per-channel color information below as percentages instead of bytes. - - - Show colors as percentages - - - - - 0 - 100 + 128 @@ -118,23 +43,10 @@ - - - - QFrame::Plain - - - 1 - - - Qt::Horizontal - - - - + - 1 + 8 0 @@ -150,11 +62,23 @@ + + + 0 + 0 + + + + + 64 + 0 + + - Radius: + Radius: - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 1 @@ -164,11 +88,17 @@ - + 0 0 + + + 0 + 28 + + <nobr>Radius controls the color picker's sampling area.</nobr> Pixel colors within this radius are mixed together. @@ -185,83 +115,195 @@ - - + + + + Qt::Horizontal + + + + + - 1 + 8 - - 0 + + + + + 0 + 0 + + + + + 64 + 0 + + + + Sample: + + + + + + + + 0 + 0 + + + + + 0 + 28 + + + + <nobr>Determines whether the color picker will sample</nobr> colors from all visible layers or only the currently selected layer. + + + 0 + + + + All Visible Layers + + + + + Current Layer + + + + + + + + + + Displays per-channel color information below as percentages instead of integers. - - 0 + + Show color data as percentages. - - 0 + + + + + + Qt::Horizontal - - 0 + + + + + + 8 - - - <nobr>Checking this box will add a new color swatch</nobr> to a palette each time you pick a color. + + + + 0 + 0 + + + + + 64 + 0 + - Add to palette: + Mix: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 1 - + + + + 0 + 0 + + + + + 0 + 28 + + + + <nobr>The mix value controls the percentage of color that is picked</nobr> and mixed with your current brush color. A full mix value picks colors completely, ignoring brush color. + + - + + + + <nobr>If checked, Left Click will update your active foreground color and</nobr> Right Click will update your active background color. [Checked by default.] + + + Update Active Colors + + + true + + + + - + - <nobr>Controls whether the color picker updates the</nobr> current foreground or not. + <nobr>Adds each picked color to an empty slot in the</nobr> currently selected palette. - Update color + Add to palette: + + + + + + + + 0 + 28 + - - - - QFrame::Plain - - - 1 - - - Qt::Horizontal - - - + + KisIntParseSpinBox + QSpinBox +
kis_int_parse_spin_box.h
+
KisSliderSpinBox - QWidget + QFrame
kis_slider_spin_box.h
+ 1
SqueezedComboBox QComboBox
squeezedcombobox.h
- - KisIntParseSpinBox - QSpinBox -
kis_int_parse_spin_box.h
-