diff --git a/libs/ui/tool/kis_tool_utils.cpp b/libs/ui/tool/kis_tool_utils.cpp index 7669791567..7ecc282eac 100644 --- a/libs/ui/tool/kis_tool_utils.cpp +++ b/libs/ui/tool/kis_tool_utils.cpp @@ -1,203 +1,202 @@ /* * Copyright (c) 2009 Boudewijn Rempt * Copyright (c) 2018 Emmet & Eoin O'Neill * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU 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 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); 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) { - pickedColor.setOpacity(OPACITY_OPAQUE_U8); out_color = pickedColor; } return validColorPicked; } KisNodeSP findNode(KisNodeSP node, const QPoint &point, bool wholeGroup, bool editableOnly) { KisNodeSP foundNode = 0; while (node) { KisLayerSP layer = qobject_cast(node.data()); if (!layer || !layer->isEditable()) { node = node->prevSibling(); continue; } KoColor color(layer->projection()->colorSpace()); layer->projection()->pixel(point.x(), point.y(), &color); KisGroupLayerSP group = dynamic_cast(layer.data()); if ((group && group->passThroughMode()) || color.opacityU8() != OPACITY_TRANSPARENT_U8) { if (layer->inherits("KisGroupLayer") && (!editableOnly || layer->isEditable())) { // if this is a group and the pixel is transparent, don't even enter it foundNode = findNode(node->lastChild(), point, wholeGroup, editableOnly); } else { foundNode = !wholeGroup ? node : node->parent(); } } if (foundNode) break; node = node->prevSibling(); } return foundNode; } bool clearImage(KisImageSP image, KisNodeSP node, KisSelectionSP selection) { if(node && node->hasEditablePaintDevice()) { KisPaintDeviceSP device = node->paintDevice(); image->barrierLock(); KisTransaction transaction(kundo2_i18n("Clear"), device); QRect dirtyRect; if (selection) { dirtyRect = selection->selectedRect(); device->clearSelection(selection); } else { dirtyRect = device->extent(); device->clear(); } transaction.commit(image->undoAdapter()); device->setDirty(dirtyRect); image->unlock(); return true; } 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) { } inline QString getConfigKey(bool defaultActivation) { return defaultActivation ? "ColorPickerDefaultActivation" : "ColorPickerTemporaryActivation"; } 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()); } 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); } } diff --git a/libs/ui/tool/kis_tool_utils.h b/libs/ui/tool/kis_tool_utils.h index c39dd180f6..e9b997dc32 100644 --- a/libs/ui/tool/kis_tool_utils.h +++ b/libs/ui/tool/kis_tool_utils.h @@ -1,77 +1,77 @@ /* * Copyright (c) 2009 Boudewijn Rempt * Copyright (c) 2018 Emmet & Eoin O'Neill * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU 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 KIS_TOOL_UTILS_H #define KIS_TOOL_UTILS_H #include #include class QPoint; class KoColor; namespace KisToolUtils { struct KRITAUI_EXPORT ColorPickerConfig { ColorPickerConfig(); bool toForegroundColor; bool updateColor; bool addPalette; bool normaliseValues; bool sampleMerged; int radius; int blend; void save(bool defaultActivation = true) const; void load(bool defaultActivation = true); private: static const QString CONFIG_GROUP_NAME; }; /** * Pick a color based on the given position on the given paint device. * * 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. * 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. * - * RETURN - Returns true if a valid color was picked. + * 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); /** * 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); } #endif // KIS_TOOL_UTILS_H diff --git a/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp b/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp index 816569093a..97252ff918 100644 --- a/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp +++ b/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp @@ -1,74 +1,73 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU 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 "kis_color_picker_stroke_strategy.h" #include "kis_tool_utils.h" #include "kis_paint_device.h" struct KisColorPickerStrokeStrategy::Private { Private() : shouldSkipWork(false) {} bool shouldSkipWork; int radius = 1; int blend = 100; }; KisColorPickerStrokeStrategy::KisColorPickerStrokeStrategy(int lod) : 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() { } void KisColorPickerStrokeStrategy::doStrokeCallback(KisStrokeJobData *data) { if (m_d->shouldSkipWork) return; Data *d = dynamic_cast(data); KIS_ASSERT_RECOVER_RETURN(d); KoColor color; KoColor previous = d->currentColor; - bool result = KisToolUtils::pickColor(color, d->dev, d->pt, &previous, m_d->radius, m_d->blend); - Q_UNUSED(result); - - emit sigColorUpdated(color); + if (KisToolUtils::pickColor(color, d->dev, d->pt, &previous, m_d->radius, m_d->blend) == true) { + emit sigColorUpdated(color); + } } KisStrokeStrategy* KisColorPickerStrokeStrategy::createLodClone(int levelOfDetail) { m_d->shouldSkipWork = true; KisColorPickerStrokeStrategy *lodStrategy = new KisColorPickerStrokeStrategy(levelOfDetail); connect(lodStrategy, &KisColorPickerStrokeStrategy::sigColorUpdated, this, &KisColorPickerStrokeStrategy::sigColorUpdated, Qt::DirectConnection); return lodStrategy; } diff --git a/plugins/tools/basictools/kis_tool_colorpicker.cc b/plugins/tools/basictools/kis_tool_colorpicker.cc index 20e1b80ca5..c40bb633d4 100644 --- a/plugins/tools/basictools/kis_tool_colorpicker.cc +++ b/plugins/tools/basictools/kis_tool_colorpicker.cc @@ -1,358 +1,358 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2018 Emmet & Eoin O'Neill * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU 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 "kis_tool_colorpicker.h" #include #include #include "kis_cursor.h" #include "KisDocument.h" #include "kis_canvas2.h" #include "KisReferenceImagesLayer.h" #include "KoCanvasBase.h" #include "kis_random_accessor_ng.h" #include "KoResourceServerProvider.h" #include #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) { setObjectName("tool_colorpicker"); m_isActivated = false; m_optionsWidget = 0; m_pickedColor = KoColor(); } KisToolColorPicker::~KisToolColorPicker() { if (m_isActivated) { m_config->save(m_toolActivationSource == KisTool::DefaultActivation); } } void KisToolColorPicker::paint(QPainter &gc, const KoViewConverter &converter) { Q_UNUSED(gc); Q_UNUSED(converter); } void KisToolColorPicker::activate(ToolActivation activation, const QSet &shapes) { m_isActivated = true; m_toolActivationSource = activation; m_config->load(m_toolActivationSource == KisTool::DefaultActivation); updateOptionWidget(); KisTool::activate(activation, shapes); } void KisToolColorPicker::deactivate() { m_config->save(m_toolActivationSource == KisTool::DefaultActivation); m_isActivated = false; KisTool::deactivate(); } bool KisToolColorPicker::pickColor(const QPointF &pos) { // Timer check. if (m_colorPickerDelayTimer.isActive()) { return false; } else { m_colorPickerDelayTimer.setSingleShot(true); m_colorPickerDelayTimer.start(100); } QScopedPointer> imageLocker; m_pickedColor.setOpacity(0.0); // 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); } } } if (m_pickedColor.opacityU8() == OPACITY_TRANSPARENT_U8) { if (!currentImage()->bounds().contains(pos.toPoint()) && !currentImage()->wrapAroundModePermitted()) { return false; } KisPaintDeviceSP dev; if (m_optionsWidget->cmbSources->currentIndex() != SAMPLE_MERGED && currentNode() && currentNode()->colorPickSourceDevice()) { dev = currentNode()->colorPickSourceDevice(); } else { imageLocker.reset(new boost::lock_guard(*currentImage())); dev = currentImage()->projection(); } KoColor previousColor = canvas()->resourceManager()->foregroundColor(); - KisToolUtils::pickColor(m_pickedColor, dev, pos.toPoint(), &previousColor, m_config->radius, m_config->blend); /*!*/ + KisToolUtils::pickColor(m_pickedColor, dev, pos.toPoint(), &previousColor, m_config->radius, m_config->blend); } if (m_config->updateColor && m_pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8) { KoColor publicColor = m_pickedColor; - publicColor.setOpacity(OPACITY_OPAQUE_U8); + publicColor.setOpacity(OPACITY_OPAQUE_U8); // Alpha is unwanted for FG and BG colors. if (m_config->toForegroundColor) { canvas()->resourceManager()->setResource(KoCanvasResourceProvider::ForegroundColor, publicColor); } else { canvas()->resourceManager()->setResource(KoCanvasResourceProvider::BackgroundColor, publicColor); } } return true; } void KisToolColorPicker::beginPrimaryAction(KoPointerEvent *event) { bool sampleMerged = m_optionsWidget->cmbSources->currentIndex() == SAMPLE_MERGED; 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; } } 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 event->ignore(); return; } displayPickedColor(); } void KisToolColorPicker::continuePrimaryAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); QPoint pos = convertToImagePixelCoordFloored(event); pickColor(pos); displayPickedColor(); } void KisToolColorPicker::endPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); if (m_config->addPalette) { KisSwatch ent; ent.setColor(m_pickedColor); // We don't ask for a name, too intrusive here KoColorSet *palette = m_palettes.at(m_optionsWidget->cmbPalette->currentIndex()); palette->add(ent); if (!palette->save()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Cannot write to palette file %1. Maybe it is read-only.", palette->filename())); } } } struct PickedChannel { QString name; QString valueText; }; void KisToolColorPicker::displayPickedColor() { if (m_pickedColor.data() && m_optionsWidget) { QList channels = m_pickedColor.colorSpace()->channels(); m_optionsWidget->listViewChannels->clear(); QVector pickedChannels; for (int i = 0; i < channels.count(); ++i) { pickedChannels.append(PickedChannel()); } for (int i = 0; i < channels.count(); ++i) { PickedChannel pc; pc.name = channels[i]->name(); if (m_config->normaliseValues) { pc.valueText = m_pickedColor.colorSpace()->normalisedChannelValueText(m_pickedColor.data(), i); } else { pc.valueText = m_pickedColor.colorSpace()->channelValueText(m_pickedColor.data(), i); } pickedChannels[channels[i]->displayPosition()] = pc; } Q_FOREACH (const PickedChannel &pc, pickedChannels) { QTreeWidgetItem *item = new QTreeWidgetItem(m_optionsWidget->listViewChannels); 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 { return m_config->toForegroundColor; } void KisToolColorPicker::slotSetUpdateColor(bool state) { m_config->updateColor = state; } void KisToolColorPicker::slotSetNormaliseValues(bool state) { m_config->normaliseValues = 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) { 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); } }