Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_colorize_mask.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
3 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include "kis_colorize_mask.h"
9
10#include <QCoreApplication>
11#include <QStack>
12
14#include "kis_pixel_selection.h"
15
16#include "kis_icon_utils.h"
17
18#include "kis_node_visitor.h"
20#include "kis_painter.h"
21#include "kis_fill_painter.h"
22#include "kis_lazy_fill_tools.h"
27
29#include "kis_multiway_cut.h"
30#include "kis_image.h"
31#include "kis_layer.h"
34#include "kis_command_utils.h"
36#include "krita_utils.h"
40#include <kis_pointer_utils.h>
41
42
43using namespace KisLazyFillTools;
44
46{
70
94
96
102
106
109
111
114
118 QPoint offset;
119
122
124 bool filteringDirty = true;
125
127
129 return !filteringDirty && originalSequenceNumber == parentDevice->sequenceNumber();
130 }
131
132 void setNeedsUpdateImpl(bool value, bool requestedByUser);
133
134 bool shouldShowFilteredSource() const;
135 bool shouldShowColoring() const;
136};
137
139 : KisEffectMask(image, name)
140 , m_d(new Private(this, image))
141{
142 connect(&m_d->updateCompressor,
143 SIGNAL(timeout()),
145
146 connect(this, SIGNAL(sigUpdateOnDirtyParent()),
147 &m_d->dirtyParentUpdateCompressor, SLOT(start()));
148
149 connect(&m_d->dirtyParentUpdateCompressor,
150 SIGNAL(timeout()),
152
153 connect(&m_d->prefilterRecalculationCompressor,
154 SIGNAL(timeout()),
156
157
158 m_d->updateCompressor.moveToThread(qApp->thread());
159}
160
164
166 : KisEffectMask(rhs),
167 m_d(new Private(*rhs.m_d, this))
168{
169 connect(&m_d->updateCompressor,
170 SIGNAL(timeout()),
172
173 connect(this, SIGNAL(sigUpdateOnDirtyParent()),
174 &m_d->dirtyParentUpdateCompressor, SLOT(start()));
175
176 connect(&m_d->dirtyParentUpdateCompressor,
177 SIGNAL(timeout()),
179
180 m_d->updateCompressor.moveToThread(qApp->thread());
181}
182
184{
185 KisLayerSP parentLayer(qobject_cast<KisLayer*>(parent().data()));
186 if (!parentLayer || !parentLayer->original()) return;
187
188 KisImageSP image = parentLayer->image();
189 if (!image) return;
190
191 const qreal samplePortion = 0.1;
192 const qreal alphaPortion =
194 image->bounds(),
195 samplePortion);
196
197 setCompositeOpId(alphaPortion > 0.3 ? COMPOSITE_BEHIND : COMPOSITE_MULT);
198}
199
201{
202 return m_d->fakePaintDevice->colorSpace();
203}
204
208 KoColorConversionTransformation::ConversionFlags conversionFlags,
209 QList<KeyStroke> *list,
211 : m_dstCS(dstCS),
212 m_renderingIntent(renderingIntent),
213 m_conversionFlags(conversionFlags),
214 m_list(list),
215 m_node(node) {}
216
217 void undo() override {
219
220 for (int i = 0; i < m_list->size(); i++) {
221 (*m_list)[i].color = m_oldColors[i];
222 }
223
224 m_node->setNeedsUpdate(true);
226 }
227
228 void redo() override {
229 if (m_oldColors.isEmpty()) {
230 Q_FOREACH(const KeyStroke &stroke, *m_list) {
231 m_oldColors << stroke.color;
232 m_newColors << stroke.color;
234 }
235 }
236
238
239 for (int i = 0; i < m_list->size(); i++) {
240 (*m_list)[i].color = m_newColors[i];
241 }
242
243 m_node->setNeedsUpdate(true);
245 }
246
247private:
250
253 KoColorConversionTransformation::ConversionFlags m_conversionFlags;
256};
257
258
259void KisColorizeMask::setProfile(const KoColorProfile *profile, KUndo2Command *parentCommand)
260{
261 m_d->fakePaintDevice->setProfile(profile, parentCommand);
262 m_d->coloringProjection->setProfile(profile, parentCommand);
263
264 for (auto & stroke : m_d->keyStrokes) {
265 stroke.color.setProfile(profile);
266 }
267}
268
271 KoColorConversionTransformation::ConversionFlags conversionFlags,
272 KoUpdater *progressUpdater)
273{
274 using namespace KisCommandUtils;
275
276 CompositeCommand *composite = new CompositeCommand();
277
278 m_d->fakePaintDevice->convertTo(dstColorSpace, renderingIntent, conversionFlags, composite, progressUpdater);
279 m_d->coloringProjection->convertTo(dstColorSpace, renderingIntent, conversionFlags, composite, progressUpdater);
280
281 KUndo2Command *strokesConversionCommand =
283 dstColorSpace, renderingIntent, conversionFlags,
284 &m_d->keyStrokes, KisColorizeMaskSP(this));
285 strokesConversionCommand->redo();
286
287 composite->addCommand(new SkipFirstRedoWrapper(strokesConversionCommand));
288
289 return composite;
290}
291
293{
294 return m_d->needsUpdate;
295}
296
298{
299 m_d->setNeedsUpdateImpl(value, true);
300}
301
303{
304 if (value != needsUpdate) {
307
308 if (!value && requestedByUser) {
310 }
311 }
312}
313
315{
318
319 const bool filteredSourceValid = m_d->filteredSourceValid(src);
320 m_d->originalSequenceNumber = src->sequenceNumber();
321 m_d->filteringDirty = false;
322
323 if (!prefilterOnly) {
324 m_d->coloringProjection->clear();
325 }
326
327 KisLayerSP parentLayer(qobject_cast<KisLayer*>(parent().data()));
328 if (!parentLayer) return;
329
330 KisImageSP image = parentLayer->image();
331 if (image) {
332 m_d->updateIsRunning = true;
333
334 QRect fillBounds;
335
336 if (m_d->limitToDeviceBounds) {
337 fillBounds |= src->exactBounds();
338 Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) {
339 fillBounds |= stroke.dev->exactBounds();
340 }
341 fillBounds &= image->bounds();
342 } else {
343 fillBounds = image->bounds();
344 }
345
346 m_d->filteredDeviceBounds = fillBounds;
347
348 KisColorizeStrokeStrategy *strategy =
350 m_d->coloringProjection,
351 m_d->filteredSource,
352 filteredSourceValid,
353 fillBounds,
354 this,
355 prefilterOnly);
356
357 strategy->setFilteringOptions(m_d->filteringOptions);
358
359 Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) {
360 const KoColor color =
361 !stroke.isTransparent ?
362 stroke.color :
364
365 strategy->addKeyStroke(stroke.dev, color);
366 }
367
368 m_d->extentBeforeUpdateStart.push(extent());
369
370 connect(strategy, SIGNAL(sigFinished(bool)), SLOT(slotRegenerationFinished(bool)));
371 connect(strategy, SIGNAL(sigCancelled()), SLOT(slotRegenerationCancelled()));
372 KisStrokeId id = image->startStroke(strategy);
373 image->endStroke(id);
374 }
375}
376
378{
379 if (!parent()) {
380 // When the colorize mask is being merged,
381 // the update is performed for all the layers,
382 // so the invisible areas around the canvas are included in the merged layer.
383 // Colorize Mask gets the info that its parent is "dirty" (needs updating),
384 // but when it arrives, the parent doesn't exist anymore and is set to null.
385 // Colorize Mask doesn't work outside of the canvas anyway (at least in time of writing).
386 return;
387 }
390
391 if (!m_d->filteredSourceValid(src)) {
392 const QRect &oldExtent = extent();
393
394 m_d->setNeedsUpdateImpl(true, false);
395 m_d->filteringDirty = true;
396
397 setDirty(oldExtent | extent());
398 }
399}
400
405
407{
408 m_d->updateIsRunning = false;
409
410 if (!prefilterOnly) {
411 m_d->setNeedsUpdateImpl(false, false);
412 }
413
414 QRect oldExtent;
415
416 if (!m_d->extentBeforeUpdateStart.isEmpty()) {
417 oldExtent = m_d->extentBeforeUpdateStart.pop();
418 } else {
419 KIS_SAFE_ASSERT_RECOVER_NOOP(!m_d->extentBeforeUpdateStart.isEmpty()); // always fail!
420 }
421
422 setDirty(oldExtent | extent());
423}
424
426{
428 m_d->setNeedsUpdateImpl(true, false);
429}
430
440
442{
444
445 Q_FOREACH (const KisBaseNode::Property &property, properties) {
447 if (m_d->needsUpdate && m_d->needsUpdate != property.state.toBool()) {
448 setNeedsUpdate(property.state.toBool());
449 }
450 }
452 if (m_d->showKeyStrokes != property.state.toBool()) {
453 setShowKeyStrokes(property.state.toBool());
454 }
455 }
457 if (m_d->showColoring != property.state.toBool()) {
458 setShowColoring(property.state.toBool());
459 }
460 }
461 }
462}
463
465{
466 return m_d->showKeyStrokes && !m_d->updateIsRunning ? m_d->fakePaintDevice : KisPaintDeviceSP();
467}
468
470{
471 return m_d->coloringProjection;
472}
473
475{
476 return
477 m_d->shouldShowColoring() && !m_d->coloringProjection->extent().isEmpty() ?
478 m_d->coloringProjection : projection();
479}
480
482{
483 return KisIconUtils::loadIcon("colorizeMask");
484}
485
486
488{
489 return v.visit(this);
490}
491
493{
494 return visitor.visit(this, undoAdapter);
495}
496
498{
499 return !updateIsRunning &&
501 !filteringDirty &&
502 filteredSource &&
503 !filteredSource->extent().isEmpty();
504}
505
507{
508 return !updateIsRunning &&
509 showColoring &&
511}
512
514 KisPaintDeviceSP &dst,
515 const QRect &rect,
516 PositionToFilthy maskPos,
517 KisRenderPassFlags flags) const
518{
519 Q_UNUSED(maskPos);
520 Q_UNUSED(flags);
521
522 if (maskPos == N_ABOVE_FILTHY) {
523 // the source layer has changed, we should update the filtered cache!
524
525 if (!m_d->filteringDirty) {
526 Q_EMIT sigUpdateOnDirtyParent();
527 }
528 }
529
530 KIS_ASSERT(dst != src);
531
532 // Draw the filling and the original layer
533 {
534 KisPainter gc(dst);
535
536 if (m_d->shouldShowFilteredSource()) {
537 const QRect drawRect = m_d->limitToDeviceBounds ? rect & m_d->filteredDeviceBounds : rect;
538
539 gc.setOpacityF(0.5);
540 gc.bitBlt(drawRect.topLeft(), m_d->filteredSource, drawRect);
541 } else {
542 gc.setOpacityToUnit();
543 gc.bitBlt(rect.topLeft(), src, rect);
544 }
545
546 if (m_d->shouldShowColoring()) {
547
548 gc.setOpacityU8(opacity());
550 gc.bitBlt(rect.topLeft(), m_d->coloringProjection, rect);
551 }
552 }
553
554 // Draw the key strokes
555 if (m_d->showKeyStrokes) {
557
558 KisCachedSelection::Guard s1(m_d->cachedSelection);
559 KisCachedSelection::Guard s2(m_d->cachedSelection);
560
561 KisSelectionSP selection = s1.selection();
562 KisPixelSelectionSP tempSelection = s2.selection()->pixelSelection();
563
565 const bool isTemporaryTargetErasing = temporaryCompositeOp() == COMPOSITE_ERASE;
566 const QRect temporaryExtent = temporaryTarget ? temporaryTarget->extent() : QRect();
567
568
569 KisFillPainter gc(dst);
570
571 QList<KeyStroke> extendedStrokes = m_d->keyStrokes;
572
573 if (m_d->currentKeyStrokeDevice &&
574 m_d->needAddCurrentKeyStroke &&
575 !isTemporaryTargetErasing) {
576
577 extendedStrokes << KeyStroke(m_d->currentKeyStrokeDevice, m_d->currentColor);
578 }
579
580 Q_FOREACH (const KeyStroke &stroke, extendedStrokes) {
583
584 if (stroke.color == m_d->currentColor ||
585 (isTemporaryTargetErasing &&
586 temporaryExtent.intersects(selection->pixelSelection()->selectedRect()))) {
587
588 if (temporaryTarget) {
589 tempSelection->copyAlphaFrom(temporaryTarget, rect);
590
591 KisPainter selectionPainter(selection->pixelSelection());
592 setupTemporaryPainter(&selectionPainter);
593 selectionPainter.bitBlt(rect.topLeft(), tempSelection, rect);
594 }
595 }
596
597 gc.fillSelection(rect, stroke.color);
598 }
599 }
600
601 return rect;
602}
603
605{
606 inline QRect operator() (const KisPaintDevice *dev) {
607 return dev->extent();
608 }
609};
610
612{
613 inline QRect operator() (const KisPaintDevice *dev) {
614 return dev->exactBounds();
615 }
616};
617
618template <class DeviceMetricPolicy>
619QRect KisColorizeMask::calculateMaskBounds(DeviceMetricPolicy boundsPolicy) const
620{
621 QRect rc;
622
623 if (m_d->shouldShowFilteredSource()) {
624 rc |= boundsPolicy(m_d->filteredSource);
625 }
626
627 if (m_d->shouldShowColoring()) {
628 rc |= boundsPolicy(m_d->coloringProjection);
629 }
630
631 if (m_d->showKeyStrokes) {
632 Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) {
633 rc |= boundsPolicy(stroke.dev);
634 }
635
637
639 if (temporaryTarget) {
640 rc |= boundsPolicy(temporaryTarget);
641 }
642 }
643
644 return rc;
645}
646
647
652
657
659{
660 return extent();
661}
662
664{
666
667 auto it = m_d->keyStrokes.begin();
668 for(; it != m_d->keyStrokes.end(); ++it) {
669 it->dev->setDefaultBounds(bounds);
670 }
671
672 m_d->coloringProjection->setDefaultBounds(bounds);
673 m_d->fakePaintDevice->setDefaultBounds(bounds);
674 m_d->filteredSource->setDefaultBounds(bounds);
675}
676
678{
679 KoColor color = _color;
680 color.convertTo(colorSpace());
681
682 WriteLocker locker(this);
683
684 m_d->setNeedsUpdateImpl(true, false);
685
687 std::find_if(m_d->keyStrokes.constBegin(),
688 m_d->keyStrokes.constEnd(),
690
691 KisPaintDeviceSP activeDevice;
692 bool newKeyStroke = false;
693
694 if (it == m_d->keyStrokes.constEnd()) {
695 activeDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
696 activeDevice->setParentNode(this);
698 newKeyStroke = true;
699 } else {
700 activeDevice = it->dev;
701 }
702
703 m_d->currentColor = color;
704 m_d->currentKeyStrokeDevice = activeDevice;
705 m_d->needAddCurrentKeyStroke = newKeyStroke;
706}
707
708
709
711 KeyStrokeAddRemoveCommand(bool add, int index, KeyStroke stroke, QList<KeyStroke> *list, KisColorizeMaskSP node, KUndo2Command *parentCommand = nullptr)
712 : FlipFlopCommand(!add, parentCommand),
713 m_index(index), m_stroke(stroke),
714 m_list(list), m_node(node) {}
715
716 void partA() override {
717 m_list->insert(m_index, m_stroke);
718 m_node->setNeedsUpdate(true);
719 Q_EMIT m_node->sigKeyStrokesListChanged();
720 }
721
722 void partB() override {
723 KIS_ASSERT_RECOVER_RETURN((*m_list)[m_index] == m_stroke);
724 m_list->removeAt(m_index);
725 m_node->setNeedsUpdate(true);
726 Q_EMIT m_node->sigKeyStrokesListChanged();
727 }
728
729private:
734};
735
737{
738 // Just fake threaded merging. It is not supported for the colorize mask.
739
741 [=] () {
742 this->mergeToLayerUnthreaded(layer, parentCommand, transactionText, timedID);
743 }
744 );
745}
746
747void KisColorizeMask::mergeToLayerUnthreaded(KisNodeSP layer, KUndo2Command *parentCommand, const KUndo2MagicString &transactionText, int timedID)
748{
749 Q_UNUSED(layer);
750
751 auto executeAndAdd = [parentCommand] (KUndo2Command *cmd) {
752 cmd->redo();
753 new KisCommandUtils::SkipFirstRedoWrapper(cmd, parentCommand);
754 };
755
756 WriteLockerSP sharedWriteLock(new WriteLocker(this));
757
759 const bool isTemporaryTargetErasing = temporaryCompositeOp() == COMPOSITE_ERASE;
760 const QRect temporaryExtent = temporaryTarget ? temporaryTarget->extent() : QRect();
761
765 if (m_d->needAddCurrentKeyStroke && !isTemporaryTargetErasing) {
766 KeyStroke key(m_d->currentKeyStrokeDevice, m_d->currentColor);
767 executeAndAdd(new KeyStrokeAddRemoveCommand(
768 true, m_d->keyStrokes.size(), key, &m_d->keyStrokes, KisColorizeMaskSP(this), nullptr));
769 }
770
772
777 if (!isTemporaryTargetErasing) {
778 mergeToLayerImpl(m_d->currentKeyStrokeDevice, parentCommand, transactionText, timedID, false, sharedWriteLock, &jobs);
779 } else {
780 Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) {
781 if (temporaryExtent.intersects(stroke.dev->extent())) {
782 mergeToLayerImpl(stroke.dev, parentCommand, transactionText, timedID, false, sharedWriteLock, &jobs);
783 }
784 }
785 }
786
787 mergeToLayerImpl(m_d->fakePaintDevice, parentCommand, transactionText, timedID, false, sharedWriteLock, &jobs);
788
797 fakeExecutor.addRunnableJobs(implicitCastList<KisRunnableStrokeJobDataBase*>(jobs));
798
799 m_d->currentKeyStrokeDevice = 0;
800 m_d->currentColor = KoColor();
802
806 if (isTemporaryTargetErasing) {
807 for (int index = 0; index < m_d->keyStrokes.size(); /*noop*/) {
808 const KeyStroke &stroke = m_d->keyStrokes[index];
809
810 if (stroke.dev->exactBounds().isEmpty()) {
811 executeAndAdd(new KeyStrokeAddRemoveCommand(
812 false, index, stroke, &m_d->keyStrokes, KisColorizeMaskSP(this), nullptr));
813 } else {
814 index++;
815 }
816 }
817 }
818}
819
820
822{
824 const bool nonAlphaDst = !(*painter->device()->colorSpace() == *alpha8);
825
826 if (nonAlphaDst) {
827 painter->bitBlt(rc.topLeft(), src, rc);
828 } else {
829 KisCachedSelection::Guard s1(m_d->cachedSelection);
830 KisPixelSelectionSP tempSelection = s1.selection()->pixelSelection();
831
832 tempSelection->copyAlphaFrom(src, rc);
833 painter->bitBlt(rc.topLeft(), tempSelection, rc);
834 }
835}
836
838{
839 return false;
840}
841
843{
844 return m_d->showColoring;
845}
846
848{
849 QRect savedExtent;
850 if (m_d->showColoring && !value) {
851 savedExtent = extent();
852 }
853
854 m_d->showColoring = value;
856
857 if (!savedExtent.isEmpty()) {
858 setDirty(savedExtent);
859 }
860}
861
863{
864 return m_d->showKeyStrokes;
865}
866
868{
869 QRect savedExtent;
870 if (m_d->showKeyStrokes && !value) {
871 savedExtent = extent();
872 }
873
874 m_d->showKeyStrokes = value;
876
877 if (!savedExtent.isEmpty()) {
878 setDirty(savedExtent);
879 }
880
882}
883
885{
886 KeyStrokeColors colors;
887
888 // TODO: thread safety!
889 for (int i = 0; i < m_d->keyStrokes.size(); i++) {
890 colors.colors << m_d->keyStrokes[i].color;
891
892 if (m_d->keyStrokes[i].isTransparent) {
893 colors.transparentIndex = i;
894 }
895 }
896
897 return colors;
898}
899
902 : m_newList(newList),
903 m_oldList(*list),
904 m_list(list),
905 m_node(node) {}
906
907 void redo() override {
908 *m_list = m_newList;
909
910 m_node->setNeedsUpdate(true);
911 Q_EMIT m_node->sigKeyStrokesListChanged();
912 m_node->setDirty();
913 }
914
915 void undo() override {
916 *m_list = m_oldList;
917
918 m_node->setNeedsUpdate(true);
919 Q_EMIT m_node->sigKeyStrokesListChanged();
920 m_node->setDirty();
921 }
922
923private:
928};
929
931{
932 KIS_ASSERT_RECOVER_RETURN(colors.colors.size() == m_d->keyStrokes.size());
933
934 QList<KeyStroke> newList = m_d->keyStrokes;
935
936 for (int i = 0; i < newList.size(); i++) {
937 newList[i].color = colors.colors[i];
938 newList[i].color.convertTo(colorSpace());
939 newList[i].isTransparent = colors.transparentIndex == i;
940 }
941
942 KisProcessingApplicator applicator(image(), KisNodeSP(this),
945 kundo2_i18n("Change Key Stroke Color"));
946 applicator.applyCommand(
948 newList, &m_d->keyStrokes, KisColorizeMaskSP(this)));
949
950 applicator.end();
951}
952
954{
955 KoColor color = _color;
956 color.convertTo(colorSpace());
957
959 std::find_if(m_d->keyStrokes.begin(),
960 m_d->keyStrokes.end(),
962
963 KIS_SAFE_ASSERT_RECOVER_RETURN(it != m_d->keyStrokes.end());
964
965 const int index = it - m_d->keyStrokes.begin();
966
967 KisProcessingApplicator applicator(image(), KisNodeSP(this),
970 kundo2_i18n("Remove Key Stroke"));
971 applicator.applyCommand(
973 false, index, *it, &m_d->keyStrokes, KisColorizeMaskSP(this)));
974
975 applicator.end();
976}
977
978
980{
982
983 Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) {
984 devices << stroke.dev;
985 }
986
987 devices << m_d->coloringProjection;
988 devices << m_d->fakePaintDevice;
989
990 return devices;
991}
992
994{
995 m_d->filteredSource->clear();
996 m_d->originalSequenceNumber = -1;
997 m_d->filteringDirty = true;
998
1001}
1002
1004{
1005 m_d->filteringOptions.useEdgeDetection = value;
1006 m_d->filteringDirty = true;
1007 setNeedsUpdate(true);
1008}
1009
1011{
1012 return m_d->filteringOptions.useEdgeDetection;
1013}
1014
1016{
1017 m_d->filteringOptions.edgeDetectionSize = value;
1018 m_d->filteringDirty = true;
1019 setNeedsUpdate(true);
1020}
1021
1023{
1024 return m_d->filteringOptions.edgeDetectionSize;
1025}
1026
1028{
1029 m_d->filteringOptions.fuzzyRadius = value;
1030 m_d->filteringDirty = true;
1031 setNeedsUpdate(true);
1032}
1033
1035{
1036 return m_d->filteringOptions.fuzzyRadius;
1037}
1038
1040{
1041 m_d->filteringOptions.cleanUpAmount = value;
1042 setNeedsUpdate(true);
1043}
1044
1046{
1047 return m_d->filteringOptions.cleanUpAmount;
1048}
1049
1051{
1052 m_d->limitToDeviceBounds = value;
1053 m_d->filteringDirty = true;
1054 setNeedsUpdate(true);
1055}
1056
1058{
1059 return m_d->limitToDeviceBounds;
1060}
1061
1063{
1064 m_d->fakePaintDevice->clear();
1065 KisFillPainter gc(m_d->fakePaintDevice);
1066
1067 KisCachedSelection::Guard s1(m_d->cachedSelection);
1068 KisSelectionSP selection = s1.selection();
1069
1070 Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) {
1071 const QRect rect = stroke.dev->extent();
1072
1075 gc.fillSelection(rect, stroke.color);
1076 }
1077}
1078
1079void KisColorizeMask::testingAddKeyStroke(KisPaintDeviceSP dev, const KoColor &color, bool isTransparent)
1080{
1081 m_d->keyStrokes << KeyStroke(dev, color, isTransparent);
1082}
1083
1085{
1087 m_d->updateIsRunning = false;
1088}
1089
1091{
1092 return m_d->filteredSource;
1093}
1094
1096{
1097 return m_d->keyStrokes;
1098}
1099
1101{
1102 m_d->keyStrokes = strokes;
1103
1104 for (auto it = m_d->keyStrokes.begin(); it != m_d->keyStrokes.end(); ++it) {
1105 it->dev->setParentNode(this);
1106 }
1107
1109 {};
1110}
1111
1113{
1114 return m_d->offset.x();
1115}
1116
1118{
1119 return m_d->offset.y();
1120}
1121
1123{
1124 const QPoint oldOffset = m_d->offset;
1125 m_d->offset.rx() = x;
1126 moveAllInternalDevices(m_d->offset - oldOffset);
1127}
1128
1130{
1131 const QPoint oldOffset = m_d->offset;
1132 m_d->offset.ry() = y;
1133 moveAllInternalDevices(m_d->offset - oldOffset);
1134}
1135
1137{
1138 KisPaintDeviceList list;
1139
1140 auto it = m_d->keyStrokes.begin();
1141 for(; it != m_d->keyStrokes.end(); ++it) {
1142 list << it->dev;
1143 }
1144
1145 list << m_d->coloringProjection;
1146 list << m_d->fakePaintDevice;
1147 list << m_d->filteredSource;
1148
1149 return list;
1150}
1151
1153{
1154 if (!parent()) return;
1155
1156 KisPaintDeviceSP src = parent()->original();
1158
1159 if (!m_d->filteredSourceValid(src)) {
1160 // update the prefiltered source if needed
1162 }
1163}
1164
1166{
1168
1169 Q_FOREACH (KisPaintDeviceSP dev, devices) {
1170 dev->moveTo(dev->offset() + diff);
1171 }
1172}
float value(const T *src, size_t ch)
QPointF s1
qreal v
QPointF s2
QVector< KisImageSignalType > KisImageSignalVector
const QString COMPOSITE_MULT
const QString COMPOSITE_ERASE
const QString COMPOSITE_BEHIND
PythonPluginManager * instance
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
virtual void redo()
KisBaseNode::PropertyList sectionModelProperties() const override
bool limitToDeviceBounds() const
void setShowColoring(bool value)
friend struct SetKeyStrokesColorSpaceCommand
bool showColoring() const
void mergeToLayerThreaded(KisNodeSP layer, KUndo2Command *parentCommand, const KUndo2MagicString &transactionText, int timedID, QVector< KisRunnableStrokeJobData * > *jobs) override
qreal cleanUpAmount() const
QIcon icon() const override
QRect nonDependentExtent() const override
friend struct SetKeyStrokeColorsCommand
void setKeyStrokesDirect(const QList< KisLazyFillTools::KeyStroke > &strokes)
void setEdgeDetectionSize(qreal value)
void setLimitToDeviceBounds(bool value)
KisPaintDeviceList getLodCapableDevices() const override
KisColorizeMask(KisImageWSP image, const QString &name)
friend struct KeyStrokeAddRemoveCommand
const KoColorSpace * colorSpace() const override
void moveAllInternalDevices(const QPoint &diff)
void setSectionModelProperties(const KisBaseNode::PropertyList &properties) override
void setProfile(const KoColorProfile *profile, KUndo2Command *parentCommand)
void setUseEdgeDetection(bool value)
void setKeyStrokesColors(KeyStrokeColors colors)
void regeneratePrefilteredDeviceIfNeeded()
const QScopedPointer< Private > m_d
QRect extent() const override
qreal fuzzyRadius() const
qint32 y() const override
void setCurrentColor(const KoColor &color) override
void slotRegenerationFinished(bool prefilterOnly)
QRect decorateRect(KisPaintDeviceSP &src, KisPaintDeviceSP &dst, const QRect &rc, PositionToFilthy maskPos, KisRenderPassFlags flags) const override
bool accept(KisNodeVisitor &v) override
KisPaintDeviceSP testingFilteredSource() const
KisPaintDeviceSP coloringProjection() const
void slotUpdateRegenerateFilling(bool prefilterOnly=false)
void setFuzzyRadius(qreal value)
KisPaintDeviceSP colorSampleSourceDevice() const override
void setShowKeyStrokes(bool value)
QVector< KisPaintDeviceSP > allPaintDevices() const
void mergeToLayerUnthreaded(KisNodeSP layer, KUndo2Command *parentCommand, const KUndo2MagicString &transactionText, int timedID)
bool useEdgeDetection() const
void setImage(KisImageWSP image) override
bool showKeyStrokes() const
void removeKeyStroke(const KoColor &color)
void slotRecalculatePrefilteredImage()
KisPaintDeviceSP paintDevice() const override
void writeMergeData(KisPainter *painter, KisPaintDeviceSP src, const QRect &rc) override
KUndo2Command * setColorSpace(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent=KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags=KoColorConversionTransformation::internalConversionFlags(), KoUpdater *progressUpdater=nullptr)
void testingAddKeyStroke(KisPaintDeviceSP dev, const KoColor &color, bool isTransparent=false)
bool supportsNonIndirectPainting() const override
void sigUpdateOnDirtyParent() const
KeyStrokeColors keyStrokesColors() const
void setNeedsUpdate(bool value)
QList< KisLazyFillTools::KeyStroke > fetchKeyStrokesDirect() const
void setCleanUpAmount(qreal value)
qreal edgeDetectionSize() const
void setY(qint32 y) override
QRect exactBounds() const override
QRect calculateMaskBounds(DeviceMetricPolicy policy) const
void setX(qint32 x) override
void sigKeyStrokesListChanged()
qint32 x() const override
void setFilteringOptions(const KisLazyFillTools::FilteringOptions &value)
void addKeyStroke(KisPaintDeviceSP dev, const KoColor &color)
void addRunnableJobs(const QVector< KisRunnableStrokeJobDataBase * > &list) override
void fillSelection(const QRect &rc, const KoColor &color)
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) override
QRect bounds() const override
void endStroke(KisStrokeId id) override
static KisBaseNode::Property getProperty(const KoID &id, bool state)
void makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect)
int sequenceNumber() const
void setDefaultBounds(KisDefaultBoundsBaseSP bounds)
QRect exactBounds() const
QRect extent() const
const KoColorSpace * colorSpace() const
void setParentNode(KisNodeWSP parent)
void moveTo(qint32 x, qint32 y)
QPoint offset() const
void setSelection(KisSelectionSP selection)
void setOpacityF(qreal opacity)
void bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
void setOpacityToUnit()
void setOpacityU8(quint8 opacity)
Set the opacity which is used in painting (like filling polygons)
KisPaintDeviceSP device
void setCompositeOpId(const KoCompositeOp *op)
void applyCommand(KUndo2Command *command, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
virtual void visit(KisNode *node, KisUndoAdapter *undoAdapter)=0
void convertTo(const KoColorSpace *cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
Definition KoColor.cpp:136
static KoColor createTransparent(const KoColorSpace *cs)
Definition KoColor.cpp:681
const KoColorSpace * colorSpace() const
return the current colorSpace
Definition KoColor.h:82
QString id() const
Definition KoID.cpp:63
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define bounds(x, a, b)
KisSharedPtr< KisDefaultBoundsBase > KisDefaultBoundsBaseSP
KisSharedPtr< KisPaintDevice > KisPaintDeviceSP
Definition kis_types.h:73
KisSharedPtr< KisNode > KisNodeSP
Definition kis_types.h:86
KUndo2MagicString kundo2_i18n(const char *text)
QIcon loadIcon(const QString &name)
void addJobSequential(QVector< Job * > &jobs, Func func)
qreal estimatePortionOfTransparentPixels(KisPaintDeviceSP dev, const QRect &rect, qreal samplePortion)
auto mem_equal_to(MemTypeNoRef Class::*ptr, MemType &&value)
mem_equal_to is an unary functor that compares a member of the object to a given value
Definition KisMpl.h:233
KeyStrokeAddRemoveCommand(bool add, int index, KeyStroke stroke, QList< KeyStroke > *list, KisColorizeMaskSP node, KUndo2Command *parentCommand=nullptr)
const QString & compositeOpId() const
virtual KisPaintDeviceSP original() const =0
KoProperties properties
KisImageWSP image
virtual PropertyList sectionModelProperties() const
void setCompositeOpId(const QString &compositeOpId)
quint8 opacity() const
virtual void setSectionModelProperties(const PropertyList &properties)
QStack< QRect > extentBeforeUpdateStart
KisCachedSelection cachedSelection
KisThreadSafeSignalCompressor prefilterRecalculationCompressor
void setNeedsUpdateImpl(bool value, bool requestedByUser)
bool filteredSourceValid(KisPaintDeviceSP parentDevice)
QList< KeyStroke > keyStrokes
Private(const Private &rhs, KisColorizeMask *_q)
KisThreadSafeSignalCompressor dirtyParentUpdateCompressor
Private(KisColorizeMask *_q, KisImageWSP image)
KisPaintDeviceSP coloringProjection
KisPaintDeviceSP currentKeyStrokeDevice
KisThreadSafeSignalCompressor updateCompressor
void setupTemporaryPainter(KisPainter *painter) const
void mergeToLayerImpl(KisPaintDeviceSP dst, KUndo2Command *parentCommand, const KUndo2MagicString &transactionText, int timedID, bool cleanResources, WriteLockerSP sharedWriteLock, QVector< KisRunnableStrokeJobData * > *jobs)
KisPaintDeviceSP original() const override=0
KisPaintDeviceSP projection() const override
Definition kis_mask.cc:234
void baseNodeChangedCallback() override
Definition kis_mask.cc:532
KisSelectionSP selection
Definition kis_mask.cc:44
int index(const KisNodeSP node) const
Definition kis_node.cpp:432
PositionToFilthy
Definition kis_node.h:58
@ N_ABOVE_FILTHY
Definition kis_node.h:59
KisNodeWSP parent
Definition kis_node.cpp:86
bool add(KisNodeSP newNode, KisNodeSP aboveThis, KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
Definition kis_node.cpp:469
virtual void setDirty()
Definition kis_node.cpp:577
void copyAlphaFrom(KisPaintDeviceSP src, const QRect &processRect)
KisPixelSelectionSP pixelSelection
static KoColorSpaceRegistry * instance()
const KoColorSpace * alpha8()
SetKeyStrokeColorsCommand(const QList< KeyStroke > newList, QList< KeyStroke > *list, KisColorizeMaskSP node)
KoColorConversionTransformation::Intent m_renderingIntent
SetKeyStrokesColorSpaceCommand(const KoColorSpace *dstCS, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, QList< KeyStroke > *list, KisColorizeMaskSP node)
KoColorConversionTransformation::ConversionFlags m_conversionFlags