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 ¢er, 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 ¢er, 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 }