File indexing completed on 2024-05-19 04:26:16

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