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