File indexing completed on 2024-12-22 04:13:02
0001 /* 0002 * SPDX-FileCopyrightText: 2022 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "KisAsyncColorSamplerHelper.h" 0008 0009 #include <QPainter> 0010 0011 #include "KoCanvasResourcesIds.h" 0012 #include "KoCanvasResourceProvider.h" 0013 #include "KoViewConverter.h" 0014 #include "KoIcon.h" 0015 #include "kis_cursor.h" 0016 #include "kis_signal_compressor_with_param.h" 0017 #include "kis_image_interfaces.h" 0018 #include "kis_config.h" 0019 #include "kis_canvas2.h" 0020 #include "KisViewManager.h" 0021 #include "KisDocument.h" 0022 #include "KisReferenceImagesLayer.h" 0023 #include "KisReferenceImagesDecoration.h" 0024 #include "kis_display_color_converter.h" 0025 #include "strokes/kis_color_sampler_stroke_strategy.h" 0026 0027 0028 namespace { 0029 std::pair<QRectF,QRectF> colorPreviewDocRectImpl(const QPointF &outlineDocPoint, bool colorPreviewShowComparePlate, const KoViewConverter *converter) 0030 { 0031 KisConfig cfg(true); 0032 const QRectF colorPreviewViewRect = cfg.colorPreviewRect(); 0033 0034 const QRectF colorPreviewBaseColorViewRect = 0035 colorPreviewShowComparePlate ? 0036 colorPreviewViewRect.translated(colorPreviewViewRect.width(), 0) : 0037 QRectF(); 0038 0039 const QRectF colorPreviewDocumentRect = converter->viewToDocument(colorPreviewViewRect); 0040 const QRectF colorPreviewBaseColorDocumentRect = 0041 converter->viewToDocument(colorPreviewBaseColorViewRect); 0042 0043 return std::make_pair(colorPreviewDocumentRect.translated(outlineDocPoint), 0044 colorPreviewBaseColorDocumentRect.translated(outlineDocPoint)); 0045 } 0046 } 0047 0048 struct KisAsyncColorSamplerHelper::Private 0049 { 0050 Private(KisCanvas2 *_canvas) 0051 : canvas(_canvas) 0052 {} 0053 0054 KisCanvas2 *canvas; 0055 0056 int sampleResourceId {0}; 0057 bool sampleCurrentLayer {true}; 0058 bool updateGlobalColor {true}; 0059 0060 bool isActive {false}; 0061 bool showPreview {false}; 0062 bool showComparePlate {false}; 0063 0064 KisStrokeId strokeId; 0065 typedef KisSignalCompressorWithParam<QPointF> SamplingCompressor; 0066 QScopedPointer<SamplingCompressor> samplingCompressor; 0067 0068 QTimer activationDelayTimer; 0069 0070 QRectF currentColorDocRect; 0071 QRectF baseColorDocRect; 0072 0073 QColor currentColor; 0074 QColor baseColor; 0075 0076 KisStrokesFacade *strokesFacade() const { 0077 return canvas->image().data(); 0078 } 0079 0080 const KoViewConverter &converter() const { 0081 return *canvas->imageView()->viewConverter(); 0082 } 0083 0084 }; 0085 0086 KisAsyncColorSamplerHelper::KisAsyncColorSamplerHelper(KisCanvas2 *canvas) 0087 : m_d(new Private(canvas)) 0088 { 0089 using namespace std::placeholders; // For _1 placeholder 0090 std::function<void(QPointF)> callback = 0091 std::bind(&KisAsyncColorSamplerHelper::slotAddSamplingJob, this, _1); 0092 m_d->samplingCompressor.reset( 0093 new Private::SamplingCompressor(100, callback, KisSignalCompressor::FIRST_ACTIVE)); 0094 0095 m_d->activationDelayTimer.setInterval(100); 0096 m_d->activationDelayTimer.setSingleShot(true); 0097 connect(&m_d->activationDelayTimer, SIGNAL(timeout()), this, SLOT(activateDelayedPreview())); 0098 } 0099 0100 KisAsyncColorSamplerHelper::~KisAsyncColorSamplerHelper() 0101 { 0102 KIS_SAFE_ASSERT_RECOVER_NOOP(!m_d->strokeId); 0103 } 0104 0105 bool KisAsyncColorSamplerHelper::isActive() const 0106 { 0107 return m_d->isActive; 0108 } 0109 0110 void KisAsyncColorSamplerHelper::activate(bool sampleCurrentLayer, bool pickFgColor) 0111 { 0112 KIS_SAFE_ASSERT_RECOVER_RETURN(!m_d->isActive); 0113 m_d->isActive = true; 0114 0115 m_d->sampleResourceId = 0116 pickFgColor ? 0117 KoCanvasResource::ForegroundColor : 0118 KoCanvasResource::BackgroundColor; 0119 0120 m_d->sampleCurrentLayer = sampleCurrentLayer; 0121 m_d->showComparePlate = false; 0122 0123 m_d->activationDelayTimer.start(); 0124 } 0125 0126 void KisAsyncColorSamplerHelper::activateDelayedPreview() 0127 { 0128 // the event may come after we have finished color 0129 // picking if the user is quick 0130 if (!m_d->isActive) return; 0131 0132 m_d->showPreview = true; 0133 0134 const KoColor currentColor = 0135 m_d->canvas->resourceManager()->koColorResource(m_d->sampleResourceId); 0136 const QColor previewColor = m_d->canvas->displayColorConverter()->toQColor(currentColor); 0137 0138 m_d->currentColor = previewColor; 0139 m_d->baseColor = previewColor; 0140 0141 updateCursor(m_d->sampleCurrentLayer, m_d->sampleResourceId == KoCanvasResource::ForegroundColor); 0142 0143 Q_EMIT sigRequestUpdateOutline(); 0144 } 0145 0146 void KisAsyncColorSamplerHelper::updateCursor(bool sampleCurrentLayer, bool pickFgColor) 0147 { 0148 const int sampleResourceId = 0149 pickFgColor ? 0150 KoCanvasResource::ForegroundColor : 0151 KoCanvasResource::BackgroundColor; 0152 0153 QCursor cursor; 0154 0155 if (sampleCurrentLayer) { 0156 if (sampleResourceId == KoCanvasResource::ForegroundColor) { 0157 cursor = KisCursor::samplerLayerForegroundCursor(); 0158 } else { 0159 cursor = KisCursor::samplerLayerBackgroundCursor(); 0160 } 0161 } else { 0162 if (sampleResourceId == KoCanvasResource::ForegroundColor) { 0163 cursor = KisCursor::samplerImageForegroundCursor(); 0164 } else { 0165 cursor = KisCursor::samplerImageBackgroundCursor(); 0166 } 0167 } 0168 0169 Q_EMIT sigRequestCursor(cursor); 0170 } 0171 0172 void KisAsyncColorSamplerHelper::setUpdateGlobalColor(bool value) 0173 { 0174 m_d->updateGlobalColor = value; 0175 } 0176 0177 bool KisAsyncColorSamplerHelper::updateGlobalColor() const 0178 { 0179 return m_d->updateGlobalColor; 0180 } 0181 0182 void KisAsyncColorSamplerHelper::deactivate() 0183 { 0184 KIS_SAFE_ASSERT_RECOVER(!m_d->strokeId) { 0185 endAction(); 0186 } 0187 0188 m_d->activationDelayTimer.stop(); 0189 0190 m_d->showPreview = false; 0191 m_d->showComparePlate = false; 0192 0193 m_d->currentColorDocRect = QRectF(); 0194 m_d->currentColor = QColor(); 0195 m_d->baseColor = QColor(); 0196 m_d->baseColorDocRect = QRectF(); 0197 0198 m_d->isActive = false; 0199 0200 Q_EMIT sigRequestCursorReset(); 0201 Q_EMIT sigRequestUpdateOutline(); 0202 } 0203 0204 void KisAsyncColorSamplerHelper::startAction(const QPointF &docPoint, int radius, int blend) 0205 { 0206 KisColorSamplerStrokeStrategy *strategy = new KisColorSamplerStrokeStrategy(radius, blend); 0207 connect(strategy, &KisColorSamplerStrokeStrategy::sigColorUpdated, 0208 this, &KisAsyncColorSamplerHelper::slotColorSamplingFinished); 0209 connect(strategy, &KisColorSamplerStrokeStrategy::sigFinalColorSelected, 0210 this, &KisAsyncColorSamplerHelper::sigFinalColorSelected); 0211 0212 m_d->strokeId = m_d->strokesFacade()->startStroke(strategy); 0213 m_d->samplingCompressor->start(docPoint); 0214 } 0215 0216 void KisAsyncColorSamplerHelper::continueAction(const QPointF &docPoint) 0217 { 0218 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->strokeId); 0219 m_d->samplingCompressor->start(docPoint); 0220 } 0221 0222 void KisAsyncColorSamplerHelper::endAction() 0223 { 0224 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->strokeId); 0225 0226 m_d->strokesFacade()->addJob(m_d->strokeId, 0227 new KisColorSamplerStrokeStrategy::FinalizeData()); 0228 0229 m_d->strokesFacade()->endStroke(m_d->strokeId); 0230 m_d->strokeId.clear(); 0231 } 0232 0233 QRectF KisAsyncColorSamplerHelper::colorPreviewDocRect(const QPointF &docPoint) 0234 { 0235 if (!m_d->showPreview) return QRectF(); 0236 0237 std::tie(m_d->currentColorDocRect, m_d->baseColorDocRect) = 0238 colorPreviewDocRectImpl(docPoint, m_d->showComparePlate, &m_d->converter()); 0239 0240 return m_d->currentColorDocRect | m_d->baseColorDocRect; 0241 } 0242 0243 void KisAsyncColorSamplerHelper::paint(QPainter &gc, const KoViewConverter &converter) 0244 { 0245 if (!m_d->showPreview) return; 0246 0247 const QRectF viewRect = converter.documentToView(m_d->currentColorDocRect); 0248 gc.fillRect(viewRect, m_d->currentColor); 0249 0250 if (m_d->showComparePlate) { 0251 const QRectF baseColorRect = converter.documentToView(m_d->baseColorDocRect); 0252 gc.fillRect(baseColorRect, m_d->baseColor); 0253 } 0254 } 0255 0256 void KisAsyncColorSamplerHelper::slotAddSamplingJob(const QPointF &docPoint) 0257 { 0258 /** 0259 * The actual sampling is delayed by a compressor, so we can get this 0260 * event when the stroke is already closed 0261 */ 0262 if (!m_d->strokeId) return; 0263 0264 KisImageSP image = m_d->canvas->image(); 0265 0266 const QPoint imagePoint = image->documentToImagePixelFloored(docPoint); 0267 0268 if (!m_d->sampleCurrentLayer) { 0269 KisSharedPtr<KisReferenceImagesLayer> referencesLayer = m_d->canvas->imageView()->document()->referenceImagesLayer(); 0270 if (referencesLayer && m_d->canvas->referenceImagesDecoration()->visible()) { 0271 QColor color = referencesLayer->getPixel(imagePoint); 0272 if (color.isValid() && color.alpha() != 0) { 0273 slotColorSamplingFinished(KoColor(color, image->colorSpace())); 0274 return; 0275 } 0276 } 0277 } 0278 0279 KisPaintDeviceSP device = m_d->sampleCurrentLayer ? 0280 m_d->canvas->imageView()->currentNode()->colorSampleSourceDevice() : 0281 image->projection(); 0282 0283 if (device) { 0284 // Used for color sampler blending. 0285 const KoColor currentColor = 0286 m_d->canvas->resourceManager()->koColorResource(m_d->sampleResourceId); 0287 0288 m_d->strokesFacade()->addJob(m_d->strokeId, 0289 new KisColorSamplerStrokeStrategy::Data(device, imagePoint, currentColor)); 0290 } else { 0291 QString message = i18n("Color sampler does not work on this layer."); 0292 m_d->canvas->viewManager()->showFloatingMessage(message, koIcon("object-locked")); 0293 } 0294 } 0295 0296 void KisAsyncColorSamplerHelper::slotColorSamplingFinished(const KoColor &rawColor) 0297 { 0298 KoColor color(rawColor); 0299 0300 color.setOpacity(OPACITY_OPAQUE_U8); 0301 0302 if (m_d->updateGlobalColor) { 0303 m_d->canvas->resourceManager()->setResource(m_d->sampleResourceId, color); 0304 } 0305 0306 Q_EMIT sigRawColorSelected(rawColor); 0307 Q_EMIT sigColorSelected(color); 0308 0309 if (!m_d->showPreview) return; 0310 0311 const QColor previewColor = m_d->canvas->displayColorConverter()->toQColor(color); 0312 0313 m_d->showComparePlate = true; 0314 m_d->currentColor = previewColor; 0315 0316 Q_EMIT sigRequestUpdateOutline(); 0317 }