File indexing completed on 2025-02-09 05:13:13
0001 /* 0002 * SPDX-FileCopyrightText: 1999 Matthias Elter <me@kde.org> 0003 * SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org> 0004 * SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com> 0005 * SPDX-FileCopyrightText: 2018 Emmet & Eoin O'Neill <emmetoneill.pdx@gmail.com> 0006 * 0007 * SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "kis_tool_colorsampler.h" 0011 0012 #include <kis_cursor.h> 0013 #include <kis_canvas2.h> 0014 #include <KoCanvasBase.h> 0015 #include <KoResourceServerProvider.h> 0016 #include <kis_canvas_resource_provider.h> 0017 #include <KisSpinBoxI18nHelper.h> 0018 #include <KisTagFilterResourceProxyModel.h> 0019 #include <KisResourceTypes.h> 0020 #include <KisViewManager.h> 0021 #include "kis_display_color_converter.h" 0022 #include "kis_tool_utils.h" 0023 0024 0025 namespace 0026 { 0027 // GUI ComboBox index constants 0028 const int SAMPLE_MERGED = 0; 0029 } 0030 0031 KisToolColorSampler::KisToolColorSampler(KoCanvasBase *canvas) 0032 : KisTool(canvas, KisCursor::samplerCursor()), 0033 m_config(new KisToolUtils::ColorSamplerConfig), 0034 m_helper(dynamic_cast<KisCanvas2*>(canvas)) 0035 { 0036 setObjectName("tool_colorsampler"); 0037 connect(&m_helper, SIGNAL(sigRequestCursor(QCursor)), this, SLOT(slotColorPickerRequestedCursor(QCursor))); 0038 connect(&m_helper, SIGNAL(sigRequestCursorReset()), this, SLOT(slotColorPickerRequestedCursorReset())); 0039 connect(&m_helper, SIGNAL(sigRequestUpdateOutline()), this, SLOT(slotColorPickerRequestedOutlineUpdate())); 0040 connect(&m_helper, SIGNAL(sigRawColorSelected(KoColor)), this, SLOT(slotColorPickerSelectedColor(KoColor))); 0041 connect(&m_helper, SIGNAL(sigFinalColorSelected(KoColor)), this, SLOT(slotColorPickerSelectionFinished(KoColor))); 0042 } 0043 0044 KisToolColorSampler::~KisToolColorSampler() 0045 { 0046 if (m_isActivated) { 0047 m_config->save(); 0048 } 0049 } 0050 0051 void KisToolColorSampler::slotColorPickerRequestedCursor(const QCursor &cursor) 0052 { 0053 useCursor(cursor); 0054 } 0055 0056 void KisToolColorSampler::slotColorPickerRequestedCursorReset() 0057 { 0058 /// we explicitly avoid resetting the cursor style 0059 /// to avoid blinking of the cursor 0060 } 0061 0062 void KisToolColorSampler::slotColorPickerRequestedOutlineUpdate() 0063 { 0064 requestUpdateOutline(m_outlineDocPoint, 0); 0065 } 0066 0067 void KisToolColorSampler::slotColorPickerSelectedColor(const KoColor &color) 0068 { 0069 /** 0070 * Please remember that m_sampledColor also have the alpha 0071 * of the picked color! 0072 */ 0073 m_sampledColor = color; 0074 displaySampledColor(m_sampledColor); 0075 } 0076 0077 void KisToolColorSampler::slotColorPickerSelectionFinished(const KoColor &color) 0078 { 0079 Q_UNUSED(color); 0080 0081 if (m_config->addColorToCurrentPalette) { 0082 KisSwatch swatch; 0083 swatch.setColor(color); 0084 // We don't ask for a name, too intrusive here 0085 0086 QModelIndex idx = m_tagFilterProxyModel->index(m_optionsWidget->cmbPalette->currentIndex(), 0); 0087 KoColorSetSP palette = qSharedPointerDynamicCast<KoColorSet>(m_tagFilterProxyModel->resourceForIndex(idx)); 0088 0089 if (palette) { 0090 KisSwatchGroup::SwatchInfo info = 0091 palette->getClosestSwatchInfo(color); 0092 0093 if (info.swatch.color() != color) { 0094 palette->addSwatch(swatch); 0095 if (!KoResourceServerProvider::instance()->paletteServer()->updateResource(palette)) { 0096 KisCanvas2 *canvas = dynamic_cast<KisCanvas2*>(this->canvas()); 0097 KIS_ASSERT(canvas); 0098 canvas->viewManager()->showFloatingMessage(i18n("Cannot write to palette file %1. Maybe it is read-only.", palette->filename()), koIcon("object-locked")); 0099 } 0100 } 0101 } 0102 } 0103 } 0104 0105 void KisToolColorSampler::paint(QPainter &gc, const KoViewConverter &converter) 0106 { 0107 m_helper.paint(gc, converter); 0108 } 0109 0110 void KisToolColorSampler::activate(const QSet<KoShape*> &shapes) 0111 { 0112 0113 m_isActivated = true; 0114 m_config->load(); 0115 0116 updateOptionWidget(); 0117 0118 KisTool::activate(shapes); 0119 } 0120 0121 void KisToolColorSampler::deactivate() 0122 { 0123 m_config->save(); 0124 0125 m_isActivated = false; 0126 KisTool::deactivate(); 0127 } 0128 0129 void KisToolColorSampler::beginPrimaryAction(KoPointerEvent *event) 0130 { 0131 m_helper.setUpdateGlobalColor(m_config->updateColor); 0132 m_helper.activate(!m_config->sampleMerged, m_config->toForegroundColor); 0133 m_helper.startAction(event->point, m_config->radius, m_config->blend); 0134 requestUpdateOutline(event->point, event); 0135 0136 setMode(KisTool::PAINT_MODE); 0137 } 0138 0139 void KisToolColorSampler::mouseMoveEvent(KoPointerEvent *event){ 0140 KisTool::mouseMoveEvent(event); 0141 } 0142 0143 void KisToolColorSampler::continuePrimaryAction(KoPointerEvent *event) 0144 { 0145 CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); 0146 0147 m_helper.continueAction(event->point); 0148 requestUpdateOutline(event->point, event); 0149 } 0150 0151 void KisToolColorSampler::endPrimaryAction(KoPointerEvent *event) 0152 { 0153 CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); 0154 0155 m_helper.endAction(); 0156 m_helper.deactivate(); 0157 requestUpdateOutline(event->point, event); 0158 0159 } 0160 void KisToolColorSampler::activatePrimaryAction() 0161 { 0162 /** 0163 * We explicitly avoid calling KisTool::activatePrimaryAction() 0164 * here, because it resets the cursor, causing cursor blinking 0165 */ 0166 m_helper.updateCursor(!m_config->sampleMerged, m_config->toForegroundColor); 0167 } 0168 0169 void KisToolColorSampler::deactivatePrimaryAction() 0170 { 0171 /** 0172 * We explicitly avoid calling KisTool::endPrimaryAction() 0173 * here, because it resets the cursor, causing cursor blinking 0174 */ 0175 } 0176 0177 void KisToolColorSampler::requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event) 0178 { 0179 Q_UNUSED(event); 0180 0181 KisConfig cfg(true); 0182 0183 QRectF colorPreviewDocUpdateRect; 0184 0185 qreal zoomX; 0186 qreal zoomY; 0187 canvas()->viewConverter()->zoom(&zoomX, &zoomY); 0188 qreal xoffset = 2.0/zoomX; 0189 qreal yoffset = 2.0/zoomY; 0190 0191 m_outlineDocPoint = outlineDocPoint; 0192 0193 colorPreviewDocUpdateRect = m_helper.colorPreviewDocRect(m_outlineDocPoint); 0194 0195 if (!colorPreviewDocUpdateRect.isEmpty()) { 0196 colorPreviewDocUpdateRect = colorPreviewDocUpdateRect.adjusted(-xoffset,-yoffset,xoffset,yoffset); 0197 } 0198 0199 if (!m_oldColorPreviewUpdateRect.isEmpty()){ 0200 canvas()->updateCanvas(m_oldColorPreviewUpdateRect); 0201 } 0202 0203 if (!colorPreviewDocUpdateRect.isEmpty()){ 0204 canvas()->updateCanvas(colorPreviewDocUpdateRect); 0205 } 0206 0207 m_oldColorPreviewUpdateRect = colorPreviewDocUpdateRect; 0208 } 0209 0210 0211 struct SampledChannel { 0212 QString name; 0213 QString valueText; 0214 }; 0215 0216 void KisToolColorSampler::displaySampledColor(const KoColor &color) 0217 { 0218 if (color.data() && m_optionsWidget) { 0219 0220 const QList<KoChannelInfo *> channels = color.colorSpace()->channels(); 0221 m_optionsWidget->listViewChannels->clear(); 0222 0223 QVector<SampledChannel> sampledChannels; 0224 for (int i = 0; i < channels.count(); ++i) { 0225 sampledChannels.append(SampledChannel()); 0226 } 0227 0228 for (int i = 0; i < channels.count(); ++i) { 0229 0230 SampledChannel pc; 0231 pc.name = channels[i]->name(); 0232 0233 if (m_config->normaliseValues) { 0234 pc.valueText = color.colorSpace()->normalisedChannelValueText(color.data(), i); 0235 } else { 0236 pc.valueText = color.colorSpace()->channelValueText(color.data(), i); 0237 } 0238 0239 sampledChannels[channels[i]->displayPosition()] = pc; 0240 0241 } 0242 0243 Q_FOREACH (const SampledChannel &pc, sampledChannels) { 0244 QTreeWidgetItem *item = new QTreeWidgetItem(m_optionsWidget->listViewChannels); 0245 item->setText(0, pc.name); 0246 item->setText(1, pc.valueText); 0247 } 0248 0249 0250 if (qEnvironmentVariableIsSet("KRITA_DEBUG_DISPLAY_COLOR")) { 0251 KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas()); 0252 KIS_ASSERT(kritaCanvas); 0253 KoColor newColor = kritaCanvas->displayColorConverter()->applyDisplayFiltering(color, Float32BitsColorDepthID); 0254 KIS_SAFE_ASSERT_RECOVER_RETURN(newColor.colorSpace()->colorModelId() == RGBAColorModelID); 0255 0256 QVector<float> values(4); 0257 newColor.colorSpace()->normalisedChannelsValue(newColor.data(), values); 0258 0259 for (int i = 0; i < values.size(); i++) { 0260 QTreeWidgetItem *item = new QTreeWidgetItem(m_optionsWidget->listViewChannels); 0261 item->setText(0, QString("DisplayCh%1").arg(i)); 0262 item->setText(1, QString::number(values[i])); 0263 } 0264 } 0265 } 0266 } 0267 0268 QWidget* KisToolColorSampler::createOptionWidget() 0269 { 0270 m_optionsWidget = new ColorSamplerOptionsWidget(0); 0271 m_optionsWidget->setObjectName(toolId() + " option widget"); 0272 m_optionsWidget->listViewChannels->setSortingEnabled(false); 0273 0274 // See https://bugs.kde.org/show_bug.cgi?id=316896 0275 QWidget *specialSpacer = new QWidget(m_optionsWidget); 0276 specialSpacer->setObjectName("SpecialSpacer"); 0277 specialSpacer->setFixedSize(0, 0); 0278 m_optionsWidget->layout()->addWidget(specialSpacer); 0279 0280 // Initialize blend KisSliderSpinBox 0281 m_optionsWidget->blend->setRange(0,100); 0282 KisSpinBoxI18nHelper::setText(m_optionsWidget->blend, 0283 i18nc("{n} is the number value, % is the percent sign", "{n}%")); 0284 0285 updateOptionWidget(); 0286 0287 connect(m_optionsWidget->cbUpdateCurrentColor, SIGNAL(toggled(bool)), SLOT(slotSetUpdateColor(bool))); 0288 connect(m_optionsWidget->cbNormaliseValues, SIGNAL(toggled(bool)), SLOT(slotSetNormaliseValues(bool))); 0289 connect(m_optionsWidget->cbPalette, SIGNAL(toggled(bool)), 0290 SLOT(slotSetAddPalette(bool))); 0291 connect(m_optionsWidget->radius, SIGNAL(valueChanged(int)), 0292 SLOT(slotChangeRadius(int))); 0293 connect(m_optionsWidget->blend, SIGNAL(valueChanged(int)), 0294 SLOT(slotChangeBlend(int))); 0295 connect(m_optionsWidget->cmbSources, SIGNAL(currentIndexChanged(int)), 0296 SLOT(slotSetColorSource(int))); 0297 0298 m_tagFilterProxyModel = new KisTagFilterResourceProxyModel(ResourceType::Palettes, this); 0299 m_optionsWidget->cmbPalette->setModel(m_tagFilterProxyModel); 0300 m_optionsWidget->cmbPalette->setModelColumn(KisAbstractResourceModel::Name); 0301 m_tagFilterProxyModel->sort(Qt::DisplayRole); 0302 0303 0304 KConfigGroup config = KSharedConfig::openConfig()->group(toolId()); 0305 QString paletteName = config.readEntry("ColorSamplerPalette", ""); 0306 if (!paletteName.isEmpty()) { 0307 for (int i = 0; i < m_tagFilterProxyModel->rowCount(); i++) { 0308 QModelIndex idx = m_tagFilterProxyModel->index(i, 0); 0309 QString name = m_tagFilterProxyModel->data(idx, Qt::UserRole + KisAbstractResourceModel::Name).toString(); 0310 if (name == paletteName) { 0311 m_optionsWidget->cmbPalette->setCurrentIndex(i); 0312 break; 0313 } 0314 } 0315 } 0316 0317 connect(m_optionsWidget->cmbPalette, SIGNAL(currentIndexChanged(int)), SLOT(slotChangePalette(int))); 0318 0319 return m_optionsWidget; 0320 } 0321 0322 void KisToolColorSampler::updateOptionWidget() 0323 { 0324 if (!m_optionsWidget) return; 0325 0326 m_optionsWidget->cbNormaliseValues->setChecked(m_config->normaliseValues); 0327 m_optionsWidget->cbUpdateCurrentColor->setChecked(m_config->updateColor); 0328 m_optionsWidget->cmbSources->setCurrentIndex(SAMPLE_MERGED + !m_config->sampleMerged); 0329 m_optionsWidget->cbPalette->setChecked(m_config->addColorToCurrentPalette); 0330 m_optionsWidget->radius->setValue(m_config->radius); 0331 m_optionsWidget->blend->setValue(m_config->blend); 0332 } 0333 0334 void KisToolColorSampler::slotSetUpdateColor(bool state) 0335 { 0336 m_config->updateColor = state; 0337 } 0338 0339 void KisToolColorSampler::slotSetNormaliseValues(bool state) 0340 { 0341 m_config->normaliseValues = state; 0342 displaySampledColor(m_sampledColor); 0343 } 0344 0345 void KisToolColorSampler::slotSetAddPalette(bool state) 0346 { 0347 m_config->addColorToCurrentPalette = state; 0348 } 0349 0350 void KisToolColorSampler::slotChangeRadius(int value) 0351 { 0352 m_config->radius = value; 0353 } 0354 0355 void KisToolColorSampler::slotChangeBlend(int value) 0356 { 0357 m_config->blend = value; 0358 } 0359 0360 void KisToolColorSampler::slotSetColorSource(int value) 0361 { 0362 m_config->sampleMerged = value == SAMPLE_MERGED; 0363 } 0364 0365 void KisToolColorSampler::slotChangePalette(int) 0366 { 0367 QString paletteName = m_optionsWidget->cmbPalette->currentData(Qt::UserRole + KisAbstractResourceModel::Name).toString(); 0368 KConfigGroup config = KSharedConfig::openConfig()->group(toolId()); 0369 config.writeEntry("ColorSamplerPalette", paletteName); 0370 }