Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_image.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
3 * SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include "kis_image.h"
9
10#include <KoConfig.h> // WORDS_BIGENDIAN
11
12#include <stdlib.h>
13#include <math.h>
14
15#include <QImage>
16#include <QPainter>
17#include <QSize>
18#include <QDateTime>
19#include <QRect>
20
21#include <klocalizedstring.h>
22
24#include "KoColor.h"
25#include "KoColorProfile.h"
28
30#include "kis_annotation.h"
31#include "kis_count_visitor.h"
32#include "kis_filter_strategy.h"
33#include "kis_group_layer.h"
35#include "kis_layer.h"
37#include "kis_paint_layer.h"
38#include "kis_projection_leaf.h"
39#include "kis_painter.h"
40#include "kis_selection.h"
41#include "kis_transaction.h"
44#include "kis_node.h"
45#include "kis_types.h"
46
47#include "kis_image_config.h"
52#include "kis_stroke_strategy.h"
54
55#include "kis_undo_stores.h"
58
71#include "kis_wrapped_rect.h"
73#include "kis_layer_utils.h"
75
76#include "kis_lod_transform.h"
77
80
82
84
86#include "kis_lockless_stack.h"
87
88#include <QtCore>
89
90#include <functional>
91
92#include "kis_time_span.h"
93
98
99#include "KisBusyWaitBroker.h"
100#include <KisStaticInitializer.h>
102
103
104// #define SANITY_CHECKS
105
106#ifdef SANITY_CHECKS
107#define SANITY_CHECK_LOCKED(name) \
108 if (!locked()) warnKrita() << "Locking policy failed:" << name \
109 << "has been called without the image" \
110 "being locked";
111#else
112#define SANITY_CHECK_LOCKED(name)
113#endif
114
116 qRegisterMetaType<KisImageSP>("KisImageSP");
117}
118
120{
121public:
122 KisImagePrivate(KisImage *_q, qint32 w, qint32 h,
123 const KoColorSpace *c,
124 KisUndoStore *undo,
125 KisImageAnimationInterface *_animationInterface)
126 : q(_q)
127 , lockedForReadOnly(false)
128 , width(w)
129 , height(h)
130 , colorSpace(c ? c : KoColorSpaceRegistry::instance()->rgb8())
132 , isolateLayer(false)
133 , isolateGroup(false)
134 , undoStore(undo ? undo : new KisDumbUndoStore())
135 , legacyUndoAdapter(undoStore.data(), _q)
137 , signalRouter(_q)
138 , animationInterface(_animationInterface)
139 , scheduler(_q, _q)
140 , axesCenter(QPointF(0.5, 0.5))
141 {
142 {
143 KisImageConfig cfg(true);
144 if (cfg.enableProgressReporting()) {
146 }
147
148 // Each of these lambdas defines a new factory function.
150 [=](bool forgettable) {
151 return KisLodSyncPair(
154 });
155
157 [=]() {
159
164
165 return std::make_pair(suspend, resume);
166 });
167
169 [this] () {
170 undoStore->purgeRedoState();
171 });
172
174 [this] () {
175
177
178 bool addedUIUpdateRequestSuccessfully = false;
179
180 for (auto it = std::make_reverse_iterator(projectionUpdatesFilters.end());
181 it != std::make_reverse_iterator(projectionUpdatesFilters.begin());
182 ++it) {
183
186
187 if (iface) {
189 addedUIUpdateRequestSuccessfully = true;
190 break;
191 }
192 }
193
194 KIS_SAFE_ASSERT_RECOVER_NOOP(addedUIUpdateRequestSuccessfully);
195 });
196 }
197
198 connect(q, SIGNAL(sigImageModified()), KisMemoryStatisticsServer::instance(), SLOT(notifyImageChanged()));
199 connect(undoStore.data(), SIGNAL(historyStateChanged()), &signalRouter, SLOT(emitImageModifiedNotification()));
200 }
201
210
217 if (rootLayer->image() == q) {
219 }
220
221 if (rootLayer->graphListener() == q) {
223 }
224
226
230 delete animationInterface;
231 }
232
234
235 quint32 lockCount = 0;
237
238 qint32 width;
239 qint32 height;
240
241 double xres = 1.0;
242 double yres = 1.0;
243
246
249 KisGroupLayerSP rootLayer; // The layers are contained in here
250 KisSelectionMaskSP targetOverlaySelectionMask; // the overlay switching stroke will try to switch into this mask
253
257
260
261 QScopedPointer<KisUndoStore> undoStore;
264
266
269
270 // filters are applied in a reversed way, from rbegin() to rend()
277
279
280 QPointF axesCenter;
282
284 const QVector<QRect> &rects,
285 const QRect &cropRect,
286 KisProjectionUpdateFlags flags);
287
289
291
292 void convertImageColorSpaceImpl(const KoColorSpace *dstColorSpace,
293 bool convertLayers,
295 KoColorConversionTransformation::ConversionFlags conversionFlags);
296
298};
299
304
309
314
315KisImage::KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace *colorSpace, const QString& name)
316 : QObject(0)
317 , KisShared()
318 , m_d(new KisImagePrivate(this, width, height,
319 colorSpace, undoStore,
321{
322 // make sure KisImage belongs to the GUI thread
323 moveToThread(qApp->thread());
325
326 setObjectName(name);
327 setRootLayer(new KisGroupLayer(this, "root", OPACITY_OPAQUE_U8));
328}
329
331{
335 waitForDone();
336
337 delete m_d;
338 disconnect(); // in case Qt gets confused
339}
340
341KisImageSP KisImage::fromQImage(const QImage &image, KisUndoStore *undoStore)
342{
343 const KoColorSpace *colorSpace = 0;
344
345 switch (image.format()) {
346 case QImage::Format_Invalid:
347 case QImage::Format_Mono:
348 case QImage::Format_MonoLSB:
350 break;
351 case QImage::Format_Indexed8:
352 case QImage::Format_RGB32:
353 case QImage::Format_ARGB32:
354 case QImage::Format_ARGB32_Premultiplied:
356 break;
357 case QImage::Format_RGB16:
359 break;
360 case QImage::Format_ARGB8565_Premultiplied:
361 case QImage::Format_RGB666:
362 case QImage::Format_ARGB6666_Premultiplied:
363 case QImage::Format_RGB555:
364 case QImage::Format_ARGB8555_Premultiplied:
365 case QImage::Format_RGB888:
366 case QImage::Format_RGB444:
367 case QImage::Format_ARGB4444_Premultiplied:
368 case QImage::Format_RGBX8888:
369 case QImage::Format_RGBA8888:
370 case QImage::Format_RGBA8888_Premultiplied:
372 break;
373 case QImage::Format_BGR30:
374 case QImage::Format_A2BGR30_Premultiplied:
375 case QImage::Format_RGB30:
376 case QImage::Format_A2RGB30_Premultiplied:
378 break;
379 case QImage::Format_Alpha8:
381 break;
382 case QImage::Format_Grayscale8:
384 break;
385 case QImage::Format_Grayscale16:
387 break;
388 case QImage::Format_RGBX64:
389 case QImage::Format_RGBA64:
390 case QImage::Format_RGBA64_Premultiplied:
392 break;
393 default:
394 colorSpace = 0;
395 }
396
397 KisImageSP img = new KisImage(undoStore, image.width(), image.height(), colorSpace, i18n("Imported Image"));
398 KisPaintLayerSP layer = new KisPaintLayer(img, img->nextLayerName(), 255);
399 layer->paintDevice()->convertFromQImage(image, 0, 0, 0);
400 img->addNode(layer.data(), img->rootLayer().data());
401
402 return img;
403}
404
405KisImage *KisImage::clone(bool exactCopy)
406{
407 return new KisImage(*this, 0, exactCopy);
408}
409
411{
413}
414
415void KisImage::copyFromImageImpl(const KisImage &rhs, int policy)
416{
417 // make sure we choose exactly one from REPLACE and CONSTRUCT
418 KIS_ASSERT_RECOVER_RETURN(bool(policy & REPLACE) != bool(policy & CONSTRUCT));
419
432 const bool sizeChanged = m_d->width != rhs.width() || m_d->height != rhs.height();
433 const bool colorSpaceChanged = *m_d->colorSpace != *rhs.colorSpace();
434 const bool resolutionChanged = m_d->xres != rhs.m_d->xres || m_d->yres != rhs.m_d->yres;
435
436 if (sizeChanged) {
437 m_d->width = rhs.width();
438 m_d->height = rhs.height();
439 }
440
441 if (colorSpaceChanged) {
442 m_d->colorSpace = rhs.colorSpace();
443 }
444
445 if (resolutionChanged) {
446 m_d->xres = rhs.m_d->xres;
447 m_d->yres = rhs.m_d->yres;
448 }
449
450 // from KisImage::KisImage(const KisImage &, KisUndoStore *, bool)
451 setObjectName(rhs.objectName());
452
453 KisNodeSP oldRoot = this->root();
454 KisNodeSP newRoot = rhs.root()->clone();
455 newRoot->setGraphListener(this);
456 newRoot->setImage(this);
457
458 m_d->rootLayer = dynamic_cast<KisGroupLayer*>(newRoot.data());
459 setRoot(newRoot);
460
461 if (oldRoot) {
462 oldRoot->setImage(0);
463 oldRoot->setGraphListener(0);
464 oldRoot->disconnect();
465 }
466
467 // only when replacing do we need to Q_EMIT signals
468#define EMIT_IF_NEEDED if (!(policy & REPLACE)) {} else emit
469
470 if (sizeChanged) {
471 EMIT_IF_NEEDED sigSizeChanged(QPointF(), QPointF());
472 }
473 if (colorSpaceChanged) {
475 }
476 if (resolutionChanged) {
478 }
479
481
482 if (rhs.m_d->proofingConfig) {
484 if (policy & REPLACE) {
485 setProofingConfiguration(proofingConfig);
486 } else {
487 m_d->proofingConfig = proofingConfig;
488 }
489 }
490
491 bool exactCopy = policy & EXACT_COPY;
492
493 if (exactCopy || rhs.m_d->isolationRootNode || rhs.m_d->overlaySelectionMask) {
496
497 QQueue<KisNodeSP> linearizedNodes;
499 [&linearizedNodes](KisNodeSP node) {
500 linearizedNodes.enqueue(node);
501 });
503 [&linearizedNodes, exactCopy, &rhs, this](KisNodeSP node) {
504 KisNodeSP refNode = linearizedNodes.dequeue();
505
506 if (exactCopy) {
507 node->setUuid(refNode->uuid());
508 }
509
510 if (rhs.m_d->isolationRootNode &&
511 rhs.m_d->isolationRootNode == refNode) {
512 m_d->isolationRootNode = node;
513 }
514
515 if (rhs.m_d->overlaySelectionMask &&
516 KisNodeSP(rhs.m_d->overlaySelectionMask) == refNode) {
517 m_d->targetOverlaySelectionMask = dynamic_cast<KisSelectionMask*>(node.data());
520 }
521
522
523 // Re-establish DefaultBounds Instances for Existing Nodes
524 // This is a workaround for copy-constructors failing to pass
525 // proper DefaultBounds due to either lacking image data on construction
526 // We should change the way "DefaultBounds" works to try to make it
527 // safer for threading races.
528 using KeyframeChannelContainer = QMap<QString, KisKeyframeChannel*>;
529 KeyframeChannelContainer keyframeChannels = node->keyframeChannels();
530 for (KeyframeChannelContainer::iterator i = keyframeChannels.begin();
531 i != keyframeChannels.end(); i++) {
532 keyframeChannels[i.key()]->setNode(node);
533 }
534 });
535 }
536
538 [](KisNodeSP node) {
539 dbgImage << "Node: " << (void *)node.data();
540 });
541
542
543
544 m_d->compositions.clear();
545
546 Q_FOREACH (KisLayerCompositionSP comp, rhs.m_d->compositions) {
547 m_d->compositions << toQShared(new KisLayerComposition(*comp, this));
548 }
549
551
552 vKisAnnotationSP newAnnotations;
553 Q_FOREACH (KisAnnotationSP annotation, rhs.m_d->annotations) {
554 newAnnotations << annotation->clone();
555 }
556 m_d->annotations = newAnnotations;
557
561
562#undef EMIT_IF_NEEDED
563}
564
565KisImage::KisImage(const KisImage& rhs, KisUndoStore *undoStore, bool exactCopy)
566 : KisNodeFacade(),
568 KisShared(),
569 m_d(new KisImagePrivate(this,
570 rhs.width(), rhs.height(),
571 rhs.colorSpace(),
572 undoStore ? undoStore : new KisDumbUndoStore(),
573 new KisImageAnimationInterface(*rhs.animationInterface(), this)))
574{
575 // make sure KisImage belongs to the GUI thread
576 moveToThread(qApp->thread());
578
579 copyFromImageImpl(rhs, CONSTRUCT | (exactCopy ? EXACT_COPY : 0));
580}
581
582void KisImage::aboutToAddANode(KisNode *parent, int index)
583{
585 SANITY_CHECK_LOCKED("aboutToAddANode");
586}
587
588void KisImage::nodeHasBeenAdded(KisNode *parent, int index, KisNodeAdditionFlags flags)
589{
590 KisNodeGraphListener::nodeHasBeenAdded(parent, index, flags);
591
593 QMap<QString, KisKeyframeChannel*> chans = node->keyframeChannels();
594 Q_FOREACH(KisKeyframeChannel* chan, chans.values()) {
595 chan->setNode(node);
596 this->keyframeChannelHasBeenAdded(node.data(), chan);
597 }
598 });
599
600 SANITY_CHECK_LOCKED("nodeHasBeenAdded");
601 m_d->signalRouter.emitNodeHasBeenAdded(parent, index, flags);
602}
603
604void KisImage::aboutToRemoveANode(KisNode *parent, int index)
605{
606 KisNodeSP deletedNode = parent->at(index);
607 if (!dynamic_cast<KisSelectionMask*>(deletedNode.data()) &&
608 deletedNode == m_d->isolationRootNode) {
609
611 }
612
614 QMap<QString, KisKeyframeChannel*> chans = node->keyframeChannels();
615 Q_FOREACH(KisKeyframeChannel* chan, chans.values()) {
616 this->keyframeChannelAboutToBeRemoved(node.data(), chan);
617 }
618 });
619
621
622 SANITY_CHECK_LOCKED("aboutToRemoveANode");
624}
625
631
636
638{
639 if (m_d->targetOverlaySelectionMask == mask) return;
640
642
643 struct UpdateOverlaySelectionStroke : public KisSimpleStrokeStrategy {
644 UpdateOverlaySelectionStroke(KisImageSP image)
645 : KisSimpleStrokeStrategy(QLatin1String("update-overlay-selection-mask"), kundo2_noi18n("update-overlay-selection-mask")),
646 m_image(image)
647 {
648 this->enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
649 setClearsRedoOnStart(false);
650 }
651
652 void initStrokeCallback() override {
653 KisSelectionMaskSP oldMask = m_image->m_d->overlaySelectionMask;
654 KisSelectionMaskSP newMask = m_image->m_d->targetOverlaySelectionMask;
655 if (oldMask == newMask) return;
656
657 KIS_SAFE_ASSERT_RECOVER_RETURN(!newMask || static_cast<KisImage*>(newMask->graphListener()) == m_image);
658
659 m_image->m_d->overlaySelectionMask = newMask;
660
661 if (oldMask || newMask) {
662 m_image->m_d->rootLayer->notifyChildMaskChanged();
663 }
664
665 if (oldMask) {
666 const QRect oldMaskRect = oldMask->graphListener() ? oldMask->extent() : m_image->bounds();
667 m_image->m_d->rootLayer->setDirtyDontResetAnimationCache(oldMaskRect);
668 }
669
670 if (newMask) {
671 newMask->setDirty();
672 }
673
674 m_image->undoAdapter()->emitSelectionChanged();
675 }
676
677 private:
678 KisImageSP m_image;
679 };
680
681 KisStrokeId id = startStroke(new UpdateOverlaySelectionStroke(this));
682 endStroke(id);
683}
684
689
694
696{
697 KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
698 if (selectionMask) {
699 return selectionMask->selection();
700 } else {
701 return 0;
702 }
703}
704
709
714
715QString KisImage::nextLayerName(const QString &_baseName) const
716{
717 QString baseName = _baseName;
718
719 int numLayers = 0;
720 int maxLayerIndex = 0;
721 QRegularExpression numberedLayerRegexp(".* (\\d+)$");
723 [&numLayers, &maxLayerIndex, &numberedLayerRegexp] (KisNodeSP node) {
724 if (node->inherits("KisLayer")) {
725 QRegularExpressionMatch match = numberedLayerRegexp.match(node->name());
726
727 if (match.hasMatch()) {
728 maxLayerIndex = qMax(maxLayerIndex, match.captured(1).toInt());
729 }
730 numLayers++;
731 }
732 });
733
734 // special case if there is only root node
735 if (numLayers == 1) {
736 return i18nc("Name for the bottom-most layer in the layerstack", "Background");
737 }
738
739 if (baseName.isEmpty()) {
740 baseName = i18n("Paint Layer");
741 }
742
743 return QString("%1 %2").arg(baseName).arg(maxLayerIndex + 1);
744}
745
750
752{
753 return m_d->lockCount != 0;
754}
755
756void KisImage::barrierLock(bool readOnly)
757{
758 if (!locked()) {
763 m_d->lockedForReadOnly = readOnly;
764 } else {
765 m_d->lockedForReadOnly &= readOnly;
766 }
767
768 m_d->lockCount++;
769}
770
771bool KisImage::tryBarrierLock(bool readOnly)
772{
773 bool result = true;
774
775 if (!locked()) {
776 result = m_d->scheduler.tryBarrierLock();
777 m_d->lockedForReadOnly = readOnly;
778 }
779
780 if (result) {
781 m_d->lockCount++;
782 m_d->lockedForReadOnly &= readOnly;
783 }
784
785 return result;
786}
787
788bool KisImage::isIdle(bool allowLocked)
789{
790 return (allowLocked || !locked()) && m_d->scheduler.isIdle();
791}
792
804
806{
807 Q_ASSERT(locked());
808
809 if (locked()) {
810 m_d->lockCount--;
811
812 if (m_d->lockCount == 0) {
814 }
815 }
816}
817
822
827
828void KisImage::setSize(const QSize& size)
829{
830 m_d->width = size.width();
831 m_d->height = size.height();
832}
833
834void KisImage::resizeImageImpl(const QRect& newRect, bool cropLayers)
835{
836 if (newRect == bounds() && !cropLayers) return;
837
838 KUndo2MagicString actionName = cropLayers ?
839 kundo2_i18n("Crop Image") :
840 kundo2_i18n("Resize Image");
841
842 KisImageSignalVector emitSignals;
843 emitSignals << ComplexSizeChangedSignal(newRect, newRect.size());
844
845 KisCropSavedExtraData *extraData =
846 new KisCropSavedExtraData(cropLayers ?
849 newRect);
850
851 KisProcessingApplicator applicator(this, m_d->rootLayer,
854 emitSignals, actionName, extraData);
855
856 if (cropLayers || !newRect.topLeft().isNull()) {
857 KisProcessingVisitorSP visitor =
858 new KisCropProcessingVisitor(newRect, cropLayers, true);
859 applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
860 }
861 applicator.applyCommand(new KisImageResizeCommand(this, newRect.size()));
862 applicator.end();
863}
864
865void KisImage::resizeImage(const QRect& newRect)
866{
867 resizeImageImpl(newRect, false);
868}
869
870void KisImage::cropImage(const QRect& newRect)
871{
872 resizeImageImpl(newRect, true);
873}
874
875void KisImage::purgeUnusedData(bool isCancellable)
876{
884 struct PurgeUnusedDataStroke : public KisRunnableBasedStrokeStrategy {
885 PurgeUnusedDataStroke(KisImageSP image, bool isCancellable)
886 : KisRunnableBasedStrokeStrategy(QLatin1String("purge-unused-data"),
887 kundo2_noi18n("purge-unused-data")),
888 m_image(image)
889 {
890 this->enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
891 this->enableJob(JOB_DOSTROKE, true);
892 setClearsRedoOnStart(false);
893 setRequestsOtherStrokesToEnd(!isCancellable);
894 setCanForgetAboutMe(isCancellable);
895 }
896
897 void initStrokeCallback() override
898 {
899 KisPaintDeviceList deviceList;
901
903 [&deviceList](KisNodeSP node) {
904 deviceList << node->getLodCapableDevices();
905 });
906
910
911 Q_FOREACH (KisPaintDeviceSP device, deviceList) {
912 if (!device) continue;
913
915 [device] () {
916 const_cast<KisPaintDevice*>(device.data())->purgeDefaultPixels();
917 });
918 }
919
920 addMutatedJobs(jobsData);
921 }
922
923 private:
924 KisImageSP m_image;
925 };
926
927 KisStrokeId id = startStroke(new PurgeUnusedDataStroke(this, isCancellable));
928 endStroke(id);
929}
930
931void KisImage::cropNode(KisNodeSP node, const QRect& newRect, const bool activeFrameOnly)
932{
933 const bool isLayer = qobject_cast<KisLayer*>(node.data());
934 KUndo2MagicString actionName = isLayer ?
935 kundo2_i18n("Crop Layer") :
936 kundo2_i18n("Crop Mask");
937
938 KisImageSignalVector emitSignals;
939
940 KisCropSavedExtraData *extraData =
942 newRect, node);
943
944 KisProcessingApplicator applicator(this, node,
946 emitSignals, actionName, extraData);
947
948 KisProcessingVisitorSP visitor =
949 new KisCropProcessingVisitor(newRect, true, false);
950
951 if (node->isAnimated() && activeFrameOnly) {
952 // Crop active frame..
953 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
954 } else {
955 // Crop all frames..
957 }
958 applicator.end();
959}
960
961void KisImage::scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy)
962{
963 bool resolutionChanged = !qFuzzyCompare(xRes(), xres) || !qFuzzyCompare(yRes(), yres);
964 bool sizeChanged = size != this->size();
965
966 if (!resolutionChanged && !sizeChanged) return;
967
968 KisImageSignalVector emitSignals;
969 if (resolutionChanged) emitSignals << ResolutionChangedSignal;
970 if (sizeChanged) emitSignals << ComplexSizeChangedSignal(bounds(), size);
971
972 KUndo2MagicString actionName = sizeChanged ?
973 kundo2_i18n("Scale Image") :
974 kundo2_i18n("Change Image Resolution");
975
976 KisProcessingApplicator::ProcessingFlags signalFlags =
977 (resolutionChanged || sizeChanged) ?
980
981 KisProcessingApplicator applicator(this, m_d->rootLayer,
983 emitSignals, actionName);
984
985 qreal sx = qreal(size.width()) / this->size().width();
986 qreal sy = qreal(size.height()) / this->size().height();
987
988 QTransform shapesCorrection;
989
990 if (resolutionChanged) {
991 shapesCorrection = QTransform::fromScale(xRes() / xres, yRes() / yres);
992 }
993
994 KisProcessingVisitorSP visitor =
996 0, 0,
997 0,
998 0, 0,
999 filterStrategy,
1000 shapesCorrection);
1001
1003
1004 if (resolutionChanged) {
1005 KUndo2Command *parent =
1007 new KisImageSetResolutionCommand(this, xres, yres, parent);
1008 applicator.applyCommand(parent);
1009 }
1010
1011 if (sizeChanged) {
1012 applicator.applyCommand(new KisImageResizeCommand(this, size));
1013 }
1014
1015 applicator.end();
1016}
1017
1018void KisImage::scaleNode(KisNodeSP node, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection)
1019{
1020 scaleNodes(KisNodeList{node}, center, scaleX, scaleY, filterStrategy, selection);
1021}
1022void KisImage::scaleNodes(KisNodeList nodes, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection)
1023{
1024 KUndo2MagicString actionName(kundo2_i18np("Scale Layer", "Scale %1 Layers", nodes.size()));
1025 KisImageSignalVector emitSignals;
1026
1027 QPointF offset;
1028 {
1029 KisTransformWorker worker(0,
1030 scaleX, scaleY,
1031 0, 0,
1032 0.0,
1033 0, 0, 0, 0);
1034 QTransform transform = worker.transform();
1035
1036 offset = center - transform.map(center);
1037 }
1038
1039 KisProcessingApplicator applicator(this, nodes,
1041 emitSignals, actionName);
1042
1044 new KisTransformProcessingVisitor(scaleX, scaleY,
1045 0, 0,
1046 0,
1047 offset.x(), offset.y(),
1048 filterStrategy);
1049
1050 visitor->setSelection(selection);
1051
1052 if (selection) {
1053 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1054 } else {
1056 }
1057
1058 applicator.end();
1059}
1060
1062 KisNodeSP rootNode,
1063 double radians,
1064 bool resizeImage,
1065 KisSelectionSP selection)
1066{
1067 rotateImpl(actionName, KisNodeList{rootNode}, radians, resizeImage, selection);
1068}
1070 KisNodeList nodes,
1071 double radians,
1072 bool resizeImage,
1073 KisSelectionSP selection)
1074{
1075 // we can either transform (and resize) the whole image or
1076 // transform a selection, we cannot do both at the same time
1077 KIS_SAFE_ASSERT_RECOVER(!(bool(selection) && resizeImage)) {
1078 selection = 0;
1079 }
1080
1081 QRect baseBounds;
1082 if (resizeImage) {
1083 baseBounds = bounds();
1084 }
1085 else if (selection) {
1086 baseBounds = selection->selectedExactRect();
1087 }
1088 else {
1089 Q_FOREACH(KisNodeSP node, nodes) {
1090 baseBounds = baseBounds.united(node->exactBounds());
1091 }
1092 }
1093
1094 QPointF offset;
1095 QSize newSize;
1096
1097 {
1098 KisTransformWorker worker(0,
1099 1.0, 1.0,
1100 0, 0,
1101 radians,
1102 0, 0, 0, 0);
1103 QTransform transform = worker.transform();
1104
1105 if (resizeImage) {
1106 QRect newRect = transform.mapRect(baseBounds);
1107 newSize = newRect.size();
1108 offset = -newRect.topLeft();
1109 }
1110 else {
1111 QPointF origin = QRectF(baseBounds).center();
1112
1113 newSize = size();
1114 offset = -(transform.map(origin) - origin);
1115 }
1116 }
1117
1118 bool sizeChanged = resizeImage &&
1119 (newSize.width() != baseBounds.width() ||
1120 newSize.height() != baseBounds.height());
1121
1122 // These signals will be emitted after processing is done
1123 KisImageSignalVector emitSignals;
1124 if (sizeChanged) emitSignals << ComplexSizeChangedSignal(baseBounds, newSize);
1125
1126 // These flags determine whether updates are transferred to the UI during processing
1127 KisProcessingApplicator::ProcessingFlags signalFlags =
1128 sizeChanged ?
1131
1132
1133 KisProcessingApplicator applicator(this, nodes,
1135 emitSignals, actionName);
1136
1138
1140 new KisTransformProcessingVisitor(1.0, 1.0, 0.0, 0.0,
1141 radians,
1142 offset.x(), offset.y(),
1143 filter);
1144 if (selection) {
1145 visitor->setSelection(selection);
1146 }
1147
1148 if (selection) {
1149 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1150 } else {
1152 }
1153
1154 if (sizeChanged) {
1155 applicator.applyCommand(new KisImageResizeCommand(this, newSize));
1156 }
1157 applicator.end();
1158}
1159
1160
1161void KisImage::rotateImage(double radians)
1162{
1163 rotateImpl(kundo2_i18n("Rotate Image"), root(), radians, true, 0);
1164}
1165
1166void KisImage::rotateNode(KisNodeSP node, double radians, KisSelectionSP selection)
1167{
1168 rotateNodes(KisNodeList{node}, radians, selection);
1169}
1170void KisImage::rotateNodes(KisNodeList nodes, double radians, KisSelectionSP selection)
1171{
1172 if (nodes.size() == 1 && nodes[0]->inherits("KisMask")) {
1173 rotateImpl(kundo2_i18n("Rotate Mask"), nodes, radians, false, selection);
1174 }
1175 else {
1176 rotateImpl(kundo2_i18np("Rotate Layer", "Rotate %1 Layers", nodes.size()), nodes, radians, false, selection);
1177 }
1178}
1179
1181 KisNodeSP rootNode,
1182 bool resizeImage,
1183 double angleX, double angleY,
1184 KisSelectionSP selection)
1185{
1186 shearImpl(actionName, KisNodeList{rootNode}, resizeImage, angleX, angleY, selection);
1187}
1189 KisNodeList nodes,
1190 bool resizeImage,
1191 double angleX, double angleY,
1192 KisSelectionSP selection)
1193{
1194 QRect baseBounds;
1195 if (resizeImage) {
1196 baseBounds = bounds();
1197 }
1198 else if (selection) {
1199 baseBounds = selection->selectedExactRect();
1200 }
1201 else {
1202 Q_FOREACH(KisNodeSP node, nodes) {
1203 baseBounds = baseBounds.united(node->exactBounds());
1204 }
1205 }
1206 const QPointF origin = QRectF(baseBounds).center();
1207
1208 //angleX, angleY are in degrees
1209 const qreal pi = 3.1415926535897932385;
1210 const qreal deg2rad = pi / 180.0;
1211
1212 qreal tanX = tan(angleX * deg2rad);
1213 qreal tanY = tan(angleY * deg2rad);
1214
1215 QPointF offset;
1216 QSize newSize;
1217
1218 {
1219 KisTransformWorker worker(0,
1220 1.0, 1.0,
1221 tanX, tanY,
1222 0,
1223 0, 0, 0, 0);
1224
1225 QRect newRect = worker.transform().mapRect(baseBounds);
1226 newSize = newRect.size();
1227 if (resizeImage) offset = -newRect.topLeft();
1228 else offset = origin - worker.transform().map(origin);
1229 }
1230
1231 if (newSize == baseBounds.size()) return;
1232
1233 KisImageSignalVector emitSignals;
1234 if (resizeImage) emitSignals << ComplexSizeChangedSignal(baseBounds, newSize);
1235
1236 KisProcessingApplicator::ProcessingFlags signalFlags =
1239
1240 KisProcessingApplicator applicator(this, nodes,
1241 signalFlags,
1242 emitSignals, actionName);
1243
1245
1247 new KisTransformProcessingVisitor(1.0, 1.0,
1248 tanX, tanY,
1249 0,
1250 offset.x(), offset.y(),
1251 filter);
1252
1253 if (selection) {
1254 visitor->setSelection(selection);
1255 }
1256
1257 if (selection) {
1258 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1259 } else {
1261 }
1262
1263 if (resizeImage) {
1264 applicator.applyCommand(new KisImageResizeCommand(this, newSize));
1265 }
1266
1267 applicator.end();
1268}
1269
1270void KisImage::shearNode(KisNodeSP node, double angleX, double angleY, KisSelectionSP selection)
1271{
1272 shearNodes(KisNodeList{node}, angleX, angleY, selection);
1273}
1274void KisImage::shearNodes(KisNodeList nodes, double angleX, double angleY, KisSelectionSP selection)
1275{
1276 if (nodes.size() == 1 && nodes[0]->inherits("KisMask")) {
1277 shearImpl(kundo2_i18n("Shear Mask"), nodes, false,
1278 angleX, angleY, selection);
1279 }
1280 else {
1281 shearImpl(kundo2_i18np("Shear Layer", "Shear %1 Layers", nodes.size()), nodes, false,
1282 angleX, angleY, selection);
1283 }
1284}
1285
1286void KisImage::shear(double angleX, double angleY)
1287{
1288 shearImpl(kundo2_i18n("Shear Image"), m_d->rootLayer, true,
1289 angleX, angleY, 0);
1290}
1291
1293 const KoColorSpace *dstColorSpace,
1295 KoColorConversionTransformation::ConversionFlags conversionFlags)
1296{
1297 if (!node->projectionLeaf()->isLayer()) return;
1298 // must not be an image root, use convertImageColorSpace() for that:
1299 KIS_SAFE_ASSERT_RECOVER_RETURN(!node->image() || (node.data() != node->image()->rootLayer().data()));
1300
1301 const KoColorSpace *srcColorSpace = node->colorSpace();
1302
1303 if (!dstColorSpace || *srcColorSpace == *dstColorSpace) return;
1304
1305 KUndo2MagicString actionName =
1306 kundo2_i18n("Convert Layer Color Space");
1307
1308 KisImageSignalVector emitSignals;
1309
1310 KisProcessingApplicator applicator(this, node,
1312 emitSignals, actionName);
1313
1314 applicator.applyVisitor(
1316 srcColorSpace, dstColorSpace,
1317 renderingIntent, conversionFlags),
1319
1320 applicator.end();
1321}
1322
1324{
1326 State initialState, KUndo2Command *parent = 0)
1327 : KisCommandUtils::FlipFlopCommand(initialState, parent),
1328 m_cs(cs),
1329 m_image(image)
1330 {
1331 }
1332
1333 void partA() override {
1334 KisImageSP image = m_image;
1335
1336 if (image) {
1338 }
1339 }
1340
1341private:
1344};
1345
1347 bool convertLayers,
1349 KoColorConversionTransformation::ConversionFlags conversionFlags)
1350{
1351 const KoColorSpace *srcColorSpace = this->colorSpace;
1352
1353 if (!dstColorSpace || *srcColorSpace == *dstColorSpace) return;
1354
1355 const KUndo2MagicString actionName =
1356 convertLayers ?
1357 kundo2_i18n("Convert Image Color Space") :
1358 kundo2_i18n("Convert Projection Color Space");
1359
1360 KisImageSignalVector emitSignals;
1361 emitSignals << ColorSpaceChangedSignal;
1362
1363 KisProcessingApplicator::ProcessingFlags flags = KisProcessingApplicator::NO_UI_UPDATES;
1364 if (convertLayers) {
1366 }
1367
1368 KisProcessingApplicator applicator(q, this->rootLayer,
1369 flags,
1370 emitSignals, actionName);
1371
1372 applicator.applyCommand(
1374 KisImageWSP(q),
1377
1378 applicator.applyVisitor(
1380 srcColorSpace, dstColorSpace,
1381 renderingIntent, conversionFlags),
1383
1384 applicator.applyCommand(
1386 KisImageWSP(q),
1389
1390
1391 applicator.end();
1392}
1393
1396 KoColorConversionTransformation::ConversionFlags conversionFlags)
1397{
1398 m_d->convertImageColorSpaceImpl(dstColorSpace, true, renderingIntent, conversionFlags);
1399}
1400
1407
1409{
1410 const KUndo2MagicString actionName = kundo2_i18n("Unify Layers Color Space");
1411
1412 KisImageSignalVector emitSignals;
1413
1414 KisProcessingApplicator::ProcessingFlags flags =
1416
1417 KisProcessingApplicator applicator(this, m_d->rootLayer,
1418 flags,
1419 emitSignals, actionName);
1420
1421 // src and dst color spaces coincide, since we should just unify
1422 // all our layers
1423 applicator.applyVisitor(
1429
1430 applicator.end();
1431}
1432
1434{
1435 const KoColorSpace *srcColorSpace = node->colorSpace();
1436
1437 if (!node->projectionLeaf()->isLayer()) return false;
1438 if (!profile || *srcColorSpace->profile() == *profile) return false;
1439
1440 KUndo2MagicString actionName = kundo2_i18n("Assign Profile to Layer");
1441
1442 KisImageSignalVector emitSignals;
1443
1444 const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
1445 if (!dstColorSpace) return false;
1446
1447 KisProcessingApplicator applicator(this, node,
1450 emitSignals, actionName);
1451
1452 applicator.applyVisitor(
1454 srcColorSpace, dstColorSpace),
1456
1457 applicator.end();
1458
1459 return true;
1460}
1461
1462
1463bool KisImage::assignImageProfile(const KoColorProfile *profile, bool blockAllUpdates)
1464{
1465 if (!profile) return false;
1466
1467 const KoColorSpace *srcColorSpace = m_d->colorSpace;
1468 bool imageProfileIsSame = *srcColorSpace->profile() == *profile;
1469
1470 imageProfileIsSame &=
1472 [profile] (KisNodeSP node) {
1473 return *node->colorSpace()->profile() != *profile;
1474 });
1475
1476 if (imageProfileIsSame) {
1477 dbgImage << "Trying to set the same image profile again" << ppVar(srcColorSpace->profile()->name()) << ppVar(profile->name());
1478 return true;
1479 }
1480
1481 KUndo2MagicString actionName = kundo2_i18n("Assign Profile");
1482
1483 KisImageSignalVector emitSignals;
1484 emitSignals << ProfileChangedSignal;
1485
1486 const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
1487 if (!dstColorSpace) return false;
1488
1489 KisProcessingApplicator applicator(this, m_d->rootLayer,
1491 (!blockAllUpdates ?
1494 emitSignals, actionName);
1495
1496 applicator.applyCommand(
1498 KisImageWSP(this),
1501
1502 applicator.applyVisitor(
1504 srcColorSpace, dstColorSpace),
1506
1507 applicator.applyCommand(
1509 KisImageWSP(this),
1512
1513
1514 applicator.end();
1515
1516 return true;
1517}
1518
1523
1525{
1526 return m_d->colorSpace;
1527}
1528
1530{
1531 return colorSpace()->profile();
1532}
1533
1534double KisImage::xRes() const
1535{
1536 return m_d->xres;
1537}
1538
1539double KisImage::yRes() const
1540{
1541 return m_d->yres;
1542}
1543
1544void KisImage::setResolution(double xres, double yres)
1545{
1546 if (xres > 0) {
1547 m_d->xres = xres;
1548 } else {
1549 qWarning() << "WARNING: Ignoring attempt to set image x resolution <= 0 (" << xres << ")!";
1550 }
1551
1552 if (yres > 0) {
1553 m_d->yres = yres;
1554 } else {
1555 qWarning() << "WARNING: Ignoring attempt to set image y resolution <= 0 (" << yres << ")!";
1556 }
1557}
1558
1559QPointF KisImage::documentToPixel(const QPointF &documentCoord) const
1560{
1561 return QPointF(documentCoord.x() * xRes(), documentCoord.y() * yRes());
1562}
1563
1564QPoint KisImage::documentToImagePixelFloored(const QPointF &documentCoord) const
1565{
1566 QPointF pixelCoord = documentToPixel(documentCoord);
1567 return QPoint(qFloor(pixelCoord.x()), qFloor(pixelCoord.y()));
1568}
1569
1570QRectF KisImage::documentToPixel(const QRectF &documentRect) const
1571{
1572 return QRectF(documentToPixel(documentRect.topLeft()), documentToPixel(documentRect.bottomRight()));
1573}
1574
1575QPointF KisImage::pixelToDocument(const QPointF &pixelCoord) const
1576{
1577 return QPointF(pixelCoord.x() / xRes(), pixelCoord.y() / yRes());
1578}
1579
1580QPointF KisImage::pixelToDocument(const QPoint &pixelCoord) const
1581{
1582 return QPointF((pixelCoord.x() + 0.5) / xRes(), (pixelCoord.y() + 0.5) / yRes());
1583}
1584
1585QRectF KisImage::pixelToDocument(const QRectF &pixelCoord) const
1586{
1587 return QRectF(pixelToDocument(pixelCoord.topLeft()), pixelToDocument(pixelCoord.bottomRight()));
1588}
1589
1590qint32 KisImage::width() const
1591{
1592 return m_d->width;
1593}
1594
1595qint32 KisImage::height() const
1596{
1597 return m_d->height;
1598}
1599
1601{
1602 Q_ASSERT(m_d->rootLayer);
1603 return m_d->rootLayer;
1604}
1605
1607{
1608 if (m_d->isolationRootNode) {
1609 return m_d->isolationRootNode->projection();
1610 }
1611
1612 Q_ASSERT(m_d->rootLayer);
1614 Q_ASSERT(projection);
1615 return projection;
1616}
1617
1618qint32 KisImage::nlayers() const
1619{
1620 QStringList list;
1621 list << "KisLayer";
1622
1623 KisCountVisitor visitor(list, KoProperties());
1624 m_d->rootLayer->accept(visitor);
1625 return visitor.count();
1626}
1627
1629{
1630 QStringList list;
1631 list << "KisLayer";
1632 KoProperties properties;
1633 properties.setProperty("visible", false);
1634 KisCountVisitor visitor(list, properties);
1635 m_d->rootLayer->accept(visitor);
1636
1637 return visitor.count();
1638}
1639
1641{
1642 const QStringList list = {"KisLayer"};
1643
1644 KoProperties koProperties;
1645 KisCountVisitor visitor(list, koProperties);
1646 const QList<KisNodeSP> childNodes = m_d->rootLayer->childNodes(list, koProperties);
1647 for (KisNodeSP childNode: childNodes) {
1648 childNode->accept(visitor);
1649 }
1650 return visitor.count();
1651}
1652
1654{
1655 KisLayerUtils::flattenImage(this, activeNode);
1656}
1657
1659{
1660 KisLayerUtils::mergeMultipleNodes(this, mergedNodes, putAfter);
1661}
1662
1664{
1665 KisLayerUtils::mergeDown(this, layer, strategy);
1666}
1667
1669{
1670 KisLayerUtils::flattenLayer(this, layer);
1671}
1672
1678
1679QImage KisImage::convertToQImage(QRect imageRect,
1680 const KoColorProfile * profile)
1681{
1682 qint32 x;
1683 qint32 y;
1684 qint32 w;
1685 qint32 h;
1686 imageRect.getRect(&x, &y, &w, &h);
1687 return convertToQImage(x, y, w, h, profile);
1688}
1689
1691 qint32 y,
1692 qint32 w,
1693 qint32 h,
1694 const KoColorProfile * profile)
1695{
1697 if (!dev) return QImage();
1698 QImage image = dev->convertToQImage(const_cast<KoColorProfile*>(profile), x, y, w, h,
1701
1702 return image;
1703}
1704
1705
1706
1707QImage KisImage::convertToQImage(const QSize& scaledImageSize, const KoColorProfile *profile)
1708{
1709 if (scaledImageSize.isEmpty()) {
1710 return QImage();
1711 }
1712
1714 KisPainter gc;
1715 gc.copyAreaOptimized(QPoint(0, 0), projection(), dev, bounds());
1716 gc.end();
1717 double scaleX = qreal(scaledImageSize.width()) / width();
1718 double scaleY = qreal(scaledImageSize.height()) / height();
1719
1720
1721 if (scaleX < 1.0/256 || scaleY < 1.0/256) {
1722 // quick checking if we're not trying to scale too much
1723 // convertToQImage uses KisFixedPoint values, which means that the scale cannot be smaller than 1/2^8
1724 // BUG:432182
1725 // FIXME: would be best to extend KisFixedPoint instead
1726 return convertToQImage(size(), profile).scaled(scaledImageSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
1727 }
1728
1729 KoDummyUpdaterHolder updaterHolder;
1730 QPointer<KoUpdater> updater = updaterHolder.updater();
1731
1732 KisTransformWorker worker(dev, scaleX, scaleY, 0.0, 0.0, 0.0, 0.0, 0.0, updater, KisFilterStrategyRegistry::instance()->value("Bicubic"));
1733 worker.run();
1734
1735 return dev->convertToQImage(profile);
1736}
1741
1742QRect KisImage::bounds() const
1743{
1744 return QRect(0, 0, width(), height());
1745}
1746
1748{
1749 QRect boundRect = bounds();
1750
1751 const int lod = currentLevelOfDetail();
1752 if (lod > 0) {
1753 KisLodTransform t(lod);
1754 boundRect = t.map(boundRect);
1755 }
1756
1757 return boundRect;
1758}
1759
1767
1769{
1770 return m_d->undoStore->presentCommand();
1771}
1772
1774{
1775 disconnect(m_d->undoStore.data(), SIGNAL(historyStateChanged()), &m_d->signalRouter, SLOT(emitImageModifiedNotification()));
1776
1779 m_d->undoStore.reset(undoStore);
1780
1781 connect(m_d->undoStore.data(), SIGNAL(historyStateChanged()), &m_d->signalRouter, SLOT(emitImageModifiedNotification()));
1782
1783}
1784
1786{
1787 return m_d->undoStore.data();
1788}
1789
1791{
1792 return &m_d->legacyUndoAdapter;
1793}
1794
1800
1809
1811{
1813
1815
1816 if (m_d->rootLayer) {
1818 m_d->rootLayer->setImage(0);
1819 m_d->rootLayer->disconnect();
1820
1821 KisPaintDeviceSP original = m_d->rootLayer->original();
1823 }
1824
1826 m_d->rootLayer->disconnect();
1828 m_d->rootLayer->setImage(this);
1829
1831 this->setDefaultProjectionColor(defaultProjectionColor);
1832}
1833
1835{
1836 // Find the icc annotation, if there is one
1837 vKisAnnotationSP_it it = m_d->annotations.begin();
1838 while (it != m_d->annotations.end()) {
1839 if ((*it)->type() == annotation->type()) {
1840 *it = annotation;
1842 return;
1843 }
1844 ++it;
1845 }
1846 m_d->annotations.push_back(annotation);
1848}
1849
1851{
1852 vKisAnnotationSP_it it = m_d->annotations.begin();
1853 while (it != m_d->annotations.end()) {
1854 if ((*it) && (*it)->type() == type) {
1855 return *it;
1856 }
1857 else if (!*it) {
1858 qWarning() << "Skipping deleted annotation";
1859 }
1860 ++it;
1861 }
1862 return KisAnnotationSP(0);
1863}
1864
1865void KisImage::removeAnnotation(const QString& type)
1866{
1867 vKisAnnotationSP_it it = m_d->annotations.begin();
1868 while (it != m_d->annotations.end()) {
1869 if ((*it)->type() == type) {
1870 m_d->annotations.erase(it);
1872 return;
1873 }
1874 ++it;
1875 }
1876}
1877
1882
1887
1892
1897
1905
1907{
1915 if (strokeStrategy->requestsOtherStrokesToEnd()) {
1917 }
1918
1919 return m_d->scheduler.startStroke(strokeStrategy);
1920}
1921
1923{
1924 KisImageConfig imageConfig(true);
1925 int patchWidth = imageConfig.updatePatchWidth();
1926 int patchHeight = imageConfig.updatePatchHeight();
1927
1928 for (int y = 0; y < rc.height(); y += patchHeight) {
1929 for (int x = 0; x < rc.width(); x += patchWidth) {
1930 QRect patchRect(x, y, patchWidth, patchHeight);
1931 patchRect &= rc;
1932
1933 KritaUtils::addJobConcurrent(jobs, std::bind(&KisImage::notifyProjectionUpdated, q, patchRect));
1934 }
1935 }
1936}
1937
1938bool KisImage::startIsolatedMode(KisNodeSP node, bool isolateLayer, bool isolateGroup)
1939{
1940 m_d->isolateLayer = isolateLayer;
1941 m_d->isolateGroup = isolateGroup;
1942 if ((isolateLayer || isolateGroup) == false) return false;
1943
1948 if (!node->projection()) return false;
1949
1950 struct StartIsolatedModeStroke : public KisRunnableBasedStrokeStrategy {
1951 StartIsolatedModeStroke(KisNodeSP node, KisImageSP image, bool isolateLayer, bool isolateGroup)
1952 : KisRunnableBasedStrokeStrategy(QLatin1String("start-isolated-mode"),
1953 kundo2_noi18n("start-isolated-mode")),
1954 m_newRoot(node),
1955 m_image(image),
1956 m_isolateLayer(isolateLayer),
1957 m_isolateGroup(isolateGroup)
1958 {
1959 this->enableJob(JOB_INIT, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
1960 this->enableJob(JOB_DOSTROKE, true);
1961 this->enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
1962 setClearsRedoOnStart(false);
1963 }
1964
1965 void initStrokeCallback() override {
1966 if (m_isolateLayer == false && m_isolateGroup == true) {
1967 // Isolate parent node unless node is the root note.
1968 m_newRoot = m_newRoot->parent() ? m_newRoot->parent() : m_newRoot;
1969 }
1970 // pass-though node don't have any projection prepared, so we should
1971 // explicitly regenerate it before activating isolated mode.
1972 m_newRoot->projectionLeaf()->explicitlyRegeneratePassThroughProjection();
1973 m_prevRoot = m_image->m_d->isolationRootNode;
1974
1975 const bool beforeVisibility = m_newRoot->projectionLeaf()->visible();
1976 const bool prevRootBeforeVisibility = m_prevRoot ? m_prevRoot->projectionLeaf()->visible() : false;
1977
1978 m_image->m_d->isolationRootNode = m_newRoot;
1979 Q_EMIT m_image->sigIsolatedModeChanged();
1980
1981 const bool afterVisibility = m_newRoot->projectionLeaf()->visible();
1982 const bool prevRootAfterVisibility = m_prevRoot ? m_prevRoot->projectionLeaf()->visible() : false;
1983
1984 m_newRootNeedsFullRefresh = beforeVisibility != afterVisibility;
1985 m_prevRootNeedsFullRefresh = prevRootBeforeVisibility != prevRootAfterVisibility;
1986 }
1987
1988 void finishStrokeCallback() override {
1989 // the GUI uses our thread to do the color space conversion so we
1990 // need to Q_EMIT this signal in multiple threads
1991
1992 if (m_prevRoot && m_prevRootNeedsFullRefresh) {
1993 m_image->refreshGraphAsync(m_prevRoot);
1994 }
1995
1996 if (m_newRootNeedsFullRefresh) {
1997 m_image->refreshGraphAsync(m_newRoot);
1998 }
1999
2000 if (!m_prevRootNeedsFullRefresh && !m_newRootNeedsFullRefresh) {
2002 m_image->m_d->notifyProjectionUpdatedInPatches(m_image->bounds(), jobs);
2003 this->runnableJobsInterface()->addRunnableJobs(jobs);
2004 }
2005
2006 m_image->invalidateAllFrames();
2007 }
2008
2009 private:
2010 KisNodeSP m_newRoot;
2011 KisNodeSP m_prevRoot;
2012 KisImageSP m_image;
2013 bool m_newRootNeedsFullRefresh = false;
2014 bool m_prevRootNeedsFullRefresh = false;
2015
2016 bool m_isolateLayer;
2017 bool m_isolateGroup;
2018 };
2019
2020 KisStrokeId id = startStroke(new StartIsolatedModeStroke(node, this, isolateLayer, isolateGroup));
2021 endStroke(id);
2022
2023 return true;
2024}
2025
2027{
2028 if (!m_d->isolationRootNode) return;
2029
2030 struct StopIsolatedModeStroke : public KisRunnableBasedStrokeStrategy {
2031 StopIsolatedModeStroke(KisImageSP image)
2032 : KisRunnableBasedStrokeStrategy(QLatin1String("stop-isolated-mode"), kundo2_noi18n("stop-isolated-mode")),
2033 m_image(image),
2034 m_oldRootNode(nullptr),
2035 m_oldNodeNeedsRefresh(false)
2036 {
2037 this->enableJob(JOB_INIT);
2038 this->enableJob(JOB_DOSTROKE, true);
2039 this->enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
2040 setClearsRedoOnStart(false);
2041 }
2042
2043 void initStrokeCallback() override {
2044 if (!m_image->m_d->isolationRootNode) return;
2045
2046 m_oldRootNode = m_image->m_d->isolationRootNode;
2047
2048 const bool beforeVisibility = m_oldRootNode->projectionLeaf()->visible();
2049 m_image->m_d->isolationRootNode = 0;
2050 m_image->m_d->isolateLayer = false;
2051 m_image->m_d->isolateGroup = false;
2052 Q_EMIT m_image->sigIsolatedModeChanged();
2053 const bool afterVisibility = m_oldRootNode->projectionLeaf()->visible();
2054
2055 m_oldNodeNeedsRefresh = (beforeVisibility != afterVisibility);
2056 }
2057
2058 void finishStrokeCallback() override {
2059
2060 m_image->invalidateAllFrames();
2061
2062 if (m_oldNodeNeedsRefresh){
2063 m_oldRootNode->setDirty(m_image->bounds());
2064 } else {
2065 // TODO: Substitute notifyProjectionUpdated() with this code
2066 // when update optimization is implemented
2067 //
2068 // QRect updateRect = bounds() | oldRootNode->extent();
2069 //oldRootNode->setDirty(updateRect);
2070
2072 m_image->m_d->notifyProjectionUpdatedInPatches(m_image->bounds(), jobs);
2073 this->runnableJobsInterface()->addRunnableJobs(jobs);
2074 }
2075 }
2076
2077 private:
2078 KisImageSP m_image;
2079 KisNodeSP m_oldRootNode;
2080 bool m_oldNodeNeedsRefresh;
2081 };
2082
2083 KisStrokeId id = startStroke(new StopIsolatedModeStroke(this));
2084 endStroke(id);
2085}
2086
2090
2092{
2093 return m_d->isolateLayer;
2094}
2095
2097{
2098 return m_d->isolateGroup;
2099}
2100
2106
2111
2113{
2114 return m_d->scheduler.cancelStroke(id);
2115}
2116
2118{
2119 return scheduler.tryCancelCurrentStrokeAsync();
2120}
2121
2126
2131
2138
2143
2149
2154
2156{
2162 refreshGraphAsync(0, bounds(), QRect());
2163 waitForDone();
2164}
2165
2166void KisImage::refreshGraphAsync(KisNodeSP root, const QVector<QRect> &rects, const QRect &cropRect, KisProjectionUpdateFlags flags)
2167{
2168 if (!root) root = m_d->rootLayer;
2169
2170 QVector<QRect> requestedRects = rects;
2171
2172 KisGroupLayer *group = dynamic_cast<KisGroupLayer*>(root.data());
2173 if (group && group->passThroughMode()) {
2184 QVector<QRect> changeRects = requestedRects;
2185 KisProjectionLeafSP leaf = root->projectionLeaf()->nextSibling();
2186 while (leaf) {
2187 if (leaf->shouldBeRendered()) {
2188 for (auto it = changeRects.begin(); it != changeRects.end(); ++it) {
2189 *it = leaf->projectionPlane()->changeRect(*it, leaf->node() == root ? KisNode::N_FILTHY : KisNode::N_ABOVE_FILTHY);
2190 }
2191 }
2192
2193 leaf = leaf->nextSibling();
2194 }
2195
2196 std::swap(requestedRects, changeRects);
2197 root = group->parent();
2198
2200 }
2201
2206 for (auto it = m_d->projectionUpdatesFilters.rbegin();
2207 it != m_d->projectionUpdatesFilters.rend();
2208 ++it) {
2209
2210 KIS_SAFE_ASSERT_RECOVER(*it) { continue; }
2211
2212 if ((*it)->filterRefreshGraph(this, root.data(), requestedRects, cropRect, flags)) {
2213 return;
2214 }
2215 }
2216
2217 if (!flags.testFlag(KisProjectionUpdateFlag::DontInvalidateFrames)) {
2218 m_d->animationInterface->notifyNodeChanged(root.data(), requestedRects, true);
2219 }
2220
2221 m_d->scheduler.fullRefreshAsync(root, requestedRects, cropRect, flags);
2222}
2223
2225{
2226 m_d->scheduler.addSpontaneousJob(spontaneousJob);
2227}
2228
2230{
2232}
2233
2242
2257
2264
2270
2276
2281
2286
2291
2293{
2295}
2296
2298{
2299 m_d->disableUIUpdateSignals.deref();
2300
2301 QRect rect;
2302 QVector<QRect> postponedUpdates;
2303
2305 postponedUpdates.append(rect);
2306 }
2307
2308 return postponedUpdates;
2309}
2310
2312{
2314
2316 int lod = currentLevelOfDetail();
2317 QRect dirtyRect = !lod ? rc : KisLodTransform::upscaledRect(rc, lod);
2318
2319 if (dirtyRect.isEmpty()) return;
2320
2321 Q_EMIT sigImageUpdated(dirtyRect);
2322 } else {
2324 }
2325}
2326
2331
2333{
2334 return m_d->scheduler.threadsLimit();
2335}
2336
2357
2360 const QVector<QRect> &rects,
2361 const QRect &cropRect,
2362 KisProjectionUpdateFlags flags)
2363{
2364 if (rects.isEmpty()) return;
2365
2366 scheduler.updateProjection(node, rects, cropRect, flags);
2367}
2368
2369void KisImage::requestProjectionUpdate(KisNode *node, const QVector<QRect> &rects, KisProjectionUpdateFlags flags)
2370{
2375 for (auto it = m_d->projectionUpdatesFilters.rbegin();
2376 it != m_d->projectionUpdatesFilters.rend();
2377 ++it) {
2378
2379 KIS_SAFE_ASSERT_RECOVER(*it) { continue; }
2380
2381 if ((*it)->filter(this, node, rects, flags)) {
2382 return;
2383 }
2384 }
2385
2386 if (!flags.testFlag(KisProjectionUpdateFlag::DontInvalidateFrames)) {
2387 m_d->animationInterface->notifyNodeChanged(node, rects, false);
2388 }
2389
2399 QVector<QRect> allSplitRects;
2400
2401 const QRect boundRect = effectiveLodBounds();
2402 Q_FOREACH (const QRect &rc, rects) {
2403 KisWrappedRect splitRect(rc, boundRect, m_d->wrapAroundModeAxis);
2404 allSplitRects.append(splitRect);
2405 }
2406
2407 m_d->requestProjectionUpdateImpl(node, allSplitRects, boundRect, flags);
2408
2409 } else {
2410 m_d->requestProjectionUpdateImpl(node, rects, bounds(), flags);
2411 }
2412
2414}
2415
2416void KisImage::invalidateFrames(const KisTimeSpan &range, const QRect &rect)
2417{
2419}
2420
2425
2430
2432{
2433 Q_UNUSED(node);
2434
2435 channel->connect(channel, SIGNAL(sigAddedKeyframe(const KisKeyframeChannel*, int)), m_d->animationInterface, SIGNAL(sigKeyframeAdded(const KisKeyframeChannel*, int)), Qt::UniqueConnection);
2436 channel->connect(channel, SIGNAL(sigKeyframeHasBeenRemoved(const KisKeyframeChannel*,int)), m_d->animationInterface, SIGNAL(sigKeyframeRemoved(const KisKeyframeChannel*, int)), Qt::UniqueConnection);
2437}
2438
2440{
2441 Q_UNUSED(node);
2442
2443 channel->disconnect(channel, SIGNAL(sigAddedKeyframe(const KisKeyframeChannel*, int)), m_d->animationInterface, SIGNAL(sigKeyframeAdded(const KisKeyframeChannel*, int)));
2444 channel->disconnect(channel, SIGNAL(sigKeyframeHasBeenRemoved(const KisKeyframeChannel*, int)), m_d->animationInterface, SIGNAL(sigKeyframeRemoved(const KisKeyframeChannel*, int)));
2445}
2446
2451
2453{
2454 m_d->compositions.append(composition);
2455}
2456
2458{
2459 m_d->compositions.removeAll(composition);
2460}
2461
2463{
2464 int index = m_d->compositions.indexOf(composition);
2465 if (index <= 0) {
2466 return;
2467 }
2468 m_d->compositions.move(index, index - 1);
2469}
2470
2472{
2473 int index = m_d->compositions.indexOf(composition);
2474 if (index >= m_d->compositions.size() -1) {
2475 return;
2476 }
2477 m_d->compositions.move(index, index + 1);
2478}
2479
2481{
2482 KisSelectionMask *mask = dynamic_cast<KisSelectionMask*>(root.data());
2483 if (mask &&
2484 (!bounds.contains(mask->paintDevice()->exactBounds()) ||
2485 mask->selection()->hasShapeSelection())) {
2486
2487 return true;
2488 }
2489
2490 KisNodeSP node = root->firstChild();
2491
2492 while (node) {
2493 if (checkMasksNeedConversion(node, bounds)) {
2494 return true;
2495 }
2496
2497 node = node->nextSibling();
2498 }
2499
2500 return false;
2501}
2502
2504{
2507 }
2508
2510
2513
2514 KisProcessingApplicator applicator(this, root(),
2517 kundo2_i18n("Crop Selections"));
2518
2519 KisProcessingVisitorSP visitor =
2521
2522 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
2523 applicator.end();
2524 }
2525}
2526
2531
2536
2537
2542
2548
2553
2562
2567
2572
2574{
2575 Q_UNUSED(node);
2576 Q_EMIT sigNodeCollapsedChanged();
2577}
2578
2583
2585{
2586 const bool changed = bool(m_d->proofingConfig) != bool(proofingConfig) ||
2587 (m_d->proofingConfig && proofingConfig && *m_d->proofingConfig != *proofingConfig);
2588
2589 // we still assign even when unchanged since they can be different
2590 // shared pointer objects
2591 m_d->proofingConfig = proofingConfig;
2592
2593 if (changed) {
2594 Q_EMIT sigProofingConfigChanged();
2595 }
2596}
2597
2605
2607{
2608 return m_d->axesCenter;
2609}
2610
2611void KisImage::setMirrorAxesCenter(const QPointF &value) const
2612{
2613 m_d->axesCenter = value;
2614}
2615
2620
2622{
2623 return m_d->allowMasksOnRootNode;
2624}
float value(const T *src, size_t ch)
QVector< KisImageSignalType > KisImageSignalVector
@ ColorSpaceChangedSignal
@ ProfileChangedSignal
@ LayersChangedSignal
@ ModifiedWithoutUndoSignal
@ ResolutionChangedSignal
WrapAroundAxis
@ WRAPAROUND_BOTH
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
const quint8 OPACITY_OPAQUE_U8
PythonPluginManager * instance
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
void notifyWaitOnImageStarted(KisImage *image)
void notifyWaitOnImageEnded(KisImage *image)
static KisBusyWaitBroker * instance()
The KisDumbUndoStore class doesn't actually save commands, so you cannot undo or redo!
static KisFilterStrategyRegistry * instance()
void invalidateFrames(const KisTimeSpan &range, const QRect &rect)
void notifyNodeChanged(const KisNode *node, const QRect &rect, bool recursive)
void requestTimeSwitchNonGUI(int time, bool useUndo=false)
int updatePatchWidth() const
int updatePatchHeight() const
bool enableProgressReporting(bool requestDefault=false) const
KisSelectionMaskSP deselectedGlobalSelection() const
Definition kis_image.cc:305
void setDeselectedGlobalSelection(KisSelectionMaskSP selectionMask)
Definition kis_image.cc:310
KisImageGlobalSelectionManagementInterface(KisImage *image)
Definition kis_image.cc:300
void emitNotification(KisImageSignalType type)
void emitNodeHasBeenAdded(KisNode *parent, int index, KisNodeAdditionFlags flags)
void emitAboutToRemoveANode(KisNode *parent, int index)
void emitNodeChanged(KisNodeSP node)
KisCompositeProgressProxy compositeProgressProxy
Definition kis_image.cc:278
KisLocklessStack< QRect > savedDisabledUIUpdates
Definition kis_image.cc:268
vKisAnnotationSP annotations
Definition kis_image.cc:265
KisSelectionMaskSP deselectedGlobalSelectionMask
Definition kis_image.cc:248
void notifyProjectionUpdatedInPatches(const QRect &rc, QVector< KisRunnableStrokeJobData * > &jobs)
KisImageAnimationInterface * animationInterface
Definition kis_image.cc:274
KisUpdateScheduler scheduler
Definition kis_image.cc:275
KisProofingConfigurationSP proofingConfig
Definition kis_image.cc:245
KisSelectionMaskSP targetOverlaySelectionMask
Definition kis_image.cc:250
KisImageGlobalSelectionManagementInterface globalSelectionInterface
Definition kis_image.cc:247
KisSelectionMaskSP overlaySelectionMask
Definition kis_image.cc:251
void requestProjectionUpdateImpl(KisNode *node, const QVector< QRect > &rects, const QRect &cropRect, KisProjectionUpdateFlags flags)
KisPostExecutionUndoAdapter postExecutionUndoAdapter
Definition kis_image.cc:263
KisLegacyUndoAdapter legacyUndoAdapter
Definition kis_image.cc:262
QAtomicInt disableUIUpdateSignals
Definition kis_image.cc:267
QScopedPointer< KisUndoStore > undoStore
Definition kis_image.cc:261
WrapAroundAxis wrapAroundModeAxis
Definition kis_image.cc:259
const KoColorSpace * colorSpace
Definition kis_image.cc:244
QAtomicInt disableDirtyRequests
Definition kis_image.cc:276
QVector< KisProjectionUpdatesFilterSP > projectionUpdatesFilters
Definition kis_image.cc:271
KisImageSignalRouter signalRouter
Definition kis_image.cc:273
QStack< KisProjectionUpdatesFilterCookie > disabledUpdatesCookies
Definition kis_image.cc:272
void convertImageColorSpaceImpl(const KoColorSpace *dstColorSpace, bool convertLayers, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
QList< KisLayerCompositionSP > compositions
Definition kis_image.cc:252
KisGroupLayerSP rootLayer
Definition kis_image.cc:249
KisImagePrivate(KisImage *_q, qint32 w, qint32 h, const KoColorSpace *c, KisUndoStore *undo, KisImageAnimationInterface *_animationInterface)
Definition kis_image.cc:122
void resizeImage(const QRect &newRect)
start asynchronous operation on resizing the image
Definition kis_image.cc:865
KisUndoAdapter * undoAdapter() const
void shearNodes(KisNodeList nodes, double angleX, double angleY, KisSelectionSP selection)
vKisAnnotationSP_it endAnnotations()
void requestStrokeEndActiveNode()
QImage convertToQImage(qint32 x1, qint32 y1, qint32 width, qint32 height, const KoColorProfile *profile)
void sigLayersChangedAsync()
int workingThreadsLimit() const
void invalidateAllFrames() override
Definition kis_image.cc:632
void addAnnotation(KisAnnotationSP annotation)
bool wrapAroundModeActive() const
void sigNodeCollapsedChanged()
KisImagePrivate * m_d
Definition kis_image.h:1275
bool canReselectGlobalSelection()
Definition kis_image.cc:705
KisImageGlobalSelectionManagementInterface * globalSelectionManagementInterface() const
Definition kis_image.cc:710
void scaleNode(KisNodeSP node, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection)
start asynchronous operation on scaling a subtree of nodes starting at node
void nodeChanged(KisNode *node) override
Definition kis_image.cc:626
bool isIsolatingLayer() const
void waitForDone()
void setLodPreferences(const KisLodPreferences &value)
bool startIsolatedMode(KisNodeSP node, bool isolateLayer, bool isolateGroup)
bool hasUpdatesRunning() const override
void disableUIUpdates() override
void setWorkingThreadsLimit(int value)
void refreshGraphAsync(KisNodeSP root, const QVector< QRect > &rects, const QRect &cropRect, KisProjectionUpdateFlags flags=KisProjectionUpdateFlag::None) override
void nodeHasBeenAdded(KisNode *parent, int index, KisNodeAdditionFlags flags) override
Definition kis_image.cc:588
void keyframeChannelAboutToBeRemoved(KisNode *node, KisKeyframeChannel *channel) override
bool cancelStroke(KisStrokeId id) override
KisGroupLayerSP rootLayer() const
void disableDirtyRequests() override
void setWrapAroundModeAxis(WrapAroundAxis value)
void shearImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode, bool resizeImage, double angleX, double angleY, KisSelectionSP selection)
void cropNode(KisNodeSP node, const QRect &newRect, const bool activeFrameOnly=false)
start asynchronous operation on cropping a subtree of nodes starting at node
Definition kis_image.cc:931
void enableDirtyRequests() override
QPointF documentToPixel(const QPointF &documentCoord) const
void keyframeChannelHasBeenAdded(KisNode *node, KisKeyframeChannel *channel) override
UndoResult tryUndoUnfinishedLod0Stroke()
void aboutToRemoveANode(KisNode *parent, int index) override
Definition kis_image.cc:604
qint32 nChildLayers() const
void shear(double angleX, double angleY)
start asynchronous operation on shearing the image
const KoColorSpace * colorSpace() const
QPointF mirrorAxesCenter() const
KisImageAnimationInterface * animationInterface() const
void unlock()
Definition kis_image.cc:805
void shearNode(KisNodeSP node, double angleX, double angleY, KisSelectionSP selection)
start asynchronous operation on shearing a subtree of nodes starting at node
void purgeUnusedData(bool isCancellable)
purge all pixels that have default pixel to free up memory
Definition kis_image.cc:875
KisCompositeProgressProxy * compositeProgressProxy()
Definition kis_image.cc:746
void requestUndoDuringStroke()
void removeComposition(KisLayerCompositionSP composition)
void setProjectionColorSpace(const KoColorSpace *colorSpace)
void setAllowMasksOnRootNode(bool value)
void copyFromImageImpl(const KisImage &rhs, int policy)
Definition kis_image.cc:415
void rotateImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode, double radians, bool resizeImage, KisSelectionSP selection)
void sigStrokeCancellationRequested()
void blockUpdates() override
blockUpdates block updating the image projection
Definition kis_image.cc:818
void sigRedoDuringStrokeRequested()
QString nextLayerName(const QString &baseName="") const
Definition kis_image.cc:715
KisAnnotationSP annotation(const QString &type)
void flattenLayer(KisLayerSP layer)
void sigImageModified()
KisProjectionUpdatesFilterSP removeProjectionUpdatesFilter(KisProjectionUpdatesFilterCookie cookie) override
removes already installed filter from the stack of updates filers
void sigInternalStopIsolatedModeRequested()
void unifyLayersColorSpace()
bool assignImageProfile(const KoColorProfile *profile, bool blockAllUpdates=false)
QRect effectiveLodBounds() const
bool isIdle(bool allowLocked=false)
Definition kis_image.cc:788
void copyFromImage(const KisImage &rhs)
Definition kis_image.cc:410
KisProjectionUpdatesFilterCookie currentProjectionUpdatesFilter() const override
QPointF pixelToDocument(const QPointF &pixelCoord) const
void scaleNodes(KisNodeList nodes, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection)
void convertImageColorSpace(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
KisSelectionMaskSP overlaySelectionMask() const
Definition kis_image.cc:685
void sigResolutionChanged(double xRes, double yRes)
void barrierLock(bool readOnly=false)
Wait until all the queued background jobs are completed and lock the image.
Definition kis_image.cc:756
KisImage * clone(bool exactCopy=false)
Definition kis_image.cc:405
void flatten(KisNodeSP activeNode)
void rotateImage(double radians)
start asynchronous operation on rotating the image
WrapAroundAxis wrapAroundModeAxis() const
void sigColorSpaceChanged(const KoColorSpace *cs)
KisPaintDeviceSP projection() const
qint32 width() const
@ REPLACE
we are replacing the current KisImage with another
Definition kis_image.h:126
@ CONSTRUCT
we are copy-constructing a new KisImage
Definition kis_image.h:125
QSize size() const
Definition kis_image.h:547
void scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy)
start asynchronous operation on scaling the image
Definition kis_image.cc:961
void resizeImageImpl(const QRect &newRect, bool cropLayers)
Definition kis_image.cc:834
void immediateLockForReadOnly()
Definition kis_image.cc:793
void aboutToAddANode(KisNode *parent, int index) override
Definition kis_image.cc:582
void setDefaultProjectionColor(const KoColor &color)
void setProofingConfiguration(KisProofingConfigurationSP proofingConfig)
setProofingConfiguration, this sets the image's proofing configuration, and signals the proofingConfi...
void explicitRegenerateLevelOfDetail()
void notifyBatchUpdateEnded() override
void notifyBatchUpdateStarted() override
bool allowMasksOnRootNode() const
void addComposition(KisLayerCompositionSP composition)
KisPostExecutionUndoAdapter * postExecutionUndoAdapter() const override
qint32 nHiddenLayers() const
void requestStrokeCancellation()
KisNode * graphOverlayNode() const override
void moveCompositionUp(KisLayerCompositionSP composition)
void requestTimeSwitch(int time) override
QPoint documentToImagePixelFloored(const QPointF &documentCoord) const
void addJob(KisStrokeId id, KisStrokeJobData *data) override
void setOverlaySelectionMask(KisSelectionMaskSP mask)
Definition kis_image.cc:637
KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace *colorSpace, const QString &name)
colorSpace can be null. In that case, it will be initialised to a default color space.
Definition kis_image.cc:315
void cropImage(const QRect &newRect)
start asynchronous operation on cropping the image
Definition kis_image.cc:870
void setMirrorAxesCenter(const QPointF &value) const
bool assignLayerProfile(KisNodeSP node, const KoColorProfile *profile)
void initialRefreshGraph()
void sigStrokeEndRequested()
void sigUndoDuringStrokeRequested()
void stopIsolatedMode()
void setSize(const QSize &size)
Definition kis_image.cc:828
QVector< QRect > enableUIUpdates() override
void nodeCollapsedChanged(KisNode *node) override
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) override
void notifySelectionChanged() override
bool isIsolatingGroup() const
void notifyAboutToBeDeleted()
void requestStrokeEnd()
KisLodPreferences lodPreferences() const
double xRes() const
double yRes() const
void sigAboutToBeDeleted()
qint32 height() const
void invalidateFrames(const KisTimeSpan &range, const QRect &rect) override
void mergeMultipleLayers(QList< KisNodeSP > mergedLayers, KisNodeSP putAfter)
int currentLevelOfDetail() const
KisSelectionSP globalSelection() const
Definition kis_image.cc:695
void sigStrokeEndRequestedActiveNodeFiltered()
qint32 nlayers() const
KisNodeSP isolationRootNode() const
QList< KisLayerCompositionSP > compositions()
void sigProofingConfigChanged()
void mergeDown(KisLayerSP l, const KisMetaData::MergeStrategy *strategy)
const KUndo2Command * lastExecutedCommand() const override
void removeAnnotation(const QString &type)
KisImageSignalRouter * signalRouter()
QRect bounds() const override
KoColor defaultProjectionColor() const
void convertLayerColorSpace(KisNodeSP node, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
void notifyLayersChanged()
use if the layers have changed completely (eg. when flattening)
friend class KisImageResizeCommand
Definition kis_image.h:1266
bool tryBarrierLock(bool readOnly=false)
Tries to lock the image without waiting for the jobs to finish.
Definition kis_image.cc:771
bool locked() const
Definition kis_image.cc:751
KisUndoStore * undoStore()
void unblockUpdates() override
unblockUpdates unblock updating the image project. This only restarts the scheduler and does not sche...
Definition kis_image.cc:823
void setModifiedWithoutUndo()
bool hasOverlaySelectionMask() const
Definition kis_image.cc:690
KisProjectionUpdatesFilterCookie addProjectionUpdatesFilter(KisProjectionUpdatesFilterSP filter) override
void moveCompositionDown(KisLayerCompositionSP composition)
bool wrapAroundModePermitted() const
void notifyProjectionUpdated(const QRect &rc) override
void sigImageUpdated(const QRect &)
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
void rotateNodes(KisNodeList nodes, double radians, KisSelectionSP selection)
void endStroke(KisStrokeId id) override
~KisImage() override
Definition kis_image.cc:330
void notifyUIUpdateCompleted(const QRect &rc) override
const KoColorProfile * profile() const
vKisAnnotationSP_it beginAnnotations()
void requestRedoDuringStroke()
void sigSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint)
void setResolution(double xres, double yres)
void setRootLayer(KisGroupLayerSP rootLayer)
static KisImageSP fromQImage(const QImage &image, KisUndoStore *undoStore)
Definition kis_image.cc:341
void convertImageProjectionColorSpace(const KoColorSpace *dstColorSpace)
void setWrapAroundModePermitted(bool value)
void requestProjectionUpdate(KisNode *node, const QVector< QRect > &rects, KisProjectionUpdateFlags flags) override
void setUndoStore(KisUndoStore *undoStore)
KisProofingConfigurationSP proofingConfiguration() const
proofingConfiguration
void rotateNode(KisNodeSP node, double radians, KisSelectionSP selection)
start asynchronous operation on rotating a subtree of nodes starting at node
KisKeyframeChannel stores and manages KisKeyframes. Maps units of time to virtual keyframe values....
void setNode(KisNodeWSP node)
static QRect upscaledRect(const QRect &srcRect, int lod)
KisPaintInformation map(KisPaintInformation pi) const
QRect exactBounds() const
KoColor defaultPixel() const
QImage convertToQImage(const KoColorProfile *dstProfile, qint32 x, qint32 y, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent=KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags=KoColorConversionTransformation::internalConversionFlags()) const
void convertFromQImage(const QImage &image, const KoColorProfile *profile, qint32 offsetX=0, qint32 offsetY=0)
static void copyAreaOptimized(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &originalSrcRect)
void setUndoStore(KisUndoStore *undoStore)
void applyVisitor(KisProcessingVisitorSP visitor, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
void applyCommand(KUndo2Command *command, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
void applyVisitorAllFrames(KisProcessingVisitorSP visitor, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
The KisProofingConfiguration struct Little struct that stores the proofing configuration for a given ...
bool requestsOtherStrokesToEnd() const
static QList< KisStrokeJobData * > createSuspendJobsData(KisImageWSP image)
static QList< KisStrokeJobData * > createResumeJobsData(KisImageWSP image)
static QList< KisStrokeJobData * > createJobsData(KisImageWSP image)
static KisTimeSpan infinite(int start)
QTransform transform() const
void setUndoStore(KisUndoStore *undoStore)
virtual const KoColorProfile * profile() const =0
static KoColor createTransparent(const KoColorSpace *cs)
Definition KoColor.cpp:681
A holder for an updater that does nothing.
Definition KoUpdater.h:116
KoUpdater * updater()
const T value(const QString &id) const
QString id() const
Definition KoID.cpp:63
void setProperty(const QString &name, const QVariant &value)
static bool qFuzzyCompare(half p1, half p2)
This file is part of the Krita application in calligra.
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#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_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define ppVar(var)
Definition kis_debug.h:155
#define dbgImage
Definition kis_debug.h:46
#define EMIT_IF_NEEDED
#define SANITY_CHECK_LOCKED(name)
Definition kis_image.cc:112
KIS_DECLARE_STATIC_INITIALIZER
Definition kis_image.cc:115
bool checkMasksNeedConversion(KisNodeSP root, const QRect &bounds)
static bool isLayer(KisNodeSP node)
QSharedPointer< T > toQShared(T *ptr)
std::pair< KisStrokeStrategy *, QList< KisStrokeJobData * > > KisSuspendResumePair
std::pair< KisStrokeStrategy *, QList< KisStrokeJobData * > > KisLodSyncPair
KisSharedPtr< KisAnnotation > KisAnnotationSP
Definition kis_types.h:179
KisWeakSharedPtr< KisImage > KisImageWSP
Definition kis_types.h:70
QSharedPointer< KisProofingConfiguration > KisProofingConfigurationSP
Definition kis_types.h:311
vKisAnnotationSP::iterator vKisAnnotationSP_it
Definition kis_types.h:181
void * KisProjectionUpdatesFilterCookie
Definition kis_types.h:285
KUndo2MagicString kundo2_i18n(const char *text)
KUndo2MagicString kundo2_noi18n(const QString &text)
KUndo2MagicString kundo2_i18np(const char *sing, const char *plur, const A1 &a1)
void flattenImage(KisImageSP image, KisNodeSP activeNode, MergeFlags flags)
KisNodeSP recursiveFindNode(KisNodeSP node, std::function< bool(KisNodeSP)> func)
void flattenLayer(KisImageSP image, KisLayerSP layer, MergeFlags flags)
void recursiveApplyNodes(NodePointer node, Functor func)
void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy *strategy, MergeFlags flags)
void mergeMultipleNodes(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, MergeFlags flags)
void addJobConcurrent(QVector< Job * > &jobs, Func func)
void makeContainerUnique(C &container)
QMap< QString, KisKeyframeChannel * > keyframeChannels
QUuid uuid() const
virtual KisPaintDeviceSP projection() const =0
virtual QRect exactBounds() const
void setUuid(const QUuid &id)
virtual const KoColorSpace * colorSpace() const =0
KisImageWSP image
bool isAnimated() const
FlipFlopCommand(State initialState, KUndo2Command *parent=0)
void setDefaultProjectionColor(KoColor color)
bool accept(KisNodeVisitor &v) override
void setImage(KisImageWSP image) override
KisPaintDeviceSP original() const override
KoColor defaultProjectionColor() const
SetImageProjectionColorSpace(const KoColorSpace *cs, KisImageWSP image, State initialState, KUndo2Command *parent=0)
KisPaintDeviceSP projection() const override
Definition kis_layer.cc:820
virtual KisSelectionMaskSP selectionMask() const
Definition kis_layer.cc:498
void notifyChildMaskChanged()
Definition kis_layer.cc:493
bool lodSupported() const
bool lodPreferred() const
KisSelectionSP selection
Definition kis_mask.cc:44
KisPaintDeviceSP paintDevice() const override
Definition kis_mask.cc:223
static KisMemoryStatisticsServer * instance()
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
void setRoot(KisNodeSP root)
virtual void aboutToRemoveANode(KisNode *parent, int index)
virtual void aboutToAddANode(KisNode *parent, int index)
virtual void requestProjectionUpdate(KisNode *node, const QVector< QRect > &rects, KisProjectionUpdateFlags flags)
virtual void nodeHasBeenAdded(KisNode *parent, int index, KisNodeAdditionFlags flags)
virtual void nodeChanged(KisNode *node)
KisNodeSP firstChild() const
Definition kis_node.cpp:361
QList< KisNodeSP > childNodes(const QStringList &nodeTypes, const KoProperties &properties) const
Definition kis_node.cpp:439
void setImage(KisImageWSP newImage) override
Definition kis_node.cpp:254
KisProjectionLeafSP projectionLeaf
Definition kis_node.cpp:93
virtual KisNodeSP clone() const =0
@ N_ABOVE_FILTHY
Definition kis_node.h:59
@ N_FILTHY
Definition kis_node.h:61
KisNodeWSP parent
Definition kis_node.cpp:86
void setGraphListener(KisNodeGraphListener *graphListener)
Definition kis_node.cpp:289
KisNodeSP nextSibling() const
Definition kis_node.cpp:408
KisNodeGraphListener * graphListener
Definition kis_node.cpp:87
KisPaintDeviceSP paintDevice
QRect extent() const override
void setDirty(const QVector< QRect > &rects) override
bool hasShapeSelection() const
QRect selectedExactRect() const
Slow, but exact way of determining the rectangle that encloses the selection.
void unlock(bool resetLodLevels=true)
void addJob(KisStrokeId id, KisStrokeJobData *data) override
void setPostSyncLod0GUIPlaneRequestForResumeCallback(const std::function< void()> &callback)
void setProgressProxy(KoProgressProxy *progressProxy)
void setSuspendResumeUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyPairFactory &factory)
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) override
void setLodPreferences(const KisLodPreferences &value)
void setLod0ToNStrokeStrategyFactory(const KisLodSyncStrokeStrategyFactory &factory)
void endStroke(KisStrokeId id) override
KisLodPreferences lodPreferences() const
void setThreadsLimit(int value)
KisPostExecutionUndoAdapter * lodNPostExecutionUndoAdapter() const
void setPurgeRedoStateCallback(const std::function< void()> &callback)
bool cancelStroke(KisStrokeId id) override
void fullRefreshAsync(KisNodeSP root, const QVector< QRect > &rects, const QRect &cropRect, KisProjectionUpdateFlags flags)
static KisUpdateTimeMonitor * instance()
void reportUpdateFinished(const QRect &rect)
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
static KoColorSpaceRegistry * instance()
const KoColorSpace * graya8(const QString &profile=QString())
const KoColorSpace * graya16(const QString &profile=QString())
const KoColorSpace * rgb8(const QString &profileName=QString())
const KoColorSpace * rgb16(const QString &profileName=QString())
const KoColorSpace * alpha8()