File indexing completed on 2024-09-22 04:03:14
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com> 0003 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "kis_colorize_mask.h" 0009 0010 #include <QCoreApplication> 0011 #include <QStack> 0012 0013 #include <KoColorSpaceRegistry.h> 0014 #include "kis_pixel_selection.h" 0015 0016 #include "kis_icon_utils.h" 0017 0018 #include "kis_node_visitor.h" 0019 #include "kis_processing_visitor.h" 0020 #include "kis_painter.h" 0021 #include "kis_fill_painter.h" 0022 #include "kis_lazy_fill_tools.h" 0023 #include "kis_cached_paint_device.h" 0024 #include "kis_paint_device_debug_utils.h" 0025 #include "kis_layer_properties_icons.h" 0026 #include "kis_thread_safe_signal_compressor.h" 0027 0028 #include "kis_colorize_stroke_strategy.h" 0029 #include "kis_multiway_cut.h" 0030 #include "kis_image.h" 0031 #include "kis_layer.h" 0032 #include "kis_macro_based_undo_store.h" 0033 #include "kis_post_execution_undo_adapter.h" 0034 #include "kis_command_utils.h" 0035 #include "kis_processing_applicator.h" 0036 #include "krita_utils.h" 0037 #include <KisFakeRunnableStrokeJobsExecutor.h> 0038 #include <KisRunnableStrokeJobData.h> 0039 #include <KisRunnableStrokeJobUtils.h> 0040 #include <kis_pointer_utils.h> 0041 0042 0043 using namespace KisLazyFillTools; 0044 0045 struct KisColorizeMask::Private 0046 { 0047 Private(KisColorizeMask *_q, KisImageWSP image) 0048 : q(_q), 0049 coloringProjection(new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8())), 0050 fakePaintDevice(new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8())), 0051 filteredSource(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8())), 0052 needAddCurrentKeyStroke(false), 0053 showKeyStrokes(true), 0054 showColoring(true), 0055 needsUpdate(true), 0056 originalSequenceNumber(-1), 0057 updateCompressor(1000, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT), 0058 dirtyParentUpdateCompressor(200, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT), 0059 prefilterRecalculationCompressor(1000, KisSignalCompressor::POSTPONE), 0060 updateIsRunning(false), 0061 filteringOptions(false, 4.0, 15, 0.7), 0062 limitToDeviceBounds(false) 0063 { 0064 KisDefaultBoundsSP bounds(new KisDefaultBounds(image)); 0065 0066 coloringProjection->setDefaultBounds(bounds); 0067 fakePaintDevice->setDefaultBounds(bounds); 0068 filteredSource->setDefaultBounds(bounds); 0069 } 0070 0071 Private(const Private &rhs, KisColorizeMask *_q) 0072 : q(_q), 0073 coloringProjection(new KisPaintDevice(*rhs.coloringProjection)), 0074 fakePaintDevice(new KisPaintDevice(*rhs.fakePaintDevice)), 0075 filteredSource(new KisPaintDevice(*rhs.filteredSource)), 0076 filteredDeviceBounds(rhs.filteredDeviceBounds), 0077 needAddCurrentKeyStroke(rhs.needAddCurrentKeyStroke), 0078 showKeyStrokes(rhs.showKeyStrokes), 0079 showColoring(rhs.showColoring), 0080 needsUpdate(false), 0081 originalSequenceNumber(-1), 0082 updateCompressor(1000, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT), 0083 dirtyParentUpdateCompressor(200, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT), 0084 prefilterRecalculationCompressor(1000, KisSignalCompressor::POSTPONE), 0085 offset(rhs.offset), 0086 updateIsRunning(false), 0087 filteringOptions(rhs.filteringOptions), 0088 limitToDeviceBounds(rhs.limitToDeviceBounds) 0089 { 0090 Q_FOREACH (const KeyStroke &stroke, rhs.keyStrokes) { 0091 keyStrokes << KeyStroke(KisPaintDeviceSP(new KisPaintDevice(*stroke.dev)), stroke.color, stroke.isTransparent); 0092 } 0093 } 0094 0095 KisColorizeMask *q = 0; 0096 0097 QList<KeyStroke> keyStrokes; 0098 KisPaintDeviceSP coloringProjection; 0099 KisPaintDeviceSP fakePaintDevice; 0100 KisPaintDeviceSP filteredSource; 0101 QRect filteredDeviceBounds; 0102 0103 KoColor currentColor; 0104 KisPaintDeviceSP currentKeyStrokeDevice; 0105 bool needAddCurrentKeyStroke; 0106 0107 bool showKeyStrokes; 0108 bool showColoring; 0109 0110 KisCachedSelection cachedSelection; 0111 0112 bool needsUpdate; 0113 int originalSequenceNumber; 0114 0115 KisThreadSafeSignalCompressor updateCompressor; 0116 KisThreadSafeSignalCompressor dirtyParentUpdateCompressor; 0117 KisThreadSafeSignalCompressor prefilterRecalculationCompressor; 0118 QPoint offset; 0119 0120 bool updateIsRunning; 0121 QStack<QRect> extentBeforeUpdateStart; 0122 0123 FilteringOptions filteringOptions; 0124 bool filteringDirty = true; 0125 0126 bool limitToDeviceBounds = false; 0127 0128 bool filteredSourceValid(KisPaintDeviceSP parentDevice) { 0129 return !filteringDirty && originalSequenceNumber == parentDevice->sequenceNumber(); 0130 } 0131 0132 void setNeedsUpdateImpl(bool value, bool requestedByUser); 0133 0134 bool shouldShowFilteredSource() const; 0135 bool shouldShowColoring() const; 0136 }; 0137 0138 KisColorizeMask::KisColorizeMask(KisImageWSP image, const QString &name) 0139 : KisEffectMask(image, name) 0140 , m_d(new Private(this, image)) 0141 { 0142 connect(&m_d->updateCompressor, 0143 SIGNAL(timeout()), 0144 SLOT(slotUpdateRegenerateFilling())); 0145 0146 connect(this, SIGNAL(sigUpdateOnDirtyParent()), 0147 &m_d->dirtyParentUpdateCompressor, SLOT(start())); 0148 0149 connect(&m_d->dirtyParentUpdateCompressor, 0150 SIGNAL(timeout()), 0151 SLOT(slotUpdateOnDirtyParent())); 0152 0153 connect(&m_d->prefilterRecalculationCompressor, 0154 SIGNAL(timeout()), 0155 SLOT(slotRecalculatePrefilteredImage())); 0156 0157 0158 m_d->updateCompressor.moveToThread(qApp->thread()); 0159 } 0160 0161 KisColorizeMask::~KisColorizeMask() 0162 { 0163 } 0164 0165 KisColorizeMask::KisColorizeMask(const KisColorizeMask& rhs) 0166 : KisEffectMask(rhs), 0167 m_d(new Private(*rhs.m_d, this)) 0168 { 0169 connect(&m_d->updateCompressor, 0170 SIGNAL(timeout()), 0171 SLOT(slotUpdateRegenerateFilling())); 0172 0173 connect(this, SIGNAL(sigUpdateOnDirtyParent()), 0174 &m_d->dirtyParentUpdateCompressor, SLOT(start())); 0175 0176 connect(&m_d->dirtyParentUpdateCompressor, 0177 SIGNAL(timeout()), 0178 SLOT(slotUpdateOnDirtyParent())); 0179 0180 m_d->updateCompressor.moveToThread(qApp->thread()); 0181 } 0182 0183 void KisColorizeMask::initializeCompositeOp() 0184 { 0185 KisLayerSP parentLayer(qobject_cast<KisLayer*>(parent().data())); 0186 if (!parentLayer || !parentLayer->original()) return; 0187 0188 KisImageSP image = parentLayer->image(); 0189 if (!image) return; 0190 0191 const qreal samplePortion = 0.1; 0192 const qreal alphaPortion = 0193 KritaUtils::estimatePortionOfTransparentPixels(parentLayer->original(), 0194 image->bounds(), 0195 samplePortion); 0196 0197 setCompositeOpId(alphaPortion > 0.3 ? COMPOSITE_BEHIND : COMPOSITE_MULT); 0198 } 0199 0200 const KoColorSpace* KisColorizeMask::colorSpace() const 0201 { 0202 return m_d->fakePaintDevice->colorSpace(); 0203 } 0204 0205 struct SetKeyStrokesColorSpaceCommand : public KUndo2Command { 0206 SetKeyStrokesColorSpaceCommand(const KoColorSpace *dstCS, 0207 KoColorConversionTransformation::Intent renderingIntent, 0208 KoColorConversionTransformation::ConversionFlags conversionFlags, 0209 QList<KeyStroke> *list, 0210 KisColorizeMaskSP node) 0211 : m_dstCS(dstCS), 0212 m_renderingIntent(renderingIntent), 0213 m_conversionFlags(conversionFlags), 0214 m_list(list), 0215 m_node(node) {} 0216 0217 void undo() override { 0218 KIS_ASSERT_RECOVER_RETURN(m_list->size() == m_oldColors.size()); 0219 0220 for (int i = 0; i < m_list->size(); i++) { 0221 (*m_list)[i].color = m_oldColors[i]; 0222 } 0223 0224 m_node->setNeedsUpdate(true); 0225 emit m_node->sigKeyStrokesListChanged(); 0226 } 0227 0228 void redo() override { 0229 if (m_oldColors.isEmpty()) { 0230 Q_FOREACH(const KeyStroke &stroke, *m_list) { 0231 m_oldColors << stroke.color; 0232 m_newColors << stroke.color; 0233 m_newColors.last().convertTo(m_dstCS, m_renderingIntent, m_conversionFlags); 0234 } 0235 } 0236 0237 KIS_ASSERT_RECOVER_RETURN(m_list->size() == m_newColors.size()); 0238 0239 for (int i = 0; i < m_list->size(); i++) { 0240 (*m_list)[i].color = m_newColors[i]; 0241 } 0242 0243 m_node->setNeedsUpdate(true); 0244 emit m_node->sigKeyStrokesListChanged(); 0245 } 0246 0247 private: 0248 QVector<KoColor> m_oldColors; 0249 QVector<KoColor> m_newColors; 0250 0251 const KoColorSpace *m_dstCS; 0252 KoColorConversionTransformation::Intent m_renderingIntent; 0253 KoColorConversionTransformation::ConversionFlags m_conversionFlags; 0254 QList<KeyStroke> *m_list; 0255 KisColorizeMaskSP m_node; 0256 }; 0257 0258 0259 void KisColorizeMask::setProfile(const KoColorProfile *profile, KUndo2Command *parentCommand) 0260 { 0261 m_d->fakePaintDevice->setProfile(profile, parentCommand); 0262 m_d->coloringProjection->setProfile(profile, parentCommand); 0263 0264 for (auto & stroke : m_d->keyStrokes) { 0265 stroke.color.setProfile(profile); 0266 } 0267 } 0268 0269 KUndo2Command *KisColorizeMask::setColorSpace(const KoColorSpace *dstColorSpace, 0270 KoColorConversionTransformation::Intent renderingIntent, 0271 KoColorConversionTransformation::ConversionFlags conversionFlags, 0272 KoUpdater *progressUpdater) 0273 { 0274 using namespace KisCommandUtils; 0275 0276 CompositeCommand *composite = new CompositeCommand(); 0277 0278 m_d->fakePaintDevice->convertTo(dstColorSpace, renderingIntent, conversionFlags, composite, progressUpdater); 0279 m_d->coloringProjection->convertTo(dstColorSpace, renderingIntent, conversionFlags, composite, progressUpdater); 0280 0281 KUndo2Command *strokesConversionCommand = 0282 new SetKeyStrokesColorSpaceCommand( 0283 dstColorSpace, renderingIntent, conversionFlags, 0284 &m_d->keyStrokes, KisColorizeMaskSP(this)); 0285 strokesConversionCommand->redo(); 0286 0287 composite->addCommand(new SkipFirstRedoWrapper(strokesConversionCommand)); 0288 0289 return composite; 0290 } 0291 0292 bool KisColorizeMask::needsUpdate() const 0293 { 0294 return m_d->needsUpdate; 0295 } 0296 0297 void KisColorizeMask::setNeedsUpdate(bool value) 0298 { 0299 m_d->setNeedsUpdateImpl(value, true); 0300 } 0301 0302 void KisColorizeMask::Private::setNeedsUpdateImpl(bool value, bool requestedByUser) 0303 { 0304 if (value != needsUpdate) { 0305 needsUpdate = value; 0306 q->baseNodeChangedCallback(); 0307 0308 if (!value && requestedByUser) { 0309 updateCompressor.start(); 0310 } 0311 } 0312 } 0313 0314 void KisColorizeMask::slotUpdateRegenerateFilling(bool prefilterOnly) 0315 { 0316 KisPaintDeviceSP src = parent()->original(); 0317 KIS_ASSERT_RECOVER_RETURN(src); 0318 0319 const bool filteredSourceValid = m_d->filteredSourceValid(src); 0320 m_d->originalSequenceNumber = src->sequenceNumber(); 0321 m_d->filteringDirty = false; 0322 0323 if (!prefilterOnly) { 0324 m_d->coloringProjection->clear(); 0325 } 0326 0327 KisLayerSP parentLayer(qobject_cast<KisLayer*>(parent().data())); 0328 if (!parentLayer) return; 0329 0330 KisImageSP image = parentLayer->image(); 0331 if (image) { 0332 m_d->updateIsRunning = true; 0333 0334 QRect fillBounds; 0335 0336 if (m_d->limitToDeviceBounds) { 0337 fillBounds |= src->exactBounds(); 0338 Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { 0339 fillBounds |= stroke.dev->exactBounds(); 0340 } 0341 fillBounds &= image->bounds(); 0342 } else { 0343 fillBounds = image->bounds(); 0344 } 0345 0346 m_d->filteredDeviceBounds = fillBounds; 0347 0348 KisColorizeStrokeStrategy *strategy = 0349 new KisColorizeStrokeStrategy(src, 0350 m_d->coloringProjection, 0351 m_d->filteredSource, 0352 filteredSourceValid, 0353 fillBounds, 0354 this, 0355 prefilterOnly); 0356 0357 strategy->setFilteringOptions(m_d->filteringOptions); 0358 0359 Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { 0360 const KoColor color = 0361 !stroke.isTransparent ? 0362 stroke.color : 0363 KoColor::createTransparent(stroke.color.colorSpace()); 0364 0365 strategy->addKeyStroke(stroke.dev, color); 0366 } 0367 0368 m_d->extentBeforeUpdateStart.push(extent()); 0369 0370 connect(strategy, SIGNAL(sigFinished(bool)), SLOT(slotRegenerationFinished(bool))); 0371 connect(strategy, SIGNAL(sigCancelled()), SLOT(slotRegenerationCancelled())); 0372 KisStrokeId id = image->startStroke(strategy); 0373 image->endStroke(id); 0374 } 0375 } 0376 0377 void KisColorizeMask::slotUpdateOnDirtyParent() 0378 { 0379 if (!parent()) { 0380 // When the colorize mask is being merged, 0381 // the update is performed for all the layers, 0382 // so the invisible areas around the canvas are included in the merged layer. 0383 // Colorize Mask gets the info that its parent is "dirty" (needs updating), 0384 // but when it arrives, the parent doesn't exist anymore and is set to null. 0385 // Colorize Mask doesn't work outside of the canvas anyway (at least in time of writing). 0386 return; 0387 } 0388 KisPaintDeviceSP src = parent()->original(); 0389 KIS_ASSERT_RECOVER_RETURN(src); 0390 0391 if (!m_d->filteredSourceValid(src)) { 0392 const QRect &oldExtent = extent(); 0393 0394 m_d->setNeedsUpdateImpl(true, false); 0395 m_d->filteringDirty = true; 0396 0397 setDirty(oldExtent | extent()); 0398 } 0399 } 0400 0401 void KisColorizeMask::slotRecalculatePrefilteredImage() 0402 { 0403 slotUpdateRegenerateFilling(true); 0404 } 0405 0406 void KisColorizeMask::slotRegenerationFinished(bool prefilterOnly) 0407 { 0408 m_d->updateIsRunning = false; 0409 0410 if (!prefilterOnly) { 0411 m_d->setNeedsUpdateImpl(false, false); 0412 } 0413 0414 QRect oldExtent; 0415 0416 if (!m_d->extentBeforeUpdateStart.isEmpty()) { 0417 oldExtent = m_d->extentBeforeUpdateStart.pop(); 0418 } else { 0419 KIS_SAFE_ASSERT_RECOVER_NOOP(!m_d->extentBeforeUpdateStart.isEmpty()); // always fail! 0420 } 0421 0422 setDirty(oldExtent | extent()); 0423 } 0424 0425 void KisColorizeMask::slotRegenerationCancelled() 0426 { 0427 slotRegenerationFinished(true); 0428 m_d->setNeedsUpdateImpl(true, false); 0429 } 0430 0431 KisBaseNode::PropertyList KisColorizeMask::sectionModelProperties() const 0432 { 0433 KisBaseNode::PropertyList l = KisMask::sectionModelProperties(); 0434 l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::colorizeNeedsUpdate, needsUpdate()); 0435 l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::colorizeEditKeyStrokes, showKeyStrokes()); 0436 l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::colorizeShowColoring, showColoring()); 0437 0438 return l; 0439 } 0440 0441 void KisColorizeMask::setSectionModelProperties(const KisBaseNode::PropertyList &properties) 0442 { 0443 KisMask::setSectionModelProperties(properties); 0444 0445 Q_FOREACH (const KisBaseNode::Property &property, properties) { 0446 if (property.id == KisLayerPropertiesIcons::colorizeNeedsUpdate.id()) { 0447 if (m_d->needsUpdate && m_d->needsUpdate != property.state.toBool()) { 0448 setNeedsUpdate(property.state.toBool()); 0449 } 0450 } 0451 if (property.id == KisLayerPropertiesIcons::colorizeEditKeyStrokes.id()) { 0452 if (m_d->showKeyStrokes != property.state.toBool()) { 0453 setShowKeyStrokes(property.state.toBool()); 0454 } 0455 } 0456 if (property.id == KisLayerPropertiesIcons::colorizeShowColoring.id()) { 0457 if (m_d->showColoring != property.state.toBool()) { 0458 setShowColoring(property.state.toBool()); 0459 } 0460 } 0461 } 0462 } 0463 0464 KisPaintDeviceSP KisColorizeMask::paintDevice() const 0465 { 0466 return m_d->showKeyStrokes && !m_d->updateIsRunning ? m_d->fakePaintDevice : KisPaintDeviceSP(); 0467 } 0468 0469 KisPaintDeviceSP KisColorizeMask::coloringProjection() const 0470 { 0471 return m_d->coloringProjection; 0472 } 0473 0474 KisPaintDeviceSP KisColorizeMask::colorSampleSourceDevice() const 0475 { 0476 return 0477 m_d->shouldShowColoring() && !m_d->coloringProjection->extent().isEmpty() ? 0478 m_d->coloringProjection : projection(); 0479 } 0480 0481 QIcon KisColorizeMask::icon() const 0482 { 0483 return KisIconUtils::loadIcon("colorizeMask"); 0484 } 0485 0486 0487 bool KisColorizeMask::accept(KisNodeVisitor &v) 0488 { 0489 return v.visit(this); 0490 } 0491 0492 void KisColorizeMask::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) 0493 { 0494 return visitor.visit(this, undoAdapter); 0495 } 0496 0497 bool KisColorizeMask::Private::shouldShowFilteredSource() const 0498 { 0499 return !updateIsRunning && 0500 showKeyStrokes && 0501 !filteringDirty && 0502 filteredSource && 0503 !filteredSource->extent().isEmpty(); 0504 } 0505 0506 bool KisColorizeMask::Private::shouldShowColoring() const 0507 { 0508 return !updateIsRunning && 0509 showColoring && 0510 coloringProjection; 0511 } 0512 0513 QRect KisColorizeMask::decorateRect(KisPaintDeviceSP &src, 0514 KisPaintDeviceSP &dst, 0515 const QRect &rect, 0516 PositionToFilthy maskPos) const 0517 { 0518 Q_UNUSED(maskPos); 0519 0520 if (maskPos == N_ABOVE_FILTHY) { 0521 // the source layer has changed, we should update the filtered cache! 0522 0523 if (!m_d->filteringDirty) { 0524 emit sigUpdateOnDirtyParent(); 0525 } 0526 } 0527 0528 KIS_ASSERT(dst != src); 0529 0530 // Draw the filling and the original layer 0531 { 0532 KisPainter gc(dst); 0533 0534 if (m_d->shouldShowFilteredSource()) { 0535 const QRect drawRect = m_d->limitToDeviceBounds ? rect & m_d->filteredDeviceBounds : rect; 0536 0537 gc.setOpacity(128); 0538 gc.bitBlt(drawRect.topLeft(), m_d->filteredSource, drawRect); 0539 } else { 0540 gc.setOpacity(255); 0541 gc.bitBlt(rect.topLeft(), src, rect); 0542 } 0543 0544 if (m_d->shouldShowColoring()) { 0545 0546 gc.setOpacity(opacity()); 0547 gc.setCompositeOpId(compositeOpId()); 0548 gc.bitBlt(rect.topLeft(), m_d->coloringProjection, rect); 0549 } 0550 } 0551 0552 // Draw the key strokes 0553 if (m_d->showKeyStrokes) { 0554 KisIndirectPaintingSupport::ReadLocker locker(this); 0555 0556 KisCachedSelection::Guard s1(m_d->cachedSelection); 0557 KisCachedSelection::Guard s2(m_d->cachedSelection); 0558 0559 KisSelectionSP selection = s1.selection(); 0560 KisPixelSelectionSP tempSelection = s2.selection()->pixelSelection(); 0561 0562 KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); 0563 const bool isTemporaryTargetErasing = temporaryCompositeOp() == COMPOSITE_ERASE; 0564 const QRect temporaryExtent = temporaryTarget ? temporaryTarget->extent() : QRect(); 0565 0566 0567 KisFillPainter gc(dst); 0568 0569 QList<KeyStroke> extendedStrokes = m_d->keyStrokes; 0570 0571 if (m_d->currentKeyStrokeDevice && 0572 m_d->needAddCurrentKeyStroke && 0573 !isTemporaryTargetErasing) { 0574 0575 extendedStrokes << KeyStroke(m_d->currentKeyStrokeDevice, m_d->currentColor); 0576 } 0577 0578 Q_FOREACH (const KeyStroke &stroke, extendedStrokes) { 0579 selection->pixelSelection()->makeCloneFromRough(stroke.dev, rect); 0580 gc.setSelection(selection); 0581 0582 if (stroke.color == m_d->currentColor || 0583 (isTemporaryTargetErasing && 0584 temporaryExtent.intersects(selection->pixelSelection()->selectedRect()))) { 0585 0586 if (temporaryTarget) { 0587 tempSelection->copyAlphaFrom(temporaryTarget, rect); 0588 0589 KisPainter selectionPainter(selection->pixelSelection()); 0590 setupTemporaryPainter(&selectionPainter); 0591 selectionPainter.bitBlt(rect.topLeft(), tempSelection, rect); 0592 } 0593 } 0594 0595 gc.fillSelection(rect, stroke.color); 0596 } 0597 } 0598 0599 return rect; 0600 } 0601 0602 struct DeviceExtentPolicy 0603 { 0604 inline QRect operator() (const KisPaintDevice *dev) { 0605 return dev->extent(); 0606 } 0607 }; 0608 0609 struct DeviceExactBoundsPolicy 0610 { 0611 inline QRect operator() (const KisPaintDevice *dev) { 0612 return dev->exactBounds(); 0613 } 0614 }; 0615 0616 template <class DeviceMetricPolicy> 0617 QRect KisColorizeMask::calculateMaskBounds(DeviceMetricPolicy boundsPolicy) const 0618 { 0619 QRect rc; 0620 0621 if (m_d->shouldShowFilteredSource()) { 0622 rc |= boundsPolicy(m_d->filteredSource); 0623 } 0624 0625 if (m_d->shouldShowColoring()) { 0626 rc |= boundsPolicy(m_d->coloringProjection); 0627 } 0628 0629 if (m_d->showKeyStrokes) { 0630 Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { 0631 rc |= boundsPolicy(stroke.dev); 0632 } 0633 0634 KisIndirectPaintingSupport::ReadLocker locker(this); 0635 0636 KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); 0637 if (temporaryTarget) { 0638 rc |= boundsPolicy(temporaryTarget); 0639 } 0640 } 0641 0642 return rc; 0643 } 0644 0645 0646 QRect KisColorizeMask::extent() const 0647 { 0648 return calculateMaskBounds(DeviceExtentPolicy()); 0649 } 0650 0651 QRect KisColorizeMask::exactBounds() const 0652 { 0653 return calculateMaskBounds(DeviceExactBoundsPolicy()); 0654 } 0655 0656 QRect KisColorizeMask::nonDependentExtent() const 0657 { 0658 return extent(); 0659 } 0660 0661 void KisColorizeMask::setImage(KisImageWSP image) 0662 { 0663 KisDefaultBoundsSP bounds(new KisDefaultBounds(image)); 0664 0665 auto it = m_d->keyStrokes.begin(); 0666 for(; it != m_d->keyStrokes.end(); ++it) { 0667 it->dev->setDefaultBounds(bounds); 0668 } 0669 0670 m_d->coloringProjection->setDefaultBounds(bounds); 0671 m_d->fakePaintDevice->setDefaultBounds(bounds); 0672 m_d->filteredSource->setDefaultBounds(bounds); 0673 } 0674 0675 void KisColorizeMask::setCurrentColor(const KoColor &_color) 0676 { 0677 KoColor color = _color; 0678 color.convertTo(colorSpace()); 0679 0680 WriteLocker locker(this); 0681 0682 m_d->setNeedsUpdateImpl(true, false); 0683 0684 QList<KeyStroke>::const_iterator it = 0685 std::find_if(m_d->keyStrokes.constBegin(), 0686 m_d->keyStrokes.constEnd(), 0687 kismpl::mem_equal_to(&KeyStroke::color, color)); 0688 0689 KisPaintDeviceSP activeDevice; 0690 bool newKeyStroke = false; 0691 0692 if (it == m_d->keyStrokes.constEnd()) { 0693 activeDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); 0694 activeDevice->setParentNode(this); 0695 activeDevice->setDefaultBounds(KisDefaultBoundsBaseSP(new KisDefaultBounds(image()))); 0696 newKeyStroke = true; 0697 } else { 0698 activeDevice = it->dev; 0699 } 0700 0701 m_d->currentColor = color; 0702 m_d->currentKeyStrokeDevice = activeDevice; 0703 m_d->needAddCurrentKeyStroke = newKeyStroke; 0704 } 0705 0706 0707 0708 struct KeyStrokeAddRemoveCommand : public KisCommandUtils::FlipFlopCommand { 0709 KeyStrokeAddRemoveCommand(bool add, int index, KeyStroke stroke, QList<KeyStroke> *list, KisColorizeMaskSP node, KUndo2Command *parentCommand = nullptr) 0710 : FlipFlopCommand(!add, parentCommand), 0711 m_index(index), m_stroke(stroke), 0712 m_list(list), m_node(node) {} 0713 0714 void partA() override { 0715 m_list->insert(m_index, m_stroke); 0716 m_node->setNeedsUpdate(true); 0717 emit m_node->sigKeyStrokesListChanged(); 0718 } 0719 0720 void partB() override { 0721 KIS_ASSERT_RECOVER_RETURN((*m_list)[m_index] == m_stroke); 0722 m_list->removeAt(m_index); 0723 m_node->setNeedsUpdate(true); 0724 emit m_node->sigKeyStrokesListChanged(); 0725 } 0726 0727 private: 0728 int m_index; 0729 KeyStroke m_stroke; 0730 QList<KeyStroke> *m_list; 0731 KisColorizeMaskSP m_node; 0732 }; 0733 0734 void KisColorizeMask::mergeToLayerThreaded(KisNodeSP layer, KUndo2Command *parentCommand, const KUndo2MagicString &transactionText,int timedID, QVector<KisRunnableStrokeJobData*> *jobs) 0735 { 0736 // Just fake threaded merging. It is not supported for the colorize mask. 0737 0738 KritaUtils::addJobSequential(*jobs, 0739 [=] () { 0740 this->mergeToLayerUnthreaded(layer, parentCommand, transactionText, timedID); 0741 } 0742 ); 0743 } 0744 0745 void KisColorizeMask::mergeToLayerUnthreaded(KisNodeSP layer, KUndo2Command *parentCommand, const KUndo2MagicString &transactionText, int timedID) 0746 { 0747 Q_UNUSED(layer); 0748 0749 auto executeAndAdd = [parentCommand] (KUndo2Command *cmd) { 0750 cmd->redo(); 0751 new KisCommandUtils::SkipFirstRedoWrapper(cmd, parentCommand); 0752 }; 0753 0754 WriteLockerSP sharedWriteLock(new WriteLocker(this)); 0755 0756 KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); 0757 const bool isTemporaryTargetErasing = temporaryCompositeOp() == COMPOSITE_ERASE; 0758 const QRect temporaryExtent = temporaryTarget ? temporaryTarget->extent() : QRect(); 0759 0760 /** 0761 * Add a new key stroke plane 0762 */ 0763 if (m_d->needAddCurrentKeyStroke && !isTemporaryTargetErasing) { 0764 KeyStroke key(m_d->currentKeyStrokeDevice, m_d->currentColor); 0765 executeAndAdd(new KeyStrokeAddRemoveCommand( 0766 true, m_d->keyStrokes.size(), key, &m_d->keyStrokes, KisColorizeMaskSP(this), nullptr)); 0767 } 0768 0769 QVector<KisRunnableStrokeJobData*> jobs; 0770 0771 /** 0772 * When erasing, the brush affects all the key strokes, not only 0773 * the current one. 0774 */ 0775 if (!isTemporaryTargetErasing) { 0776 mergeToLayerImpl(m_d->currentKeyStrokeDevice, parentCommand, transactionText, timedID, false, sharedWriteLock, &jobs); 0777 } else { 0778 Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { 0779 if (temporaryExtent.intersects(stroke.dev->extent())) { 0780 mergeToLayerImpl(stroke.dev, parentCommand, transactionText, timedID, false, sharedWriteLock, &jobs); 0781 } 0782 } 0783 } 0784 0785 mergeToLayerImpl(m_d->fakePaintDevice, parentCommand, transactionText, timedID, false, sharedWriteLock, &jobs); 0786 0787 /** 0788 * When merging, we use barrier jobs only for ensuring that the merge jobs 0789 * are not split by the update jobs. Merge jobs hold the shared lock, so 0790 * forcing them out of CPU will basically cause a deadlock. When running in 0791 * the fake executor, the jobs cannot be split anyway, so there is no danger 0792 * in that. 0793 */ 0794 KisFakeRunnableStrokeJobsExecutor fakeExecutor(KisFakeRunnableStrokeJobsExecutor::AllowBarrierJobs); 0795 fakeExecutor.addRunnableJobs(implicitCastList<KisRunnableStrokeJobDataBase*>(jobs)); 0796 0797 m_d->currentKeyStrokeDevice = 0; 0798 m_d->currentColor = KoColor(); 0799 releaseResources(); 0800 0801 /** 0802 * Try removing the key strokes that has been completely erased 0803 */ 0804 if (isTemporaryTargetErasing) { 0805 for (int index = 0; index < m_d->keyStrokes.size(); /*noop*/) { 0806 const KeyStroke &stroke = m_d->keyStrokes[index]; 0807 0808 if (stroke.dev->exactBounds().isEmpty()) { 0809 executeAndAdd(new KeyStrokeAddRemoveCommand( 0810 false, index, stroke, &m_d->keyStrokes, KisColorizeMaskSP(this), nullptr)); 0811 } else { 0812 index++; 0813 } 0814 } 0815 } 0816 } 0817 0818 0819 void KisColorizeMask::writeMergeData(KisPainter *painter, KisPaintDeviceSP src, const QRect &rc) 0820 { 0821 const KoColorSpace *alpha8 = KoColorSpaceRegistry::instance()->alpha8(); 0822 const bool nonAlphaDst = !(*painter->device()->colorSpace() == *alpha8); 0823 0824 if (nonAlphaDst) { 0825 painter->bitBlt(rc.topLeft(), src, rc); 0826 } else { 0827 KisCachedSelection::Guard s1(m_d->cachedSelection); 0828 KisPixelSelectionSP tempSelection = s1.selection()->pixelSelection(); 0829 0830 tempSelection->copyAlphaFrom(src, rc); 0831 painter->bitBlt(rc.topLeft(), tempSelection, rc); 0832 } 0833 } 0834 0835 bool KisColorizeMask::supportsNonIndirectPainting() const 0836 { 0837 return false; 0838 } 0839 0840 bool KisColorizeMask::showColoring() const 0841 { 0842 return m_d->showColoring; 0843 } 0844 0845 void KisColorizeMask::setShowColoring(bool value) 0846 { 0847 QRect savedExtent; 0848 if (m_d->showColoring && !value) { 0849 savedExtent = extent(); 0850 } 0851 0852 m_d->showColoring = value; 0853 baseNodeChangedCallback(); 0854 0855 if (!savedExtent.isEmpty()) { 0856 setDirty(savedExtent); 0857 } 0858 } 0859 0860 bool KisColorizeMask::showKeyStrokes() const 0861 { 0862 return m_d->showKeyStrokes; 0863 } 0864 0865 void KisColorizeMask::setShowKeyStrokes(bool value) 0866 { 0867 QRect savedExtent; 0868 if (m_d->showKeyStrokes && !value) { 0869 savedExtent = extent(); 0870 } 0871 0872 m_d->showKeyStrokes = value; 0873 baseNodeChangedCallback(); 0874 0875 if (!savedExtent.isEmpty()) { 0876 setDirty(savedExtent); 0877 } 0878 0879 regeneratePrefilteredDeviceIfNeeded(); 0880 } 0881 0882 KisColorizeMask::KeyStrokeColors KisColorizeMask::keyStrokesColors() const 0883 { 0884 KeyStrokeColors colors; 0885 0886 // TODO: thread safety! 0887 for (int i = 0; i < m_d->keyStrokes.size(); i++) { 0888 colors.colors << m_d->keyStrokes[i].color; 0889 0890 if (m_d->keyStrokes[i].isTransparent) { 0891 colors.transparentIndex = i; 0892 } 0893 } 0894 0895 return colors; 0896 } 0897 0898 struct SetKeyStrokeColorsCommand : public KUndo2Command { 0899 SetKeyStrokeColorsCommand(const QList<KeyStroke> newList, QList<KeyStroke> *list, KisColorizeMaskSP node) 0900 : m_newList(newList), 0901 m_oldList(*list), 0902 m_list(list), 0903 m_node(node) {} 0904 0905 void redo() override { 0906 *m_list = m_newList; 0907 0908 m_node->setNeedsUpdate(true); 0909 emit m_node->sigKeyStrokesListChanged(); 0910 m_node->setDirty(); 0911 } 0912 0913 void undo() override { 0914 *m_list = m_oldList; 0915 0916 m_node->setNeedsUpdate(true); 0917 emit m_node->sigKeyStrokesListChanged(); 0918 m_node->setDirty(); 0919 } 0920 0921 private: 0922 QList<KeyStroke> m_newList; 0923 QList<KeyStroke> m_oldList; 0924 QList<KeyStroke> *m_list; 0925 KisColorizeMaskSP m_node; 0926 }; 0927 0928 void KisColorizeMask::setKeyStrokesColors(KeyStrokeColors colors) 0929 { 0930 KIS_ASSERT_RECOVER_RETURN(colors.colors.size() == m_d->keyStrokes.size()); 0931 0932 QList<KeyStroke> newList = m_d->keyStrokes; 0933 0934 for (int i = 0; i < newList.size(); i++) { 0935 newList[i].color = colors.colors[i]; 0936 newList[i].color.convertTo(colorSpace()); 0937 newList[i].isTransparent = colors.transparentIndex == i; 0938 } 0939 0940 KisProcessingApplicator applicator(image(), KisNodeSP(this), 0941 KisProcessingApplicator::NONE, 0942 KisImageSignalVector(), 0943 kundo2_i18n("Change Key Stroke Color")); 0944 applicator.applyCommand( 0945 new SetKeyStrokeColorsCommand( 0946 newList, &m_d->keyStrokes, KisColorizeMaskSP(this))); 0947 0948 applicator.end(); 0949 } 0950 0951 void KisColorizeMask::removeKeyStroke(const KoColor &_color) 0952 { 0953 KoColor color = _color; 0954 color.convertTo(colorSpace()); 0955 0956 QList<KeyStroke>::iterator it = 0957 std::find_if(m_d->keyStrokes.begin(), 0958 m_d->keyStrokes.end(), 0959 kismpl::mem_equal_to(&KeyStroke::color, color)); 0960 0961 KIS_SAFE_ASSERT_RECOVER_RETURN(it != m_d->keyStrokes.end()); 0962 0963 const int index = it - m_d->keyStrokes.begin(); 0964 0965 KisProcessingApplicator applicator(image(), KisNodeSP(this), 0966 KisProcessingApplicator::NONE, 0967 KisImageSignalVector(), 0968 kundo2_i18n("Remove Key Stroke")); 0969 applicator.applyCommand( 0970 new KeyStrokeAddRemoveCommand( 0971 false, index, *it, &m_d->keyStrokes, KisColorizeMaskSP(this))); 0972 0973 applicator.end(); 0974 } 0975 0976 0977 QVector<KisPaintDeviceSP> KisColorizeMask::allPaintDevices() const 0978 { 0979 QVector<KisPaintDeviceSP> devices; 0980 0981 Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { 0982 devices << stroke.dev; 0983 } 0984 0985 devices << m_d->coloringProjection; 0986 devices << m_d->fakePaintDevice; 0987 0988 return devices; 0989 } 0990 0991 void KisColorizeMask::resetCache() 0992 { 0993 m_d->filteredSource->clear(); 0994 m_d->originalSequenceNumber = -1; 0995 m_d->filteringDirty = true; 0996 0997 rerenderFakePaintDevice(); 0998 slotUpdateRegenerateFilling(true); 0999 } 1000 1001 void KisColorizeMask::setUseEdgeDetection(bool value) 1002 { 1003 m_d->filteringOptions.useEdgeDetection = value; 1004 m_d->filteringDirty = true; 1005 setNeedsUpdate(true); 1006 } 1007 1008 bool KisColorizeMask::useEdgeDetection() const 1009 { 1010 return m_d->filteringOptions.useEdgeDetection; 1011 } 1012 1013 void KisColorizeMask::setEdgeDetectionSize(qreal value) 1014 { 1015 m_d->filteringOptions.edgeDetectionSize = value; 1016 m_d->filteringDirty = true; 1017 setNeedsUpdate(true); 1018 } 1019 1020 qreal KisColorizeMask::edgeDetectionSize() const 1021 { 1022 return m_d->filteringOptions.edgeDetectionSize; 1023 } 1024 1025 void KisColorizeMask::setFuzzyRadius(qreal value) 1026 { 1027 m_d->filteringOptions.fuzzyRadius = value; 1028 m_d->filteringDirty = true; 1029 setNeedsUpdate(true); 1030 } 1031 1032 qreal KisColorizeMask::fuzzyRadius() const 1033 { 1034 return m_d->filteringOptions.fuzzyRadius; 1035 } 1036 1037 void KisColorizeMask::setCleanUpAmount(qreal value) 1038 { 1039 m_d->filteringOptions.cleanUpAmount = value; 1040 setNeedsUpdate(true); 1041 } 1042 1043 qreal KisColorizeMask::cleanUpAmount() const 1044 { 1045 return m_d->filteringOptions.cleanUpAmount; 1046 } 1047 1048 void KisColorizeMask::setLimitToDeviceBounds(bool value) 1049 { 1050 m_d->limitToDeviceBounds = value; 1051 m_d->filteringDirty = true; 1052 setNeedsUpdate(true); 1053 } 1054 1055 bool KisColorizeMask::limitToDeviceBounds() const 1056 { 1057 return m_d->limitToDeviceBounds; 1058 } 1059 1060 void KisColorizeMask::rerenderFakePaintDevice() 1061 { 1062 m_d->fakePaintDevice->clear(); 1063 KisFillPainter gc(m_d->fakePaintDevice); 1064 1065 KisCachedSelection::Guard s1(m_d->cachedSelection); 1066 KisSelectionSP selection = s1.selection(); 1067 1068 Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { 1069 const QRect rect = stroke.dev->extent(); 1070 1071 selection->pixelSelection()->makeCloneFromRough(stroke.dev, rect); 1072 gc.setSelection(selection); 1073 gc.fillSelection(rect, stroke.color); 1074 } 1075 } 1076 1077 void KisColorizeMask::testingAddKeyStroke(KisPaintDeviceSP dev, const KoColor &color, bool isTransparent) 1078 { 1079 m_d->keyStrokes << KeyStroke(dev, color, isTransparent); 1080 } 1081 1082 void KisColorizeMask::forceRegenerateMask() 1083 { 1084 slotUpdateRegenerateFilling(); 1085 m_d->updateIsRunning = false; 1086 } 1087 1088 KisPaintDeviceSP KisColorizeMask::testingFilteredSource() const 1089 { 1090 return m_d->filteredSource; 1091 } 1092 1093 QList<KeyStroke> KisColorizeMask::fetchKeyStrokesDirect() const 1094 { 1095 return m_d->keyStrokes; 1096 } 1097 1098 void KisColorizeMask::setKeyStrokesDirect(const QList<KisLazyFillTools::KeyStroke> &strokes) 1099 { 1100 m_d->keyStrokes = strokes; 1101 1102 for (auto it = m_d->keyStrokes.begin(); it != m_d->keyStrokes.end(); ++it) { 1103 it->dev->setParentNode(this); 1104 } 1105 1106 KIS_SAFE_ASSERT_RECOVER(image()) 1107 {}; 1108 } 1109 1110 qint32 KisColorizeMask::x() const 1111 { 1112 return m_d->offset.x(); 1113 } 1114 1115 qint32 KisColorizeMask::y() const 1116 { 1117 return m_d->offset.y(); 1118 } 1119 1120 void KisColorizeMask::setX(qint32 x) 1121 { 1122 const QPoint oldOffset = m_d->offset; 1123 m_d->offset.rx() = x; 1124 moveAllInternalDevices(m_d->offset - oldOffset); 1125 } 1126 1127 void KisColorizeMask::setY(qint32 y) 1128 { 1129 const QPoint oldOffset = m_d->offset; 1130 m_d->offset.ry() = y; 1131 moveAllInternalDevices(m_d->offset - oldOffset); 1132 } 1133 1134 KisPaintDeviceList KisColorizeMask::getLodCapableDevices() const 1135 { 1136 KisPaintDeviceList list; 1137 1138 auto it = m_d->keyStrokes.begin(); 1139 for(; it != m_d->keyStrokes.end(); ++it) { 1140 list << it->dev; 1141 } 1142 1143 list << m_d->coloringProjection; 1144 list << m_d->fakePaintDevice; 1145 list << m_d->filteredSource; 1146 1147 return list; 1148 } 1149 1150 void KisColorizeMask::regeneratePrefilteredDeviceIfNeeded() 1151 { 1152 if (!parent()) return; 1153 1154 KisPaintDeviceSP src = parent()->original(); 1155 KIS_ASSERT_RECOVER_RETURN(src); 1156 1157 if (!m_d->filteredSourceValid(src)) { 1158 // update the prefiltered source if needed 1159 slotUpdateRegenerateFilling(true); 1160 } 1161 } 1162 1163 void KisColorizeMask::moveAllInternalDevices(const QPoint &diff) 1164 { 1165 QVector<KisPaintDeviceSP> devices = allPaintDevices(); 1166 1167 Q_FOREACH (KisPaintDeviceSP dev, devices) { 1168 dev->moveTo(dev->offset() + diff); 1169 } 1170 }