1 /*
2 * Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
3 * Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include "kis_image.h"
21
22 #include <KoConfig.h> // WORDS_BIGENDIAN
23
24 #include <stdlib.h>
25 #include <math.h>
26
27 #include <QImage>
28 #include <QPainter>
29 #include <QSize>
30 #include <QDateTime>
31 #include <QRect>
32 #include <QtConcurrent>
33
34 #include <klocalizedstring.h>
35
36 #include "KoColorSpaceRegistry.h"
37 #include "KoColor.h"
38 #include "KoColorProfile.h"
39 #include <KoCompositeOpRegistry.h>
40 #include "KisProofingConfiguration.h"
41
42 #include "kis_adjustment_layer.h"
43 #include "kis_annotation.h"
44 #include "kis_count_visitor.h"
45 #include "kis_filter_strategy.h"
46 #include "kis_group_layer.h"
47 #include "commands/kis_image_commands.h"
48 #include "kis_layer.h"
49 #include "kis_meta_data_merge_strategy_registry.h"
50 #include "kis_paint_layer.h"
51 #include "kis_projection_leaf.h"
52 #include "kis_painter.h"
53 #include "kis_selection.h"
54 #include "kis_transaction.h"
55 #include "kis_meta_data_merge_strategy.h"
56 #include "kis_memory_statistics_server.h"
57 #include "kis_node.h"
58 #include "kis_types.h"
59
60 #include "kis_image_config.h"
61 #include "kis_update_scheduler.h"
62 #include "kis_image_signal_router.h"
63 #include "kis_image_animation_interface.h"
64 #include "kis_stroke_strategy.h"
65 #include "kis_simple_stroke_strategy.h"
66 #include "kis_image_barrier_locker.h"
67
68
69 #include "kis_undo_stores.h"
70 #include "kis_legacy_undo_adapter.h"
71 #include "kis_post_execution_undo_adapter.h"
72
73 #include "kis_transform_worker.h"
74 #include "kis_processing_applicator.h"
75 #include "processing/kis_crop_processing_visitor.h"
76 #include "processing/kis_crop_selections_processing_visitor.h"
77 #include "processing/kis_transform_processing_visitor.h"
78 #include "processing/kis_convert_color_space_processing_visitor.h"
79 #include "processing/kis_assign_profile_processing_visitor.h"
80 #include "commands_new/kis_image_resize_command.h"
81 #include "commands_new/kis_image_set_resolution_command.h"
82 #include "commands_new/kis_activate_selection_mask_command.h"
83 #include "kis_do_something_command.h"
84 #include "kis_composite_progress_proxy.h"
85 #include "kis_layer_composition.h"
86 #include "kis_wrapped_rect.h"
87 #include "kis_crop_saved_extra_data.h"
88 #include "kis_layer_utils.h"
89
90 #include "kis_lod_transform.h"
91
92 #include "kis_suspend_projection_updates_stroke_strategy.h"
93 #include "kis_sync_lod_cache_stroke_strategy.h"
94
95 #include "kis_projection_updates_filter.h"
96
97 #include "kis_layer_projection_plane.h"
98
99 #include "kis_update_time_monitor.h"
100 #include "kis_lockless_stack.h"
101
102 #include <QtCore>
103
104 #include <functional>
105
106 #include "kis_time_range.h"
107
108 #include "KisRunnableBasedStrokeStrategy.h"
109 #include "KisRunnableStrokeJobData.h"
110 #include "KisRunnableStrokeJobUtils.h"
111 #include "KisRunnableStrokeJobsInterface.h"
112
113 #include "KisBusyWaitBroker.h"
114
115
116 // #define SANITY_CHECKS
117
118 #ifdef SANITY_CHECKS
119 #define SANITY_CHECK_LOCKED(name) \
120 if (!locked()) warnKrita() << "Locking policy failed:" << name \
121 << "has been called without the image" \
122 "being locked";
123 #else
124 #define SANITY_CHECK_LOCKED(name)
125 #endif
126
127
128 struct KisImageSPStaticRegistrar {
KisImageSPStaticRegistrarKisImageSPStaticRegistrar129 KisImageSPStaticRegistrar() {
130 qRegisterMetaType<KisImageSP>("KisImageSP");
131 }
132 };
133 static KisImageSPStaticRegistrar __registrar;
134
135 class KisImage::KisImagePrivate
136 {
137 public:
KisImagePrivate(KisImage * _q,qint32 w,qint32 h,const KoColorSpace * c,KisUndoStore * undo,KisImageAnimationInterface * _animationInterface)138 KisImagePrivate(KisImage *_q, qint32 w, qint32 h,
139 const KoColorSpace *c,
140 KisUndoStore *undo,
141 KisImageAnimationInterface *_animationInterface)
142 : q(_q)
143 , lockedForReadOnly(false)
144 , width(w)
145 , height(h)
146 , colorSpace(c ? c : KoColorSpaceRegistry::instance()->rgb8())
147 , undoStore(undo ? undo : new KisDumbUndoStore())
148 , legacyUndoAdapter(undoStore.data(), _q)
149 , postExecutionUndoAdapter(undoStore.data(), _q)
150 , signalRouter(_q)
151 , animationInterface(_animationInterface)
152 , scheduler(_q, _q)
153 , axesCenter(QPointF(0.5, 0.5))
154 {
155 {
156 KisImageConfig cfg(true);
157 if (cfg.enableProgressReporting()) {
158 scheduler.setProgressProxy(&compositeProgressProxy);
159 }
160
161 // Each of these lambdas defines a new factory function.
162 scheduler.setLod0ToNStrokeStrategyFactory(
163 [=](bool forgettable) {
164 return KisLodSyncPair(
165 new KisSyncLodCacheStrokeStrategy(KisImageWSP(q), forgettable),
166 KisSyncLodCacheStrokeStrategy::createJobsData(KisImageWSP(q)));
167 });
168
169 scheduler.setSuspendResumeUpdatesStrokeStrategyFactory(
170 [=]() {
171 KisSuspendProjectionUpdatesStrokeStrategy::SharedDataSP data = KisSuspendProjectionUpdatesStrokeStrategy::createSharedData();
172
173 KisSuspendResumePair suspend(new KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP(q), true, data),
174 KisSuspendProjectionUpdatesStrokeStrategy::createSuspendJobsData(KisImageWSP(q)));
175 KisSuspendResumePair resume(new KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP(q), false, data),
176 KisSuspendProjectionUpdatesStrokeStrategy::createResumeJobsData(KisImageWSP(q)));
177
178 return std::make_pair(suspend, resume);
179 });
180 }
181
182 connect(q, SIGNAL(sigImageModified()), KisMemoryStatisticsServer::instance(), SLOT(notifyImageChanged()));
183 }
184
~KisImagePrivate()185 ~KisImagePrivate() {
186 /**
187 * Stop animation interface. It may use the rootLayer.
188 */
189 delete animationInterface;
190
191 /**
192 * First delete the nodes, while strokes
193 * and undo are still alive
194 */
195 rootLayer.clear();
196 }
197
198 KisImage *q;
199
200 quint32 lockCount = 0;
201 bool lockedForReadOnly;
202
203 qint32 width;
204 qint32 height;
205
206 double xres = 1.0;
207 double yres = 1.0;
208
209 const KoColorSpace * colorSpace;
210 KisProofingConfigurationSP proofingConfig;
211
212 KisSelectionSP deselectedGlobalSelection;
213 KisGroupLayerSP rootLayer; // The layers are contained in here
214 KisSelectionMaskSP targetOverlaySelectionMask; // the overlay switching stroke will try to switch into this mask
215 KisSelectionMaskSP overlaySelectionMask;
216 QList<KisLayerCompositionSP> compositions;
217 KisNodeSP isolatedRootNode;
218 bool wrapAroundModePermitted = false;
219
220 QScopedPointer<KisUndoStore> undoStore;
221 KisLegacyUndoAdapter legacyUndoAdapter;
222 KisPostExecutionUndoAdapter postExecutionUndoAdapter;
223
224 vKisAnnotationSP annotations;
225
226 QAtomicInt disableUIUpdateSignals;
227 KisLocklessStack<QRect> savedDisabledUIUpdates;
228
229 // filters are applied in a reversed way, from rbegin() to rend()
230 QVector<KisProjectionUpdatesFilterSP> projectionUpdatesFilters;
231 QStack<KisProjectionUpdatesFilterCookie> disabledUpdatesCookies;
232 KisImageSignalRouter signalRouter;
233 KisImageAnimationInterface *animationInterface;
234 KisUpdateScheduler scheduler;
235 QAtomicInt disableDirtyRequests;
236
237
238 KisCompositeProgressProxy compositeProgressProxy;
239
240 bool blockLevelOfDetail = false;
241
242 QPointF axesCenter;
243 bool allowMasksOnRootNode = false;
244
245 bool tryCancelCurrentStrokeAsync();
246
247 void notifyProjectionUpdatedInPatches(const QRect &rc, QVector<KisRunnableStrokeJobData *> &jobs);
248
249 void convertImageColorSpaceImpl(const KoColorSpace *dstColorSpace,
250 bool convertLayers,
251 KoColorConversionTransformation::Intent renderingIntent,
252 KoColorConversionTransformation::ConversionFlags conversionFlags);
253
254 struct SetImageProjectionColorSpace;
255 };
256
KisImage(KisUndoStore * undoStore,qint32 width,qint32 height,const KoColorSpace * colorSpace,const QString & name)257 KisImage::KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace *colorSpace, const QString& name)
258 : QObject(0)
259 , KisShared()
260 , m_d(new KisImagePrivate(this, width, height,
261 colorSpace, undoStore,
262 new KisImageAnimationInterface(this)))
263 {
264 // make sure KisImage belongs to the GUI thread
265 moveToThread(qApp->thread());
266 connect(this, SIGNAL(sigInternalStopIsolatedModeRequested()), SLOT(stopIsolatedMode()));
267
268 setObjectName(name);
269 setRootLayer(new KisGroupLayer(this, "root", OPACITY_OPAQUE_U8));
270 }
271
~KisImage()272 KisImage::~KisImage()
273 {
274 /**
275 * Request the tools to end currently running strokes
276 */
277 waitForDone();
278
279 delete m_d;
280 disconnect(); // in case Qt gets confused
281 }
282
fromQImage(const QImage & image,KisUndoStore * undoStore)283 KisImageSP KisImage::fromQImage(const QImage &image, KisUndoStore *undoStore)
284 {
285 const KoColorSpace *colorSpace = 0;
286
287 switch (image.format()) {
288 case QImage::Format_Invalid:
289 case QImage::Format_Mono:
290 case QImage::Format_MonoLSB:
291 colorSpace = KoColorSpaceRegistry::instance()->graya8();
292 break;
293 case QImage::Format_Indexed8:
294 case QImage::Format_RGB32:
295 case QImage::Format_ARGB32:
296 case QImage::Format_ARGB32_Premultiplied:
297 colorSpace = KoColorSpaceRegistry::instance()->rgb8();
298 break;
299 case QImage::Format_RGB16:
300 colorSpace = KoColorSpaceRegistry::instance()->rgb16();
301 break;
302 case QImage::Format_ARGB8565_Premultiplied:
303 case QImage::Format_RGB666:
304 case QImage::Format_ARGB6666_Premultiplied:
305 case QImage::Format_RGB555:
306 case QImage::Format_ARGB8555_Premultiplied:
307 case QImage::Format_RGB888:
308 case QImage::Format_RGB444:
309 case QImage::Format_ARGB4444_Premultiplied:
310 case QImage::Format_RGBX8888:
311 case QImage::Format_RGBA8888:
312 case QImage::Format_RGBA8888_Premultiplied:
313 colorSpace = KoColorSpaceRegistry::instance()->rgb8();
314 break;
315 case QImage::Format_BGR30:
316 case QImage::Format_A2BGR30_Premultiplied:
317 case QImage::Format_RGB30:
318 case QImage::Format_A2RGB30_Premultiplied:
319 colorSpace = KoColorSpaceRegistry::instance()->rgb8();
320 break;
321 case QImage::Format_Alpha8:
322 colorSpace = KoColorSpaceRegistry::instance()->alpha8();
323 break;
324 case QImage::Format_Grayscale8:
325 colorSpace = KoColorSpaceRegistry::instance()->graya8();
326 break;
327 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
328 case QImage::Format_Grayscale16:
329 colorSpace = KoColorSpaceRegistry::instance()->graya16();
330 break;
331 #endif
332 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
333 case QImage::Format_RGBX64:
334 case QImage::Format_RGBA64:
335 case QImage::Format_RGBA64_Premultiplied:
336 colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), 0);
337 break;
338 #endif
339 default:
340 colorSpace = 0;
341 }
342
343 KisImageSP img = new KisImage(undoStore, image.width(), image.height(), colorSpace, i18n("Imported Image"));
344 KisPaintLayerSP layer = new KisPaintLayer(img, img->nextLayerName(), 255);
345 layer->paintDevice()->convertFromQImage(image, 0, 0, 0);
346 img->addNode(layer.data(), img->rootLayer().data());
347
348 return img;
349 }
350
clone(bool exactCopy)351 KisImage *KisImage::clone(bool exactCopy)
352 {
353 return new KisImage(*this, 0, exactCopy);
354 }
355
copyFromImage(const KisImage & rhs)356 void KisImage::copyFromImage(const KisImage &rhs)
357 {
358 copyFromImageImpl(rhs, REPLACE);
359 }
360
copyFromImageImpl(const KisImage & rhs,int policy)361 void KisImage::copyFromImageImpl(const KisImage &rhs, int policy)
362 {
363 // make sure we choose exactly one from REPLACE and CONSTRUCT
364 KIS_ASSERT_RECOVER_RETURN((policy & REPLACE) != (policy & CONSTRUCT));
365
366 // only when replacing do we need to emit signals
367 #define EMIT_IF_NEEDED if (!(policy & REPLACE)) {} else emit
368
369 if (policy & REPLACE) { // if we are constructing the image, these are already set
370 if (m_d->width != rhs.width() || m_d->height != rhs.height()) {
371 m_d->width = rhs.width();
372 m_d->height = rhs.height();
373 emit sigSizeChanged(QPointF(), QPointF());
374 }
375 if (m_d->colorSpace != rhs.colorSpace()) {
376 m_d->colorSpace = rhs.colorSpace();
377 emit sigColorSpaceChanged(m_d->colorSpace);
378 }
379 }
380
381 // from KisImage::KisImage(const KisImage &, KisUndoStore *, bool)
382 setObjectName(rhs.objectName());
383
384 if (m_d->xres != rhs.m_d->xres || m_d->yres != rhs.m_d->yres) {
385 m_d->xres = rhs.m_d->xres;
386 m_d->yres = rhs.m_d->yres;
387 EMIT_IF_NEEDED sigResolutionChanged(m_d->xres, m_d->yres);
388 }
389
390 m_d->allowMasksOnRootNode = rhs.m_d->allowMasksOnRootNode;
391
392 if (rhs.m_d->proofingConfig) {
393 KisProofingConfigurationSP proofingConfig(new KisProofingConfiguration(*rhs.m_d->proofingConfig));
394 if (policy & REPLACE) {
395 setProofingConfiguration(proofingConfig);
396 } else {
397 m_d->proofingConfig = proofingConfig;
398 }
399 }
400
401 KisNodeSP newRoot = rhs.root()->clone();
402 newRoot->setGraphListener(this);
403 newRoot->setImage(this);
404 m_d->rootLayer = dynamic_cast<KisGroupLayer*>(newRoot.data());
405 setRoot(newRoot);
406
407 bool exactCopy = policy & EXACT_COPY;
408
409 if (exactCopy || rhs.m_d->isolatedRootNode || rhs.m_d->overlaySelectionMask) {
410 QQueue<KisNodeSP> linearizedNodes;
411 KisLayerUtils::recursiveApplyNodes(rhs.root(),
412 [&linearizedNodes](KisNodeSP node) {
413 linearizedNodes.enqueue(node);
414 });
415 KisLayerUtils::recursiveApplyNodes(newRoot,
416 [&linearizedNodes, exactCopy, &rhs, this](KisNodeSP node) {
417 KisNodeSP refNode = linearizedNodes.dequeue();
418
419 if (exactCopy) {
420 node->setUuid(refNode->uuid());
421 }
422
423 if (rhs.m_d->isolatedRootNode &&
424 rhs.m_d->isolatedRootNode == refNode) {
425 m_d->isolatedRootNode = node;
426 }
427
428 if (rhs.m_d->overlaySelectionMask &&
429 KisNodeSP(rhs.m_d->overlaySelectionMask) == refNode) {
430 m_d->targetOverlaySelectionMask = dynamic_cast<KisSelectionMask*>(node.data());
431 m_d->overlaySelectionMask = m_d->targetOverlaySelectionMask;
432 m_d->rootLayer->notifyChildMaskChanged();
433 }
434 });
435 }
436
437 KisLayerUtils::recursiveApplyNodes(newRoot,
438 [](KisNodeSP node) {
439 dbgImage << "Node: " << (void *)node.data();
440 });
441
442 m_d->compositions.clear();
443
444 Q_FOREACH (KisLayerCompositionSP comp, rhs.m_d->compositions) {
445 m_d->compositions << toQShared(new KisLayerComposition(*comp, this));
446 }
447
448 EMIT_IF_NEEDED sigLayersChangedAsync();
449
450 vKisAnnotationSP newAnnotations;
451 Q_FOREACH (KisAnnotationSP annotation, rhs.m_d->annotations) {
452 newAnnotations << annotation->clone();
453 }
454 m_d->annotations = newAnnotations;
455
456 KIS_ASSERT_RECOVER_NOOP(rhs.m_d->projectionUpdatesFilters.isEmpty());
457 KIS_ASSERT_RECOVER_NOOP(!rhs.m_d->disableUIUpdateSignals);
458 KIS_ASSERT_RECOVER_NOOP(!rhs.m_d->disableDirtyRequests);
459
460 m_d->blockLevelOfDetail = rhs.m_d->blockLevelOfDetail;
461
462 #undef EMIT_IF_NEEDED
463 }
464
KisImage(const KisImage & rhs,KisUndoStore * undoStore,bool exactCopy)465 KisImage::KisImage(const KisImage& rhs, KisUndoStore *undoStore, bool exactCopy)
466 : KisNodeFacade(),
467 KisNodeGraphListener(),
468 KisShared(),
469 m_d(new KisImagePrivate(this,
470 rhs.width(), rhs.height(),
471 rhs.colorSpace(),
472 undoStore ? undoStore : new KisDumbUndoStore(),
473 new KisImageAnimationInterface(*rhs.animationInterface(), this)))
474 {
475 // make sure KisImage belongs to the GUI thread
476 moveToThread(qApp->thread());
477 connect(this, SIGNAL(sigInternalStopIsolatedModeRequested()), SLOT(stopIsolatedMode()));
478
479 copyFromImageImpl(rhs, CONSTRUCT | (exactCopy ? EXACT_COPY : 0));
480 }
481
aboutToAddANode(KisNode * parent,int index)482 void KisImage::aboutToAddANode(KisNode *parent, int index)
483 {
484 KisNodeGraphListener::aboutToAddANode(parent, index);
485 SANITY_CHECK_LOCKED("aboutToAddANode");
486 }
487
nodeHasBeenAdded(KisNode * parent,int index)488 void KisImage::nodeHasBeenAdded(KisNode *parent, int index)
489 {
490 KisNodeGraphListener::nodeHasBeenAdded(parent, index);
491
492 SANITY_CHECK_LOCKED("nodeHasBeenAdded");
493 m_d->signalRouter.emitNodeHasBeenAdded(parent, index);
494 }
495
aboutToRemoveANode(KisNode * parent,int index)496 void KisImage::aboutToRemoveANode(KisNode *parent, int index)
497 {
498 KisNodeSP deletedNode = parent->at(index);
499 if (!dynamic_cast<KisSelectionMask*>(deletedNode.data()) &&
500 deletedNode == m_d->isolatedRootNode) {
501
502 emit sigInternalStopIsolatedModeRequested();
503 }
504
505 KisNodeGraphListener::aboutToRemoveANode(parent, index);
506
507 SANITY_CHECK_LOCKED("aboutToRemoveANode");
508 m_d->signalRouter.emitAboutToRemoveANode(parent, index);
509 }
510
nodeChanged(KisNode * node)511 void KisImage::nodeChanged(KisNode* node)
512 {
513 KisNodeGraphListener::nodeChanged(node);
514 requestStrokeEnd();
515 m_d->signalRouter.emitNodeChanged(node);
516 }
517
invalidateAllFrames()518 void KisImage::invalidateAllFrames()
519 {
520 invalidateFrames(KisTimeRange::infinite(0), QRect());
521 }
522
setOverlaySelectionMask(KisSelectionMaskSP mask)523 void KisImage::setOverlaySelectionMask(KisSelectionMaskSP mask)
524 {
525 if (m_d->targetOverlaySelectionMask == mask) return;
526
527 m_d->targetOverlaySelectionMask = mask;
528
529 struct UpdateOverlaySelectionStroke : public KisSimpleStrokeStrategy {
530 UpdateOverlaySelectionStroke(KisImageSP image)
531 : KisSimpleStrokeStrategy(QLatin1String("update-overlay-selection-mask"), kundo2_noi18n("update-overlay-selection-mask")),
532 m_image(image)
533 {
534 this->enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
535 setClearsRedoOnStart(false);
536 }
537
538 void initStrokeCallback() {
539 KisSelectionMaskSP oldMask = m_image->m_d->overlaySelectionMask;
540 KisSelectionMaskSP newMask = m_image->m_d->targetOverlaySelectionMask;
541 if (oldMask == newMask) return;
542
543 KIS_SAFE_ASSERT_RECOVER_RETURN(!newMask || newMask->graphListener() == m_image);
544
545 m_image->m_d->overlaySelectionMask = newMask;
546
547 if (oldMask || newMask) {
548 m_image->m_d->rootLayer->notifyChildMaskChanged();
549 }
550
551 if (oldMask) {
552 m_image->m_d->rootLayer->setDirtyDontResetAnimationCache(oldMask->extent());
553 }
554
555 if (newMask) {
556 newMask->setDirty();
557 }
558
559 m_image->undoAdapter()->emitSelectionChanged();
560 }
561
562 private:
563 KisImageSP m_image;
564 };
565
566 KisStrokeId id = startStroke(new UpdateOverlaySelectionStroke(this));
567 endStroke(id);
568 }
569
overlaySelectionMask() const570 KisSelectionMaskSP KisImage::overlaySelectionMask() const
571 {
572 return m_d->overlaySelectionMask;
573 }
574
hasOverlaySelectionMask() const575 bool KisImage::hasOverlaySelectionMask() const
576 {
577 return m_d->overlaySelectionMask;
578 }
579
globalSelection() const580 KisSelectionSP KisImage::globalSelection() const
581 {
582 KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
583 if (selectionMask) {
584 return selectionMask->selection();
585 } else {
586 return 0;
587 }
588 }
589
setGlobalSelection(KisSelectionSP globalSelection)590 void KisImage::setGlobalSelection(KisSelectionSP globalSelection)
591 {
592 KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
593
594 if (!globalSelection) {
595 if (selectionMask) {
596 removeNode(selectionMask);
597 }
598 }
599 else {
600 if (!selectionMask) {
601 selectionMask = new KisSelectionMask(this, i18n("Selection Mask"));
602 selectionMask->initSelection(m_d->rootLayer);
603 addNode(selectionMask);
604 // If we do not set the selection now, the setActive call coming next
605 // can be very, very expensive, depending on the size of the image.
606 selectionMask->setSelection(globalSelection);
607 selectionMask->setActive(true);
608 }
609 else {
610 selectionMask->setSelection(globalSelection);
611 }
612
613 KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->rootLayer->childCount() > 0);
614 KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->rootLayer->selectionMask());
615 }
616
617 m_d->deselectedGlobalSelection = 0;
618 m_d->legacyUndoAdapter.emitSelectionChanged();
619 }
620
deselectGlobalSelection()621 void KisImage::deselectGlobalSelection()
622 {
623 KisSelectionSP savedSelection = globalSelection();
624 setGlobalSelection(0);
625 m_d->deselectedGlobalSelection = savedSelection;
626 }
627
canReselectGlobalSelection()628 bool KisImage::canReselectGlobalSelection()
629 {
630 return m_d->deselectedGlobalSelection;
631 }
632
reselectGlobalSelection()633 void KisImage::reselectGlobalSelection()
634 {
635 if(m_d->deselectedGlobalSelection) {
636 setGlobalSelection(m_d->deselectedGlobalSelection);
637 }
638 }
639
nextLayerName(const QString & _baseName) const640 QString KisImage::nextLayerName(const QString &_baseName) const
641 {
642 QString baseName = _baseName;
643
644 int numLayers = 0;
645 int maxLayerIndex = 0;
646 QRegularExpression numberedLayerRegexp(".* (\\d+)$");
647 KisLayerUtils::recursiveApplyNodes(root(),
648 [&numLayers, &maxLayerIndex, &numberedLayerRegexp] (KisNodeSP node) {
649 if (node->inherits("KisLayer")) {
650 QRegularExpressionMatch match = numberedLayerRegexp.match(node->name());
651
652 if (match.hasMatch()) {
653 maxLayerIndex = qMax(maxLayerIndex, match.captured(1).toInt());
654 }
655 numLayers++;
656 }
657 });
658
659 // special case if there is only root node
660 if (numLayers == 1) {
661 return i18n("background");
662 }
663
664 if (baseName.isEmpty()) {
665 baseName = i18n("Paint Layer");
666 }
667
668 return QString("%1 %2").arg(baseName).arg(maxLayerIndex + 1);
669 }
670
compositeProgressProxy()671 KisCompositeProgressProxy* KisImage::compositeProgressProxy()
672 {
673 return &m_d->compositeProgressProxy;
674 }
675
locked() const676 bool KisImage::locked() const
677 {
678 return m_d->lockCount != 0;
679 }
680
barrierLock(bool readOnly)681 void KisImage::barrierLock(bool readOnly)
682 {
683 if (!locked()) {
684 requestStrokeEnd();
685 KisBusyWaitBroker::instance()->notifyWaitOnImageStarted(this);
686 m_d->scheduler.barrierLock();
687 KisBusyWaitBroker::instance()->notifyWaitOnImageEnded(this);
688 m_d->lockedForReadOnly = readOnly;
689 } else {
690 m_d->lockedForReadOnly &= readOnly;
691 }
692
693 m_d->lockCount++;
694 }
695
tryBarrierLock(bool readOnly)696 bool KisImage::tryBarrierLock(bool readOnly)
697 {
698 bool result = true;
699
700 if (!locked()) {
701 result = m_d->scheduler.tryBarrierLock();
702 m_d->lockedForReadOnly = readOnly;
703 }
704
705 if (result) {
706 m_d->lockCount++;
707 m_d->lockedForReadOnly &= readOnly;
708 }
709
710 return result;
711 }
712
isIdle(bool allowLocked)713 bool KisImage::isIdle(bool allowLocked)
714 {
715 return (allowLocked || !locked()) && m_d->scheduler.isIdle();
716 }
717
lock()718 void KisImage::lock()
719 {
720 if (!locked()) {
721 requestStrokeEnd();
722 KisBusyWaitBroker::instance()->notifyWaitOnImageStarted(this);
723 m_d->scheduler.lock();
724 KisBusyWaitBroker::instance()->notifyWaitOnImageEnded(this);
725 }
726 m_d->lockCount++;
727 m_d->lockedForReadOnly = false;
728 }
729
unlock()730 void KisImage::unlock()
731 {
732 Q_ASSERT(locked());
733
734 if (locked()) {
735 m_d->lockCount--;
736
737 if (m_d->lockCount == 0) {
738 m_d->scheduler.unlock(!m_d->lockedForReadOnly);
739 }
740 }
741 }
742
blockUpdates()743 void KisImage::blockUpdates()
744 {
745 m_d->scheduler.blockUpdates();
746 }
747
unblockUpdates()748 void KisImage::unblockUpdates()
749 {
750 m_d->scheduler.unblockUpdates();
751 }
752
setSize(const QSize & size)753 void KisImage::setSize(const QSize& size)
754 {
755 m_d->width = size.width();
756 m_d->height = size.height();
757 }
758
resizeImageImpl(const QRect & newRect,bool cropLayers)759 void KisImage::resizeImageImpl(const QRect& newRect, bool cropLayers)
760 {
761 if (newRect == bounds() && !cropLayers) return;
762
763 KUndo2MagicString actionName = cropLayers ?
764 kundo2_i18n("Crop Image") :
765 kundo2_i18n("Resize Image");
766
767 KisImageSignalVector emitSignals;
768 emitSignals << ComplexSizeChangedSignal(newRect, newRect.size());
769 emitSignals << ModifiedSignal;
770
771 KisCropSavedExtraData *extraData =
772 new KisCropSavedExtraData(cropLayers ?
773 KisCropSavedExtraData::CROP_IMAGE :
774 KisCropSavedExtraData::RESIZE_IMAGE,
775 newRect);
776
777 KisProcessingApplicator applicator(this, m_d->rootLayer,
778 KisProcessingApplicator::RECURSIVE |
779 KisProcessingApplicator::NO_UI_UPDATES,
780 emitSignals, actionName, extraData);
781
782 if (cropLayers || !newRect.topLeft().isNull()) {
783 KisProcessingVisitorSP visitor =
784 new KisCropProcessingVisitor(newRect, cropLayers, true);
785 applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
786 }
787 applicator.applyCommand(new KisImageResizeCommand(this, newRect.size()));
788 applicator.end();
789 }
790
resizeImage(const QRect & newRect)791 void KisImage::resizeImage(const QRect& newRect)
792 {
793 resizeImageImpl(newRect, false);
794 }
795
cropImage(const QRect & newRect)796 void KisImage::cropImage(const QRect& newRect)
797 {
798 resizeImageImpl(newRect, true);
799 }
800
purgeUnusedData(bool isCancellable)801 void KisImage::purgeUnusedData(bool isCancellable)
802 {
803 /**
804 * WARNING: don't use this function unless you know what you are doing!
805 *
806 * It breaks undo on layers! Therefore, after calling it, KisImage is not
807 * undo-capable anymore!
808 */
809
810 struct PurgeUnusedDataStroke : public KisRunnableBasedStrokeStrategy {
811 PurgeUnusedDataStroke(KisImageSP image, bool isCancellable)
812 : KisRunnableBasedStrokeStrategy(QLatin1String("purge-unused-data"),
813 kundo2_noi18n("purge-unused-data")),
814 m_image(image)
815 {
816 this->enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
817 this->enableJob(JOB_DOSTROKE, true);
818 setClearsRedoOnStart(false);
819 setRequestsOtherStrokesToEnd(!isCancellable);
820 setCanForgetAboutMe(isCancellable);
821 }
822
823 void initStrokeCallback() {
824 KisPaintDeviceList deviceList;
825 QVector<KisStrokeJobData*> jobsData;
826
827 KisLayerUtils::recursiveApplyNodes(m_image->root(),
828 [&deviceList](KisNodeSP node) {
829 deviceList << node->getLodCapableDevices();
830 });
831
832 /// make sure we deduplicate the list to avoid
833 /// concurrent write access to the devices
834 KritaUtils::makeContainerUnique(deviceList);
835
836 Q_FOREACH (KisPaintDeviceSP device, deviceList) {
837 if (!device) continue;
838
839 KritaUtils::addJobConcurrent(jobsData,
840 [device] () {
841 const_cast<KisPaintDevice*>(device.data())->purgeDefaultPixels();
842 });
843 }
844
845 addMutatedJobs(jobsData);
846 }
847
848 private:
849 KisImageSP m_image;
850 };
851
852 KisStrokeId id = startStroke(new PurgeUnusedDataStroke(this, isCancellable));
853 endStroke(id);
854 }
855
cropNode(KisNodeSP node,const QRect & newRect)856 void KisImage::cropNode(KisNodeSP node, const QRect& newRect)
857 {
858 bool isLayer = qobject_cast<KisLayer*>(node.data());
859 KUndo2MagicString actionName = isLayer ?
860 kundo2_i18n("Crop Layer") :
861 kundo2_i18n("Crop Mask");
862
863 KisImageSignalVector emitSignals;
864 emitSignals << ModifiedSignal;
865
866 KisCropSavedExtraData *extraData =
867 new KisCropSavedExtraData(KisCropSavedExtraData::CROP_LAYER,
868 newRect, node);
869
870 KisProcessingApplicator applicator(this, node,
871 KisProcessingApplicator::RECURSIVE,
872 emitSignals, actionName, extraData);
873
874 KisProcessingVisitorSP visitor =
875 new KisCropProcessingVisitor(newRect, true, false);
876 applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
877 applicator.end();
878 }
879
scaleImage(const QSize & size,qreal xres,qreal yres,KisFilterStrategy * filterStrategy)880 void KisImage::scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy)
881 {
882 bool resolutionChanged = xres != xRes() && yres != yRes();
883 bool sizeChanged = size != this->size();
884
885 if (!resolutionChanged && !sizeChanged) return;
886
887 KisImageSignalVector emitSignals;
888 if (resolutionChanged) emitSignals << ResolutionChangedSignal;
889 if (sizeChanged) emitSignals << ComplexSizeChangedSignal(bounds(), size);
890 emitSignals << ModifiedSignal;
891
892 KUndo2MagicString actionName = sizeChanged ?
893 kundo2_i18n("Scale Image") :
894 kundo2_i18n("Change Image Resolution");
895
896 KisProcessingApplicator::ProcessingFlags signalFlags =
897 (resolutionChanged || sizeChanged) ?
898 KisProcessingApplicator::NO_UI_UPDATES :
899 KisProcessingApplicator::NONE;
900
901 KisProcessingApplicator applicator(this, m_d->rootLayer,
902 KisProcessingApplicator::RECURSIVE | signalFlags,
903 emitSignals, actionName);
904
905 qreal sx = qreal(size.width()) / this->size().width();
906 qreal sy = qreal(size.height()) / this->size().height();
907
908 QTransform shapesCorrection;
909
910 if (resolutionChanged) {
911 shapesCorrection = QTransform::fromScale(xRes() / xres, yRes() / yres);
912 }
913
914 KisProcessingVisitorSP visitor =
915 new KisTransformProcessingVisitor(sx, sy,
916 0, 0,
917 QPointF(),
918 0,
919 0, 0,
920 filterStrategy,
921 shapesCorrection);
922
923 applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
924
925 if (resolutionChanged) {
926 KUndo2Command *parent =
927 new KisResetShapesCommand(m_d->rootLayer);
928 new KisImageSetResolutionCommand(this, xres, yres, parent);
929 applicator.applyCommand(parent);
930 }
931
932 if (sizeChanged) {
933 applicator.applyCommand(new KisImageResizeCommand(this, size));
934 }
935
936 applicator.end();
937 }
938
scaleNode(KisNodeSP node,const QPointF & center,qreal scaleX,qreal scaleY,KisFilterStrategy * filterStrategy,KisSelectionSP selection)939 void KisImage::scaleNode(KisNodeSP node, const QPointF ¢er, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection)
940 {
941 KUndo2MagicString actionName(kundo2_i18n("Scale Layer"));
942 KisImageSignalVector emitSignals;
943 emitSignals << ModifiedSignal;
944
945 QPointF offset;
946 {
947 KisTransformWorker worker(0,
948 scaleX, scaleY,
949 0, 0, 0, 0,
950 0.0,
951 0, 0, 0, 0);
952 QTransform transform = worker.transform();
953
954 offset = center - transform.map(center);
955 }
956
957 KisProcessingApplicator applicator(this, node,
958 KisProcessingApplicator::RECURSIVE,
959 emitSignals, actionName);
960
961 KisTransformProcessingVisitor *visitor =
962 new KisTransformProcessingVisitor(scaleX, scaleY,
963 0, 0,
964 QPointF(),
965 0,
966 offset.x(), offset.y(),
967 filterStrategy);
968
969 visitor->setSelection(selection);
970
971 if (selection) {
972 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
973 } else {
974 applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
975 }
976
977 applicator.end();
978 }
979
rotateImpl(const KUndo2MagicString & actionName,KisNodeSP rootNode,double radians,bool resizeImage,KisSelectionSP selection)980 void KisImage::rotateImpl(const KUndo2MagicString &actionName,
981 KisNodeSP rootNode,
982 double radians,
983 bool resizeImage,
984 KisSelectionSP selection)
985 {
986 // we can either transform (and resize) the whole image or
987 // transform a selection, we cannot do both at the same time
988 KIS_SAFE_ASSERT_RECOVER(!(bool(selection) && resizeImage)) {
989 selection = 0;
990 }
991
992 const QRect baseBounds =
993 resizeImage ? bounds() :
994 selection ? selection->selectedExactRect() :
995 rootNode->exactBounds();
996
997 QPointF offset;
998 QSize newSize;
999
1000 {
1001 KisTransformWorker worker(0,
1002 1.0, 1.0,
1003 0, 0, 0, 0,
1004 radians,
1005 0, 0, 0, 0);
1006 QTransform transform = worker.transform();
1007
1008 if (resizeImage) {
1009 QRect newRect = transform.mapRect(baseBounds);
1010 newSize = newRect.size();
1011 offset = -newRect.topLeft();
1012 }
1013 else {
1014 QPointF origin = QRectF(baseBounds).center();
1015
1016 newSize = size();
1017 offset = -(transform.map(origin) - origin);
1018 }
1019 }
1020
1021 bool sizeChanged = resizeImage &&
1022 (newSize.width() != baseBounds.width() ||
1023 newSize.height() != baseBounds.height());
1024
1025 // These signals will be emitted after processing is done
1026 KisImageSignalVector emitSignals;
1027 if (sizeChanged) emitSignals << ComplexSizeChangedSignal(baseBounds, newSize);
1028 emitSignals << ModifiedSignal;
1029
1030 // These flags determine whether updates are transferred to the UI during processing
1031 KisProcessingApplicator::ProcessingFlags signalFlags =
1032 sizeChanged ?
1033 KisProcessingApplicator::NO_UI_UPDATES :
1034 KisProcessingApplicator::NONE;
1035
1036
1037 KisProcessingApplicator applicator(this, rootNode,
1038 KisProcessingApplicator::RECURSIVE | signalFlags,
1039 emitSignals, actionName);
1040
1041 KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Bicubic");
1042
1043 KisTransformProcessingVisitor *visitor =
1044 new KisTransformProcessingVisitor(1.0, 1.0, 0.0, 0.0,
1045 QPointF(),
1046 radians,
1047 offset.x(), offset.y(),
1048 filter);
1049 if (selection) {
1050 visitor->setSelection(selection);
1051 }
1052
1053 if (selection) {
1054 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1055 } else {
1056 applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
1057 }
1058
1059 if (sizeChanged) {
1060 applicator.applyCommand(new KisImageResizeCommand(this, newSize));
1061 }
1062 applicator.end();
1063 }
1064
1065
rotateImage(double radians)1066 void KisImage::rotateImage(double radians)
1067 {
1068 rotateImpl(kundo2_i18n("Rotate Image"), root(), radians, true, 0);
1069 }
1070
rotateNode(KisNodeSP node,double radians,KisSelectionSP selection)1071 void KisImage::rotateNode(KisNodeSP node, double radians, KisSelectionSP selection)
1072 {
1073 if (node->inherits("KisMask")) {
1074 rotateImpl(kundo2_i18n("Rotate Mask"), node, radians, false, selection);
1075 } else {
1076 rotateImpl(kundo2_i18n("Rotate Layer"), node, radians, false, selection);
1077 }
1078 }
1079
shearImpl(const KUndo2MagicString & actionName,KisNodeSP rootNode,bool resizeImage,double angleX,double angleY,KisSelectionSP selection)1080 void KisImage::shearImpl(const KUndo2MagicString &actionName,
1081 KisNodeSP rootNode,
1082 bool resizeImage,
1083 double angleX, double angleY,
1084 KisSelectionSP selection)
1085 {
1086 const QRect baseBounds =
1087 resizeImage ? bounds() :
1088 selection ? selection->selectedExactRect() :
1089 rootNode->exactBounds();
1090
1091 const QPointF origin = QRectF(baseBounds).center();
1092
1093 //angleX, angleY are in degrees
1094 const qreal pi = 3.1415926535897932385;
1095 const qreal deg2rad = pi / 180.0;
1096
1097 qreal tanX = tan(angleX * deg2rad);
1098 qreal tanY = tan(angleY * deg2rad);
1099
1100 QPointF offset;
1101 QSize newSize;
1102
1103 {
1104 KisTransformWorker worker(0,
1105 1.0, 1.0,
1106 tanX, tanY, origin.x(), origin.y(),
1107 0,
1108 0, 0, 0, 0);
1109
1110 QRect newRect = worker.transform().mapRect(baseBounds);
1111 newSize = newRect.size();
1112 if (resizeImage) offset = -newRect.topLeft();
1113 }
1114
1115 if (newSize == baseBounds.size()) return;
1116
1117 KisImageSignalVector emitSignals;
1118 if (resizeImage) emitSignals << ComplexSizeChangedSignal(baseBounds, newSize);
1119 emitSignals << ModifiedSignal;
1120
1121 KisProcessingApplicator::ProcessingFlags signalFlags =
1122 KisProcessingApplicator::RECURSIVE;
1123 if (resizeImage) signalFlags |= KisProcessingApplicator::NO_UI_UPDATES;
1124
1125 KisProcessingApplicator applicator(this, rootNode,
1126 signalFlags,
1127 emitSignals, actionName);
1128
1129 KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Bilinear");
1130
1131 KisTransformProcessingVisitor *visitor =
1132 new KisTransformProcessingVisitor(1.0, 1.0,
1133 tanX, tanY, origin,
1134 0,
1135 offset.x(), offset.y(),
1136 filter);
1137
1138 if (selection) {
1139 visitor->setSelection(selection);
1140 }
1141
1142 if (selection) {
1143 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1144 } else {
1145 applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
1146 }
1147
1148 if (resizeImage) {
1149 applicator.applyCommand(new KisImageResizeCommand(this, newSize));
1150 }
1151
1152 applicator.end();
1153 }
1154
shearNode(KisNodeSP node,double angleX,double angleY,KisSelectionSP selection)1155 void KisImage::shearNode(KisNodeSP node, double angleX, double angleY, KisSelectionSP selection)
1156 {
1157 if (node->inherits("KisMask")) {
1158 shearImpl(kundo2_i18n("Shear Mask"), node, false,
1159 angleX, angleY, selection);
1160 } else {
1161 shearImpl(kundo2_i18n("Shear Layer"), node, false,
1162 angleX, angleY, selection);
1163 }
1164 }
1165
shear(double angleX,double angleY)1166 void KisImage::shear(double angleX, double angleY)
1167 {
1168 shearImpl(kundo2_i18n("Shear Image"), m_d->rootLayer, true,
1169 angleX, angleY, 0);
1170 }
1171
convertLayerColorSpace(KisNodeSP node,const KoColorSpace * dstColorSpace,KoColorConversionTransformation::Intent renderingIntent,KoColorConversionTransformation::ConversionFlags conversionFlags)1172 void KisImage::convertLayerColorSpace(KisNodeSP node,
1173 const KoColorSpace *dstColorSpace,
1174 KoColorConversionTransformation::Intent renderingIntent,
1175 KoColorConversionTransformation::ConversionFlags conversionFlags)
1176 {
1177 if (!node->projectionLeaf()->isLayer()) return;
1178
1179 if (!dstColorSpace) return;
1180
1181 const bool hasNodesToConvert =
1182 KisLayerUtils::recursiveFindNode(node,
1183 [dstColorSpace](KisNodeSP node) {
1184 return *node->colorSpace() != *dstColorSpace;
1185 });
1186
1187 if (!hasNodesToConvert) return;
1188
1189 KUndo2MagicString actionName =
1190 kundo2_i18n("Convert Layer Color Space");
1191
1192 KisImageSignalVector emitSignals;
1193 emitSignals << ModifiedSignal;
1194
1195 KisProcessingApplicator applicator(this, node,
1196 KisProcessingApplicator::RECURSIVE,
1197 emitSignals, actionName);
1198
1199 applicator.applyVisitor(
1200 new KisConvertColorSpaceProcessingVisitor(
1201 dstColorSpace,
1202 renderingIntent, conversionFlags),
1203 KisStrokeJobData::CONCURRENT);
1204
1205 applicator.end();
1206 }
1207
1208 struct KisImage::KisImagePrivate::SetImageProjectionColorSpace : public KisCommandUtils::FlipFlopCommand
1209 {
SetImageProjectionColorSpaceKisImage::KisImagePrivate::SetImageProjectionColorSpace1210 SetImageProjectionColorSpace(const KoColorSpace *cs, KisImageWSP image,
1211 State initialState, KUndo2Command *parent = 0)
1212 : KisCommandUtils::FlipFlopCommand(initialState, parent),
1213 m_cs(cs),
1214 m_image(image)
1215 {
1216 }
1217
partAKisImage::KisImagePrivate::SetImageProjectionColorSpace1218 void partA() override {
1219 KisImageSP image = m_image;
1220
1221 if (image) {
1222 image->setProjectionColorSpace(m_cs);
1223 }
1224 }
1225
1226 private:
1227 const KoColorSpace *m_cs;
1228 KisImageWSP m_image;
1229 };
1230
convertImageColorSpaceImpl(const KoColorSpace * dstColorSpace,bool convertLayers,KoColorConversionTransformation::Intent renderingIntent,KoColorConversionTransformation::ConversionFlags conversionFlags)1231 void KisImage::KisImagePrivate::convertImageColorSpaceImpl(const KoColorSpace *dstColorSpace,
1232 bool convertLayers,
1233 KoColorConversionTransformation::Intent renderingIntent,
1234 KoColorConversionTransformation::ConversionFlags conversionFlags)
1235 {
1236 const KoColorSpace *srcColorSpace = this->colorSpace;
1237
1238 if (!dstColorSpace) return;
1239
1240 if (convertLayers) {
1241 const bool hasNodesToConvert =
1242 KisLayerUtils::recursiveFindNode(this->rootLayer,
1243 [dstColorSpace](KisNodeSP node) {
1244 return *node->colorSpace() != *dstColorSpace;
1245 });
1246
1247 if (!hasNodesToConvert && *this->colorSpace == *dstColorSpace) return;
1248 } else {
1249 if (*this->colorSpace == *dstColorSpace) return;
1250 }
1251
1252 const KUndo2MagicString actionName =
1253 convertLayers ?
1254 kundo2_i18n("Convert Image Color Space") :
1255 kundo2_i18n("Convert Projection Color Space");
1256
1257 KisImageSignalVector emitSignals;
1258 emitSignals << ColorSpaceChangedSignal;
1259 emitSignals << ModifiedSignal;
1260
1261 KisProcessingApplicator applicator(q, this->rootLayer,
1262 KisProcessingApplicator::RECURSIVE |
1263 KisProcessingApplicator::NO_UI_UPDATES,
1264 emitSignals, actionName);
1265
1266 applicator.applyCommand(
1267 new KisImagePrivate::SetImageProjectionColorSpace(dstColorSpace,
1268 KisImageWSP(q),
1269 KisCommandUtils::FlipFlopCommand::INITIALIZING),
1270 KisStrokeJobData::BARRIER);
1271
1272 if (convertLayers) {
1273 applicator.applyVisitor(
1274 new KisConvertColorSpaceProcessingVisitor(
1275 dstColorSpace,
1276 renderingIntent, conversionFlags),
1277 KisStrokeJobData::CONCURRENT);
1278 } else {
1279 applicator.applyCommand(
1280 new KisDoSomethingCommand<
1281 KisDoSomethingCommandOps::ResetOp, KisGroupLayerSP>
1282 (this->rootLayer, false));
1283 applicator.applyCommand(
1284 new KisDoSomethingCommand<
1285 KisDoSomethingCommandOps::ResetOp, KisGroupLayerSP>
1286 (this->rootLayer, true));
1287 }
1288
1289 applicator.applyCommand(
1290 new KisImagePrivate::SetImageProjectionColorSpace(srcColorSpace,
1291 KisImageWSP(q),
1292 KisCommandUtils::FlipFlopCommand::FINALIZING),
1293 KisStrokeJobData::BARRIER);
1294
1295
1296 applicator.end();
1297 }
1298
convertImageColorSpace(const KoColorSpace * dstColorSpace,KoColorConversionTransformation::Intent renderingIntent,KoColorConversionTransformation::ConversionFlags conversionFlags)1299 void KisImage::convertImageColorSpace(const KoColorSpace *dstColorSpace,
1300 KoColorConversionTransformation::Intent renderingIntent,
1301 KoColorConversionTransformation::ConversionFlags conversionFlags)
1302 {
1303 m_d->convertImageColorSpaceImpl(dstColorSpace, true, renderingIntent, conversionFlags);
1304 }
1305
convertImageProjectionColorSpace(const KoColorSpace * dstColorSpace)1306 void KisImage::convertImageProjectionColorSpace(const KoColorSpace *dstColorSpace)
1307 {
1308 m_d->convertImageColorSpaceImpl(dstColorSpace, false,
1309 KoColorConversionTransformation::internalRenderingIntent(),
1310 KoColorConversionTransformation::internalConversionFlags());
1311 }
1312
1313
assignLayerProfile(KisNodeSP node,const KoColorProfile * profile)1314 bool KisImage::assignLayerProfile(KisNodeSP node, const KoColorProfile *profile)
1315 {
1316 const KoColorSpace *srcColorSpace = node->colorSpace();
1317
1318 if (!node->projectionLeaf()->isLayer()) return false;
1319 if (!profile || *srcColorSpace->profile() == *profile) return false;
1320
1321 KUndo2MagicString actionName = kundo2_i18n("Assign Profile to Layer");
1322
1323 KisImageSignalVector emitSignals;
1324 emitSignals << ModifiedSignal;
1325
1326 const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
1327 if (!dstColorSpace) return false;
1328
1329 KisProcessingApplicator applicator(this, node,
1330 KisProcessingApplicator::RECURSIVE |
1331 KisProcessingApplicator::NO_UI_UPDATES,
1332 emitSignals, actionName);
1333
1334 applicator.applyVisitor(
1335 new KisAssignProfileProcessingVisitor(
1336 srcColorSpace, dstColorSpace),
1337 KisStrokeJobData::CONCURRENT);
1338
1339 applicator.end();
1340
1341 return true;
1342 }
1343
1344
assignImageProfile(const KoColorProfile * profile,bool blockAllUpdates)1345 bool KisImage::assignImageProfile(const KoColorProfile *profile, bool blockAllUpdates)
1346 {
1347 if (!profile) return false;
1348
1349 const KoColorSpace *srcColorSpace = m_d->colorSpace;
1350 bool imageProfileIsSame = *srcColorSpace->profile() == *profile;
1351
1352 imageProfileIsSame &=
1353 !KisLayerUtils::recursiveFindNode(m_d->rootLayer,
1354 [profile] (KisNodeSP node) {
1355 return *node->colorSpace()->profile() != *profile;
1356 });
1357
1358 if (imageProfileIsSame) {
1359 dbgImage << "Trying to set the same image profile again" << ppVar(srcColorSpace->profile()->name()) << ppVar(profile->name());
1360 return true;
1361 }
1362
1363 KUndo2MagicString actionName = kundo2_i18n("Assign Profile");
1364
1365 KisImageSignalVector emitSignals;
1366 emitSignals << ProfileChangedSignal;
1367 emitSignals << ModifiedSignal;
1368
1369 const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
1370 if (!dstColorSpace) return false;
1371
1372 KisProcessingApplicator applicator(this, m_d->rootLayer,
1373 KisProcessingApplicator::RECURSIVE |
1374 (!blockAllUpdates ?
1375 KisProcessingApplicator::NO_UI_UPDATES :
1376 KisProcessingApplicator::NO_IMAGE_UPDATES),
1377 emitSignals, actionName);
1378
1379 applicator.applyCommand(
1380 new KisImagePrivate::SetImageProjectionColorSpace(dstColorSpace,
1381 KisImageWSP(this),
1382 KisCommandUtils::FlipFlopCommand::INITIALIZING),
1383 KisStrokeJobData::BARRIER);
1384
1385 applicator.applyVisitor(
1386 new KisAssignProfileProcessingVisitor(
1387 srcColorSpace, dstColorSpace),
1388 KisStrokeJobData::CONCURRENT);
1389
1390 applicator.applyCommand(
1391 new KisImagePrivate::SetImageProjectionColorSpace(srcColorSpace,
1392 KisImageWSP(this),
1393 KisCommandUtils::FlipFlopCommand::FINALIZING),
1394 KisStrokeJobData::BARRIER);
1395
1396
1397 applicator.end();
1398
1399 return true;
1400 }
1401
setProjectionColorSpace(const KoColorSpace * colorSpace)1402 void KisImage::setProjectionColorSpace(const KoColorSpace * colorSpace)
1403 {
1404 m_d->colorSpace = colorSpace;
1405 }
1406
colorSpace() const1407 const KoColorSpace * KisImage::colorSpace() const
1408 {
1409 return m_d->colorSpace;
1410 }
1411
profile() const1412 const KoColorProfile * KisImage::profile() const
1413 {
1414 return colorSpace()->profile();
1415 }
1416
xRes() const1417 double KisImage::xRes() const
1418 {
1419 return m_d->xres;
1420 }
1421
yRes() const1422 double KisImage::yRes() const
1423 {
1424 return m_d->yres;
1425 }
1426
setResolution(double xres,double yres)1427 void KisImage::setResolution(double xres, double yres)
1428 {
1429 m_d->xres = xres;
1430 m_d->yres = yres;
1431 }
1432
documentToPixel(const QPointF & documentCoord) const1433 QPointF KisImage::documentToPixel(const QPointF &documentCoord) const
1434 {
1435 return QPointF(documentCoord.x() * xRes(), documentCoord.y() * yRes());
1436 }
1437
documentToImagePixelFloored(const QPointF & documentCoord) const1438 QPoint KisImage::documentToImagePixelFloored(const QPointF &documentCoord) const
1439 {
1440 QPointF pixelCoord = documentToPixel(documentCoord);
1441 return QPoint(qFloor(pixelCoord.x()), qFloor(pixelCoord.y()));
1442 }
1443
documentToPixel(const QRectF & documentRect) const1444 QRectF KisImage::documentToPixel(const QRectF &documentRect) const
1445 {
1446 return QRectF(documentToPixel(documentRect.topLeft()), documentToPixel(documentRect.bottomRight()));
1447 }
1448
pixelToDocument(const QPointF & pixelCoord) const1449 QPointF KisImage::pixelToDocument(const QPointF &pixelCoord) const
1450 {
1451 return QPointF(pixelCoord.x() / xRes(), pixelCoord.y() / yRes());
1452 }
1453
pixelToDocument(const QPoint & pixelCoord) const1454 QPointF KisImage::pixelToDocument(const QPoint &pixelCoord) const
1455 {
1456 return QPointF((pixelCoord.x() + 0.5) / xRes(), (pixelCoord.y() + 0.5) / yRes());
1457 }
1458
pixelToDocument(const QRectF & pixelCoord) const1459 QRectF KisImage::pixelToDocument(const QRectF &pixelCoord) const
1460 {
1461 return QRectF(pixelToDocument(pixelCoord.topLeft()), pixelToDocument(pixelCoord.bottomRight()));
1462 }
1463
width() const1464 qint32 KisImage::width() const
1465 {
1466 return m_d->width;
1467 }
1468
height() const1469 qint32 KisImage::height() const
1470 {
1471 return m_d->height;
1472 }
1473
rootLayer() const1474 KisGroupLayerSP KisImage::rootLayer() const
1475 {
1476 Q_ASSERT(m_d->rootLayer);
1477 return m_d->rootLayer;
1478 }
1479
projection() const1480 KisPaintDeviceSP KisImage::projection() const
1481 {
1482 if (m_d->isolatedRootNode) {
1483 return m_d->isolatedRootNode->projection();
1484 }
1485
1486
1487 Q_ASSERT(m_d->rootLayer);
1488 KisPaintDeviceSP projection = m_d->rootLayer->projection();
1489 Q_ASSERT(projection);
1490 return projection;
1491 }
1492
nlayers() const1493 qint32 KisImage::nlayers() const
1494 {
1495 QStringList list;
1496 list << "KisLayer";
1497
1498 KisCountVisitor visitor(list, KoProperties());
1499 m_d->rootLayer->accept(visitor);
1500 return visitor.count();
1501 }
1502
nHiddenLayers() const1503 qint32 KisImage::nHiddenLayers() const
1504 {
1505 QStringList list;
1506 list << "KisLayer";
1507 KoProperties properties;
1508 properties.setProperty("visible", false);
1509 KisCountVisitor visitor(list, properties);
1510 m_d->rootLayer->accept(visitor);
1511
1512 return visitor.count();
1513 }
1514
flatten(KisNodeSP activeNode)1515 void KisImage::flatten(KisNodeSP activeNode)
1516 {
1517 KisLayerUtils::flattenImage(this, activeNode);
1518 }
1519
mergeMultipleLayers(QList<KisNodeSP> mergedNodes,KisNodeSP putAfter)1520 void KisImage::mergeMultipleLayers(QList<KisNodeSP> mergedNodes, KisNodeSP putAfter)
1521 {
1522 if (!KisLayerUtils::tryMergeSelectionMasks(this, mergedNodes, putAfter)) {
1523 KisLayerUtils::mergeMultipleLayers(this, mergedNodes, putAfter);
1524 }
1525 }
1526
mergeDown(KisLayerSP layer,const KisMetaData::MergeStrategy * strategy)1527 void KisImage::mergeDown(KisLayerSP layer, const KisMetaData::MergeStrategy* strategy)
1528 {
1529 KisLayerUtils::mergeDown(this, layer, strategy);
1530 }
1531
flattenLayer(KisLayerSP layer)1532 void KisImage::flattenLayer(KisLayerSP layer)
1533 {
1534 KisLayerUtils::flattenLayer(this, layer);
1535 }
1536
1537
setModified()1538 void KisImage::setModified()
1539 {
1540 m_d->signalRouter.emitNotification(ModifiedSignal);
1541 }
1542
setModifiedWithoutUndo()1543 void KisImage::setModifiedWithoutUndo()
1544 {
1545 m_d->signalRouter.emitNotification(ModifiedWithoutUndoSignal);
1546 m_d->signalRouter.emitNotification(ModifiedSignal);
1547 }
1548
convertToQImage(QRect imageRect,const KoColorProfile * profile)1549 QImage KisImage::convertToQImage(QRect imageRect,
1550 const KoColorProfile * profile)
1551 {
1552 qint32 x;
1553 qint32 y;
1554 qint32 w;
1555 qint32 h;
1556 imageRect.getRect(&x, &y, &w, &h);
1557 return convertToQImage(x, y, w, h, profile);
1558 }
1559
convertToQImage(qint32 x,qint32 y,qint32 w,qint32 h,const KoColorProfile * profile)1560 QImage KisImage::convertToQImage(qint32 x,
1561 qint32 y,
1562 qint32 w,
1563 qint32 h,
1564 const KoColorProfile * profile)
1565 {
1566 KisPaintDeviceSP dev = projection();
1567 if (!dev) return QImage();
1568 QImage image = dev->convertToQImage(const_cast<KoColorProfile*>(profile), x, y, w, h,
1569 KoColorConversionTransformation::internalRenderingIntent(),
1570 KoColorConversionTransformation::internalConversionFlags());
1571
1572 return image;
1573 }
1574
1575
1576
convertToQImage(const QSize & scaledImageSize,const KoColorProfile * profile)1577 QImage KisImage::convertToQImage(const QSize& scaledImageSize, const KoColorProfile *profile)
1578 {
1579 if (scaledImageSize.isEmpty()) {
1580 return QImage();
1581 }
1582
1583 KisPaintDeviceSP dev = new KisPaintDevice(colorSpace());
1584 KisPainter gc;
1585 gc.copyAreaOptimized(QPoint(0, 0), projection(), dev, bounds());
1586 gc.end();
1587 double scaleX = qreal(scaledImageSize.width()) / width();
1588 double scaleY = qreal(scaledImageSize.height()) / height();
1589
1590
1591 if (scaleX < 1.0/256 || scaleY < 1.0/256) {
1592 // quick checking if we're not trying to scale too much
1593 // convertToQImage uses KisFixedPoint values, which means that the scale cannot be smaller than 1/2^8
1594 // BUG:432182
1595 // FIXME: would be best to extend KisFixedPoint instead
1596 return convertToQImage(size(), profile).scaled(scaledImageSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
1597 }
1598
1599 QPointer<KoUpdater> updater = new KoDummyUpdater();
1600
1601 KisTransformWorker worker(dev, scaleX, scaleY, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, updater, KisFilterStrategyRegistry::instance()->value("Bicubic"));
1602 worker.run();
1603
1604 delete updater;
1605
1606 return dev->convertToQImage(profile);
1607 }
notifyLayersChanged()1608 void KisImage::notifyLayersChanged()
1609 {
1610 m_d->signalRouter.emitNotification(LayersChangedSignal);
1611 }
1612
bounds() const1613 QRect KisImage::bounds() const
1614 {
1615 return QRect(0, 0, width(), height());
1616 }
1617
effectiveLodBounds() const1618 QRect KisImage::effectiveLodBounds() const
1619 {
1620 QRect boundRect = bounds();
1621
1622 const int lod = currentLevelOfDetail();
1623 if (lod > 0) {
1624 KisLodTransform t(lod);
1625 boundRect = t.map(boundRect);
1626 }
1627
1628 return boundRect;
1629 }
1630
postExecutionUndoAdapter() const1631 KisPostExecutionUndoAdapter* KisImage::postExecutionUndoAdapter() const
1632 {
1633 const int lod = currentLevelOfDetail();
1634 return lod > 0 ?
1635 m_d->scheduler.lodNPostExecutionUndoAdapter() :
1636 &m_d->postExecutionUndoAdapter;
1637 }
1638
lastExecutedCommand() const1639 const KUndo2Command* KisImage::lastExecutedCommand() const
1640 {
1641 return m_d->undoStore->presentCommand();
1642 }
1643
setUndoStore(KisUndoStore * undoStore)1644 void KisImage::setUndoStore(KisUndoStore *undoStore)
1645 {
1646
1647 m_d->legacyUndoAdapter.setUndoStore(undoStore);
1648 m_d->postExecutionUndoAdapter.setUndoStore(undoStore);
1649 m_d->undoStore.reset(undoStore);
1650 }
1651
undoStore()1652 KisUndoStore* KisImage::undoStore()
1653 {
1654 return m_d->undoStore.data();
1655 }
1656
undoAdapter() const1657 KisUndoAdapter* KisImage::undoAdapter() const
1658 {
1659 return &m_d->legacyUndoAdapter;
1660 }
1661
setDefaultProjectionColor(const KoColor & color)1662 void KisImage::setDefaultProjectionColor(const KoColor &color)
1663 {
1664 KIS_ASSERT_RECOVER_RETURN(m_d->rootLayer);
1665 m_d->rootLayer->setDefaultProjectionColor(color);
1666 }
1667
defaultProjectionColor() const1668 KoColor KisImage::defaultProjectionColor() const
1669 {
1670 KIS_ASSERT_RECOVER(m_d->rootLayer) {
1671 return KoColor(Qt::transparent, m_d->colorSpace);
1672 }
1673
1674 return m_d->rootLayer->defaultProjectionColor();
1675 }
1676
setRootLayer(KisGroupLayerSP rootLayer)1677 void KisImage::setRootLayer(KisGroupLayerSP rootLayer)
1678 {
1679 emit sigInternalStopIsolatedModeRequested();
1680
1681 KoColor defaultProjectionColor(Qt::transparent, m_d->colorSpace);
1682
1683 if (m_d->rootLayer) {
1684 m_d->rootLayer->setGraphListener(0);
1685 m_d->rootLayer->disconnect();
1686
1687 KisPaintDeviceSP original = m_d->rootLayer->original();
1688 defaultProjectionColor = original->defaultPixel();
1689 }
1690
1691 m_d->rootLayer = rootLayer;
1692 m_d->rootLayer->disconnect();
1693 m_d->rootLayer->setGraphListener(this);
1694 m_d->rootLayer->setImage(this);
1695
1696 setRoot(m_d->rootLayer.data());
1697 this->setDefaultProjectionColor(defaultProjectionColor);
1698 }
1699
addAnnotation(KisAnnotationSP annotation)1700 void KisImage::addAnnotation(KisAnnotationSP annotation)
1701 {
1702 // Find the icc annotation, if there is one
1703 vKisAnnotationSP_it it = m_d->annotations.begin();
1704 while (it != m_d->annotations.end()) {
1705 if ((*it)->type() == annotation->type()) {
1706 *it = annotation;
1707 return;
1708 }
1709 ++it;
1710 }
1711 m_d->annotations.push_back(annotation);
1712 }
1713
annotation(const QString & type)1714 KisAnnotationSP KisImage::annotation(const QString& type)
1715 {
1716 vKisAnnotationSP_it it = m_d->annotations.begin();
1717 while (it != m_d->annotations.end()) {
1718 if ((*it)->type() == type) {
1719 return *it;
1720 }
1721 ++it;
1722 }
1723 return KisAnnotationSP(0);
1724 }
1725
removeAnnotation(const QString & type)1726 void KisImage::removeAnnotation(const QString& type)
1727 {
1728 vKisAnnotationSP_it it = m_d->annotations.begin();
1729 while (it != m_d->annotations.end()) {
1730 if ((*it)->type() == type) {
1731 m_d->annotations.erase(it);
1732 return;
1733 }
1734 ++it;
1735 }
1736 }
1737
beginAnnotations()1738 vKisAnnotationSP_it KisImage::beginAnnotations()
1739 {
1740 return m_d->annotations.begin();
1741 }
1742
endAnnotations()1743 vKisAnnotationSP_it KisImage::endAnnotations()
1744 {
1745 return m_d->annotations.end();
1746 }
1747
notifyAboutToBeDeleted()1748 void KisImage::notifyAboutToBeDeleted()
1749 {
1750 emit sigAboutToBeDeleted();
1751 }
1752
signalRouter()1753 KisImageSignalRouter* KisImage::signalRouter()
1754 {
1755 return &m_d->signalRouter;
1756 }
1757
waitForDone()1758 void KisImage::waitForDone()
1759 {
1760 requestStrokeEnd();
1761 KisBusyWaitBroker::instance()->notifyWaitOnImageStarted(this);
1762 m_d->scheduler.waitForDone();
1763 KisBusyWaitBroker::instance()->notifyWaitOnImageEnded(this);
1764 }
1765
startStroke(KisStrokeStrategy * strokeStrategy)1766 KisStrokeId KisImage::startStroke(KisStrokeStrategy *strokeStrategy)
1767 {
1768 /**
1769 * Ask open strokes to end gracefully. All the strokes clients
1770 * (including the one calling this method right now) will get
1771 * a notification that they should probably end their strokes.
1772 * However this is purely their choice whether to end a stroke
1773 * or not.
1774 */
1775 if (strokeStrategy->requestsOtherStrokesToEnd()) {
1776 requestStrokeEnd();
1777 }
1778
1779 /**
1780 * Some of the strokes can cancel their work with undoing all the
1781 * changes they did to the paint devices. The problem is that undo
1782 * stack will know nothing about it. Therefore, just notify it
1783 * explicitly
1784 */
1785 if (strokeStrategy->clearsRedoOnStart()) {
1786 m_d->undoStore->purgeRedoState();
1787 }
1788
1789 return m_d->scheduler.startStroke(strokeStrategy);
1790 }
1791
notifyProjectionUpdatedInPatches(const QRect & rc,QVector<KisRunnableStrokeJobData * > & jobs)1792 void KisImage::KisImagePrivate::notifyProjectionUpdatedInPatches(const QRect &rc, QVector<KisRunnableStrokeJobData*> &jobs)
1793 {
1794 KisImageConfig imageConfig(true);
1795 int patchWidth = imageConfig.updatePatchWidth();
1796 int patchHeight = imageConfig.updatePatchHeight();
1797
1798 for (int y = 0; y < rc.height(); y += patchHeight) {
1799 for (int x = 0; x < rc.width(); x += patchWidth) {
1800 QRect patchRect(x, y, patchWidth, patchHeight);
1801 patchRect &= rc;
1802
1803 KritaUtils::addJobConcurrent(jobs, std::bind(&KisImage::notifyProjectionUpdated, q, patchRect));
1804 }
1805 }
1806 }
1807
startIsolatedMode(KisNodeSP node)1808 bool KisImage::startIsolatedMode(KisNodeSP node)
1809 {
1810 struct StartIsolatedModeStroke : public KisRunnableBasedStrokeStrategy {
1811 StartIsolatedModeStroke(KisNodeSP node, KisImageSP image)
1812 : KisRunnableBasedStrokeStrategy(QLatin1String("start-isolated-mode"),
1813 kundo2_noi18n("start-isolated-mode")),
1814 m_newRoot(node),
1815 m_image(image)
1816 {
1817 this->enableJob(JOB_INIT, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
1818 this->enableJob(JOB_DOSTROKE, true);
1819 this->enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
1820 setClearsRedoOnStart(false);
1821 }
1822
1823 void initStrokeCallback() override {
1824 // pass-though node don't have any projection prepared, so we should
1825 // explicitly regenerate it before activating isolated mode.
1826 m_newRoot->projectionLeaf()->explicitlyRegeneratePassThroughProjection();
1827
1828 m_prevRoot = m_image->m_d->isolatedRootNode;
1829
1830 const bool beforeVisibility = m_newRoot->projectionLeaf()->visible();
1831 const bool prevRootBeforeVisibility = m_prevRoot ? m_prevRoot->projectionLeaf()->visible() : false;
1832
1833 m_image->m_d->isolatedRootNode = m_newRoot;
1834 emit m_image->sigIsolatedModeChanged();
1835
1836 const bool afterVisibility = m_newRoot->projectionLeaf()->visible();
1837 const bool prevRootAfterVisibility = m_prevRoot ? m_prevRoot->projectionLeaf()->visible() : false;
1838
1839 m_newRootNeedsFullRefresh = beforeVisibility != afterVisibility;
1840 m_prevRootNeedsFullRefresh = prevRootBeforeVisibility != prevRootAfterVisibility;
1841 }
1842
1843 void finishStrokeCallback() override {
1844 // the GUI uses our thread to do the color space conversion so we
1845 // need to emit this signal in multiple threads
1846
1847 if (m_prevRoot && m_prevRootNeedsFullRefresh) {
1848 m_image->refreshGraphAsync(m_prevRoot);
1849 }
1850
1851 if (m_newRootNeedsFullRefresh) {
1852 m_image->refreshGraphAsync(m_newRoot);
1853 }
1854
1855 if (!m_prevRootNeedsFullRefresh && !m_newRootNeedsFullRefresh) {
1856 QVector<KisRunnableStrokeJobData*> jobs;
1857 m_image->m_d->notifyProjectionUpdatedInPatches(m_image->bounds(), jobs);
1858 this->runnableJobsInterface()->addRunnableJobs(jobs);
1859 }
1860
1861 m_image->invalidateAllFrames();
1862 }
1863
1864 private:
1865 KisNodeSP m_newRoot;
1866 KisNodeSP m_prevRoot;
1867 KisImageSP m_image;
1868 bool m_newRootNeedsFullRefresh = false;
1869 bool m_prevRootNeedsFullRefresh = false;
1870 };
1871
1872 KisStrokeId id = startStroke(new StartIsolatedModeStroke(node, this));
1873 endStroke(id);
1874
1875 return true;
1876 }
1877
stopIsolatedMode()1878 void KisImage::stopIsolatedMode()
1879 {
1880 if (!m_d->isolatedRootNode) return;
1881
1882 struct StopIsolatedModeStroke : public KisRunnableBasedStrokeStrategy {
1883 StopIsolatedModeStroke(KisImageSP image)
1884 : KisRunnableBasedStrokeStrategy(QLatin1String("stop-isolated-mode"), kundo2_noi18n("stop-isolated-mode")),
1885 m_image(image),
1886 m_oldRootNode(nullptr),
1887 m_oldNodeNeedsRefresh(false)
1888 {
1889 this->enableJob(JOB_INIT);
1890 this->enableJob(JOB_DOSTROKE, true);
1891 this->enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
1892 setClearsRedoOnStart(false);
1893 }
1894
1895 void initStrokeCallback() {
1896 if (!m_image->m_d->isolatedRootNode) return;
1897
1898 m_oldRootNode = m_image->m_d->isolatedRootNode;
1899 const bool beforeVisibility = m_oldRootNode->projectionLeaf()->visible();
1900 m_image->m_d->isolatedRootNode = 0;
1901 emit m_image->sigIsolatedModeChanged();
1902 const bool afterVisibility = m_oldRootNode->projectionLeaf()->visible();
1903
1904 m_oldNodeNeedsRefresh = (beforeVisibility != afterVisibility);
1905 }
1906
1907 void finishStrokeCallback() override {
1908
1909 m_image->invalidateAllFrames();
1910
1911 if (m_oldNodeNeedsRefresh){
1912 m_oldRootNode->setDirty(m_image->bounds());
1913 } else {
1914 // TODO: Substitute notifyProjectionUpdated() with this code
1915 // when update optimization is implemented
1916 //
1917 // QRect updateRect = bounds() | oldRootNode->extent();
1918 //oldRootNode->setDirty(updateRect);
1919
1920 QVector<KisRunnableStrokeJobData*> jobs;
1921 m_image->m_d->notifyProjectionUpdatedInPatches(m_image->bounds(), jobs);
1922 this->runnableJobsInterface()->addRunnableJobs(jobs);
1923 }
1924 }
1925
1926 private:
1927 KisImageSP m_image;
1928 KisNodeSP m_oldRootNode;
1929 bool m_oldNodeNeedsRefresh;
1930 };
1931
1932 KisStrokeId id = startStroke(new StopIsolatedModeStroke(this));
1933 endStroke(id);
1934 }
1935
isolatedModeRoot() const1936 KisNodeSP KisImage::isolatedModeRoot() const
1937 {
1938 return m_d->isolatedRootNode;
1939 }
1940
addJob(KisStrokeId id,KisStrokeJobData * data)1941 void KisImage::addJob(KisStrokeId id, KisStrokeJobData *data)
1942 {
1943 KisUpdateTimeMonitor::instance()->reportJobStarted(data);
1944 m_d->scheduler.addJob(id, data);
1945 }
1946
endStroke(KisStrokeId id)1947 void KisImage::endStroke(KisStrokeId id)
1948 {
1949 m_d->scheduler.endStroke(id);
1950 }
1951
cancelStroke(KisStrokeId id)1952 bool KisImage::cancelStroke(KisStrokeId id)
1953 {
1954 return m_d->scheduler.cancelStroke(id);
1955 }
1956
tryCancelCurrentStrokeAsync()1957 bool KisImage::KisImagePrivate::tryCancelCurrentStrokeAsync()
1958 {
1959 return scheduler.tryCancelCurrentStrokeAsync();
1960 }
1961
requestUndoDuringStroke()1962 void KisImage::requestUndoDuringStroke()
1963 {
1964 emit sigUndoDuringStrokeRequested();
1965 }
1966
requestRedoDuringStroke()1967 void KisImage::requestRedoDuringStroke()
1968 {
1969 emit sigRedoDuringStrokeRequested();
1970 }
1971
requestStrokeCancellation()1972 void KisImage::requestStrokeCancellation()
1973 {
1974 if (!m_d->tryCancelCurrentStrokeAsync()) {
1975 emit sigStrokeCancellationRequested();
1976 }
1977 }
1978
tryUndoUnfinishedLod0Stroke()1979 UndoResult KisImage::tryUndoUnfinishedLod0Stroke()
1980 {
1981 return m_d->scheduler.tryUndoLastStrokeAsync();
1982 }
1983
requestStrokeEnd()1984 void KisImage::requestStrokeEnd()
1985 {
1986 emit sigStrokeEndRequested();
1987 emit sigStrokeEndRequestedActiveNodeFiltered();
1988 }
1989
requestStrokeEndActiveNode()1990 void KisImage::requestStrokeEndActiveNode()
1991 {
1992 emit sigStrokeEndRequested();
1993 }
1994
refreshGraph(KisNodeSP root)1995 void KisImage::refreshGraph(KisNodeSP root)
1996 {
1997 refreshGraph(root, bounds(), bounds());
1998 }
1999
refreshGraph(KisNodeSP root,const QRect & rc,const QRect & cropRect)2000 void KisImage::refreshGraph(KisNodeSP root, const QRect &rc, const QRect &cropRect)
2001 {
2002 if (!root) root = m_d->rootLayer;
2003
2004 m_d->animationInterface->notifyNodeChanged(root.data(), rc, true);
2005 m_d->scheduler.fullRefresh(root, rc, cropRect);
2006 }
2007
initialRefreshGraph()2008 void KisImage::initialRefreshGraph()
2009 {
2010 /**
2011 * NOTE: Tricky part. We set crop rect to null, so the clones
2012 * will not rely on precalculated projections of their sources
2013 */
2014
2015 refreshGraphAsync(0, bounds(), QRect());
2016 waitForDone();
2017 }
2018
refreshGraphAsync(KisNodeSP root)2019 void KisImage::refreshGraphAsync(KisNodeSP root)
2020 {
2021 refreshGraphAsync(root, bounds(), bounds());
2022 }
2023
refreshGraphAsync(KisNodeSP root,const QRect & rc)2024 void KisImage::refreshGraphAsync(KisNodeSP root, const QRect &rc)
2025 {
2026 refreshGraphAsync(root, rc, bounds());
2027 }
2028
refreshGraphAsync(KisNodeSP root,const QRect & rc,const QRect & cropRect)2029 void KisImage::refreshGraphAsync(KisNodeSP root, const QRect &rc, const QRect &cropRect)
2030 {
2031 refreshGraphAsync(root, QVector<QRect>({rc}), cropRect);
2032 }
2033
refreshGraphAsync(KisNodeSP root,const QVector<QRect> & rects,const QRect & cropRect)2034 void KisImage::refreshGraphAsync(KisNodeSP root, const QVector<QRect> &rects, const QRect &cropRect)
2035 {
2036 if (!root) root = m_d->rootLayer;
2037
2038 /**
2039 * We iterate through the filters in a reversed way. It makes the most nested filters
2040 * to execute first.
2041 */
2042 for (auto it = m_d->projectionUpdatesFilters.rbegin();
2043 it != m_d->projectionUpdatesFilters.rend();
2044 ++it) {
2045
2046 KIS_SAFE_ASSERT_RECOVER(*it) { continue; }
2047
2048 if ((*it)->filterRefreshGraph(this, root.data(), rects, cropRect)) {
2049 return;
2050 }
2051 }
2052
2053
2054 m_d->animationInterface->notifyNodeChanged(root.data(), rects, true);
2055 m_d->scheduler.fullRefreshAsync(root, rects, cropRect);
2056 }
2057
2058
requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy,const QRect & rc,const QRect & cropRect)2059 void KisImage::requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QRect &rc, const QRect &cropRect)
2060 {
2061 requestProjectionUpdateNoFilthy(pseudoFilthy, rc, cropRect, true);
2062 }
2063
requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy,const QRect & rc,const QRect & cropRect,const bool resetAnimationCache)2064 void KisImage::requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QRect &rc, const QRect &cropRect, const bool resetAnimationCache)
2065 {
2066 KIS_ASSERT_RECOVER_RETURN(pseudoFilthy);
2067
2068 if (resetAnimationCache) {
2069 m_d->animationInterface->notifyNodeChanged(pseudoFilthy.data(), rc, false);
2070 }
2071 m_d->scheduler.updateProjectionNoFilthy(pseudoFilthy, rc, cropRect);
2072 }
2073
addSpontaneousJob(KisSpontaneousJob * spontaneousJob)2074 void KisImage::addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
2075 {
2076 m_d->scheduler.addSpontaneousJob(spontaneousJob);
2077 }
2078
hasUpdatesRunning() const2079 bool KisImage::hasUpdatesRunning() const
2080 {
2081 return m_d->scheduler.hasUpdatesRunning();
2082 }
2083
addProjectionUpdatesFilter(KisProjectionUpdatesFilterSP filter)2084 KisProjectionUpdatesFilterCookie KisImage::addProjectionUpdatesFilter(KisProjectionUpdatesFilterSP filter)
2085 {
2086 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(filter, KisProjectionUpdatesFilterCookie());
2087
2088 m_d->projectionUpdatesFilters.append(filter);
2089
2090 return KisProjectionUpdatesFilterCookie(filter.data());
2091 }
2092
removeProjectionUpdatesFilter(KisProjectionUpdatesFilterCookie cookie)2093 KisProjectionUpdatesFilterSP KisImage::removeProjectionUpdatesFilter(KisProjectionUpdatesFilterCookie cookie)
2094 {
2095 KIS_SAFE_ASSERT_RECOVER_NOOP(cookie);
2096 KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->projectionUpdatesFilters.last() == cookie);
2097
2098 auto it = std::find(m_d->projectionUpdatesFilters.begin(), m_d->projectionUpdatesFilters.end(), cookie);
2099 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(it != m_d->projectionUpdatesFilters.end(), KisProjectionUpdatesFilterSP());
2100
2101 KisProjectionUpdatesFilterSP filter = *it;
2102
2103 m_d->projectionUpdatesFilters.erase(it);
2104
2105 return filter;
2106 }
2107
currentProjectionUpdatesFilter() const2108 KisProjectionUpdatesFilterCookie KisImage::currentProjectionUpdatesFilter() const
2109 {
2110 return !m_d->projectionUpdatesFilters.isEmpty() ?
2111 m_d->projectionUpdatesFilters.last().data() :
2112 KisProjectionUpdatesFilterCookie();
2113 }
2114
disableDirtyRequests()2115 void KisImage::disableDirtyRequests()
2116 {
2117 m_d->disabledUpdatesCookies.push(
2118 addProjectionUpdatesFilter(toQShared(new KisDropAllProjectionUpdatesFilter())));
2119 }
2120
enableDirtyRequests()2121 void KisImage::enableDirtyRequests()
2122 {
2123 KIS_SAFE_ASSERT_RECOVER_RETURN(!m_d->disabledUpdatesCookies.isEmpty());
2124 removeProjectionUpdatesFilter(m_d->disabledUpdatesCookies.pop());
2125 }
2126
disableUIUpdates()2127 void KisImage::disableUIUpdates()
2128 {
2129 m_d->disableUIUpdateSignals.ref();
2130 }
2131
notifyBatchUpdateStarted()2132 void KisImage::notifyBatchUpdateStarted()
2133 {
2134 m_d->signalRouter.emitNotifyBatchUpdateStarted();
2135 }
2136
notifyBatchUpdateEnded()2137 void KisImage::notifyBatchUpdateEnded()
2138 {
2139 m_d->signalRouter.emitNotifyBatchUpdateEnded();
2140 }
2141
notifyUIUpdateCompleted(const QRect & rc)2142 void KisImage::notifyUIUpdateCompleted(const QRect &rc)
2143 {
2144 notifyProjectionUpdated(rc);
2145 }
2146
enableUIUpdates()2147 QVector<QRect> KisImage::enableUIUpdates()
2148 {
2149 m_d->disableUIUpdateSignals.deref();
2150
2151 QRect rect;
2152 QVector<QRect> postponedUpdates;
2153
2154 while (m_d->savedDisabledUIUpdates.pop(rect)) {
2155 postponedUpdates.append(rect);
2156 }
2157
2158 return postponedUpdates;
2159 }
2160
notifyProjectionUpdated(const QRect & rc)2161 void KisImage::notifyProjectionUpdated(const QRect &rc)
2162 {
2163 KisUpdateTimeMonitor::instance()->reportUpdateFinished(rc);
2164
2165 if (!m_d->disableUIUpdateSignals) {
2166 int lod = currentLevelOfDetail();
2167 QRect dirtyRect = !lod ? rc : KisLodTransform::upscaledRect(rc, lod);
2168
2169 if (dirtyRect.isEmpty()) return;
2170
2171 emit sigImageUpdated(dirtyRect);
2172 } else {
2173 m_d->savedDisabledUIUpdates.push(rc);
2174 }
2175 }
2176
setWorkingThreadsLimit(int value)2177 void KisImage::setWorkingThreadsLimit(int value)
2178 {
2179 m_d->scheduler.setThreadsLimit(value);
2180 }
2181
workingThreadsLimit() const2182 int KisImage::workingThreadsLimit() const
2183 {
2184 return m_d->scheduler.threadsLimit();
2185 }
2186
notifySelectionChanged()2187 void KisImage::notifySelectionChanged()
2188 {
2189 /**
2190 * The selection is calculated asynchronously, so it is not
2191 * handled by disableUIUpdates() and other special signals of
2192 * KisImageSignalRouter
2193 */
2194 m_d->legacyUndoAdapter.emitSelectionChanged();
2195
2196 /**
2197 * Editing of selection masks doesn't necessary produce a
2198 * setDirty() call, so in the end of the stroke we need to request
2199 * direct update of the UI's cache.
2200 */
2201 if (m_d->isolatedRootNode &&
2202 dynamic_cast<KisSelectionMask*>(m_d->isolatedRootNode.data())) {
2203
2204 notifyProjectionUpdated(bounds());
2205 }
2206 }
2207
requestProjectionUpdateImpl(KisNode * node,const QVector<QRect> & rects,const QRect & cropRect)2208 void KisImage::requestProjectionUpdateImpl(KisNode *node,
2209 const QVector<QRect> &rects,
2210 const QRect &cropRect)
2211 {
2212 if (rects.isEmpty()) return;
2213
2214 m_d->scheduler.updateProjection(node, rects, cropRect);
2215 }
2216
requestProjectionUpdate(KisNode * node,const QVector<QRect> & rects,bool resetAnimationCache)2217 void KisImage::requestProjectionUpdate(KisNode *node, const QVector<QRect> &rects, bool resetAnimationCache)
2218 {
2219 /**
2220 * We iterate through the filters in a reversed way. It makes the most nested filters
2221 * to execute first.
2222 */
2223 for (auto it = m_d->projectionUpdatesFilters.rbegin();
2224 it != m_d->projectionUpdatesFilters.rend();
2225 ++it) {
2226
2227 KIS_SAFE_ASSERT_RECOVER(*it) { continue; }
2228
2229 if ((*it)->filter(this, node, rects, resetAnimationCache)) {
2230 return;
2231 }
2232 }
2233
2234 if (resetAnimationCache) {
2235 m_d->animationInterface->notifyNodeChanged(node, rects, false);
2236 }
2237
2238 /**
2239 * Here we use 'permitted' instead of 'active' intentively,
2240 * because the updates may come after the actual stroke has been
2241 * finished. And having some more updates for the stroke not
2242 * supporting the wrap-around mode will not make much harm.
2243 */
2244 if (m_d->wrapAroundModePermitted) {
2245 QVector<QRect> allSplitRects;
2246
2247 const QRect boundRect = effectiveLodBounds();
2248 Q_FOREACH (const QRect &rc, rects) {
2249 KisWrappedRect splitRect(rc, boundRect);
2250 allSplitRects.append(splitRect);
2251 }
2252
2253 requestProjectionUpdateImpl(node, allSplitRects, boundRect);
2254
2255 } else {
2256 requestProjectionUpdateImpl(node, rects, bounds());
2257 }
2258
2259 KisNodeGraphListener::requestProjectionUpdate(node, rects, resetAnimationCache);
2260 }
2261
invalidateFrames(const KisTimeRange & range,const QRect & rect)2262 void KisImage::invalidateFrames(const KisTimeRange &range, const QRect &rect)
2263 {
2264 m_d->animationInterface->invalidateFrames(range, rect);
2265 }
2266
requestTimeSwitch(int time)2267 void KisImage::requestTimeSwitch(int time)
2268 {
2269 m_d->animationInterface->requestTimeSwitchNonGUI(time);
2270 }
2271
graphOverlayNode() const2272 KisNode *KisImage::graphOverlayNode() const
2273 {
2274 return m_d->overlaySelectionMask.data();
2275 }
2276
compositions()2277 QList<KisLayerCompositionSP> KisImage::compositions()
2278 {
2279 return m_d->compositions;
2280 }
2281
addComposition(KisLayerCompositionSP composition)2282 void KisImage::addComposition(KisLayerCompositionSP composition)
2283 {
2284 m_d->compositions.append(composition);
2285 }
2286
removeComposition(KisLayerCompositionSP composition)2287 void KisImage::removeComposition(KisLayerCompositionSP composition)
2288 {
2289 m_d->compositions.removeAll(composition);
2290 }
2291
moveCompositionUp(KisLayerCompositionSP composition)2292 void KisImage::moveCompositionUp(KisLayerCompositionSP composition)
2293 {
2294 int index = m_d->compositions.indexOf(composition);
2295 if (index <= 0) {
2296 return;
2297 }
2298 m_d->compositions.move(index, index - 1);
2299 }
2300
moveCompositionDown(KisLayerCompositionSP composition)2301 void KisImage::moveCompositionDown(KisLayerCompositionSP composition)
2302 {
2303 int index = m_d->compositions.indexOf(composition);
2304 if (index >= m_d->compositions.size() -1) {
2305 return;
2306 }
2307 m_d->compositions.move(index, index + 1);
2308 }
2309
checkMasksNeedConversion(KisNodeSP root,const QRect & bounds)2310 bool checkMasksNeedConversion(KisNodeSP root, const QRect &bounds)
2311 {
2312 KisSelectionMask *mask = dynamic_cast<KisSelectionMask*>(root.data());
2313 if (mask &&
2314 (!bounds.contains(mask->paintDevice()->exactBounds()) ||
2315 mask->selection()->hasShapeSelection())) {
2316
2317 return true;
2318 }
2319
2320 KisNodeSP node = root->firstChild();
2321
2322 while (node) {
2323 if (checkMasksNeedConversion(node, bounds)) {
2324 return true;
2325 }
2326
2327 node = node->nextSibling();
2328 }
2329
2330 return false;
2331 }
2332
setWrapAroundModePermitted(bool value)2333 void KisImage::setWrapAroundModePermitted(bool value)
2334 {
2335 if (m_d->wrapAroundModePermitted != value) {
2336 requestStrokeEnd();
2337 }
2338
2339 m_d->wrapAroundModePermitted = value;
2340
2341 if (m_d->wrapAroundModePermitted &&
2342 checkMasksNeedConversion(root(), bounds())) {
2343
2344 KisProcessingApplicator applicator(this, root(),
2345 KisProcessingApplicator::RECURSIVE,
2346 KisImageSignalVector() << ModifiedSignal,
2347 kundo2_i18n("Crop Selections"));
2348
2349 KisProcessingVisitorSP visitor =
2350 new KisCropSelectionsProcessingVisitor(bounds());
2351
2352 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
2353 applicator.end();
2354 }
2355 }
2356
wrapAroundModePermitted() const2357 bool KisImage::wrapAroundModePermitted() const
2358 {
2359 return m_d->wrapAroundModePermitted;
2360 }
2361
wrapAroundModeActive() const2362 bool KisImage::wrapAroundModeActive() const
2363 {
2364 return m_d->wrapAroundModePermitted &&
2365 m_d->scheduler.wrapAroundModeSupported();
2366 }
2367
setDesiredLevelOfDetail(int lod)2368 void KisImage::setDesiredLevelOfDetail(int lod)
2369 {
2370 if (m_d->blockLevelOfDetail) {
2371 qWarning() << "WARNING: KisImage::setDesiredLevelOfDetail()"
2372 << "was called while LoD functionality was being blocked!";
2373 return;
2374 }
2375
2376 m_d->scheduler.setDesiredLevelOfDetail(lod);
2377 }
2378
currentLevelOfDetail() const2379 int KisImage::currentLevelOfDetail() const
2380 {
2381 if (m_d->blockLevelOfDetail) {
2382 return 0;
2383 }
2384
2385 return m_d->scheduler.currentLevelOfDetail();
2386 }
2387
setLevelOfDetailBlocked(bool value)2388 void KisImage::setLevelOfDetailBlocked(bool value)
2389 {
2390 KisImageBarrierLockerRaw l(this);
2391
2392 if (value && !m_d->blockLevelOfDetail) {
2393 m_d->scheduler.setDesiredLevelOfDetail(0);
2394 }
2395
2396 m_d->blockLevelOfDetail = value;
2397 }
2398
explicitRegenerateLevelOfDetail()2399 void KisImage::explicitRegenerateLevelOfDetail()
2400 {
2401 if (!m_d->blockLevelOfDetail) {
2402 m_d->scheduler.explicitRegenerateLevelOfDetail();
2403 }
2404 }
2405
levelOfDetailBlocked() const2406 bool KisImage::levelOfDetailBlocked() const
2407 {
2408 return m_d->blockLevelOfDetail;
2409 }
2410
nodeCollapsedChanged(KisNode * node)2411 void KisImage::nodeCollapsedChanged(KisNode * node)
2412 {
2413 Q_UNUSED(node);
2414 emit sigNodeCollapsedChanged();
2415 }
2416
animationInterface() const2417 KisImageAnimationInterface* KisImage::animationInterface() const
2418 {
2419 return m_d->animationInterface;
2420 }
2421
setProofingConfiguration(KisProofingConfigurationSP proofingConfig)2422 void KisImage::setProofingConfiguration(KisProofingConfigurationSP proofingConfig)
2423 {
2424 m_d->proofingConfig = proofingConfig;
2425 emit sigProofingConfigChanged();
2426 }
2427
proofingConfiguration() const2428 KisProofingConfigurationSP KisImage::proofingConfiguration() const
2429 {
2430 if (m_d->proofingConfig) {
2431 return m_d->proofingConfig;
2432 }
2433 return KisProofingConfigurationSP();
2434 }
2435
mirrorAxesCenter() const2436 QPointF KisImage::mirrorAxesCenter() const
2437 {
2438 return m_d->axesCenter;
2439 }
2440
setMirrorAxesCenter(const QPointF & value) const2441 void KisImage::setMirrorAxesCenter(const QPointF &value) const
2442 {
2443 m_d->axesCenter = value;
2444 }
2445
setAllowMasksOnRootNode(bool value)2446 void KisImage::setAllowMasksOnRootNode(bool value)
2447 {
2448 m_d->allowMasksOnRootNode = value;
2449 }
2450
allowMasksOnRootNode() const2451 bool KisImage::allowMasksOnRootNode() const
2452 {
2453 return m_d->allowMasksOnRootNode;
2454 }
2455