File indexing completed on 2024-05-12 15:58:20

0001 /*
0002  *  SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
0003  *  SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "kis_image.h"
0009 
0010 #include <KoConfig.h> // WORDS_BIGENDIAN
0011 
0012 #include <stdlib.h>
0013 #include <math.h>
0014 
0015 #include <QImage>
0016 #include <QPainter>
0017 #include <QSize>
0018 #include <QDateTime>
0019 #include <QRect>
0020 #include <QtConcurrent>
0021 
0022 #include <klocalizedstring.h>
0023 
0024 #include "KoColorSpaceRegistry.h"
0025 #include "KoColor.h"
0026 #include "KoColorProfile.h"
0027 #include <KoCompositeOpRegistry.h>
0028 #include "KisProofingConfiguration.h"
0029 
0030 #include "kis_adjustment_layer.h"
0031 #include "kis_annotation.h"
0032 #include "kis_count_visitor.h"
0033 #include "kis_filter_strategy.h"
0034 #include "kis_group_layer.h"
0035 #include "commands/kis_image_commands.h"
0036 #include "kis_layer.h"
0037 #include "kis_meta_data_merge_strategy_registry.h"
0038 #include "kis_paint_layer.h"
0039 #include "kis_projection_leaf.h"
0040 #include "kis_painter.h"
0041 #include "kis_selection.h"
0042 #include "kis_transaction.h"
0043 #include "kis_meta_data_merge_strategy.h"
0044 #include "kis_memory_statistics_server.h"
0045 #include "kis_node.h"
0046 #include "kis_types.h"
0047 
0048 #include "kis_image_config.h"
0049 #include "kis_update_scheduler.h"
0050 #include "kis_image_signal_router.h"
0051 #include "kis_image_animation_interface.h"
0052 #include "kis_keyframe_channel.h"
0053 #include "kis_stroke_strategy.h"
0054 #include "kis_simple_stroke_strategy.h"
0055 #include "kis_image_barrier_locker.h"
0056 
0057 
0058 #include "kis_undo_stores.h"
0059 #include "kis_legacy_undo_adapter.h"
0060 #include "kis_post_execution_undo_adapter.h"
0061 
0062 #include "kis_transform_worker.h"
0063 #include "kis_processing_applicator.h"
0064 #include "processing/kis_crop_processing_visitor.h"
0065 #include "processing/kis_crop_selections_processing_visitor.h"
0066 #include "processing/kis_transform_processing_visitor.h"
0067 #include "processing/kis_convert_color_space_processing_visitor.h"
0068 #include "processing/kis_assign_profile_processing_visitor.h"
0069 #include "commands_new/kis_image_resize_command.h"
0070 #include "commands_new/kis_image_set_resolution_command.h"
0071 #include "commands_new/kis_activate_selection_mask_command.h"
0072 #include "kis_composite_progress_proxy.h"
0073 #include "kis_layer_composition.h"
0074 #include "kis_wrapped_rect.h"
0075 #include "kis_crop_saved_extra_data.h"
0076 #include "kis_layer_utils.h"
0077 #include "kis_keyframe_channel.h"
0078 
0079 #include "kis_lod_transform.h"
0080 
0081 #include "kis_suspend_projection_updates_stroke_strategy.h"
0082 #include "kis_sync_lod_cache_stroke_strategy.h"
0083 
0084 #include "kis_projection_updates_filter.h"
0085 
0086 #include "kis_layer_projection_plane.h"
0087 
0088 #include "kis_update_time_monitor.h"
0089 #include "kis_lockless_stack.h"
0090 
0091 #include <QtCore>
0092 
0093 #include <functional>
0094 
0095 #include "kis_time_span.h"
0096 
0097 #include "KisRunnableBasedStrokeStrategy.h"
0098 #include "KisRunnableStrokeJobData.h"
0099 #include "KisRunnableStrokeJobUtils.h"
0100 #include "KisRunnableStrokeJobsInterface.h"
0101 
0102 #include "KisBusyWaitBroker.h"
0103 
0104 
0105 // #define SANITY_CHECKS
0106 
0107 #ifdef SANITY_CHECKS
0108 #define SANITY_CHECK_LOCKED(name)                                       \
0109     if (!locked()) warnKrita() << "Locking policy failed:" << name          \
0110                                << "has been called without the image"       \
0111                                   "being locked";
0112 #else
0113 #define SANITY_CHECK_LOCKED(name)
0114 #endif
0115 
0116 
0117 struct KisImageSPStaticRegistrar {
0118     KisImageSPStaticRegistrar() {
0119         qRegisterMetaType<KisImageSP>("KisImageSP");
0120     }
0121 };
0122 static KisImageSPStaticRegistrar __registrar1;
0123 
0124 class KisImage::KisImagePrivate
0125 {
0126 public:
0127     KisImagePrivate(KisImage *_q, qint32 w, qint32 h,
0128                     const KoColorSpace *c,
0129                     KisUndoStore *undo,
0130                     KisImageAnimationInterface *_animationInterface)
0131         : q(_q)
0132         , lockedForReadOnly(false)
0133         , width(w)
0134         , height(h)
0135         , colorSpace(c ? c : KoColorSpaceRegistry::instance()->rgb8())
0136         , isolateLayer(false)
0137         , isolateGroup(false)
0138         , undoStore(undo ? undo : new KisDumbUndoStore())
0139         , legacyUndoAdapter(undoStore.data(), _q)
0140         , postExecutionUndoAdapter(undoStore.data(), _q)
0141         , signalRouter(_q)
0142         , animationInterface(_animationInterface)
0143         , scheduler(_q, _q)
0144         , axesCenter(QPointF(0.5, 0.5))
0145     {
0146         {
0147             KisImageConfig cfg(true);
0148             if (cfg.enableProgressReporting()) {
0149                 scheduler.setProgressProxy(&compositeProgressProxy);
0150             }
0151 
0152             // Each of these lambdas defines a new factory function.
0153             scheduler.setLod0ToNStrokeStrategyFactory(
0154                 [=](bool forgettable) {
0155                     return KisLodSyncPair(
0156                         new KisSyncLodCacheStrokeStrategy(KisImageWSP(q), forgettable),
0157                         KisSyncLodCacheStrokeStrategy::createJobsData(KisImageWSP(q)));
0158                 });
0159 
0160             scheduler.setSuspendResumeUpdatesStrokeStrategyFactory(
0161                 [=]() {
0162                     KisSuspendProjectionUpdatesStrokeStrategy::SharedDataSP data = KisSuspendProjectionUpdatesStrokeStrategy::createSharedData();
0163 
0164                     KisSuspendResumePair suspend(new KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP(q), true, data),
0165                                                  KisSuspendProjectionUpdatesStrokeStrategy::createSuspendJobsData(KisImageWSP(q)));
0166                     KisSuspendResumePair resume(new KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP(q), false, data),
0167                                                 KisSuspendProjectionUpdatesStrokeStrategy::createResumeJobsData(KisImageWSP(q)));
0168 
0169                     return std::make_pair(suspend, resume);
0170                 });
0171 
0172             scheduler.setPurgeRedoStateCallback(
0173                         [this] () {
0174                 undoStore->purgeRedoState();
0175             });
0176         }
0177 
0178         connect(q, SIGNAL(sigImageModified()), KisMemoryStatisticsServer::instance(), SLOT(notifyImageChanged()));
0179         connect(undoStore.data(), SIGNAL(historyStateChanged()), &signalRouter, SLOT(emitImageModifiedNotification()));
0180     }
0181 
0182     ~KisImagePrivate() {
0183         /**
0184          * First delete the nodes, while strokes
0185          * and undo are still alive
0186          */
0187 
0188         KIS_SAFE_ASSERT_RECOVER_NOOP(rootLayer->graphListener() == q);
0189         KIS_SAFE_ASSERT_RECOVER_NOOP(rootLayer->image() == q);
0190 
0191         /**
0192          * Firstly we need to disconnect the nodes from the image,
0193          * because some of the nodes (e.g. KisGroupLayer) may
0194          * request the image back via defaultBouds() and/or
0195          * animationInyterface()
0196          */
0197         if (rootLayer->image() == q) {
0198             rootLayer->setImage(0);
0199         }
0200 
0201         if (rootLayer->graphListener() == q) {
0202             rootLayer->setGraphListener(0);
0203         }
0204 
0205         rootLayer.clear();
0206 
0207         /**
0208          * Stop animation interface. It may use the rootLayer.
0209          */
0210         delete animationInterface;
0211     }
0212 
0213     KisImage *q;
0214 
0215     quint32 lockCount = 0;
0216     bool lockedForReadOnly;
0217 
0218     qint32 width;
0219     qint32 height;
0220 
0221     double xres = 1.0;
0222     double yres = 1.0;
0223 
0224     const KoColorSpace * colorSpace;
0225     KisProofingConfigurationSP proofingConfig;
0226 
0227     KisSelectionSP deselectedGlobalSelection;
0228     KisGroupLayerSP rootLayer; // The layers are contained in here
0229     KisSelectionMaskSP targetOverlaySelectionMask; // the overlay switching stroke will try to switch into this mask
0230     KisSelectionMaskSP overlaySelectionMask;
0231     QList<KisLayerCompositionSP> compositions;
0232 
0233     KisNodeSP isolationRootNode;
0234     bool isolateLayer;
0235     bool isolateGroup;
0236 
0237     bool wrapAroundModePermitted = false;
0238 
0239     QScopedPointer<KisUndoStore> undoStore;
0240     KisLegacyUndoAdapter legacyUndoAdapter;
0241     KisPostExecutionUndoAdapter postExecutionUndoAdapter;
0242 
0243     vKisAnnotationSP annotations;
0244 
0245     QAtomicInt disableUIUpdateSignals;
0246     KisLocklessStack<QRect> savedDisabledUIUpdates;
0247 
0248     // filters are applied in a reversed way, from rbegin() to rend()
0249     QVector<KisProjectionUpdatesFilterSP> projectionUpdatesFilters;
0250     QStack<KisProjectionUpdatesFilterCookie> disabledUpdatesCookies;
0251     KisImageSignalRouter signalRouter;
0252     KisImageAnimationInterface *animationInterface;
0253     KisUpdateScheduler scheduler;
0254     QAtomicInt disableDirtyRequests;
0255 
0256     KisCompositeProgressProxy compositeProgressProxy;
0257 
0258     QPointF axesCenter;
0259     bool allowMasksOnRootNode = false;
0260 
0261     bool tryCancelCurrentStrokeAsync();
0262 
0263     void notifyProjectionUpdatedInPatches(const QRect &rc, QVector<KisRunnableStrokeJobData *> &jobs);
0264 
0265     void convertImageColorSpaceImpl(const KoColorSpace *dstColorSpace,
0266                                     bool convertLayers,
0267                                     KoColorConversionTransformation::Intent renderingIntent,
0268                                     KoColorConversionTransformation::ConversionFlags conversionFlags);
0269 
0270     struct SetImageProjectionColorSpace;
0271 };
0272 
0273 KisImage::KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace *colorSpace, const QString& name)
0274         : QObject(0)
0275         , KisShared()
0276         , m_d(new KisImagePrivate(this, width, height,
0277                                   colorSpace, undoStore,
0278                                   new KisImageAnimationInterface(this)))
0279 {
0280     // make sure KisImage belongs to the GUI thread
0281     moveToThread(qApp->thread());
0282     connect(this, SIGNAL(sigInternalStopIsolatedModeRequested()), SLOT(stopIsolatedMode()));
0283 
0284     setObjectName(name);
0285     setRootLayer(new KisGroupLayer(this, "root", OPACITY_OPAQUE_U8));
0286 }
0287 
0288 KisImage::~KisImage()
0289 {
0290     /**
0291      * Request the tools to end currently running strokes
0292      */
0293     waitForDone();
0294 
0295     delete m_d;
0296     disconnect(); // in case Qt gets confused
0297 }
0298 
0299 KisImageSP KisImage::fromQImage(const QImage &image, KisUndoStore *undoStore)
0300 {
0301     const KoColorSpace *colorSpace = 0;
0302 
0303     switch (image.format()) {
0304     case QImage::Format_Invalid:
0305     case QImage::Format_Mono:
0306     case QImage::Format_MonoLSB:
0307         colorSpace = KoColorSpaceRegistry::instance()->graya8();
0308         break;
0309     case QImage::Format_Indexed8:
0310     case QImage::Format_RGB32:
0311     case QImage::Format_ARGB32:
0312     case QImage::Format_ARGB32_Premultiplied:
0313         colorSpace = KoColorSpaceRegistry::instance()->rgb8();
0314         break;
0315     case QImage::Format_RGB16:
0316         colorSpace = KoColorSpaceRegistry::instance()->rgb16();
0317         break;
0318     case QImage::Format_ARGB8565_Premultiplied:
0319     case QImage::Format_RGB666:
0320     case QImage::Format_ARGB6666_Premultiplied:
0321     case QImage::Format_RGB555:
0322     case QImage::Format_ARGB8555_Premultiplied:
0323     case QImage::Format_RGB888:
0324     case QImage::Format_RGB444:
0325     case QImage::Format_ARGB4444_Premultiplied:
0326     case QImage::Format_RGBX8888:
0327     case QImage::Format_RGBA8888:
0328     case QImage::Format_RGBA8888_Premultiplied:
0329         colorSpace = KoColorSpaceRegistry::instance()->rgb8();
0330         break;
0331     case QImage::Format_BGR30:
0332     case QImage::Format_A2BGR30_Premultiplied:
0333     case QImage::Format_RGB30:
0334     case QImage::Format_A2RGB30_Premultiplied:
0335         colorSpace = KoColorSpaceRegistry::instance()->rgb8();
0336         break;
0337     case QImage::Format_Alpha8:
0338         colorSpace = KoColorSpaceRegistry::instance()->alpha8();
0339         break;
0340     case QImage::Format_Grayscale8:
0341         colorSpace = KoColorSpaceRegistry::instance()->graya8();
0342         break;
0343 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
0344     case QImage::Format_Grayscale16:
0345         colorSpace = KoColorSpaceRegistry::instance()->graya16();
0346         break;
0347 #endif
0348 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
0349     case QImage::Format_RGBX64:
0350     case QImage::Format_RGBA64:
0351     case QImage::Format_RGBA64_Premultiplied:
0352         colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), 0);
0353         break;
0354 #endif
0355     default:
0356         colorSpace = 0;
0357     }
0358 
0359     KisImageSP img = new KisImage(undoStore, image.width(), image.height(), colorSpace, i18n("Imported Image"));
0360     KisPaintLayerSP layer = new KisPaintLayer(img, img->nextLayerName(), 255);
0361     layer->paintDevice()->convertFromQImage(image, 0, 0, 0);
0362     img->addNode(layer.data(), img->rootLayer().data());
0363 
0364     return img;
0365 }
0366 
0367 KisImage *KisImage::clone(bool exactCopy)
0368 {
0369     return new KisImage(*this, 0, exactCopy);
0370 }
0371 
0372 void KisImage::copyFromImage(const KisImage &rhs)
0373 {
0374     copyFromImageImpl(rhs, REPLACE);
0375 }
0376 
0377 void KisImage::copyFromImageImpl(const KisImage &rhs, int policy)
0378 {
0379     // make sure we choose exactly one from REPLACE and CONSTRUCT
0380     KIS_ASSERT_RECOVER_RETURN(bool(policy & REPLACE) != bool(policy & CONSTRUCT));
0381 
0382     /**
0383      * We should replace the root before amitting any signals, because some of the layers
0384      * may be subscribed to sigSizeChanged() signal (e.g. KisSelectionBasedLayer). So the
0385      * old layers should be fully detached before we actually emit this signal.
0386      *
0387      * See bug 447599 for more details.
0388      */
0389 
0390     KisNodeSP oldRoot = this->root();
0391     KisNodeSP newRoot = rhs.root()->clone();
0392     newRoot->setGraphListener(this);
0393     newRoot->setImage(this);
0394 
0395     m_d->rootLayer = dynamic_cast<KisGroupLayer*>(newRoot.data());
0396     setRoot(newRoot);
0397 
0398     if (oldRoot) {
0399         oldRoot->setImage(0);
0400         oldRoot->setGraphListener(0);
0401         oldRoot->disconnect();
0402     }
0403 
0404 
0405     // only when replacing do we need to emit signals
0406 #define EMIT_IF_NEEDED if (!(policy & REPLACE)) {} else emit
0407 
0408     if (policy & REPLACE) { // if we are constructing the image, these are already set
0409         if (m_d->width != rhs.width() || m_d->height != rhs.height()) {
0410             m_d->width = rhs.width();
0411             m_d->height = rhs.height();
0412             emit sigSizeChanged(QPointF(), QPointF());
0413         }
0414         if (m_d->colorSpace != rhs.colorSpace()) {
0415             m_d->colorSpace = rhs.colorSpace();
0416             emit sigColorSpaceChanged(m_d->colorSpace);
0417         }
0418     }
0419 
0420     // from KisImage::KisImage(const KisImage &, KisUndoStore *, bool)
0421     setObjectName(rhs.objectName());
0422 
0423     if (m_d->xres != rhs.m_d->xres || m_d->yres != rhs.m_d->yres) {
0424         m_d->xres = rhs.m_d->xres;
0425         m_d->yres = rhs.m_d->yres;
0426         EMIT_IF_NEEDED sigResolutionChanged(m_d->xres, m_d->yres);
0427     }
0428 
0429     m_d->allowMasksOnRootNode = rhs.m_d->allowMasksOnRootNode;
0430 
0431     if (rhs.m_d->proofingConfig) {
0432         KisProofingConfigurationSP proofingConfig(new KisProofingConfiguration(*rhs.m_d->proofingConfig));
0433         if (policy & REPLACE) {
0434             setProofingConfiguration(proofingConfig);
0435         } else {
0436             m_d->proofingConfig = proofingConfig;
0437         }
0438     }
0439 
0440     bool exactCopy = policy & EXACT_COPY;
0441 
0442     if (exactCopy || rhs.m_d->isolationRootNode || rhs.m_d->overlaySelectionMask) {
0443         m_d->isolateLayer = rhs.m_d->isolateLayer;
0444         m_d->isolateGroup = rhs.m_d->isolateGroup;
0445 
0446         QQueue<KisNodeSP> linearizedNodes;
0447         KisLayerUtils::recursiveApplyNodes(rhs.root(),
0448                                            [&linearizedNodes](KisNodeSP node) {
0449                                                linearizedNodes.enqueue(node);
0450                                            });
0451         KisLayerUtils::recursiveApplyNodes(newRoot,
0452                                            [&linearizedNodes, exactCopy, &rhs, this](KisNodeSP node) {
0453                                                KisNodeSP refNode = linearizedNodes.dequeue();
0454 
0455                                                if (exactCopy) {
0456                                                    node->setUuid(refNode->uuid());
0457                                                }
0458 
0459                                                if (rhs.m_d->isolationRootNode &&
0460                                                    rhs.m_d->isolationRootNode == refNode) {
0461                                                    m_d->isolationRootNode = node;
0462                                                }
0463 
0464                                                if (rhs.m_d->overlaySelectionMask &&
0465                                                    KisNodeSP(rhs.m_d->overlaySelectionMask) == refNode) {
0466                                                    m_d->targetOverlaySelectionMask = dynamic_cast<KisSelectionMask*>(node.data());
0467                                                    m_d->overlaySelectionMask = m_d->targetOverlaySelectionMask;
0468                                                    m_d->rootLayer->notifyChildMaskChanged();
0469                                                }
0470 
0471 
0472                                                // Re-establish DefaultBounds Instances for Existing Nodes
0473                                                // This is a workaround for copy-constructors failing to pass
0474                                                // proper DefaultBounds due to either lacking image data on construction
0475                                                // We should change the way "DefaultBounds" works to try to make it
0476                                                // safer for threading races.
0477                                                using KeyframeChannelContainer = QMap<QString, KisKeyframeChannel*>;
0478                                                KeyframeChannelContainer keyframeChannels = node->keyframeChannels();
0479                                                for (KeyframeChannelContainer::iterator i = keyframeChannels.begin();
0480                                                     i != keyframeChannels.end(); i++) {
0481                                                    keyframeChannels[i.key()]->setNode(node);
0482                                                }
0483                                            });
0484     }
0485 
0486     KisLayerUtils::recursiveApplyNodes(newRoot,
0487                                        [](KisNodeSP node) {
0488                                            dbgImage << "Node: " << (void *)node.data();
0489                                        });
0490 
0491 
0492 
0493     m_d->compositions.clear();
0494 
0495     Q_FOREACH (KisLayerCompositionSP comp, rhs.m_d->compositions) {
0496         m_d->compositions << toQShared(new KisLayerComposition(*comp, this));
0497     }
0498 
0499     EMIT_IF_NEEDED sigLayersChangedAsync();
0500 
0501     vKisAnnotationSP newAnnotations;
0502     Q_FOREACH (KisAnnotationSP annotation, rhs.m_d->annotations) {
0503         newAnnotations << annotation->clone();
0504     }
0505     m_d->annotations = newAnnotations;
0506 
0507     KIS_ASSERT_RECOVER_NOOP(rhs.m_d->projectionUpdatesFilters.isEmpty());
0508     KIS_ASSERT_RECOVER_NOOP(!rhs.m_d->disableUIUpdateSignals);
0509     KIS_ASSERT_RECOVER_NOOP(!rhs.m_d->disableDirtyRequests);
0510 
0511 #undef EMIT_IF_NEEDED
0512 }
0513 
0514 KisImage::KisImage(const KisImage& rhs, KisUndoStore *undoStore, bool exactCopy)
0515     : KisNodeFacade(),
0516       KisNodeGraphListener(),
0517       KisShared(),
0518       m_d(new KisImagePrivate(this,
0519                               rhs.width(), rhs.height(),
0520                               rhs.colorSpace(),
0521                               undoStore ? undoStore : new KisDumbUndoStore(),
0522                               new KisImageAnimationInterface(*rhs.animationInterface(), this)))
0523 {
0524     // make sure KisImage belongs to the GUI thread
0525     moveToThread(qApp->thread());
0526     connect(this, SIGNAL(sigInternalStopIsolatedModeRequested()), SLOT(stopIsolatedMode()));
0527 
0528     copyFromImageImpl(rhs, CONSTRUCT | (exactCopy ? EXACT_COPY : 0));
0529 }
0530 
0531 void KisImage::aboutToAddANode(KisNode *parent, int index)
0532 {
0533     KisNodeGraphListener::aboutToAddANode(parent, index);
0534     SANITY_CHECK_LOCKED("aboutToAddANode");
0535 }
0536 
0537 void KisImage::nodeHasBeenAdded(KisNode *parent, int index)
0538 {
0539     KisNodeGraphListener::nodeHasBeenAdded(parent, index);
0540 
0541     KisLayerUtils::recursiveApplyNodes(KisSharedPtr<KisNode>(parent), [this](KisNodeSP node){
0542        QMap<QString, KisKeyframeChannel*> chans = node->keyframeChannels();
0543        Q_FOREACH(KisKeyframeChannel* chan, chans.values()) {
0544            chan->setNode(node);
0545            this->keyframeChannelHasBeenAdded(node.data(), chan);
0546        }
0547     });
0548 
0549     SANITY_CHECK_LOCKED("nodeHasBeenAdded");
0550     m_d->signalRouter.emitNodeHasBeenAdded(parent, index);
0551 }
0552 
0553 void KisImage::aboutToRemoveANode(KisNode *parent, int index)
0554 {
0555     KisNodeSP deletedNode = parent->at(index);
0556     if (!dynamic_cast<KisSelectionMask*>(deletedNode.data()) &&
0557         deletedNode == m_d->isolationRootNode) {
0558 
0559         emit sigInternalStopIsolatedModeRequested();
0560     }
0561 
0562     KisLayerUtils::recursiveApplyNodes(KisSharedPtr<KisNode>(parent), [this](KisNodeSP node){
0563        QMap<QString, KisKeyframeChannel*> chans = node->keyframeChannels();
0564        Q_FOREACH(KisKeyframeChannel* chan, chans.values()) {
0565            this->keyframeChannelAboutToBeRemoved(node.data(), chan);
0566        }
0567     });
0568 
0569     KisNodeGraphListener::aboutToRemoveANode(parent, index);
0570 
0571     SANITY_CHECK_LOCKED("aboutToRemoveANode");
0572     m_d->signalRouter.emitAboutToRemoveANode(parent, index);
0573 }
0574 
0575 void KisImage::nodeChanged(KisNode* node)
0576 {
0577     KisNodeGraphListener::nodeChanged(node);
0578     requestStrokeEnd();
0579     m_d->signalRouter.emitNodeChanged(node);
0580 }
0581 
0582 void KisImage::invalidateAllFrames()
0583 {
0584     invalidateFrames(KisTimeSpan::infinite(0), QRect());
0585 }
0586 
0587 void KisImage::setOverlaySelectionMask(KisSelectionMaskSP mask)
0588 {
0589     if (m_d->targetOverlaySelectionMask == mask) return;
0590 
0591     m_d->targetOverlaySelectionMask = mask;
0592 
0593     struct UpdateOverlaySelectionStroke : public KisSimpleStrokeStrategy {
0594         UpdateOverlaySelectionStroke(KisImageSP image)
0595             : KisSimpleStrokeStrategy(QLatin1String("update-overlay-selection-mask"), kundo2_noi18n("update-overlay-selection-mask")),
0596               m_image(image)
0597         {
0598             this->enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
0599             setClearsRedoOnStart(false);
0600         }
0601 
0602         void initStrokeCallback() override {
0603             KisSelectionMaskSP oldMask = m_image->m_d->overlaySelectionMask;
0604             KisSelectionMaskSP newMask = m_image->m_d->targetOverlaySelectionMask;
0605             if (oldMask == newMask) return;
0606 
0607             KIS_SAFE_ASSERT_RECOVER_RETURN(!newMask || static_cast<KisImage*>(newMask->graphListener()) == m_image);
0608 
0609             m_image->m_d->overlaySelectionMask = newMask;
0610 
0611             if (oldMask || newMask) {
0612                 m_image->m_d->rootLayer->notifyChildMaskChanged();
0613             }
0614 
0615             if (oldMask) {
0616                 m_image->m_d->rootLayer->setDirtyDontResetAnimationCache(oldMask->extent());
0617             }
0618 
0619             if (newMask) {
0620                 newMask->setDirty();
0621             }
0622 
0623             m_image->undoAdapter()->emitSelectionChanged();
0624         }
0625 
0626     private:
0627         KisImageSP m_image;
0628     };
0629 
0630     KisStrokeId id = startStroke(new UpdateOverlaySelectionStroke(this));
0631     endStroke(id);
0632 }
0633 
0634 KisSelectionMaskSP KisImage::overlaySelectionMask() const
0635 {
0636     return m_d->overlaySelectionMask;
0637 }
0638 
0639 bool KisImage::hasOverlaySelectionMask() const
0640 {
0641     return m_d->overlaySelectionMask;
0642 }
0643 
0644 KisSelectionSP KisImage::globalSelection() const
0645 {
0646     KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
0647     if (selectionMask) {
0648         return selectionMask->selection();
0649     } else {
0650         return 0;
0651     }
0652 }
0653 
0654 void KisImage::setGlobalSelection(KisSelectionSP globalSelection)
0655 {
0656     KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
0657 
0658     if (!globalSelection) {
0659         if (selectionMask) {
0660             removeNode(selectionMask);
0661         }
0662     }
0663     else {
0664         if (!selectionMask) {
0665             selectionMask = new KisSelectionMask(this, i18n("Selection Mask"));
0666             selectionMask->initSelection(m_d->rootLayer);
0667             addNode(selectionMask);
0668             // If we do not set the selection now, the setActive call coming next
0669             // can be very, very expensive, depending on the size of the image.
0670             selectionMask->setSelection(globalSelection);
0671             selectionMask->setActive(true);
0672         }
0673         else {
0674             selectionMask->setSelection(globalSelection);
0675         }
0676 
0677         KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->rootLayer->childCount() > 0);
0678         KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->rootLayer->selectionMask());
0679     }
0680 
0681     m_d->deselectedGlobalSelection = 0;
0682     m_d->legacyUndoAdapter.emitSelectionChanged();
0683 }
0684 
0685 void KisImage::deselectGlobalSelection()
0686 {
0687     KisSelectionSP savedSelection = globalSelection();
0688     setGlobalSelection(0);
0689     m_d->deselectedGlobalSelection = savedSelection;
0690 }
0691 
0692 bool KisImage::canReselectGlobalSelection()
0693 {
0694     return m_d->deselectedGlobalSelection;
0695 }
0696 
0697 void KisImage::reselectGlobalSelection()
0698 {
0699     if(m_d->deselectedGlobalSelection) {
0700         setGlobalSelection(m_d->deselectedGlobalSelection);
0701     }
0702 }
0703 
0704 QString KisImage::nextLayerName(const QString &_baseName) const
0705 {
0706     QString baseName = _baseName;
0707 
0708     int numLayers = 0;
0709     int maxLayerIndex = 0;
0710     QRegularExpression numberedLayerRegexp(".* (\\d+)$");
0711     KisLayerUtils::recursiveApplyNodes(root(),
0712         [&numLayers, &maxLayerIndex, &numberedLayerRegexp] (KisNodeSP node) {
0713             if (node->inherits("KisLayer")) {
0714                 QRegularExpressionMatch match = numberedLayerRegexp.match(node->name());
0715 
0716                 if (match.hasMatch()) {
0717                     maxLayerIndex = qMax(maxLayerIndex, match.captured(1).toInt());
0718                 }
0719                 numLayers++;
0720             }
0721         });
0722 
0723     // special case if there is only root node
0724     if (numLayers == 1) {
0725         return i18n("Background");
0726     }
0727 
0728     if (baseName.isEmpty()) {
0729         baseName = i18n("Paint Layer");
0730     }
0731 
0732     return QString("%1 %2").arg(baseName).arg(maxLayerIndex + 1);
0733 }
0734 
0735 KisCompositeProgressProxy* KisImage::compositeProgressProxy()
0736 {
0737     return &m_d->compositeProgressProxy;
0738 }
0739 
0740 bool KisImage::locked() const
0741 {
0742     return m_d->lockCount != 0;
0743 }
0744 
0745 void KisImage::barrierLock(bool readOnly)
0746 {
0747     if (!locked()) {
0748         requestStrokeEnd();
0749         KisBusyWaitBroker::instance()->notifyWaitOnImageStarted(this);
0750         m_d->scheduler.barrierLock();
0751         KisBusyWaitBroker::instance()->notifyWaitOnImageEnded(this);
0752         m_d->lockedForReadOnly = readOnly;
0753     } else {
0754         m_d->lockedForReadOnly &= readOnly;
0755     }
0756 
0757     m_d->lockCount++;
0758 }
0759 
0760 bool KisImage::tryBarrierLock(bool readOnly)
0761 {
0762     bool result = true;
0763 
0764     if (!locked()) {
0765         result = m_d->scheduler.tryBarrierLock();
0766         m_d->lockedForReadOnly = readOnly;
0767     }
0768 
0769     if (result) {
0770         m_d->lockCount++;
0771         m_d->lockedForReadOnly &= readOnly;
0772     }
0773 
0774     return result;
0775 }
0776 
0777 bool KisImage::isIdle(bool allowLocked)
0778 {
0779     return (allowLocked || !locked()) && m_d->scheduler.isIdle();
0780 }
0781 
0782 void KisImage::immediateLockForReadOnly()
0783 {
0784     if (!locked()) {
0785         requestStrokeEnd();
0786         KisBusyWaitBroker::instance()->notifyWaitOnImageStarted(this);
0787         m_d->scheduler.immediateLockForReadOnly();
0788         KisBusyWaitBroker::instance()->notifyWaitOnImageEnded(this);
0789     }
0790     m_d->lockCount++;
0791     m_d->lockedForReadOnly = true;
0792 }
0793 
0794 void KisImage::unlock()
0795 {
0796     Q_ASSERT(locked());
0797 
0798     if (locked()) {
0799         m_d->lockCount--;
0800 
0801         if (m_d->lockCount == 0) {
0802             m_d->scheduler.unlock(!m_d->lockedForReadOnly);
0803         }
0804     }
0805 }
0806 
0807 void KisImage::blockUpdates()
0808 {
0809     m_d->scheduler.blockUpdates();
0810 }
0811 
0812 void KisImage::unblockUpdates()
0813 {
0814     m_d->scheduler.unblockUpdates();
0815 }
0816 
0817 void KisImage::setSize(const QSize& size)
0818 {
0819     m_d->width = size.width();
0820     m_d->height = size.height();
0821 }
0822 
0823 void KisImage::resizeImageImpl(const QRect& newRect, bool cropLayers)
0824 {
0825     if (newRect == bounds() && !cropLayers) return;
0826 
0827     KUndo2MagicString actionName = cropLayers ?
0828         kundo2_i18n("Crop Image") :
0829         kundo2_i18n("Resize Image");
0830 
0831     KisImageSignalVector emitSignals;
0832     emitSignals << ComplexSizeChangedSignal(newRect, newRect.size());
0833 
0834     KisCropSavedExtraData *extraData =
0835         new KisCropSavedExtraData(cropLayers ?
0836                                   KisCropSavedExtraData::CROP_IMAGE :
0837                                   KisCropSavedExtraData::RESIZE_IMAGE,
0838                                   newRect);
0839 
0840     KisProcessingApplicator applicator(this, m_d->rootLayer,
0841                                        KisProcessingApplicator::RECURSIVE |
0842                                        KisProcessingApplicator::NO_UI_UPDATES,
0843                                        emitSignals, actionName, extraData);
0844 
0845     if (cropLayers || !newRect.topLeft().isNull()) {
0846         KisProcessingVisitorSP visitor =
0847             new KisCropProcessingVisitor(newRect, cropLayers, true);
0848         applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
0849     }
0850     applicator.applyCommand(new KisImageResizeCommand(this, newRect.size()));
0851     applicator.end();
0852 }
0853 
0854 void KisImage::resizeImage(const QRect& newRect)
0855 {
0856     resizeImageImpl(newRect, false);
0857 }
0858 
0859 void KisImage::cropImage(const QRect& newRect)
0860 {
0861     resizeImageImpl(newRect, true);
0862 }
0863 
0864 void KisImage::purgeUnusedData(bool isCancellable)
0865 {
0866     /**
0867      * WARNING: don't use this function unless you know what you are doing!
0868      *
0869      * It breaks undo on layers! Therefore, after calling it, KisImage is not
0870      * undo-capable anymore!
0871      */
0872 
0873     struct PurgeUnusedDataStroke : public KisRunnableBasedStrokeStrategy {
0874         PurgeUnusedDataStroke(KisImageSP image, bool isCancellable)
0875             : KisRunnableBasedStrokeStrategy(QLatin1String("purge-unused-data"),
0876                                              kundo2_noi18n("purge-unused-data")),
0877               m_image(image)
0878         {
0879             this->enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
0880             this->enableJob(JOB_DOSTROKE, true);
0881             setClearsRedoOnStart(false);
0882             setRequestsOtherStrokesToEnd(!isCancellable);
0883             setCanForgetAboutMe(isCancellable);
0884         }
0885 
0886         void initStrokeCallback() override
0887         {
0888             KisPaintDeviceList deviceList;
0889             QVector<KisStrokeJobData*> jobsData;
0890 
0891             KisLayerUtils::recursiveApplyNodes(m_image->root(),
0892                 [&deviceList](KisNodeSP node) {
0893                    deviceList << node->getLodCapableDevices();
0894                  });
0895 
0896             /// make sure we deduplicate the list to avoid
0897             /// concurrent write access to the devices
0898             KritaUtils::makeContainerUnique(deviceList);
0899 
0900             Q_FOREACH (KisPaintDeviceSP device, deviceList) {
0901                 if (!device) continue;
0902 
0903                 KritaUtils::addJobConcurrent(jobsData,
0904                     [device] () {
0905                         const_cast<KisPaintDevice*>(device.data())->purgeDefaultPixels();
0906                     });
0907             }
0908 
0909             addMutatedJobs(jobsData);
0910         }
0911 
0912     private:
0913         KisImageSP m_image;
0914     };
0915 
0916     KisStrokeId id = startStroke(new PurgeUnusedDataStroke(this, isCancellable));
0917     endStroke(id);
0918 }
0919 
0920 void KisImage::cropNode(KisNodeSP node, const QRect& newRect, const bool activeFrameOnly)
0921 {
0922     const bool isLayer = qobject_cast<KisLayer*>(node.data());
0923     KUndo2MagicString actionName = isLayer ?
0924         kundo2_i18n("Crop Layer") :
0925         kundo2_i18n("Crop Mask");
0926 
0927     KisImageSignalVector emitSignals;
0928 
0929     KisCropSavedExtraData *extraData =
0930         new KisCropSavedExtraData(KisCropSavedExtraData::CROP_LAYER,
0931                                   newRect, node);
0932 
0933     KisProcessingApplicator applicator(this, node,
0934                                        KisProcessingApplicator::RECURSIVE,
0935                                        emitSignals, actionName, extraData);
0936 
0937     KisProcessingVisitorSP visitor =
0938         new KisCropProcessingVisitor(newRect, true, false);
0939 
0940     if (node->isAnimated() && activeFrameOnly) {
0941         // Crop active frame..
0942         applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
0943     } else {
0944         // Crop all frames..
0945         applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
0946     }
0947     applicator.end();
0948 }
0949 
0950 void KisImage::scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy)
0951 {
0952     bool resolutionChanged = !qFuzzyCompare(xRes(), xres) || !qFuzzyCompare(yRes(), yres);
0953     bool sizeChanged = size != this->size();
0954 
0955     if (!resolutionChanged && !sizeChanged) return;
0956 
0957     KisImageSignalVector emitSignals;
0958     if (resolutionChanged) emitSignals << ResolutionChangedSignal;
0959     if (sizeChanged) emitSignals << ComplexSizeChangedSignal(bounds(), size);
0960 
0961     KUndo2MagicString actionName = sizeChanged ?
0962         kundo2_i18n("Scale Image") :
0963         kundo2_i18n("Change Image Resolution");
0964 
0965     KisProcessingApplicator::ProcessingFlags signalFlags =
0966         (resolutionChanged || sizeChanged) ?
0967                 KisProcessingApplicator::NO_UI_UPDATES :
0968                 KisProcessingApplicator::NONE;
0969 
0970     KisProcessingApplicator applicator(this, m_d->rootLayer,
0971                                        KisProcessingApplicator::RECURSIVE | signalFlags,
0972                                        emitSignals, actionName);
0973 
0974     qreal sx = qreal(size.width()) / this->size().width();
0975     qreal sy = qreal(size.height()) / this->size().height();
0976 
0977     QTransform shapesCorrection;
0978 
0979     if (resolutionChanged) {
0980         shapesCorrection = QTransform::fromScale(xRes() / xres, yRes() / yres);
0981     }
0982 
0983     KisProcessingVisitorSP visitor =
0984         new KisTransformProcessingVisitor(sx, sy,
0985                                           0, 0,
0986                                           QPointF(),
0987                                           0,
0988                                           0, 0,
0989                                           filterStrategy,
0990                                           shapesCorrection);
0991 
0992     applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
0993 
0994     if (resolutionChanged) {
0995         KUndo2Command *parent =
0996             new KisResetShapesCommand(m_d->rootLayer);
0997         new KisImageSetResolutionCommand(this, xres, yres, parent);
0998         applicator.applyCommand(parent);
0999     }
1000 
1001     if (sizeChanged) {
1002         applicator.applyCommand(new KisImageResizeCommand(this, size));
1003     }
1004 
1005     applicator.end();
1006 }
1007 
1008 void KisImage::scaleNode(KisNodeSP node, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection)
1009 {
1010     KUndo2MagicString actionName(kundo2_i18n("Scale Layer"));
1011     KisImageSignalVector emitSignals;
1012 
1013     QPointF offset;
1014     {
1015         KisTransformWorker worker(0,
1016                                   scaleX, scaleY,
1017                                   0, 0, 0, 0,
1018                                   0.0,
1019                                   0, 0, 0, 0);
1020         QTransform transform = worker.transform();
1021 
1022         offset = center - transform.map(center);
1023     }
1024 
1025     KisProcessingApplicator applicator(this, node,
1026                                        KisProcessingApplicator::RECURSIVE,
1027                                        emitSignals, actionName);
1028 
1029     KisTransformProcessingVisitor *visitor =
1030         new KisTransformProcessingVisitor(scaleX, scaleY,
1031                                           0, 0,
1032                                           QPointF(),
1033                                           0,
1034                                           offset.x(), offset.y(),
1035                                           filterStrategy);
1036 
1037     visitor->setSelection(selection);
1038 
1039     if (selection) {
1040         applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1041     } else {
1042         applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
1043     }
1044 
1045     applicator.end();
1046 }
1047 
1048 void KisImage::rotateImpl(const KUndo2MagicString &actionName,
1049                           KisNodeSP rootNode,
1050                           double radians,
1051                           bool resizeImage,
1052                           KisSelectionSP selection)
1053 {
1054     // we can either transform (and resize) the whole image or
1055     // transform a selection, we cannot do both at the same time
1056     KIS_SAFE_ASSERT_RECOVER(!(bool(selection) && resizeImage)) {
1057         selection = 0;
1058     }
1059 
1060     const QRect baseBounds =
1061         resizeImage ? bounds() :
1062         selection ? selection->selectedExactRect() :
1063         rootNode->exactBounds();
1064 
1065     QPointF offset;
1066     QSize newSize;
1067 
1068     {
1069         KisTransformWorker worker(0,
1070                                   1.0, 1.0,
1071                                   0, 0, 0, 0,
1072                                   radians,
1073                                   0, 0, 0, 0);
1074         QTransform transform = worker.transform();
1075 
1076         if (resizeImage) {
1077             QRect newRect = transform.mapRect(baseBounds);
1078             newSize = newRect.size();
1079             offset = -newRect.topLeft();
1080         }
1081         else {
1082             QPointF origin = QRectF(baseBounds).center();
1083 
1084             newSize = size();
1085             offset = -(transform.map(origin) - origin);
1086         }
1087     }
1088 
1089     bool sizeChanged = resizeImage &&
1090         (newSize.width() != baseBounds.width() ||
1091          newSize.height() != baseBounds.height());
1092 
1093     // These signals will be emitted after processing is done
1094     KisImageSignalVector emitSignals;
1095     if (sizeChanged) emitSignals << ComplexSizeChangedSignal(baseBounds, newSize);
1096 
1097     // These flags determine whether updates are transferred to the UI during processing
1098     KisProcessingApplicator::ProcessingFlags signalFlags =
1099         sizeChanged ?
1100         KisProcessingApplicator::NO_UI_UPDATES :
1101         KisProcessingApplicator::NONE;
1102 
1103 
1104     KisProcessingApplicator applicator(this, rootNode,
1105                                        KisProcessingApplicator::RECURSIVE | signalFlags,
1106                                        emitSignals, actionName);
1107 
1108     KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Bicubic");
1109 
1110     KisTransformProcessingVisitor *visitor =
1111             new KisTransformProcessingVisitor(1.0, 1.0, 0.0, 0.0,
1112                                               QPointF(),
1113                                               radians,
1114                                               offset.x(), offset.y(),
1115                                               filter);
1116     if (selection) {
1117         visitor->setSelection(selection);
1118     }
1119 
1120     if (selection) {
1121         applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1122     } else {
1123         applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
1124     }
1125 
1126     if (sizeChanged) {
1127         applicator.applyCommand(new KisImageResizeCommand(this, newSize));
1128     }
1129     applicator.end();
1130 }
1131 
1132 
1133 void KisImage::rotateImage(double radians)
1134 {
1135     rotateImpl(kundo2_i18n("Rotate Image"), root(), radians, true, 0);
1136 }
1137 
1138 void KisImage::rotateNode(KisNodeSP node, double radians, KisSelectionSP selection)
1139 {
1140     if (node->inherits("KisMask")) {
1141         rotateImpl(kundo2_i18n("Rotate Mask"), node, radians, false, selection);
1142     } else {
1143         rotateImpl(kundo2_i18n("Rotate Layer"), node, radians, false, selection);
1144     }
1145 }
1146 
1147 void KisImage::shearImpl(const KUndo2MagicString &actionName,
1148                          KisNodeSP rootNode,
1149                          bool resizeImage,
1150                          double angleX, double angleY,
1151                          KisSelectionSP selection)
1152 {
1153     const QRect baseBounds =
1154         resizeImage ? bounds() :
1155         selection ? selection->selectedExactRect() :
1156         rootNode->exactBounds();
1157 
1158     const QPointF origin = QRectF(baseBounds).center();
1159 
1160     //angleX, angleY are in degrees
1161     const qreal pi = 3.1415926535897932385;
1162     const qreal deg2rad = pi / 180.0;
1163 
1164     qreal tanX = tan(angleX * deg2rad);
1165     qreal tanY = tan(angleY * deg2rad);
1166 
1167     QPointF offset;
1168     QSize newSize;
1169 
1170     {
1171         KisTransformWorker worker(0,
1172                                   1.0, 1.0,
1173                                   tanX, tanY, origin.x(), origin.y(),
1174                                   0,
1175                                   0, 0, 0, 0);
1176 
1177         QRect newRect = worker.transform().mapRect(baseBounds);
1178         newSize = newRect.size();
1179         if (resizeImage) offset = -newRect.topLeft();
1180     }
1181 
1182     if (newSize == baseBounds.size()) return;
1183 
1184     KisImageSignalVector emitSignals;
1185     if (resizeImage) emitSignals << ComplexSizeChangedSignal(baseBounds, newSize);
1186 
1187     KisProcessingApplicator::ProcessingFlags signalFlags =
1188         KisProcessingApplicator::RECURSIVE;
1189     if (resizeImage) signalFlags |= KisProcessingApplicator::NO_UI_UPDATES;
1190 
1191     KisProcessingApplicator applicator(this, rootNode,
1192                                        signalFlags,
1193                                        emitSignals, actionName);
1194 
1195     KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Bilinear");
1196 
1197     KisTransformProcessingVisitor *visitor =
1198             new KisTransformProcessingVisitor(1.0, 1.0,
1199                                               tanX, tanY, origin,
1200                                               0,
1201                                               offset.x(), offset.y(),
1202                                               filter);
1203 
1204     if (selection) {
1205         visitor->setSelection(selection);
1206     }
1207 
1208     if (selection) {
1209         applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1210     } else {
1211         applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
1212     }
1213 
1214     if (resizeImage) {
1215         applicator.applyCommand(new KisImageResizeCommand(this, newSize));
1216     }
1217 
1218     applicator.end();
1219 }
1220 
1221 void KisImage::shearNode(KisNodeSP node, double angleX, double angleY, KisSelectionSP selection)
1222 {
1223     if (node->inherits("KisMask")) {
1224         shearImpl(kundo2_i18n("Shear Mask"), node, false,
1225                   angleX, angleY, selection);
1226     } else {
1227         shearImpl(kundo2_i18n("Shear Layer"), node, false,
1228                   angleX, angleY, selection);
1229     }
1230 }
1231 
1232 void KisImage::shear(double angleX, double angleY)
1233 {
1234     shearImpl(kundo2_i18n("Shear Image"), m_d->rootLayer, true,
1235               angleX, angleY, 0);
1236 }
1237 
1238 void KisImage::convertLayerColorSpace(KisNodeSP node,
1239                                       const KoColorSpace *dstColorSpace,
1240                                       KoColorConversionTransformation::Intent renderingIntent,
1241                                       KoColorConversionTransformation::ConversionFlags conversionFlags)
1242 {
1243     if (!node->projectionLeaf()->isLayer()) return;
1244     // must not be an image root, use convertImageColorSpace() for that:
1245     KIS_SAFE_ASSERT_RECOVER_RETURN(!node->image() || (node.data() != node->image()->rootLayer().data()));
1246 
1247     const KoColorSpace *srcColorSpace = node->colorSpace();
1248 
1249     if (!dstColorSpace || *srcColorSpace == *dstColorSpace) return;
1250 
1251     KUndo2MagicString actionName =
1252         kundo2_i18n("Convert Layer Color Space");
1253 
1254     KisImageSignalVector emitSignals;
1255 
1256     KisProcessingApplicator applicator(this, node,
1257                                        KisProcessingApplicator::RECURSIVE,
1258                                        emitSignals, actionName);
1259 
1260     applicator.applyVisitor(
1261         new KisConvertColorSpaceProcessingVisitor(
1262             srcColorSpace, dstColorSpace,
1263             renderingIntent, conversionFlags),
1264         KisStrokeJobData::CONCURRENT);
1265 
1266     applicator.end();
1267 }
1268 
1269 struct KisImage::KisImagePrivate::SetImageProjectionColorSpace : public KisCommandUtils::FlipFlopCommand
1270 {
1271     SetImageProjectionColorSpace(const KoColorSpace *cs, KisImageWSP image,
1272                                  State initialState, KUndo2Command *parent = 0)
1273         : KisCommandUtils::FlipFlopCommand(initialState, parent),
1274           m_cs(cs),
1275           m_image(image)
1276     {
1277     }
1278 
1279     void partA() override {
1280         KisImageSP image = m_image;
1281 
1282         if (image) {
1283             image->setProjectionColorSpace(m_cs);
1284         }
1285     }
1286 
1287 private:
1288     const KoColorSpace *m_cs;
1289     KisImageWSP m_image;
1290 };
1291 
1292 void KisImage::KisImagePrivate::convertImageColorSpaceImpl(const KoColorSpace *dstColorSpace,
1293                                                            bool convertLayers,
1294                                                            KoColorConversionTransformation::Intent renderingIntent,
1295                                                            KoColorConversionTransformation::ConversionFlags conversionFlags)
1296 {
1297     const KoColorSpace *srcColorSpace = this->colorSpace;
1298 
1299     if (!dstColorSpace || *srcColorSpace == *dstColorSpace) return;
1300 
1301     const KUndo2MagicString actionName =
1302         convertLayers ?
1303         kundo2_i18n("Convert Image Color Space") :
1304         kundo2_i18n("Convert Projection Color Space");
1305 
1306     KisImageSignalVector emitSignals;
1307     emitSignals << ColorSpaceChangedSignal;
1308 
1309     KisProcessingApplicator::ProcessingFlags flags = KisProcessingApplicator::NO_UI_UPDATES;
1310     if (convertLayers) {
1311         flags |= KisProcessingApplicator::RECURSIVE;
1312     }
1313 
1314     KisProcessingApplicator applicator(q, this->rootLayer,
1315                                        flags,
1316                                        emitSignals, actionName);
1317 
1318     applicator.applyCommand(
1319         new KisImagePrivate::SetImageProjectionColorSpace(dstColorSpace,
1320                                                           KisImageWSP(q),
1321                                                           KisCommandUtils::FlipFlopCommand::INITIALIZING),
1322         KisStrokeJobData::BARRIER);
1323 
1324     applicator.applyVisitor(
1325                 new KisConvertColorSpaceProcessingVisitor(
1326                     srcColorSpace, dstColorSpace,
1327                     renderingIntent, conversionFlags),
1328                 KisStrokeJobData::CONCURRENT);
1329 
1330     applicator.applyCommand(
1331         new KisImagePrivate::SetImageProjectionColorSpace(srcColorSpace,
1332                                                           KisImageWSP(q),
1333                                                           KisCommandUtils::FlipFlopCommand::FINALIZING),
1334         KisStrokeJobData::BARRIER);
1335 
1336 
1337     applicator.end();
1338 }
1339 
1340 void KisImage::convertImageColorSpace(const KoColorSpace *dstColorSpace,
1341                                       KoColorConversionTransformation::Intent renderingIntent,
1342                                       KoColorConversionTransformation::ConversionFlags conversionFlags)
1343 {
1344     m_d->convertImageColorSpaceImpl(dstColorSpace, true, renderingIntent, conversionFlags);
1345 }
1346 
1347 void KisImage::convertImageProjectionColorSpace(const KoColorSpace *dstColorSpace)
1348 {
1349     m_d->convertImageColorSpaceImpl(dstColorSpace, false,
1350                                     KoColorConversionTransformation::internalRenderingIntent(),
1351                                     KoColorConversionTransformation::internalConversionFlags());
1352 }
1353 
1354 
1355 bool KisImage::assignLayerProfile(KisNodeSP node, const KoColorProfile *profile)
1356 {
1357     const KoColorSpace *srcColorSpace = node->colorSpace();
1358 
1359     if (!node->projectionLeaf()->isLayer()) return false;
1360     if (!profile || *srcColorSpace->profile() == *profile) return false;
1361 
1362     KUndo2MagicString actionName = kundo2_i18n("Assign Profile to Layer");
1363 
1364     KisImageSignalVector emitSignals;
1365 
1366     const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
1367     if (!dstColorSpace) return false;
1368 
1369     KisProcessingApplicator applicator(this, node,
1370                                        KisProcessingApplicator::RECURSIVE |
1371                                        KisProcessingApplicator::NO_UI_UPDATES,
1372                                        emitSignals, actionName);
1373 
1374     applicator.applyVisitor(
1375         new KisAssignProfileProcessingVisitor(
1376             srcColorSpace, dstColorSpace),
1377         KisStrokeJobData::CONCURRENT);
1378 
1379     applicator.end();
1380 
1381     return true;
1382 }
1383 
1384 
1385 bool KisImage::assignImageProfile(const KoColorProfile *profile, bool blockAllUpdates)
1386 {
1387     if (!profile) return false;
1388 
1389     const KoColorSpace *srcColorSpace = m_d->colorSpace;
1390     bool imageProfileIsSame = *srcColorSpace->profile() == *profile;
1391 
1392     imageProfileIsSame &=
1393         !KisLayerUtils::recursiveFindNode(m_d->rootLayer,
1394             [profile] (KisNodeSP node) {
1395                 return *node->colorSpace()->profile() != *profile;
1396             });
1397 
1398     if (imageProfileIsSame) {
1399         dbgImage << "Trying to set the same image profile again" << ppVar(srcColorSpace->profile()->name()) << ppVar(profile->name());
1400         return true;
1401     }
1402 
1403     KUndo2MagicString actionName = kundo2_i18n("Assign Profile");
1404 
1405     KisImageSignalVector emitSignals;
1406     emitSignals << ProfileChangedSignal;
1407 
1408     const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
1409     if (!dstColorSpace) return false;
1410 
1411     KisProcessingApplicator applicator(this, m_d->rootLayer,
1412                                        KisProcessingApplicator::RECURSIVE |
1413                                        (!blockAllUpdates ?
1414                                             KisProcessingApplicator::NO_UI_UPDATES :
1415                                             KisProcessingApplicator::NO_IMAGE_UPDATES),
1416                                        emitSignals, actionName);
1417 
1418     applicator.applyCommand(
1419         new KisImagePrivate::SetImageProjectionColorSpace(dstColorSpace,
1420                                                           KisImageWSP(this),
1421                                                           KisCommandUtils::FlipFlopCommand::INITIALIZING),
1422         KisStrokeJobData::BARRIER);
1423 
1424     applicator.applyVisitor(
1425         new KisAssignProfileProcessingVisitor(
1426             srcColorSpace, dstColorSpace),
1427         KisStrokeJobData::CONCURRENT);
1428 
1429     applicator.applyCommand(
1430         new KisImagePrivate::SetImageProjectionColorSpace(srcColorSpace,
1431                                                           KisImageWSP(this),
1432                                                           KisCommandUtils::FlipFlopCommand::FINALIZING),
1433         KisStrokeJobData::BARRIER);
1434 
1435 
1436     applicator.end();
1437 
1438     return true;
1439 }
1440 
1441 void KisImage::setProjectionColorSpace(const KoColorSpace * colorSpace)
1442 {
1443     m_d->colorSpace = colorSpace;
1444 }
1445 
1446 const KoColorSpace * KisImage::colorSpace() const
1447 {
1448     return m_d->colorSpace;
1449 }
1450 
1451 const KoColorProfile * KisImage::profile() const
1452 {
1453     return colorSpace()->profile();
1454 }
1455 
1456 double KisImage::xRes() const
1457 {
1458     return m_d->xres;
1459 }
1460 
1461 double KisImage::yRes() const
1462 {
1463     return m_d->yres;
1464 }
1465 
1466 void KisImage::setResolution(double xres, double yres)
1467 {
1468     m_d->xres = xres;
1469     m_d->yres = yres;
1470 }
1471 
1472 QPointF KisImage::documentToPixel(const QPointF &documentCoord) const
1473 {
1474     return QPointF(documentCoord.x() * xRes(), documentCoord.y() * yRes());
1475 }
1476 
1477 QPoint KisImage::documentToImagePixelFloored(const QPointF &documentCoord) const
1478 {
1479     QPointF pixelCoord = documentToPixel(documentCoord);
1480     return QPoint(qFloor(pixelCoord.x()), qFloor(pixelCoord.y()));
1481 }
1482 
1483 QRectF KisImage::documentToPixel(const QRectF &documentRect) const
1484 {
1485     return QRectF(documentToPixel(documentRect.topLeft()), documentToPixel(documentRect.bottomRight()));
1486 }
1487 
1488 QPointF KisImage::pixelToDocument(const QPointF &pixelCoord) const
1489 {
1490     return QPointF(pixelCoord.x() / xRes(), pixelCoord.y() / yRes());
1491 }
1492 
1493 QPointF KisImage::pixelToDocument(const QPoint &pixelCoord) const
1494 {
1495     return QPointF((pixelCoord.x() + 0.5) / xRes(), (pixelCoord.y() + 0.5) / yRes());
1496 }
1497 
1498 QRectF KisImage::pixelToDocument(const QRectF &pixelCoord) const
1499 {
1500     return QRectF(pixelToDocument(pixelCoord.topLeft()), pixelToDocument(pixelCoord.bottomRight()));
1501 }
1502 
1503 qint32 KisImage::width() const
1504 {
1505     return m_d->width;
1506 }
1507 
1508 qint32 KisImage::height() const
1509 {
1510     return m_d->height;
1511 }
1512 
1513 KisGroupLayerSP KisImage::rootLayer() const
1514 {
1515     Q_ASSERT(m_d->rootLayer);
1516     return m_d->rootLayer;
1517 }
1518 
1519 KisPaintDeviceSP KisImage::projection() const
1520 {
1521     if (m_d->isolationRootNode) {
1522         return m_d->isolationRootNode->projection();
1523     }
1524 
1525     Q_ASSERT(m_d->rootLayer);
1526     KisPaintDeviceSP projection = m_d->rootLayer->projection();
1527     Q_ASSERT(projection);
1528     return projection;
1529 }
1530 
1531 qint32 KisImage::nlayers() const
1532 {
1533     QStringList list;
1534     list << "KisLayer";
1535 
1536     KisCountVisitor visitor(list, KoProperties());
1537     m_d->rootLayer->accept(visitor);
1538     return visitor.count();
1539 }
1540 
1541 qint32 KisImage::nHiddenLayers() const
1542 {
1543     QStringList list;
1544     list << "KisLayer";
1545     KoProperties properties;
1546     properties.setProperty("visible", false);
1547     KisCountVisitor visitor(list, properties);
1548     m_d->rootLayer->accept(visitor);
1549 
1550     return visitor.count();
1551 }
1552 
1553 qint32 KisImage::nChildLayers() const
1554 {
1555     QStringList list;
1556     list << "KisLayer";
1557 
1558     KoProperties koProperties;
1559     KisCountVisitor visitor(list, koProperties);
1560     for (auto childNode : m_d->rootLayer->childNodes(list, koProperties)) {
1561         childNode->accept(visitor);
1562     }
1563     return visitor.count();
1564 }
1565 
1566 void KisImage::flatten(KisNodeSP activeNode)
1567 {
1568     KisLayerUtils::flattenImage(this, activeNode);
1569 }
1570 
1571 void KisImage::mergeMultipleLayers(QList<KisNodeSP> mergedNodes, KisNodeSP putAfter)
1572 {
1573     if (!KisLayerUtils::tryMergeSelectionMasks(this, mergedNodes, putAfter)) {
1574         KisLayerUtils::mergeMultipleLayers(this, mergedNodes, putAfter);
1575     }
1576 }
1577 
1578 void KisImage::mergeDown(KisLayerSP layer, const KisMetaData::MergeStrategy* strategy)
1579 {
1580     KisLayerUtils::mergeDown(this, layer, strategy);
1581 }
1582 
1583 void KisImage::flattenLayer(KisLayerSP layer)
1584 {
1585     KisLayerUtils::flattenLayer(this, layer);
1586 }
1587 
1588 void KisImage::setModifiedWithoutUndo()
1589 {
1590     m_d->signalRouter.emitNotification(ModifiedWithoutUndoSignal);
1591     emit sigImageModified();
1592 }
1593 
1594 QImage KisImage::convertToQImage(QRect imageRect,
1595                                  const KoColorProfile * profile)
1596 {
1597     qint32 x;
1598     qint32 y;
1599     qint32 w;
1600     qint32 h;
1601     imageRect.getRect(&x, &y, &w, &h);
1602     return convertToQImage(x, y, w, h, profile);
1603 }
1604 
1605 QImage KisImage::convertToQImage(qint32 x,
1606                                  qint32 y,
1607                                  qint32 w,
1608                                  qint32 h,
1609                                  const KoColorProfile * profile)
1610 {
1611     KisPaintDeviceSP dev = projection();
1612     if (!dev) return QImage();
1613     QImage image = dev->convertToQImage(const_cast<KoColorProfile*>(profile), x, y, w, h,
1614                                         KoColorConversionTransformation::internalRenderingIntent(),
1615                                         KoColorConversionTransformation::internalConversionFlags());
1616 
1617     return image;
1618 }
1619 
1620 
1621 
1622 QImage KisImage::convertToQImage(const QSize& scaledImageSize, const KoColorProfile *profile)
1623 {
1624     if (scaledImageSize.isEmpty()) {
1625         return QImage();
1626     }
1627 
1628     KisPaintDeviceSP dev = new KisPaintDevice(colorSpace());
1629     KisPainter gc;
1630     gc.copyAreaOptimized(QPoint(0, 0), projection(), dev, bounds());
1631     gc.end();
1632     double scaleX = qreal(scaledImageSize.width()) / width();
1633     double scaleY = qreal(scaledImageSize.height()) / height();
1634 
1635 
1636     if (scaleX < 1.0/256 || scaleY < 1.0/256) {
1637         // quick checking if we're not trying to scale too much
1638         // convertToQImage uses KisFixedPoint values, which means that the scale cannot be smaller than 1/2^8
1639         // BUG:432182
1640         // FIXME: would be best to extend KisFixedPoint instead
1641         return convertToQImage(size(), profile).scaled(scaledImageSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
1642     }
1643 
1644     QPointer<KoUpdater> updater = new KoDummyUpdater();
1645 
1646     KisTransformWorker worker(dev, scaleX, scaleY, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, updater, KisFilterStrategyRegistry::instance()->value("Bicubic"));
1647     worker.run();
1648 
1649     delete updater;
1650 
1651     return dev->convertToQImage(profile);
1652 }
1653 void KisImage::notifyLayersChanged()
1654 {
1655     m_d->signalRouter.emitNotification(LayersChangedSignal);
1656 }
1657 
1658 QRect KisImage::bounds() const
1659 {
1660     return QRect(0, 0, width(), height());
1661 }
1662 
1663 QRect KisImage::effectiveLodBounds() const
1664 {
1665     QRect boundRect = bounds();
1666 
1667     const int lod = currentLevelOfDetail();
1668     if (lod > 0) {
1669         KisLodTransform t(lod);
1670         boundRect = t.map(boundRect);
1671     }
1672 
1673     return boundRect;
1674 }
1675 
1676 KisPostExecutionUndoAdapter* KisImage::postExecutionUndoAdapter() const
1677 {
1678     const int lod = currentLevelOfDetail();
1679     return lod > 0 ?
1680         m_d->scheduler.lodNPostExecutionUndoAdapter() :
1681         &m_d->postExecutionUndoAdapter;
1682 }
1683 
1684 const KUndo2Command* KisImage::lastExecutedCommand() const
1685 {
1686     return m_d->undoStore->presentCommand();
1687 }
1688 
1689 void KisImage::setUndoStore(KisUndoStore *undoStore)
1690 {
1691     disconnect(m_d->undoStore.data(), SIGNAL(historyStateChanged()), &m_d->signalRouter, SLOT(emitImageModifiedNotification()));
1692 
1693     m_d->legacyUndoAdapter.setUndoStore(undoStore);
1694     m_d->postExecutionUndoAdapter.setUndoStore(undoStore);
1695     m_d->undoStore.reset(undoStore);
1696 
1697     connect(m_d->undoStore.data(), SIGNAL(historyStateChanged()), &m_d->signalRouter, SLOT(emitImageModifiedNotification()));
1698 
1699 }
1700 
1701 KisUndoStore* KisImage::undoStore()
1702 {
1703     return m_d->undoStore.data();
1704 }
1705 
1706 KisUndoAdapter* KisImage::undoAdapter() const
1707 {
1708     return &m_d->legacyUndoAdapter;
1709 }
1710 
1711 void KisImage::setDefaultProjectionColor(const KoColor &color)
1712 {
1713     KIS_ASSERT_RECOVER_RETURN(m_d->rootLayer);
1714     m_d->rootLayer->setDefaultProjectionColor(color);
1715 }
1716 
1717 KoColor KisImage::defaultProjectionColor() const
1718 {
1719     KIS_ASSERT_RECOVER(m_d->rootLayer) {
1720         return KoColor(Qt::transparent, m_d->colorSpace);
1721     }
1722 
1723     return m_d->rootLayer->defaultProjectionColor();
1724 }
1725 
1726 void KisImage::setRootLayer(KisGroupLayerSP rootLayer)
1727 {
1728     emit sigInternalStopIsolatedModeRequested();
1729 
1730     KoColor defaultProjectionColor(Qt::transparent, m_d->colorSpace);
1731 
1732     if (m_d->rootLayer) {
1733         m_d->rootLayer->setGraphListener(0);
1734         m_d->rootLayer->setImage(0);
1735         m_d->rootLayer->disconnect();
1736 
1737         KisPaintDeviceSP original = m_d->rootLayer->original();
1738         defaultProjectionColor = original->defaultPixel();
1739     }
1740 
1741     m_d->rootLayer = rootLayer;
1742     m_d->rootLayer->disconnect();
1743     m_d->rootLayer->setGraphListener(this);
1744     m_d->rootLayer->setImage(this);
1745 
1746     setRoot(m_d->rootLayer.data());
1747     this->setDefaultProjectionColor(defaultProjectionColor);
1748 }
1749 
1750 void KisImage::addAnnotation(KisAnnotationSP annotation)
1751 {
1752     // Find the icc annotation, if there is one
1753     vKisAnnotationSP_it it = m_d->annotations.begin();
1754     while (it != m_d->annotations.end()) {
1755         if ((*it)->type() == annotation->type()) {
1756             *it = annotation;
1757             setModifiedWithoutUndo();
1758             return;
1759         }
1760         ++it;
1761     }
1762     m_d->annotations.push_back(annotation);
1763     setModifiedWithoutUndo();
1764 }
1765 
1766 KisAnnotationSP KisImage::annotation(const QString& type)
1767 {
1768     vKisAnnotationSP_it it = m_d->annotations.begin();
1769     while (it != m_d->annotations.end()) {
1770         if ((*it) && (*it)->type() == type) {
1771             return *it;
1772         }
1773         else if (!*it) {
1774             qWarning() << "Skipping deleted annotation";
1775         }
1776         ++it;
1777     }
1778     return KisAnnotationSP(0);
1779 }
1780 
1781 void KisImage::removeAnnotation(const QString& type)
1782 {
1783     vKisAnnotationSP_it it = m_d->annotations.begin();
1784     while (it != m_d->annotations.end()) {
1785         if ((*it)->type() == type) {
1786             m_d->annotations.erase(it);
1787             setModifiedWithoutUndo();
1788             return;
1789         }
1790         ++it;
1791     }
1792 }
1793 
1794 vKisAnnotationSP_it KisImage::beginAnnotations()
1795 {
1796     return m_d->annotations.begin();
1797 }
1798 
1799 vKisAnnotationSP_it KisImage::endAnnotations()
1800 {
1801     return m_d->annotations.end();
1802 }
1803 
1804 void KisImage::notifyAboutToBeDeleted()
1805 {
1806     emit sigAboutToBeDeleted();
1807 }
1808 
1809 KisImageSignalRouter* KisImage::signalRouter()
1810 {
1811     return &m_d->signalRouter;
1812 }
1813 
1814 void KisImage::waitForDone()
1815 {
1816     requestStrokeEnd();
1817     KisBusyWaitBroker::instance()->notifyWaitOnImageStarted(this);
1818     m_d->scheduler.waitForDone();
1819     KisBusyWaitBroker::instance()->notifyWaitOnImageEnded(this);
1820 }
1821 
1822 KisStrokeId KisImage::startStroke(KisStrokeStrategy *strokeStrategy)
1823 {
1824     /**
1825      * Ask open strokes to end gracefully. All the strokes clients
1826      * (including the one calling this method right now) will get
1827      * a notification that they should probably end their strokes.
1828      * However this is purely their choice whether to end a stroke
1829      * or not.
1830      */
1831     if (strokeStrategy->requestsOtherStrokesToEnd()) {
1832         requestStrokeEnd();
1833     }
1834 
1835     return m_d->scheduler.startStroke(strokeStrategy);
1836 }
1837 
1838 void KisImage::KisImagePrivate::notifyProjectionUpdatedInPatches(const QRect &rc, QVector<KisRunnableStrokeJobData*> &jobs)
1839 {
1840     KisImageConfig imageConfig(true);
1841     int patchWidth = imageConfig.updatePatchWidth();
1842     int patchHeight = imageConfig.updatePatchHeight();
1843 
1844     for (int y = 0; y < rc.height(); y += patchHeight) {
1845         for (int x = 0; x < rc.width(); x += patchWidth) {
1846             QRect patchRect(x, y, patchWidth, patchHeight);
1847             patchRect &= rc;
1848 
1849             KritaUtils::addJobConcurrent(jobs, std::bind(&KisImage::notifyProjectionUpdated, q, patchRect));
1850         }
1851     }
1852 }
1853 
1854 bool KisImage::startIsolatedMode(KisNodeSP node, bool isolateLayer, bool isolateGroup)
1855 {
1856     m_d->isolateLayer = isolateLayer;
1857     m_d->isolateGroup = isolateGroup;
1858     if ((isolateLayer || isolateGroup) == false) return false;
1859 
1860     /**
1861      * Isolation of trnsform masks is not possible, so we should
1862      * not allow that
1863      */
1864     if (!node->projection()) return false;
1865 
1866     struct StartIsolatedModeStroke : public KisRunnableBasedStrokeStrategy {
1867         StartIsolatedModeStroke(KisNodeSP node, KisImageSP image, bool isolateLayer, bool isolateGroup)
1868             : KisRunnableBasedStrokeStrategy(QLatin1String("start-isolated-mode"),
1869                                              kundo2_noi18n("start-isolated-mode")),
1870               m_newRoot(node),
1871               m_image(image),
1872               m_isolateLayer(isolateLayer),
1873               m_isolateGroup(isolateGroup)
1874         {
1875             this->enableJob(JOB_INIT, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
1876             this->enableJob(JOB_DOSTROKE, true);
1877             this->enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
1878             setClearsRedoOnStart(false);
1879         }
1880 
1881         void initStrokeCallback() override {
1882             if (m_isolateLayer == false && m_isolateGroup == true) {
1883                 // Isolate parent node unless node is the root note.
1884                 m_newRoot = m_newRoot->parent() ? m_newRoot->parent() : m_newRoot;
1885             }
1886             // pass-though node don't have any projection prepared, so we should
1887             // explicitly regenerate it before activating isolated mode.
1888             m_newRoot->projectionLeaf()->explicitlyRegeneratePassThroughProjection();
1889             m_prevRoot = m_image->m_d->isolationRootNode;
1890 
1891             const bool beforeVisibility = m_newRoot->projectionLeaf()->visible();
1892             const bool prevRootBeforeVisibility = m_prevRoot ? m_prevRoot->projectionLeaf()->visible() : false;
1893 
1894             m_image->m_d->isolationRootNode = m_newRoot;
1895             emit m_image->sigIsolatedModeChanged();
1896 
1897             const bool afterVisibility = m_newRoot->projectionLeaf()->visible();
1898             const bool prevRootAfterVisibility = m_prevRoot ? m_prevRoot->projectionLeaf()->visible() : false;
1899 
1900             m_newRootNeedsFullRefresh = beforeVisibility != afterVisibility;
1901             m_prevRootNeedsFullRefresh = prevRootBeforeVisibility != prevRootAfterVisibility;
1902         }
1903 
1904         void finishStrokeCallback() override {
1905             // the GUI uses our thread to do the color space conversion so we
1906             // need to emit this signal in multiple threads
1907 
1908             if (m_prevRoot && m_prevRootNeedsFullRefresh) {
1909                 m_image->refreshGraphAsync(m_prevRoot);
1910             }
1911 
1912             if (m_newRootNeedsFullRefresh) {
1913                 m_image->refreshGraphAsync(m_newRoot);
1914             }
1915 
1916             if (!m_prevRootNeedsFullRefresh && !m_newRootNeedsFullRefresh) {
1917                 QVector<KisRunnableStrokeJobData*> jobs;
1918                 m_image->m_d->notifyProjectionUpdatedInPatches(m_image->bounds(), jobs);
1919                 this->runnableJobsInterface()->addRunnableJobs(jobs);
1920             }
1921 
1922             m_image->invalidateAllFrames();
1923         }
1924 
1925     private:
1926         KisNodeSP m_newRoot;
1927         KisNodeSP m_prevRoot;
1928         KisImageSP m_image;
1929         bool m_newRootNeedsFullRefresh = false;
1930         bool m_prevRootNeedsFullRefresh = false;
1931 
1932         bool m_isolateLayer;
1933         bool m_isolateGroup;
1934     };
1935 
1936     KisStrokeId id = startStroke(new StartIsolatedModeStroke(node, this, isolateLayer, isolateGroup));
1937     endStroke(id);
1938 
1939     return true;
1940 }
1941 
1942 void KisImage::stopIsolatedMode()
1943 {
1944     if (!m_d->isolationRootNode)  return;
1945 
1946     struct StopIsolatedModeStroke : public KisRunnableBasedStrokeStrategy {
1947         StopIsolatedModeStroke(KisImageSP image)
1948             : KisRunnableBasedStrokeStrategy(QLatin1String("stop-isolated-mode"), kundo2_noi18n("stop-isolated-mode")),
1949               m_image(image),
1950               m_oldRootNode(nullptr),
1951               m_oldNodeNeedsRefresh(false)
1952         {
1953             this->enableJob(JOB_INIT);
1954             this->enableJob(JOB_DOSTROKE, true);
1955             this->enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
1956             setClearsRedoOnStart(false);
1957         }
1958 
1959         void initStrokeCallback() override {
1960             if (!m_image->m_d->isolationRootNode)  return;
1961 
1962             m_oldRootNode = m_image->m_d->isolationRootNode;
1963 
1964             const bool beforeVisibility = m_oldRootNode->projectionLeaf()->visible();
1965             m_image->m_d->isolationRootNode = 0;
1966             m_image->m_d->isolateLayer = false;
1967             m_image->m_d->isolateGroup = false;
1968             emit m_image->sigIsolatedModeChanged();
1969             const bool afterVisibility = m_oldRootNode->projectionLeaf()->visible();
1970 
1971             m_oldNodeNeedsRefresh = (beforeVisibility != afterVisibility);
1972         }
1973 
1974         void finishStrokeCallback() override {
1975 
1976             m_image->invalidateAllFrames();
1977 
1978             if (m_oldNodeNeedsRefresh){
1979                 m_oldRootNode->setDirty(m_image->bounds());
1980             } else {
1981                 // TODO: Substitute notifyProjectionUpdated() with this code
1982                 // when update optimization is implemented
1983                 //
1984                 // QRect updateRect = bounds() | oldRootNode->extent();
1985                 //oldRootNode->setDirty(updateRect);
1986 
1987                 QVector<KisRunnableStrokeJobData*> jobs;
1988                 m_image->m_d->notifyProjectionUpdatedInPatches(m_image->bounds(), jobs);
1989                 this->runnableJobsInterface()->addRunnableJobs(jobs);
1990             }
1991         }
1992 
1993     private:
1994         KisImageSP m_image;
1995         KisNodeSP m_oldRootNode;
1996         bool m_oldNodeNeedsRefresh;
1997     };
1998 
1999     KisStrokeId id = startStroke(new StopIsolatedModeStroke(this));
2000     endStroke(id);
2001 }
2002 
2003 KisNodeSP KisImage::isolationRootNode() const {
2004     return m_d->isolationRootNode;
2005 }
2006 
2007 bool KisImage::isIsolatingLayer() const
2008 {
2009     return m_d->isolateLayer;
2010 }
2011 
2012 bool KisImage::isIsolatingGroup() const
2013 {
2014      return m_d->isolateGroup;
2015 }
2016 
2017 void KisImage::addJob(KisStrokeId id, KisStrokeJobData *data)
2018 {
2019     KisUpdateTimeMonitor::instance()->reportJobStarted(data);
2020     m_d->scheduler.addJob(id, data);
2021 }
2022 
2023 void KisImage::endStroke(KisStrokeId id)
2024 {
2025     m_d->scheduler.endStroke(id);
2026 }
2027 
2028 bool KisImage::cancelStroke(KisStrokeId id)
2029 {
2030     return m_d->scheduler.cancelStroke(id);
2031 }
2032 
2033 bool KisImage::KisImagePrivate::tryCancelCurrentStrokeAsync()
2034 {
2035     return scheduler.tryCancelCurrentStrokeAsync();
2036 }
2037 
2038 void KisImage::requestUndoDuringStroke()
2039 {
2040     emit sigUndoDuringStrokeRequested();
2041 }
2042 
2043 void KisImage::requestRedoDuringStroke()
2044 {
2045     emit sigRedoDuringStrokeRequested();
2046 }
2047 
2048 void KisImage::requestStrokeCancellation()
2049 {
2050     if (!m_d->tryCancelCurrentStrokeAsync()) {
2051         emit sigStrokeCancellationRequested();
2052     }
2053 }
2054 
2055 UndoResult KisImage::tryUndoUnfinishedLod0Stroke()
2056 {
2057     return m_d->scheduler.tryUndoLastStrokeAsync();
2058 }
2059 
2060 void KisImage::requestStrokeEnd()
2061 {
2062     emit sigStrokeEndRequested();
2063     emit sigStrokeEndRequestedActiveNodeFiltered();
2064 }
2065 
2066 void KisImage::requestStrokeEndActiveNode()
2067 {
2068     emit sigStrokeEndRequested();
2069 }
2070 
2071 void KisImage::refreshGraph(KisNodeSP root)
2072 {
2073     refreshGraph(root, bounds(), bounds());
2074 }
2075 
2076 void KisImage::refreshGraph(KisNodeSP root, const QRect &rc, const QRect &cropRect)
2077 {
2078     if (!root) root = m_d->rootLayer;
2079 
2080     m_d->animationInterface->notifyNodeChanged(root.data(), rc, true);
2081     m_d->scheduler.fullRefresh(root, rc, cropRect);
2082 }
2083 
2084 void KisImage::initialRefreshGraph()
2085 {
2086     /**
2087      * NOTE: Tricky part. We set crop rect to null, so the clones
2088      * will not rely on precalculated projections of their sources
2089      */
2090 
2091     refreshGraphAsync(0, bounds(), QRect());
2092     waitForDone();
2093 }
2094 
2095 void KisImage::refreshGraphAsync(KisNodeSP root, UpdateFlags flags)
2096 {
2097     refreshGraphAsync(root, bounds(), bounds(), flags);
2098 }
2099 
2100 void KisImage::refreshGraphAsync(KisNodeSP root, const QRect &rc, UpdateFlags flags)
2101 {
2102     refreshGraphAsync(root, rc, bounds(), flags);
2103 }
2104 
2105 void KisImage::refreshGraphAsync(KisNodeSP root, const QRect &rc, const QRect &cropRect, UpdateFlags flags)
2106 {
2107     refreshGraphAsync(root, QVector<QRect>({rc}), cropRect, flags);
2108 }
2109 
2110 void KisImage::refreshGraphAsync(KisNodeSP root, const QVector<QRect> &rects, const QRect &cropRect, UpdateFlags flags)
2111 {
2112     if (!root) root = m_d->rootLayer;
2113 
2114     /**
2115      * We iterate through the filters in a reversed way. It makes the most nested filters
2116      * to execute first.
2117      */
2118     for (auto it = m_d->projectionUpdatesFilters.rbegin();
2119          it != m_d->projectionUpdatesFilters.rend();
2120          ++it) {
2121 
2122         KIS_SAFE_ASSERT_RECOVER(*it) { continue; }
2123 
2124         if ((*it)->filterRefreshGraph(this, root.data(), rects, cropRect, flags)) {
2125             return;
2126         }
2127     }
2128 
2129     m_d->animationInterface->notifyNodeChanged(root.data(), rects, true);
2130 
2131     if (flags & NoFilthyUpdate) {
2132         m_d->scheduler.fullRefreshAsyncNoFilthy(root, rects, cropRect);
2133     } else {
2134         m_d->scheduler.fullRefreshAsync(root, rects, cropRect);
2135     }
2136 }
2137 
2138 
2139 void KisImage::requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QRect &rc, const QRect &cropRect)
2140 {
2141     requestProjectionUpdateNoFilthy(pseudoFilthy, rc, cropRect, true);
2142 }
2143 
2144 void KisImage::requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QRect &rc, const QRect &cropRect, const bool resetAnimationCache)
2145 {
2146     requestProjectionUpdateNoFilthy(pseudoFilthy, QVector<QRect>({rc}), cropRect, resetAnimationCache);
2147 }
2148 
2149 void KisImage::requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QVector<QRect> &rects, const QRect &cropRect, const bool resetAnimationCache)
2150 {
2151     KIS_ASSERT_RECOVER_RETURN(pseudoFilthy);
2152 
2153     /**
2154      * We iterate through the filters in a reversed way. It makes the most nested filters
2155      * to execute first.
2156      */
2157     for (auto it = m_d->projectionUpdatesFilters.rbegin();
2158          it != m_d->projectionUpdatesFilters.rend();
2159          ++it) {
2160 
2161         KIS_SAFE_ASSERT_RECOVER(*it) { continue; }
2162 
2163         if ((*it)->filterProjectionUpdateNoFilthy(this, pseudoFilthy.data(), rects, cropRect, resetAnimationCache)) {
2164             return;
2165         }
2166     }
2167 
2168     if (resetAnimationCache) {
2169         m_d->animationInterface->notifyNodeChanged(pseudoFilthy.data(), rects, false);
2170     }
2171 
2172     m_d->scheduler.updateProjectionNoFilthy(pseudoFilthy, rects, cropRect);
2173 }
2174 
2175 void KisImage::addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
2176 {
2177     m_d->scheduler.addSpontaneousJob(spontaneousJob);
2178 }
2179 
2180 bool KisImage::hasUpdatesRunning() const
2181 {
2182     return m_d->scheduler.hasUpdatesRunning();
2183 }
2184 
2185 KisProjectionUpdatesFilterCookie KisImage::addProjectionUpdatesFilter(KisProjectionUpdatesFilterSP filter)
2186 {
2187     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(filter, KisProjectionUpdatesFilterCookie());
2188 
2189     m_d->projectionUpdatesFilters.append(filter);
2190 
2191     return KisProjectionUpdatesFilterCookie(filter.data());
2192 }
2193 
2194 KisProjectionUpdatesFilterSP KisImage::removeProjectionUpdatesFilter(KisProjectionUpdatesFilterCookie cookie)
2195 {
2196     KIS_SAFE_ASSERT_RECOVER_NOOP(cookie);
2197     KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->projectionUpdatesFilters.last() == cookie);
2198 
2199     auto it = std::find(m_d->projectionUpdatesFilters.begin(), m_d->projectionUpdatesFilters.end(), cookie);
2200     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(it != m_d->projectionUpdatesFilters.end(), KisProjectionUpdatesFilterSP());
2201 
2202     KisProjectionUpdatesFilterSP filter = *it;
2203 
2204     m_d->projectionUpdatesFilters.erase(it);
2205 
2206     return filter;
2207 }
2208 
2209 KisProjectionUpdatesFilterCookie KisImage::currentProjectionUpdatesFilter() const
2210 {
2211     return !m_d->projectionUpdatesFilters.isEmpty() ?
2212                 m_d->projectionUpdatesFilters.last().data() :
2213                 KisProjectionUpdatesFilterCookie();
2214 }
2215 
2216 void KisImage::disableDirtyRequests()
2217 {
2218     m_d->disabledUpdatesCookies.push(
2219         addProjectionUpdatesFilter(toQShared(new KisDropAllProjectionUpdatesFilter())));
2220 }
2221 
2222 void KisImage::enableDirtyRequests()
2223 {
2224     KIS_SAFE_ASSERT_RECOVER_RETURN(!m_d->disabledUpdatesCookies.isEmpty());
2225     removeProjectionUpdatesFilter(m_d->disabledUpdatesCookies.pop());
2226 }
2227 
2228 void KisImage::disableUIUpdates()
2229 {
2230     m_d->disableUIUpdateSignals.ref();
2231 }
2232 
2233 void KisImage::notifyBatchUpdateStarted()
2234 {
2235     m_d->signalRouter.emitNotifyBatchUpdateStarted();
2236 }
2237 
2238 void KisImage::notifyBatchUpdateEnded()
2239 {
2240     m_d->signalRouter.emitNotifyBatchUpdateEnded();
2241 }
2242 
2243 void KisImage::notifyUIUpdateCompleted(const QRect &rc)
2244 {
2245     notifyProjectionUpdated(rc);
2246 }
2247 
2248 QVector<QRect> KisImage::enableUIUpdates()
2249 {
2250     m_d->disableUIUpdateSignals.deref();
2251 
2252     QRect rect;
2253     QVector<QRect> postponedUpdates;
2254 
2255     while (m_d->savedDisabledUIUpdates.pop(rect)) {
2256         postponedUpdates.append(rect);
2257     }
2258 
2259     return postponedUpdates;
2260 }
2261 
2262 void KisImage::notifyProjectionUpdated(const QRect &rc)
2263 {
2264     KisUpdateTimeMonitor::instance()->reportUpdateFinished(rc);
2265 
2266     if (!m_d->disableUIUpdateSignals) {
2267         int lod = currentLevelOfDetail();
2268         QRect dirtyRect = !lod ? rc : KisLodTransform::upscaledRect(rc, lod);
2269 
2270         if (dirtyRect.isEmpty()) return;
2271 
2272         emit sigImageUpdated(dirtyRect);
2273     } else {
2274         m_d->savedDisabledUIUpdates.push(rc);
2275     }
2276 }
2277 
2278 void KisImage::setWorkingThreadsLimit(int value)
2279 {
2280     m_d->scheduler.setThreadsLimit(value);
2281 }
2282 
2283 int KisImage::workingThreadsLimit() const
2284 {
2285     return m_d->scheduler.threadsLimit();
2286 }
2287 
2288 void KisImage::notifySelectionChanged()
2289 {
2290     /**
2291      * The selection is calculated asynchronously, so it is not
2292      * handled by disableUIUpdates() and other special signals of
2293      * KisImageSignalRouter
2294      */
2295     m_d->legacyUndoAdapter.emitSelectionChanged();
2296 
2297     /**
2298      * Editing of selection masks doesn't necessary produce a
2299      * setDirty() call, so in the end of the stroke we need to request
2300      * direct update of the UI's cache.
2301      */
2302     if (m_d->isolationRootNode &&
2303         dynamic_cast<KisSelectionMask*>(m_d->isolationRootNode.data())) {
2304 
2305         notifyProjectionUpdated(bounds());
2306     }
2307 }
2308 
2309 void KisImage::requestProjectionUpdateImpl(KisNode *node,
2310                                            const QVector<QRect> &rects,
2311                                            const QRect &cropRect)
2312 {
2313     if (rects.isEmpty()) return;
2314 
2315     m_d->scheduler.updateProjection(node, rects, cropRect);
2316 }
2317 
2318 void KisImage::requestProjectionUpdate(KisNode *node, const QVector<QRect> &rects, bool resetAnimationCache)
2319 {
2320     /**
2321      * We iterate through the filters in a reversed way. It makes the most nested filters
2322      * to execute first.
2323      */
2324     for (auto it = m_d->projectionUpdatesFilters.rbegin();
2325          it != m_d->projectionUpdatesFilters.rend();
2326          ++it) {
2327 
2328         KIS_SAFE_ASSERT_RECOVER(*it) { continue; }
2329 
2330         if ((*it)->filter(this, node, rects, resetAnimationCache)) {
2331             return;
2332         }
2333     }
2334 
2335     if (resetAnimationCache) {
2336         m_d->animationInterface->notifyNodeChanged(node, rects, false);
2337     }
2338 
2339     /**
2340      * Here we use 'permitted' instead of 'active' intentively,
2341      * because the updates may come after the actual stroke has been
2342      * finished. And having some more updates for the stroke not
2343      * supporting the wrap-around mode will not make much harm.
2344      */
2345     if (m_d->wrapAroundModePermitted) {
2346         QVector<QRect> allSplitRects;
2347 
2348         const QRect boundRect = effectiveLodBounds();
2349         Q_FOREACH (const QRect &rc, rects) {
2350             KisWrappedRect splitRect(rc, boundRect);
2351             allSplitRects.append(splitRect);
2352         }
2353 
2354         requestProjectionUpdateImpl(node, allSplitRects, boundRect);
2355 
2356     } else {
2357         requestProjectionUpdateImpl(node, rects, bounds());
2358     }
2359 
2360     KisNodeGraphListener::requestProjectionUpdate(node, rects, resetAnimationCache);
2361 }
2362 
2363 void KisImage::invalidateFrames(const KisTimeSpan &range, const QRect &rect)
2364 {
2365     m_d->animationInterface->invalidateFrames(range, rect);
2366 }
2367 
2368 void KisImage::requestTimeSwitch(int time)
2369 {
2370     m_d->animationInterface->requestTimeSwitchNonGUI(time);
2371 }
2372 
2373 KisNode *KisImage::graphOverlayNode() const
2374 {
2375     return m_d->overlaySelectionMask.data();
2376 }
2377 
2378 void KisImage::keyframeChannelHasBeenAdded(KisNode *node, KisKeyframeChannel *channel)
2379 {
2380     Q_UNUSED(node);
2381     channel->connect(channel, SIGNAL(sigAddedKeyframe(const KisKeyframeChannel*, int)), m_d->animationInterface, SIGNAL(sigKeyframeAdded(const KisKeyframeChannel*, int)), Qt::UniqueConnection);
2382     channel->connect(channel, SIGNAL(sigRemovingKeyframe(const KisKeyframeChannel*,int)), m_d->animationInterface, SIGNAL(sigKeyframeRemoved(const KisKeyframeChannel*, int)), Qt::UniqueConnection);
2383 }
2384 
2385 void KisImage::keyframeChannelAboutToBeRemoved(KisNode *node, KisKeyframeChannel *channel)
2386 {
2387     Q_UNUSED(node);
2388 
2389     channel->disconnect(channel, SIGNAL(sigAddedKeyframe(const KisKeyframeChannel*, int)), m_d->animationInterface, SIGNAL(sigKeyframeAdded(const KisKeyframeChannel*, int)));
2390     channel->disconnect(channel, SIGNAL(sigRemovingKeyframe(const KisKeyframeChannel*, int)), m_d->animationInterface, SIGNAL(sigKeyframeRemoved(const KisKeyframeChannel*, int)));
2391 }
2392 
2393 QList<KisLayerCompositionSP> KisImage::compositions()
2394 {
2395     return m_d->compositions;
2396 }
2397 
2398 void KisImage::addComposition(KisLayerCompositionSP composition)
2399 {
2400     m_d->compositions.append(composition);
2401 }
2402 
2403 void KisImage::removeComposition(KisLayerCompositionSP composition)
2404 {
2405     m_d->compositions.removeAll(composition);
2406 }
2407 
2408 void KisImage::moveCompositionUp(KisLayerCompositionSP composition)
2409 {
2410     int index = m_d->compositions.indexOf(composition);
2411     if (index <= 0) {
2412         return;
2413     }
2414     m_d->compositions.move(index, index - 1);
2415 }
2416 
2417 void KisImage::moveCompositionDown(KisLayerCompositionSP composition)
2418 {
2419     int index = m_d->compositions.indexOf(composition);
2420     if (index >= m_d->compositions.size() -1) {
2421         return;
2422     }
2423     m_d->compositions.move(index, index + 1);
2424 }
2425 
2426 bool checkMasksNeedConversion(KisNodeSP root, const QRect &bounds)
2427 {
2428     KisSelectionMask *mask = dynamic_cast<KisSelectionMask*>(root.data());
2429     if (mask &&
2430         (!bounds.contains(mask->paintDevice()->exactBounds()) ||
2431          mask->selection()->hasShapeSelection())) {
2432 
2433         return true;
2434     }
2435 
2436     KisNodeSP node = root->firstChild();
2437 
2438     while (node) {
2439         if (checkMasksNeedConversion(node, bounds)) {
2440             return true;
2441         }
2442 
2443         node = node->nextSibling();
2444     }
2445 
2446     return false;
2447 }
2448 
2449 void KisImage::setWrapAroundModePermitted(bool value)
2450 {
2451     if (m_d->wrapAroundModePermitted != value) {
2452         requestStrokeEnd();
2453     }
2454 
2455     m_d->wrapAroundModePermitted = value;
2456 
2457     if (m_d->wrapAroundModePermitted &&
2458         checkMasksNeedConversion(root(), bounds())) {
2459 
2460         KisProcessingApplicator applicator(this, root(),
2461                                            KisProcessingApplicator::RECURSIVE,
2462                                            KisImageSignalVector(),
2463                                            kundo2_i18n("Crop Selections"));
2464 
2465         KisProcessingVisitorSP visitor =
2466             new KisCropSelectionsProcessingVisitor(bounds());
2467 
2468         applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
2469         applicator.end();
2470     }
2471 }
2472 
2473 bool KisImage::wrapAroundModePermitted() const
2474 {
2475     return m_d->wrapAroundModePermitted;
2476 }
2477 
2478 bool KisImage::wrapAroundModeActive() const
2479 {
2480     return m_d->wrapAroundModePermitted &&
2481         m_d->scheduler.wrapAroundModeSupported();
2482 }
2483 
2484 int KisImage::currentLevelOfDetail() const
2485 {
2486     return m_d->scheduler.currentLevelOfDetail();
2487 }
2488 
2489 void KisImage::explicitRegenerateLevelOfDetail()
2490 {
2491     const KisLodPreferences pref = m_d->scheduler.lodPreferences();
2492 
2493     if (pref.lodSupported() && pref.lodPreferred()) {
2494         m_d->scheduler.explicitRegenerateLevelOfDetail();
2495     }
2496 }
2497 
2498 void KisImage::setLodPreferences(const KisLodPreferences &value)
2499 {
2500     m_d->scheduler.setLodPreferences(value);
2501 }
2502 
2503 KisLodPreferences KisImage::lodPreferences() const
2504 {
2505     return m_d->scheduler.lodPreferences();
2506 }
2507 
2508 void KisImage::nodeCollapsedChanged(KisNode * node)
2509 {
2510     Q_UNUSED(node);
2511     emit sigNodeCollapsedChanged();
2512 }
2513 
2514 KisImageAnimationInterface* KisImage::animationInterface() const
2515 {
2516     return m_d->animationInterface;
2517 }
2518 
2519 void KisImage::setProofingConfiguration(KisProofingConfigurationSP proofingConfig)
2520 {
2521     m_d->proofingConfig = proofingConfig;
2522     emit sigProofingConfigChanged();
2523 }
2524 
2525 KisProofingConfigurationSP KisImage::proofingConfiguration() const
2526 {
2527     if (m_d->proofingConfig) {
2528         return m_d->proofingConfig;
2529     }
2530     return KisProofingConfigurationSP();
2531 }
2532 
2533 QPointF KisImage::mirrorAxesCenter() const
2534 {
2535     return m_d->axesCenter;
2536 }
2537 
2538 void KisImage::setMirrorAxesCenter(const QPointF &value) const
2539 {
2540     m_d->axesCenter = value;
2541 }
2542 
2543 void KisImage::setAllowMasksOnRootNode(bool value)
2544 {
2545     m_d->allowMasksOnRootNode = value;
2546 }
2547 
2548 bool KisImage::allowMasksOnRootNode() const
2549 {
2550     return m_d->allowMasksOnRootNode;
2551 }