Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_image.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
3 * SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include "kis_image.h"
9
10#include <KoConfig.h> // WORDS_BIGENDIAN
11
12#include <stdlib.h>
13#include <math.h>
14
15#include <QImage>
16#include <QPainter>
17#include <QSize>
18#include <QDateTime>
19#include <QRect>
20
21#include <klocalizedstring.h>
22
24#include "KoColor.h"
25#include "KoColorProfile.h"
28
30#include "kis_annotation.h"
31#include "kis_count_visitor.h"
32#include "kis_filter_strategy.h"
33#include "kis_group_layer.h"
35#include "kis_layer.h"
37#include "kis_paint_layer.h"
38#include "kis_projection_leaf.h"
39#include "kis_painter.h"
40#include "kis_selection.h"
41#include "kis_transaction.h"
44#include "kis_node.h"
45#include "kis_types.h"
46
47#include "kis_image_config.h"
52#include "kis_stroke_strategy.h"
54
55#include "kis_undo_stores.h"
58
71#include "kis_wrapped_rect.h"
73#include "kis_layer_utils.h"
75
76#include "kis_lod_transform.h"
77
80
82
84
86#include "kis_lockless_stack.h"
87
88#include <QtCore>
89
90#include <functional>
91
92#include "kis_time_span.h"
93
98
99#include "KisBusyWaitBroker.h"
100#include <KisStaticInitializer.h>
102
103
104// #define SANITY_CHECKS
105
106#ifdef SANITY_CHECKS
107#define SANITY_CHECK_LOCKED(name) \
108 if (!locked()) warnKrita() << "Locking policy failed:" << name \
109 << "has been called without the image" \
110 "being locked";
111#else
112#define SANITY_CHECK_LOCKED(name)
113#endif
114
116 qRegisterMetaType<KisImageSP>("KisImageSP");
117}
118
120{
121public:
122 KisImagePrivate(KisImage *_q, qint32 w, qint32 h,
123 const KoColorSpace *c,
124 KisUndoStore *undo,
125 KisImageAnimationInterface *_animationInterface)
126 : q(_q)
127 , lockedForReadOnly(false)
128 , width(w)
129 , height(h)
130 , colorSpace(c ? c : KoColorSpaceRegistry::instance()->rgb8())
132 , isolateLayer(false)
133 , isolateGroup(false)
134 , undoStore(undo ? undo : new KisDumbUndoStore())
135 , legacyUndoAdapter(undoStore.data(), _q)
137 , signalRouter(_q)
138 , animationInterface(_animationInterface)
139 , scheduler(_q, _q)
140 , axesCenter(QPointF(0.5, 0.5))
141 {
142 {
143 KisImageConfig cfg(true);
144 if (cfg.enableProgressReporting()) {
146 }
147
148 // Each of these lambdas defines a new factory function.
150 [=](bool forgettable) {
151 return KisLodSyncPair(
154 });
155
157 [=]() {
159
164
165 return std::make_pair(suspend, resume);
166 });
167
169 [this] () {
170 undoStore->purgeRedoState();
171 });
172
174 [this] () {
175
177
178 bool addedUIUpdateRequestSuccessfully = false;
179
180 for (auto it = std::make_reverse_iterator(projectionUpdatesFilters.end());
181 it != std::make_reverse_iterator(projectionUpdatesFilters.begin());
182 ++it) {
183
186
187 if (iface) {
189 addedUIUpdateRequestSuccessfully = true;
190 break;
191 }
192 }
193
194 KIS_SAFE_ASSERT_RECOVER_NOOP(addedUIUpdateRequestSuccessfully);
195 });
196 }
197
198 connect(q, SIGNAL(sigImageModified()), KisMemoryStatisticsServer::instance(), SLOT(notifyImageChanged()));
199 connect(undoStore.data(), SIGNAL(historyStateChanged()), &signalRouter, SLOT(emitImageModifiedNotification()));
200 }
201
210
217 if (rootLayer->image() == q) {
219 }
220
221 if (rootLayer->graphListener() == q) {
223 }
224
226
230 delete animationInterface;
231 }
232
234
235 quint32 lockCount = 0;
237
238 qint32 width;
239 qint32 height;
240
241 double xres = 1.0;
242 double yres = 1.0;
243
246
249 KisGroupLayerSP rootLayer; // The layers are contained in here
250 KisSelectionMaskSP targetOverlaySelectionMask; // the overlay switching stroke will try to switch into this mask
253
257
260
261 QScopedPointer<KisUndoStore> undoStore;
264
266
269
270 // filters are applied in a reversed way, from rbegin() to rend()
277
279
280 QPointF axesCenter;
282
284 const QVector<QRect> &rects,
285 const QRect &cropRect,
286 KisProjectionUpdateFlags flags);
287
289
291
292 void convertImageColorSpaceImpl(const KoColorSpace *dstColorSpace,
293 bool convertLayers,
295 KoColorConversionTransformation::ConversionFlags conversionFlags);
296
298};
299
304
309
314
315KisImage::KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace *colorSpace, const QString& name)
316 : QObject(0)
317 , KisShared()
318 , m_d(new KisImagePrivate(this, width, height,
319 colorSpace, undoStore,
321{
322 // make sure KisImage belongs to the GUI thread
323 moveToThread(qApp->thread());
324 connect(this, SIGNAL(sigInternalStopIsolatedModeRequested()), SLOT(stopIsolatedMode()));
325
326 setObjectName(name);
327 setRootLayer(new KisGroupLayer(this, "root", OPACITY_OPAQUE_U8));
328}
329
331{
335 waitForDone();
336
337 delete m_d;
338 disconnect(); // in case Qt gets confused
339}
340
341KisImageSP KisImage::fromQImage(const QImage &image, KisUndoStore *undoStore)
342{
343 const KoColorSpace *colorSpace = 0;
344
345 switch (image.format()) {
346 case QImage::Format_Invalid:
347 case QImage::Format_Mono:
348 case QImage::Format_MonoLSB:
350 break;
351 case QImage::Format_Indexed8:
352 case QImage::Format_RGB32:
353 case QImage::Format_ARGB32:
354 case QImage::Format_ARGB32_Premultiplied:
356 break;
357 case QImage::Format_RGB16:
359 break;
360 case QImage::Format_ARGB8565_Premultiplied:
361 case QImage::Format_RGB666:
362 case QImage::Format_ARGB6666_Premultiplied:
363 case QImage::Format_RGB555:
364 case QImage::Format_ARGB8555_Premultiplied:
365 case QImage::Format_RGB888:
366 case QImage::Format_RGB444:
367 case QImage::Format_ARGB4444_Premultiplied:
368 case QImage::Format_RGBX8888:
369 case QImage::Format_RGBA8888:
370 case QImage::Format_RGBA8888_Premultiplied:
372 break;
373 case QImage::Format_BGR30:
374 case QImage::Format_A2BGR30_Premultiplied:
375 case QImage::Format_RGB30:
376 case QImage::Format_A2RGB30_Premultiplied:
378 break;
379 case QImage::Format_Alpha8:
381 break;
382 case QImage::Format_Grayscale8:
384 break;
385 case QImage::Format_Grayscale16:
387 break;
388 case QImage::Format_RGBX64:
389 case QImage::Format_RGBA64:
390 case QImage::Format_RGBA64_Premultiplied:
392 break;
393 default:
394 colorSpace = 0;
395 }
396
397 KisImageSP img = new KisImage(undoStore, image.width(), image.height(), colorSpace, i18n("Imported Image"));
398 KisPaintLayerSP layer = new KisPaintLayer(img, img->nextLayerName(), 255);
399 layer->paintDevice()->convertFromQImage(image, 0, 0, 0);
400 img->addNode(layer.data(), img->rootLayer().data());
401
402 return img;
403}
404
405KisImage *KisImage::clone(bool exactCopy)
406{
407 return new KisImage(*this, 0, exactCopy);
408}
409
411{
413}
414
415void KisImage::copyFromImageImpl(const KisImage &rhs, int policy)
416{
417 // make sure we choose exactly one from REPLACE and CONSTRUCT
418 KIS_ASSERT_RECOVER_RETURN(bool(policy & REPLACE) != bool(policy & CONSTRUCT));
419
432 const bool sizeChanged = m_d->width != rhs.width() || m_d->height != rhs.height();
433 const bool colorSpaceChanged = *m_d->colorSpace != *rhs.colorSpace();
434 const bool resolutionChanged = m_d->xres != rhs.m_d->xres || m_d->yres != rhs.m_d->yres;
435
436 if (sizeChanged) {
437 m_d->width = rhs.width();
438 m_d->height = rhs.height();
439 }
440
441 if (colorSpaceChanged) {
442 m_d->colorSpace = rhs.colorSpace();
443 }
444
445 if (resolutionChanged) {
446 m_d->xres = rhs.m_d->xres;
447 m_d->yres = rhs.m_d->yres;
448 }
449
450 // from KisImage::KisImage(const KisImage &, KisUndoStore *, bool)
451 setObjectName(rhs.objectName());
452
453 KisNodeSP oldRoot = this->root();
454 KisNodeSP newRoot = rhs.root()->clone();
455 newRoot->setGraphListener(this);
456 newRoot->setImage(this);
457
458 m_d->rootLayer = dynamic_cast<KisGroupLayer*>(newRoot.data());
459 setRoot(newRoot);
460
461 if (oldRoot) {
462 oldRoot->setImage(0);
463 oldRoot->setGraphListener(0);
464 oldRoot->disconnect();
465 }
466
467 // only when replacing do we need to Q_EMIT signals
468#define EMIT_IF_NEEDED if (!(policy & REPLACE)) {} else emit
469
470 if (sizeChanged) {
471 EMIT_IF_NEEDED sigSizeChanged(QPointF(), QPointF());
472 }
473 if (colorSpaceChanged) {
475 }
476 if (resolutionChanged) {
478 }
479
481
482 if (rhs.m_d->proofingConfig) {
484 if (policy & REPLACE) {
485 setProofingConfiguration(proofingConfig);
486 } else {
487 m_d->proofingConfig = proofingConfig;
488 }
489 }
490
491 bool exactCopy = policy & EXACT_COPY;
492
493 if (exactCopy || rhs.m_d->isolationRootNode || rhs.m_d->overlaySelectionMask) {
496
497 QQueue<KisNodeSP> linearizedNodes;
499 [&linearizedNodes](KisNodeSP node) {
500 linearizedNodes.enqueue(node);
501 });
503 [&linearizedNodes, exactCopy, &rhs, this](KisNodeSP node) {
504 KisNodeSP refNode = linearizedNodes.dequeue();
505
506 if (exactCopy) {
507 node->setUuid(refNode->uuid());
508 }
509
510 if (rhs.m_d->isolationRootNode &&
511 rhs.m_d->isolationRootNode == refNode) {
512 m_d->isolationRootNode = node;
513 }
514
515 if (rhs.m_d->overlaySelectionMask &&
516 KisNodeSP(rhs.m_d->overlaySelectionMask) == refNode) {
517 m_d->targetOverlaySelectionMask = dynamic_cast<KisSelectionMask*>(node.data());
520 }
521
522
523 // Re-establish DefaultBounds Instances for Existing Nodes
524 // This is a workaround for copy-constructors failing to pass
525 // proper DefaultBounds due to either lacking image data on construction
526 // We should change the way "DefaultBounds" works to try to make it
527 // safer for threading races.
528 using KeyframeChannelContainer = QMap<QString, KisKeyframeChannel*>;
529 KeyframeChannelContainer keyframeChannels = node->keyframeChannels();
530 for (KeyframeChannelContainer::iterator i = keyframeChannels.begin();
531 i != keyframeChannels.end(); i++) {
532 keyframeChannels[i.key()]->setNode(node);
533 }
534 });
535 }
536
538 [](KisNodeSP node) {
539 dbgImage << "Node: " << (void *)node.data();
540 });
541
542
543
544 m_d->compositions.clear();
545
546 Q_FOREACH (KisLayerCompositionSP comp, rhs.m_d->compositions) {
547 m_d->compositions << toQShared(new KisLayerComposition(*comp, this));
548 }
549
551
552 vKisAnnotationSP newAnnotations;
553 Q_FOREACH (KisAnnotationSP annotation, rhs.m_d->annotations) {
554 newAnnotations << annotation->clone();
555 }
556 m_d->annotations = newAnnotations;
557
561
562#undef EMIT_IF_NEEDED
563}
564
565KisImage::KisImage(const KisImage& rhs, KisUndoStore *undoStore, bool exactCopy)
566 : KisNodeFacade(),
568 KisShared(),
569 m_d(new KisImagePrivate(this,
570 rhs.width(), rhs.height(),
571 rhs.colorSpace(),
572 undoStore ? undoStore : new KisDumbUndoStore(),
573 new KisImageAnimationInterface(*rhs.animationInterface(), this)))
574{
575 // make sure KisImage belongs to the GUI thread
576 moveToThread(qApp->thread());
577 connect(this, SIGNAL(sigInternalStopIsolatedModeRequested()), SLOT(stopIsolatedMode()));
578
579 copyFromImageImpl(rhs, CONSTRUCT | (exactCopy ? EXACT_COPY : 0));
580}
581
582void KisImage::aboutToAddANode(KisNode *parent, int index)
583{
585 SANITY_CHECK_LOCKED("aboutToAddANode");
586}
587
588void KisImage::nodeHasBeenAdded(KisNode *parent, int index, KisNodeAdditionFlags flags)
589{
590 KisNodeGraphListener::nodeHasBeenAdded(parent, index, flags);
591
593 QMap<QString, KisKeyframeChannel*> chans = node->keyframeChannels();
594 Q_FOREACH(KisKeyframeChannel* chan, chans.values()) {
595 chan->setNode(node);
596 this->keyframeChannelHasBeenAdded(node.data(), chan);
597 }
598 });
599
600 SANITY_CHECK_LOCKED("nodeHasBeenAdded");
601 m_d->signalRouter.emitNodeHasBeenAdded(parent, index, flags);
602}
603
604void KisImage::aboutToRemoveANode(KisNode *parent, int index)
605{
606 KisNodeSP deletedNode = parent->at(index);
607 if (!dynamic_cast<KisSelectionMask*>(deletedNode.data()) &&
608 deletedNode == m_d->isolationRootNode) {
609
611 }
612
614 QMap<QString, KisKeyframeChannel*> chans = node->keyframeChannels();
615 Q_FOREACH(KisKeyframeChannel* chan, chans.values()) {
616 this->keyframeChannelAboutToBeRemoved(node.data(), chan);
617 }
618 });
619
621
622 SANITY_CHECK_LOCKED("aboutToRemoveANode");
624}
625
631
636
638{
639 if (m_d->targetOverlaySelectionMask == mask) return;
640
642
643 struct UpdateOverlaySelectionStroke : public KisSimpleStrokeStrategy {
644 UpdateOverlaySelectionStroke(KisImageSP image)
645 : KisSimpleStrokeStrategy(QLatin1String("update-overlay-selection-mask"), kundo2_noi18n("update-overlay-selection-mask")),
646 m_image(image)
647 {
648 this->enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
649 setClearsRedoOnStart(false);
650 }
651
652 void initStrokeCallback() override {
653 KisSelectionMaskSP oldMask = m_image->m_d->overlaySelectionMask;
654 KisSelectionMaskSP newMask = m_image->m_d->targetOverlaySelectionMask;
655 if (oldMask == newMask) return;
656
657 KIS_SAFE_ASSERT_RECOVER_RETURN(!newMask || static_cast<KisImage*>(newMask->graphListener()) == m_image);
658
659 m_image->m_d->overlaySelectionMask = newMask;
660
661 if (oldMask || newMask) {
662 m_image->m_d->rootLayer->notifyChildMaskChanged();
663 }
664
665 if (oldMask) {
666 const QRect oldMaskRect = oldMask->graphListener() ? oldMask->extent() : m_image->bounds();
667 m_image->m_d->rootLayer->setDirtyDontResetAnimationCache(oldMaskRect);
668 }
669
670 if (newMask) {
671 newMask->setDirty();
672 }
673
674 m_image->undoAdapter()->emitSelectionChanged();
675 }
676
677 private:
678 KisImageSP m_image;
679 };
680
681 KisStrokeId id = startStroke(new UpdateOverlaySelectionStroke(this));
682 endStroke(id);
683}
684
689
694
696{
697 KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
698 if (selectionMask) {
699 return selectionMask->selection();
700 } else {
701 return 0;
702 }
703}
704
709
714
715QString KisImage::nextLayerName(const QString &_baseName) const
716{
717 QString baseName = _baseName;
718
719 int numLayers = 0;
720 int maxLayerIndex = 0;
721 QRegularExpression numberedLayerRegexp(".* (\\d+)$");
723 [&numLayers, &maxLayerIndex, &numberedLayerRegexp] (KisNodeSP node) {
724 if (node->inherits("KisLayer")) {
725 QRegularExpressionMatch match = numberedLayerRegexp.match(node->name());
726
727 if (match.hasMatch()) {
728 maxLayerIndex = qMax(maxLayerIndex, match.captured(1).toInt());
729 }
730 numLayers++;
731 }
732 });
733
734 // special case if there is only root node
735 if (numLayers == 1) {
736 return i18nc("Name for the bottom-most layer in the layerstack", "Background");
737 }
738
739 if (baseName.isEmpty()) {
740 baseName = i18n("Paint Layer");
741 }
742
743 return QString("%1 %2").arg(baseName).arg(maxLayerIndex + 1);
744}
745
750
752{
753 return m_d->lockCount != 0;
754}
755
756void KisImage::barrierLock(bool readOnly)
757{
758 if (!locked()) {
763 m_d->lockedForReadOnly = readOnly;
764 } else {
765 m_d->lockedForReadOnly &= readOnly;
766 }
767
768 m_d->lockCount++;
769}
770
771bool KisImage::tryBarrierLock(bool readOnly)
772{
773 bool result = true;
774
775 if (!locked()) {
776 result = m_d->scheduler.tryBarrierLock();
777 m_d->lockedForReadOnly = readOnly;
778 }
779
780 if (result) {
781 m_d->lockCount++;
782 m_d->lockedForReadOnly &= readOnly;
783 }
784
785 return result;
786}
787
788bool KisImage::isIdle(bool allowLocked)
789{
790 return (allowLocked || !locked()) && m_d->scheduler.isIdle();
791}
792
804
806{
807 Q_ASSERT(locked());
808
809 if (locked()) {
810 m_d->lockCount--;
811
812 if (m_d->lockCount == 0) {
814 }
815 }
816}
817
822
827
828void KisImage::setSize(const QSize& size)
829{
830 m_d->width = size.width();
831 m_d->height = size.height();
832}
833
834void KisImage::resizeImageImpl(const QRect& newRect, bool cropLayers)
835{
836 if (newRect == bounds() && !cropLayers) return;
837
838 KUndo2MagicString actionName = cropLayers ?
839 kundo2_i18n("Crop Image") :
840 kundo2_i18n("Resize Image");
841
842 KisImageSignalVector emitSignals;
843 emitSignals << ComplexSizeChangedSignal(newRect, newRect.size());
844
845 KisCropSavedExtraData *extraData =
846 new KisCropSavedExtraData(cropLayers ?
849 newRect);
850
851 KisProcessingApplicator applicator(this, m_d->rootLayer,
854 emitSignals, actionName, extraData);
855
856 if (cropLayers || !newRect.topLeft().isNull()) {
857 KisProcessingVisitorSP visitor =
858 new KisCropProcessingVisitor(newRect, cropLayers, true);
859 applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
860 }
861 applicator.applyCommand(new KisImageResizeCommand(this, newRect.size()));
862 applicator.end();
863}
864
865void KisImage::resizeImage(const QRect& newRect)
866{
867 resizeImageImpl(newRect, false);
868}
869
870void KisImage::cropImage(const QRect& newRect)
871{
872 resizeImageImpl(newRect, true);
873}
874
875void KisImage::purgeUnusedData(bool isCancellable)
876{
883 struct PurgeUnusedDataStroke : public KisRunnableBasedStrokeStrategy {
884 PurgeUnusedDataStroke(KisImageSP image, bool isCancellable)
885 : KisRunnableBasedStrokeStrategy(QLatin1String("purge-unused-data"),
886 kundo2_i18n("Purge Unused Data")),
887 m_image(image),
888 m_finalCommand(new KUndo2Command(this->name()))
889
890 {
891 this->enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
892 this->enableJob(JOB_DOSTROKE, true);
893 this->enableJob(JOB_FINISH, true, KisStrokeJobData::SEQUENTIAL);
894
895 setClearsRedoOnStart(!isCancellable);
896 setRequestsOtherStrokesToEnd(!isCancellable);
897 setCanForgetAboutMe(isCancellable);
898 }
899
900 void initStrokeCallback() override
901 {
902 KisPaintDeviceList paintDevicesList;
903 KisPaintDeviceList projectionsList;
905
907 [&paintDevicesList, &projectionsList, this](KisNodeSP node) {
908 KisPaintDeviceList deviceList = node->getLodCapableDevices();
909
910 Q_FOREACH (KisPaintDeviceSP dev, deviceList) {
911 if (!dev) continue;
912
913 // we do **not** strip paint devices in the forgettable
914 // mode, since we should handle transactions for them
915 if (dev == node->paintDevice() && !canForgetAboutMe()) {
916 paintDevicesList << dev;
917 } else {
918 projectionsList << dev;
919 }
920 }
921 });
922
925 KritaUtils::makeContainerUnique(paintDevicesList);
926 KritaUtils::makeContainerUnique(projectionsList);
927
928 Q_FOREACH(KisPaintDeviceSP dev, paintDevicesList) {
929 projectionsList.removeAll(dev);
930
931 // all transactions will be linked to the final command via the
932 // parent-child relationship
933 m_transactions.emplace_back(dev, m_finalCommand.data(), -1, nullptr, KisTransaction::None);
934 }
935
936 // now, when the transactions are started, we can merge the two lists
937 paintDevicesList << projectionsList;
938 projectionsList.clear();
939
940 Q_FOREACH (KisPaintDeviceSP device, paintDevicesList) {
942 [device] () {
943 const_cast<KisPaintDevice*>(device.data())->purgeDefaultPixels();
944 });
945 }
946
947 addMutatedJobs(jobsData);
948 }
949
950 void finishStrokeCallback() override {
951 for (auto it = m_transactions.begin(); it != m_transactions.end(); ++it) {
952 QScopedPointer<KUndo2Command> cmd(it->endAndTake());
953
954 // verify the transaction command is linked to m_finalCommand,
955 // if not, just delete on return
956 KIS_SAFE_ASSERT_RECOVER(cmd->hasParent()) { continue; }
957
958 // if has a parent, release...
959 (void)cmd.take();
960 }
961
962 m_transactions.clear();
963
964 m_finalCommand->redo();
965 m_image->postExecutionUndoAdapter()->addCommand(toQShared(m_finalCommand.take()));
966
967 // now reset the thumbnail generation limitation
969 [](KisNodeSP node) {
970 if (node->preferredThumbnailBoundsMode() != KisThumbnailBoundsMode::Precise) {
971 node->setPreferredThumbnailBoundsMode(KisThumbnailBoundsMode::Precise);
972 }
973 });
974 }
975
976 private:
977 KisImageSP m_image;
978 QScopedPointer<KUndo2Command> m_finalCommand;
979 std::vector<KisTransaction> m_transactions;
980 };
981
982 KisStrokeId id = startStroke(new PurgeUnusedDataStroke(this, isCancellable));
983 endStroke(id);
984}
985
986void KisImage::cropNode(KisNodeSP node, const QRect& newRect, const bool activeFrameOnly)
987{
988 const bool isLayer = qobject_cast<KisLayer*>(node.data());
989 KUndo2MagicString actionName = isLayer ?
990 kundo2_i18n("Crop Layer") :
991 kundo2_i18n("Crop Mask");
992
993 KisImageSignalVector emitSignals;
994
995 KisCropSavedExtraData *extraData =
997 newRect, node);
998
999 KisProcessingApplicator applicator(this, node,
1001 emitSignals, actionName, extraData);
1002
1003 KisProcessingVisitorSP visitor =
1004 new KisCropProcessingVisitor(newRect, true, false);
1005
1006 if (node->isAnimated() && activeFrameOnly) {
1007 // Crop active frame..
1008 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1009 } else {
1010 // Crop all frames..
1012 }
1013 applicator.end();
1014}
1015
1016void KisImage::scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy)
1017{
1018 bool resolutionChanged = !qFuzzyCompare(xRes(), xres) || !qFuzzyCompare(yRes(), yres);
1019 bool sizeChanged = size != this->size();
1020
1021 if (!resolutionChanged && !sizeChanged) return;
1022
1023 KisImageSignalVector emitSignals;
1024 if (resolutionChanged) emitSignals << ResolutionChangedSignal;
1025 if (sizeChanged) emitSignals << ComplexSizeChangedSignal(bounds(), size);
1026
1027 KUndo2MagicString actionName = sizeChanged ?
1028 kundo2_i18n("Scale Image") :
1029 kundo2_i18n("Change Image Resolution");
1030
1031 KisProcessingApplicator::ProcessingFlags signalFlags =
1032 (resolutionChanged || sizeChanged) ?
1035
1036 KisProcessingApplicator applicator(this, m_d->rootLayer,
1038 emitSignals, actionName);
1039
1040 qreal sx = qreal(size.width()) / this->size().width();
1041 qreal sy = qreal(size.height()) / this->size().height();
1042
1043 QTransform shapesCorrection;
1044
1045 if (resolutionChanged) {
1046 shapesCorrection = QTransform::fromScale(xRes() / xres, yRes() / yres);
1047 }
1048
1049 KisProcessingVisitorSP visitor =
1051 0, 0,
1052 0,
1053 0, 0,
1054 filterStrategy,
1055 shapesCorrection);
1056
1058
1059 if (resolutionChanged) {
1060 KUndo2Command *parent =
1062 new KisImageSetResolutionCommand(this, xres, yres, parent);
1063 applicator.applyCommand(parent);
1064 }
1065
1066 if (sizeChanged) {
1067 applicator.applyCommand(new KisImageResizeCommand(this, size));
1068 }
1069
1070 applicator.end();
1071}
1072
1073void KisImage::scaleNode(KisNodeSP node, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection)
1074{
1075 scaleNodes(KisNodeList{node}, center, scaleX, scaleY, filterStrategy, selection);
1076}
1077void KisImage::scaleNodes(KisNodeList nodes, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection)
1078{
1079 KUndo2MagicString actionName(kundo2_i18np("Scale Layer", "Scale %1 Layers", nodes.size()));
1080 KisImageSignalVector emitSignals;
1081
1082 QPointF offset;
1083 {
1084 KisTransformWorker worker(0,
1085 scaleX, scaleY,
1086 0, 0,
1087 0.0,
1088 0, 0, 0, 0);
1089 QTransform transform = worker.transform();
1090
1091 offset = center - transform.map(center);
1092 }
1093
1094 KisProcessingApplicator applicator(this, nodes,
1096 emitSignals, actionName);
1097
1099 new KisTransformProcessingVisitor(scaleX, scaleY,
1100 0, 0,
1101 0,
1102 offset.x(), offset.y(),
1103 filterStrategy);
1104
1105 visitor->setSelection(selection);
1106
1107 if (selection) {
1108 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1109 } else {
1111 }
1112
1113 applicator.end();
1114}
1115
1117 KisNodeSP rootNode,
1118 double radians,
1119 bool resizeImage,
1120 KisSelectionSP selection)
1121{
1122 rotateImpl(actionName, KisNodeList{rootNode}, radians, resizeImage, selection);
1123}
1125 KisNodeList nodes,
1126 double radians,
1127 bool resizeImage,
1128 KisSelectionSP selection)
1129{
1130 // we can either transform (and resize) the whole image or
1131 // transform a selection, we cannot do both at the same time
1132 KIS_SAFE_ASSERT_RECOVER(!(bool(selection) && resizeImage)) {
1133 selection = 0;
1134 }
1135
1136 QRect baseBounds;
1137 if (resizeImage) {
1138 baseBounds = bounds();
1139 }
1140 else if (selection) {
1141 baseBounds = selection->selectedExactRect();
1142 }
1143 else {
1144 Q_FOREACH(KisNodeSP node, nodes) {
1145 baseBounds = baseBounds.united(node->exactBounds());
1146 }
1147 }
1148
1149 QPointF offset;
1150 QSize newSize;
1151
1152 {
1153 KisTransformWorker worker(0,
1154 1.0, 1.0,
1155 0, 0,
1156 radians,
1157 0, 0, 0, 0);
1158 QTransform transform = worker.transform();
1159
1160 if (resizeImage) {
1161 QRect newRect = transform.mapRect(baseBounds);
1162 newSize = newRect.size();
1163 offset = -newRect.topLeft();
1164 }
1165 else {
1166 QPointF origin = QRectF(baseBounds).center();
1167
1168 newSize = size();
1169 offset = -(transform.map(origin) - origin);
1170 }
1171 }
1172
1173 bool sizeChanged = resizeImage &&
1174 (newSize.width() != baseBounds.width() ||
1175 newSize.height() != baseBounds.height());
1176
1177 // These signals will be emitted after processing is done
1178 KisImageSignalVector emitSignals;
1179 if (sizeChanged) emitSignals << ComplexSizeChangedSignal(baseBounds, newSize);
1180
1181 // These flags determine whether updates are transferred to the UI during processing
1182 KisProcessingApplicator::ProcessingFlags signalFlags =
1183 sizeChanged ?
1186
1187
1188 KisProcessingApplicator applicator(this, nodes,
1190 emitSignals, actionName);
1191
1193
1195 new KisTransformProcessingVisitor(1.0, 1.0, 0.0, 0.0,
1196 radians,
1197 offset.x(), offset.y(),
1198 filter);
1199 if (selection) {
1200 visitor->setSelection(selection);
1201 }
1202
1203 if (selection) {
1204 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1205 } else {
1207 }
1208
1209 if (sizeChanged) {
1210 applicator.applyCommand(new KisImageResizeCommand(this, newSize));
1211 }
1212 applicator.end();
1213}
1214
1215
1216void KisImage::rotateImage(double radians)
1217{
1218 rotateImpl(kundo2_i18n("Rotate Image"), root(), radians, true, 0);
1219}
1220
1221void KisImage::rotateNode(KisNodeSP node, double radians, KisSelectionSP selection)
1222{
1223 rotateNodes(KisNodeList{node}, radians, selection);
1224}
1225void KisImage::rotateNodes(KisNodeList nodes, double radians, KisSelectionSP selection)
1226{
1227 if (nodes.size() == 1 && nodes[0]->inherits("KisMask")) {
1228 rotateImpl(kundo2_i18n("Rotate Mask"), nodes, radians, false, selection);
1229 }
1230 else {
1231 rotateImpl(kundo2_i18np("Rotate Layer", "Rotate %1 Layers", nodes.size()), nodes, radians, false, selection);
1232 }
1233}
1234
1236 KisNodeSP rootNode,
1237 bool resizeImage,
1238 double angleX, double angleY,
1239 KisSelectionSP selection)
1240{
1241 shearImpl(actionName, KisNodeList{rootNode}, resizeImage, angleX, angleY, selection);
1242}
1244 KisNodeList nodes,
1245 bool resizeImage,
1246 double angleX, double angleY,
1247 KisSelectionSP selection)
1248{
1249 QRect baseBounds;
1250 if (resizeImage) {
1251 baseBounds = bounds();
1252 }
1253 else if (selection) {
1254 baseBounds = selection->selectedExactRect();
1255 }
1256 else {
1257 Q_FOREACH(KisNodeSP node, nodes) {
1258 baseBounds = baseBounds.united(node->exactBounds());
1259 }
1260 }
1261 const QPointF origin = QRectF(baseBounds).center();
1262
1263 //angleX, angleY are in degrees
1264 const qreal pi = 3.1415926535897932385;
1265 const qreal deg2rad = pi / 180.0;
1266
1267 qreal tanX = tan(angleX * deg2rad);
1268 qreal tanY = tan(angleY * deg2rad);
1269
1270 QPointF offset;
1271 QSize newSize;
1272
1273 {
1274 KisTransformWorker worker(0,
1275 1.0, 1.0,
1276 tanX, tanY,
1277 0,
1278 0, 0, 0, 0);
1279
1280 QRect newRect = worker.transform().mapRect(baseBounds);
1281 newSize = newRect.size();
1282 if (resizeImage) offset = -newRect.topLeft();
1283 else offset = origin - worker.transform().map(origin);
1284 }
1285
1286 if (newSize == baseBounds.size()) return;
1287
1288 KisImageSignalVector emitSignals;
1289 if (resizeImage) emitSignals << ComplexSizeChangedSignal(baseBounds, newSize);
1290
1291 KisProcessingApplicator::ProcessingFlags signalFlags =
1294
1295 KisProcessingApplicator applicator(this, nodes,
1296 signalFlags,
1297 emitSignals, actionName);
1298
1300
1302 new KisTransformProcessingVisitor(1.0, 1.0,
1303 tanX, tanY,
1304 0,
1305 offset.x(), offset.y(),
1306 filter);
1307
1308 if (selection) {
1309 visitor->setSelection(selection);
1310 }
1311
1312 if (selection) {
1313 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1314 } else {
1316 }
1317
1318 if (resizeImage) {
1319 applicator.applyCommand(new KisImageResizeCommand(this, newSize));
1320 }
1321
1322 applicator.end();
1323}
1324
1325void KisImage::shearNode(KisNodeSP node, double angleX, double angleY, KisSelectionSP selection)
1326{
1327 shearNodes(KisNodeList{node}, angleX, angleY, selection);
1328}
1329void KisImage::shearNodes(KisNodeList nodes, double angleX, double angleY, KisSelectionSP selection)
1330{
1331 if (nodes.size() == 1 && nodes[0]->inherits("KisMask")) {
1332 shearImpl(kundo2_i18n("Shear Mask"), nodes, false,
1333 angleX, angleY, selection);
1334 }
1335 else {
1336 shearImpl(kundo2_i18np("Shear Layer", "Shear %1 Layers", nodes.size()), nodes, false,
1337 angleX, angleY, selection);
1338 }
1339}
1340
1341void KisImage::shear(double angleX, double angleY)
1342{
1343 shearImpl(kundo2_i18n("Shear Image"), m_d->rootLayer, true,
1344 angleX, angleY, 0);
1345}
1346
1348 const KoColorSpace *dstColorSpace,
1350 KoColorConversionTransformation::ConversionFlags conversionFlags)
1351{
1352 if (!node->projectionLeaf()->isLayer()) return;
1353 // must not be an image root, use convertImageColorSpace() for that:
1354 KIS_SAFE_ASSERT_RECOVER_RETURN(!node->image() || (node.data() != node->image()->rootLayer().data()));
1355
1356 const KoColorSpace *srcColorSpace = node->colorSpace();
1357
1358 if (!dstColorSpace || *srcColorSpace == *dstColorSpace) return;
1359
1360 KUndo2MagicString actionName =
1361 kundo2_i18n("Convert Layer Color Space");
1362
1363 KisImageSignalVector emitSignals;
1364
1365 KisProcessingApplicator applicator(this, node,
1367 emitSignals, actionName);
1368
1369 applicator.applyVisitor(
1371 srcColorSpace, dstColorSpace,
1372 renderingIntent, conversionFlags),
1374
1375 applicator.end();
1376}
1377
1379{
1381 State initialState, KUndo2Command *parent = 0)
1382 : KisCommandUtils::FlipFlopCommand(initialState, parent),
1383 m_cs(cs),
1384 m_image(image)
1385 {
1386 }
1387
1388 void partA() override {
1389 KisImageSP image = m_image;
1390
1391 if (image) {
1393 }
1394 }
1395
1396private:
1399};
1400
1402 bool convertLayers,
1404 KoColorConversionTransformation::ConversionFlags conversionFlags)
1405{
1406 const KoColorSpace *srcColorSpace = this->colorSpace;
1407
1408 if (!dstColorSpace || *srcColorSpace == *dstColorSpace) return;
1409
1410 const KUndo2MagicString actionName =
1411 convertLayers ?
1412 kundo2_i18n("Convert Image Color Space") :
1413 kundo2_i18n("Convert Projection Color Space");
1414
1415 KisImageSignalVector emitSignals;
1416 emitSignals << ColorSpaceChangedSignal;
1417
1418 KisProcessingApplicator::ProcessingFlags flags = KisProcessingApplicator::NO_UI_UPDATES;
1419 if (convertLayers) {
1421 }
1422
1423 KisProcessingApplicator applicator(q, this->rootLayer,
1424 flags,
1425 emitSignals, actionName);
1426
1427 applicator.applyCommand(
1429 KisImageWSP(q),
1432
1433 applicator.applyVisitor(
1435 srcColorSpace, dstColorSpace,
1436 renderingIntent, conversionFlags),
1438
1439 applicator.applyCommand(
1441 KisImageWSP(q),
1444
1445
1446 applicator.end();
1447}
1448
1451 KoColorConversionTransformation::ConversionFlags conversionFlags)
1452{
1453 m_d->convertImageColorSpaceImpl(dstColorSpace, true, renderingIntent, conversionFlags);
1454}
1455
1462
1464{
1465 const KUndo2MagicString actionName = kundo2_i18n("Unify Layers Color Space");
1466
1467 KisImageSignalVector emitSignals;
1468
1469 KisProcessingApplicator::ProcessingFlags flags =
1471
1472 KisProcessingApplicator applicator(this, m_d->rootLayer,
1473 flags,
1474 emitSignals, actionName);
1475
1476 // src and dst color spaces coincide, since we should just unify
1477 // all our layers
1478 applicator.applyVisitor(
1484
1485 applicator.end();
1486}
1487
1489{
1490 const KoColorSpace *srcColorSpace = node->colorSpace();
1491
1492 if (!node->projectionLeaf()->isLayer()) return false;
1493 if (!profile || *srcColorSpace->profile() == *profile) return false;
1494
1495 KUndo2MagicString actionName = kundo2_i18n("Assign Profile to Layer");
1496
1497 KisImageSignalVector emitSignals;
1498
1499 const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
1500 if (!dstColorSpace) return false;
1501
1502 KisProcessingApplicator applicator(this, node,
1505 emitSignals, actionName);
1506
1507 applicator.applyVisitor(
1509 srcColorSpace, dstColorSpace),
1511
1512 applicator.end();
1513
1514 return true;
1515}
1516
1517
1518bool KisImage::assignImageProfile(const KoColorProfile *profile, bool blockAllUpdates)
1519{
1520 if (!profile) return false;
1521
1522 const KoColorSpace *srcColorSpace = m_d->colorSpace;
1523 bool imageProfileIsSame = *srcColorSpace->profile() == *profile;
1524
1525 imageProfileIsSame &=
1527 [profile] (KisNodeSP node) {
1528 return *node->colorSpace()->profile() != *profile;
1529 });
1530
1531 if (imageProfileIsSame) {
1532 dbgImage << "Trying to set the same image profile again" << ppVar(srcColorSpace->profile()->name()) << ppVar(profile->name());
1533 return true;
1534 }
1535
1536 KUndo2MagicString actionName = kundo2_i18n("Assign Profile");
1537
1538 KisImageSignalVector emitSignals;
1539 emitSignals << ProfileChangedSignal;
1540
1541 const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
1542 if (!dstColorSpace) return false;
1543
1544 KisProcessingApplicator applicator(this, m_d->rootLayer,
1546 (!blockAllUpdates ?
1549 emitSignals, actionName);
1550
1551 applicator.applyCommand(
1553 KisImageWSP(this),
1556
1557 applicator.applyVisitor(
1559 srcColorSpace, dstColorSpace),
1561
1562 applicator.applyCommand(
1564 KisImageWSP(this),
1567
1568
1569 applicator.end();
1570
1571 return true;
1572}
1573
1578
1580{
1581 return m_d->colorSpace;
1582}
1583
1585{
1586 return colorSpace()->profile();
1587}
1588
1589double KisImage::xRes() const
1590{
1591 return m_d->xres;
1592}
1593
1594double KisImage::yRes() const
1595{
1596 return m_d->yres;
1597}
1598
1599void KisImage::setResolution(double xres, double yres)
1600{
1601 if (xres > 0) {
1602 m_d->xres = xres;
1603 } else {
1604 qWarning() << "WARNING: Ignoring attempt to set image x resolution <= 0 (" << xres << ")!";
1605 }
1606
1607 if (yres > 0) {
1608 m_d->yres = yres;
1609 } else {
1610 qWarning() << "WARNING: Ignoring attempt to set image y resolution <= 0 (" << yres << ")!";
1611 }
1612}
1613
1614QPointF KisImage::documentToPixel(const QPointF &documentCoord) const
1615{
1616 return QPointF(documentCoord.x() * xRes(), documentCoord.y() * yRes());
1617}
1618
1619QPoint KisImage::documentToImagePixelFloored(const QPointF &documentCoord) const
1620{
1621 QPointF pixelCoord = documentToPixel(documentCoord);
1622 return QPoint(qFloor(pixelCoord.x()), qFloor(pixelCoord.y()));
1623}
1624
1625QRectF KisImage::documentToPixel(const QRectF &documentRect) const
1626{
1627 return QRectF(documentToPixel(documentRect.topLeft()), documentToPixel(documentRect.bottomRight()));
1628}
1629
1630QPointF KisImage::pixelToDocument(const QPointF &pixelCoord) const
1631{
1632 return QPointF(pixelCoord.x() / xRes(), pixelCoord.y() / yRes());
1633}
1634
1635QPointF KisImage::pixelToDocument(const QPoint &pixelCoord) const
1636{
1637 return QPointF((pixelCoord.x() + 0.5) / xRes(), (pixelCoord.y() + 0.5) / yRes());
1638}
1639
1640QRectF KisImage::pixelToDocument(const QRectF &pixelCoord) const
1641{
1642 return QRectF(pixelToDocument(pixelCoord.topLeft()), pixelToDocument(pixelCoord.bottomRight()));
1643}
1644
1645qint32 KisImage::width() const
1646{
1647 return m_d->width;
1648}
1649
1650qint32 KisImage::height() const
1651{
1652 return m_d->height;
1653}
1654
1656{
1657 Q_ASSERT(m_d->rootLayer);
1658 return m_d->rootLayer;
1659}
1660
1662{
1663 if (m_d->isolationRootNode) {
1664 return m_d->isolationRootNode->projection();
1665 }
1666
1667 Q_ASSERT(m_d->rootLayer);
1669 Q_ASSERT(projection);
1670 return projection;
1671}
1672
1673qint32 KisImage::nlayers() const
1674{
1675 QStringList list;
1676 list << "KisLayer";
1677
1678 KisCountVisitor visitor(list, KoProperties());
1679 m_d->rootLayer->accept(visitor);
1680 return visitor.count();
1681}
1682
1684{
1685 QStringList list;
1686 list << "KisLayer";
1687 KoProperties properties;
1688 properties.setProperty("visible", false);
1689 KisCountVisitor visitor(list, properties);
1690 m_d->rootLayer->accept(visitor);
1691
1692 return visitor.count();
1693}
1694
1696{
1697 const QStringList list = {"KisLayer"};
1698
1699 KoProperties koProperties;
1700 KisCountVisitor visitor(list, koProperties);
1701 const QList<KisNodeSP> childNodes = m_d->rootLayer->childNodes(list, koProperties);
1702 for (KisNodeSP childNode: childNodes) {
1703 childNode->accept(visitor);
1704 }
1705 return visitor.count();
1706}
1707
1709{
1710 KisLayerUtils::flattenImage(this, activeNode);
1711}
1712
1714{
1715 KisLayerUtils::mergeMultipleNodes(this, mergedNodes, putAfter);
1716}
1717
1719{
1720 KisLayerUtils::mergeDown(this, layer, strategy);
1721}
1722
1724{
1725 KisLayerUtils::flattenLayer(this, layer);
1726}
1727
1733
1734QImage KisImage::convertToQImage(QRect imageRect,
1735 const KoColorProfile * profile)
1736{
1737 qint32 x;
1738 qint32 y;
1739 qint32 w;
1740 qint32 h;
1741 imageRect.getRect(&x, &y, &w, &h);
1742 return convertToQImage(x, y, w, h, profile);
1743}
1744
1746 qint32 y,
1747 qint32 w,
1748 qint32 h,
1749 const KoColorProfile * profile)
1750{
1752 if (!dev) return QImage();
1753 QImage image = dev->convertToQImage(const_cast<KoColorProfile*>(profile), x, y, w, h,
1756
1757 return image;
1758}
1759
1760
1761
1762QImage KisImage::convertToQImage(const QSize& scaledImageSize, const KoColorProfile *profile)
1763{
1764 if (scaledImageSize.isEmpty()) {
1765 return QImage();
1766 }
1767
1769 KisPainter gc;
1770 gc.copyAreaOptimized(QPoint(0, 0), projection(), dev, bounds());
1771 gc.end();
1772 double scaleX = qreal(scaledImageSize.width()) / width();
1773 double scaleY = qreal(scaledImageSize.height()) / height();
1774
1775
1776 if (scaleX < 1.0/256 || scaleY < 1.0/256) {
1777 // quick checking if we're not trying to scale too much
1778 // convertToQImage uses KisFixedPoint values, which means that the scale cannot be smaller than 1/2^8
1779 // BUG:432182
1780 // FIXME: would be best to extend KisFixedPoint instead
1781 return convertToQImage(size(), profile).scaled(scaledImageSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
1782 }
1783
1784 KoDummyUpdaterHolder updaterHolder;
1785 QPointer<KoUpdater> updater = updaterHolder.updater();
1786
1787 KisTransformWorker worker(dev, scaleX, scaleY, 0.0, 0.0, 0.0, 0.0, 0.0, updater, KisFilterStrategyRegistry::instance()->value("Bicubic"));
1788 worker.run();
1789
1790 return dev->convertToQImage(profile);
1791}
1796
1797QRect KisImage::bounds() const
1798{
1799 return QRect(0, 0, width(), height());
1800}
1801
1803{
1804 QRect boundRect = bounds();
1805
1806 const int lod = currentLevelOfDetail();
1807 if (lod > 0) {
1808 KisLodTransform t(lod);
1809 boundRect = t.map(boundRect);
1810 }
1811
1812 return boundRect;
1813}
1814
1822
1824{
1825 return m_d->undoStore->presentCommand();
1826}
1827
1829{
1830 disconnect(m_d->undoStore.data(), SIGNAL(historyStateChanged()), &m_d->signalRouter, SLOT(emitImageModifiedNotification()));
1831
1834 m_d->undoStore.reset(undoStore);
1835
1836 connect(m_d->undoStore.data(), SIGNAL(historyStateChanged()), &m_d->signalRouter, SLOT(emitImageModifiedNotification()));
1837
1838}
1839
1841{
1842 return m_d->undoStore.data();
1843}
1844
1846{
1847 return &m_d->legacyUndoAdapter;
1848}
1849
1855
1864
1866{
1868
1870
1871 if (m_d->rootLayer) {
1873 m_d->rootLayer->setImage(0);
1874 m_d->rootLayer->disconnect();
1875
1876 KisPaintDeviceSP original = m_d->rootLayer->original();
1878 }
1879
1881 m_d->rootLayer->disconnect();
1883 m_d->rootLayer->setImage(this);
1884
1886 this->setDefaultProjectionColor(defaultProjectionColor);
1887}
1888
1890{
1891 // Find the icc annotation, if there is one
1892 vKisAnnotationSP_it it = m_d->annotations.begin();
1893 while (it != m_d->annotations.end()) {
1894 if ((*it)->type() == annotation->type()) {
1895 *it = annotation;
1897 return;
1898 }
1899 ++it;
1900 }
1901 m_d->annotations.push_back(annotation);
1903}
1904
1906{
1907 vKisAnnotationSP_it it = m_d->annotations.begin();
1908 while (it != m_d->annotations.end()) {
1909 if ((*it) && (*it)->type() == type) {
1910 return *it;
1911 }
1912 else if (!*it) {
1913 qWarning() << "Skipping deleted annotation";
1914 }
1915 ++it;
1916 }
1917 return KisAnnotationSP(0);
1918}
1919
1920void KisImage::removeAnnotation(const QString& type)
1921{
1922 vKisAnnotationSP_it it = m_d->annotations.begin();
1923 while (it != m_d->annotations.end()) {
1924 if ((*it)->type() == type) {
1925 m_d->annotations.erase(it);
1927 return;
1928 }
1929 ++it;
1930 }
1931}
1932
1937
1942
1947
1952
1960
1962{
1970 if (strokeStrategy->requestsOtherStrokesToEnd()) {
1972 }
1973
1974 return m_d->scheduler.startStroke(strokeStrategy);
1975}
1976
1978{
1979 KisImageConfig imageConfig(true);
1980 int patchWidth = imageConfig.updatePatchWidth();
1981 int patchHeight = imageConfig.updatePatchHeight();
1982
1983 for (int y = 0; y < rc.height(); y += patchHeight) {
1984 for (int x = 0; x < rc.width(); x += patchWidth) {
1985 QRect patchRect(x, y, patchWidth, patchHeight);
1986 patchRect &= rc;
1987
1988 KritaUtils::addJobConcurrent(jobs, std::bind(&KisImage::notifyProjectionUpdated, q, patchRect));
1989 }
1990 }
1991}
1992
1993bool KisImage::startIsolatedMode(KisNodeSP node, bool isolateLayer, bool isolateGroup)
1994{
1995 m_d->isolateLayer = isolateLayer;
1996 m_d->isolateGroup = isolateGroup;
1997 if ((isolateLayer || isolateGroup) == false) return false;
1998
2003 if (!node->projection()) return false;
2004
2005 struct StartIsolatedModeStroke : public KisRunnableBasedStrokeStrategy {
2006 StartIsolatedModeStroke(KisNodeSP node, KisImageSP image, bool isolateLayer, bool isolateGroup)
2007 : KisRunnableBasedStrokeStrategy(QLatin1String("start-isolated-mode"),
2008 kundo2_noi18n("start-isolated-mode")),
2009 m_newRoot(node),
2010 m_image(image),
2011 m_isolateLayer(isolateLayer),
2012 m_isolateGroup(isolateGroup)
2013 {
2014 this->enableJob(JOB_INIT, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
2015 this->enableJob(JOB_DOSTROKE, true);
2016 this->enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
2017 setClearsRedoOnStart(false);
2018 }
2019
2020 void initStrokeCallback() override {
2021 if (m_isolateLayer == false && m_isolateGroup == true) {
2022 // Isolate parent node unless node is the root note.
2023 m_newRoot = m_newRoot->parent() ? m_newRoot->parent() : m_newRoot;
2024 }
2025 // pass-though node don't have any projection prepared, so we should
2026 // explicitly regenerate it before activating isolated mode.
2027 m_newRoot->projectionLeaf()->explicitlyRegeneratePassThroughProjection();
2028 m_prevRoot = m_image->m_d->isolationRootNode;
2029
2030 const bool beforeVisibility = m_newRoot->projectionLeaf()->visible();
2031 const bool prevRootBeforeVisibility = m_prevRoot ? m_prevRoot->projectionLeaf()->visible() : false;
2032
2033 m_image->m_d->isolationRootNode = m_newRoot;
2034 Q_EMIT m_image->sigIsolatedModeChanged();
2035
2036 const bool afterVisibility = m_newRoot->projectionLeaf()->visible();
2037 const bool prevRootAfterVisibility = m_prevRoot ? m_prevRoot->projectionLeaf()->visible() : false;
2038
2039 m_newRootNeedsFullRefresh = beforeVisibility != afterVisibility;
2040 m_prevRootNeedsFullRefresh = prevRootBeforeVisibility != prevRootAfterVisibility;
2041 }
2042
2043 void finishStrokeCallback() override {
2044 // the GUI uses our thread to do the color space conversion so we
2045 // need to Q_EMIT this signal in multiple threads
2046
2047 if (m_prevRoot && m_prevRootNeedsFullRefresh) {
2048 m_image->refreshGraphAsync(m_prevRoot);
2049 }
2050
2051 if (m_newRootNeedsFullRefresh) {
2052 m_image->refreshGraphAsync(m_newRoot);
2053 }
2054
2055 if (!m_prevRootNeedsFullRefresh && !m_newRootNeedsFullRefresh) {
2057 m_image->m_d->notifyProjectionUpdatedInPatches(m_image->bounds(), jobs);
2058 this->runnableJobsInterface()->addRunnableJobs(jobs);
2059 }
2060
2061 m_image->invalidateAllFrames();
2062 }
2063
2064 private:
2065 KisNodeSP m_newRoot;
2066 KisNodeSP m_prevRoot;
2067 KisImageSP m_image;
2068 bool m_newRootNeedsFullRefresh = false;
2069 bool m_prevRootNeedsFullRefresh = false;
2070
2071 bool m_isolateLayer;
2072 bool m_isolateGroup;
2073 };
2074
2075 KisStrokeId id = startStroke(new StartIsolatedModeStroke(node, this, isolateLayer, isolateGroup));
2076 endStroke(id);
2077
2078 return true;
2079}
2080
2082{
2083 if (!m_d->isolationRootNode) return;
2084
2085 struct StopIsolatedModeStroke : public KisRunnableBasedStrokeStrategy {
2086 StopIsolatedModeStroke(KisImageSP image)
2087 : KisRunnableBasedStrokeStrategy(QLatin1String("stop-isolated-mode"), kundo2_noi18n("stop-isolated-mode")),
2088 m_image(image),
2089 m_oldRootNode(nullptr),
2090 m_oldNodeNeedsRefresh(false)
2091 {
2092 this->enableJob(JOB_INIT);
2093 this->enableJob(JOB_DOSTROKE, true);
2094 this->enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
2095 setClearsRedoOnStart(false);
2096 }
2097
2098 void initStrokeCallback() override {
2099 if (!m_image->m_d->isolationRootNode) return;
2100
2101 m_oldRootNode = m_image->m_d->isolationRootNode;
2102
2103 const bool beforeVisibility = m_oldRootNode->projectionLeaf()->visible();
2104 m_image->m_d->isolationRootNode = 0;
2105 m_image->m_d->isolateLayer = false;
2106 m_image->m_d->isolateGroup = false;
2107 Q_EMIT m_image->sigIsolatedModeChanged();
2108 const bool afterVisibility = m_oldRootNode->projectionLeaf()->visible();
2109
2110 m_oldNodeNeedsRefresh = (beforeVisibility != afterVisibility);
2111 }
2112
2113 void finishStrokeCallback() override {
2114
2115 m_image->invalidateAllFrames();
2116
2117 if (m_oldNodeNeedsRefresh){
2118 m_oldRootNode->setDirty(m_image->bounds());
2119 } else {
2120 // TODO: Substitute notifyProjectionUpdated() with this code
2121 // when update optimization is implemented
2122 //
2123 // QRect updateRect = bounds() | oldRootNode->extent();
2124 //oldRootNode->setDirty(updateRect);
2125
2127 m_image->m_d->notifyProjectionUpdatedInPatches(m_image->bounds(), jobs);
2128 this->runnableJobsInterface()->addRunnableJobs(jobs);
2129 }
2130 }
2131
2132 private:
2133 KisImageSP m_image;
2134 KisNodeSP m_oldRootNode;
2135 bool m_oldNodeNeedsRefresh;
2136 };
2137
2138 KisStrokeId id = startStroke(new StopIsolatedModeStroke(this));
2139 endStroke(id);
2140}
2141
2145
2147{
2148 return m_d->isolateLayer;
2149}
2150
2152{
2153 return m_d->isolateGroup;
2154}
2155
2161
2166
2168{
2169 return m_d->scheduler.cancelStroke(id);
2170}
2171
2173{
2174 return scheduler.tryCancelCurrentStrokeAsync();
2175}
2176
2181
2186
2193
2198
2204
2209
2211{
2217 refreshGraphAsync(0, bounds(), QRect());
2218 waitForDone();
2219}
2220
2221void KisImage::refreshGraphAsync(KisNodeSP root, const QVector<QRect> &rects, const QRect &cropRect, KisProjectionUpdateFlags flags)
2222{
2223 if (!root) root = m_d->rootLayer;
2224
2225 QVector<QRect> requestedRects = rects;
2226
2227 KisGroupLayer *group = dynamic_cast<KisGroupLayer*>(root.data());
2228 if (group && group->passThroughMode()) {
2239 QVector<QRect> changeRects = requestedRects;
2240 KisProjectionLeafSP leaf = root->projectionLeaf()->nextSibling();
2241 while (leaf) {
2242 if (leaf->shouldBeRendered()) {
2243 for (auto it = changeRects.begin(); it != changeRects.end(); ++it) {
2244 *it = leaf->projectionPlane()->changeRect(*it, leaf->node() == root ? KisNode::N_FILTHY : KisNode::N_ABOVE_FILTHY);
2245 }
2246 }
2247
2248 leaf = leaf->nextSibling();
2249 }
2250
2251 std::swap(requestedRects, changeRects);
2252 root = group->parent();
2253
2255 }
2256
2261 for (auto it = m_d->projectionUpdatesFilters.rbegin();
2262 it != m_d->projectionUpdatesFilters.rend();
2263 ++it) {
2264
2265 KIS_SAFE_ASSERT_RECOVER(*it) { continue; }
2266
2267 if ((*it)->filterRefreshGraph(this, root.data(), requestedRects, cropRect, flags)) {
2268 return;
2269 }
2270 }
2271
2272 if (!flags.testFlag(KisProjectionUpdateFlag::DontInvalidateFrames)) {
2273 m_d->animationInterface->notifyNodeChanged(root.data(), requestedRects, true);
2274 }
2275
2276 m_d->scheduler.fullRefreshAsync(root, requestedRects, cropRect, flags);
2277}
2278
2280{
2281 m_d->scheduler.addSpontaneousJob(spontaneousJob);
2282}
2283
2285{
2287}
2288
2297
2312
2319
2325
2331
2336
2341
2346
2348{
2350}
2351
2353{
2354 m_d->disableUIUpdateSignals.deref();
2355
2356 QRect rect;
2357 QVector<QRect> postponedUpdates;
2358
2360 postponedUpdates.append(rect);
2361 }
2362
2363 return postponedUpdates;
2364}
2365
2367{
2369
2371 int lod = currentLevelOfDetail();
2372 QRect dirtyRect = !lod ? rc : KisLodTransform::upscaledRect(rc, lod);
2373
2374 if (dirtyRect.isEmpty()) return;
2375
2376 Q_EMIT sigImageUpdated(dirtyRect);
2377 } else {
2379 }
2380}
2381
2386
2388{
2389 return m_d->scheduler.threadsLimit();
2390}
2391
2412
2415 const QVector<QRect> &rects,
2416 const QRect &cropRect,
2417 KisProjectionUpdateFlags flags)
2418{
2419 if (rects.isEmpty()) return;
2420
2421 scheduler.updateProjection(node, rects, cropRect, flags);
2422}
2423
2424void KisImage::requestProjectionUpdate(KisNode *node, const QVector<QRect> &rects, KisProjectionUpdateFlags flags)
2425{
2430 for (auto it = m_d->projectionUpdatesFilters.rbegin();
2431 it != m_d->projectionUpdatesFilters.rend();
2432 ++it) {
2433
2434 KIS_SAFE_ASSERT_RECOVER(*it) { continue; }
2435
2436 if ((*it)->filter(this, node, rects, flags)) {
2437 return;
2438 }
2439 }
2440
2441 if (!flags.testFlag(KisProjectionUpdateFlag::DontInvalidateFrames)) {
2442 m_d->animationInterface->notifyNodeChanged(node, rects, false);
2443 }
2444
2454 QVector<QRect> allSplitRects;
2455
2456 const QRect boundRect = effectiveLodBounds();
2457 Q_FOREACH (const QRect &rc, rects) {
2458 KisWrappedRect splitRect(rc, boundRect, m_d->wrapAroundModeAxis);
2459 allSplitRects.append(splitRect);
2460 }
2461
2462 m_d->requestProjectionUpdateImpl(node, allSplitRects, boundRect, flags);
2463
2464 } else {
2465 m_d->requestProjectionUpdateImpl(node, rects, bounds(), flags);
2466 }
2467
2469}
2470
2471void KisImage::invalidateFrames(const KisTimeSpan &range, const QRect &rect)
2472{
2474}
2475
2480
2485
2487{
2488 Q_UNUSED(node);
2489
2490 channel->connect(channel, SIGNAL(sigAddedKeyframe(const KisKeyframeChannel*, int)), m_d->animationInterface, SIGNAL(sigKeyframeAdded(const KisKeyframeChannel*, int)), Qt::UniqueConnection);
2491 channel->connect(channel, SIGNAL(sigKeyframeHasBeenRemoved(const KisKeyframeChannel*,int)), m_d->animationInterface, SIGNAL(sigKeyframeRemoved(const KisKeyframeChannel*, int)), Qt::UniqueConnection);
2492}
2493
2495{
2496 Q_UNUSED(node);
2497
2498 channel->disconnect(channel, SIGNAL(sigAddedKeyframe(const KisKeyframeChannel*, int)), m_d->animationInterface, SIGNAL(sigKeyframeAdded(const KisKeyframeChannel*, int)));
2499 channel->disconnect(channel, SIGNAL(sigKeyframeHasBeenRemoved(const KisKeyframeChannel*, int)), m_d->animationInterface, SIGNAL(sigKeyframeRemoved(const KisKeyframeChannel*, int)));
2500}
2501
2506
2508{
2509 m_d->compositions.append(composition);
2510}
2511
2513{
2514 m_d->compositions.removeAll(composition);
2515}
2516
2518{
2519 int index = m_d->compositions.indexOf(composition);
2520 if (index <= 0) {
2521 return;
2522 }
2523 m_d->compositions.move(index, index - 1);
2524}
2525
2527{
2528 int index = m_d->compositions.indexOf(composition);
2529 if (index >= m_d->compositions.size() -1) {
2530 return;
2531 }
2532 m_d->compositions.move(index, index + 1);
2533}
2534
2536{
2537 KisSelectionMask *mask = dynamic_cast<KisSelectionMask*>(root.data());
2538 if (mask &&
2539 (!bounds.contains(mask->paintDevice()->exactBounds()) ||
2540 mask->selection()->hasShapeSelection())) {
2541
2542 return true;
2543 }
2544
2545 KisNodeSP node = root->firstChild();
2546
2547 while (node) {
2548 if (checkMasksNeedConversion(node, bounds)) {
2549 return true;
2550 }
2551
2552 node = node->nextSibling();
2553 }
2554
2555 return false;
2556}
2557
2559{
2562 }
2563
2565
2568
2569 KisProcessingApplicator applicator(this, root(),
2572 kundo2_i18n("Crop Selections"));
2573
2574 KisProcessingVisitorSP visitor =
2576
2577 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
2578 applicator.end();
2579 }
2580}
2581
2586
2591
2592
2597
2603
2608
2617
2622
2627
2629{
2630 Q_UNUSED(node);
2631 Q_EMIT sigNodeCollapsedChanged();
2632}
2633
2638
2640{
2641 const bool changed = bool(m_d->proofingConfig) != bool(proofingConfig) ||
2642 (m_d->proofingConfig && proofingConfig && *m_d->proofingConfig != *proofingConfig);
2643
2644 // we still assign even when unchanged since they can be different
2645 // shared pointer objects
2646 m_d->proofingConfig = proofingConfig;
2647
2648 if (changed) {
2649 Q_EMIT sigProofingConfigChanged();
2650 }
2651}
2652
2660
2662{
2663 return m_d->axesCenter;
2664}
2665
2666void KisImage::setMirrorAxesCenter(const QPointF &value) const
2667{
2668 m_d->axesCenter = value;
2669}
2670
2675
2677{
2678 return m_d->allowMasksOnRootNode;
2679}
float value(const T *src, size_t ch)
QVector< KisImageSignalType > KisImageSignalVector
@ ColorSpaceChangedSignal
@ ProfileChangedSignal
@ LayersChangedSignal
@ ModifiedWithoutUndoSignal
@ ResolutionChangedSignal
WrapAroundAxis
@ WRAPAROUND_BOTH
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
const quint8 OPACITY_OPAQUE_U8
PythonPluginManager * instance
void notifyWaitOnImageStarted(KisImage *image)
void notifyWaitOnImageEnded(KisImage *image)
static KisBusyWaitBroker * instance()
The KisDumbUndoStore class doesn't actually save commands, so you cannot undo or redo!
static KisFilterStrategyRegistry * instance()
void invalidateFrames(const KisTimeSpan &range, const QRect &rect)
void notifyNodeChanged(const KisNode *node, const QRect &rect, bool recursive)
void requestTimeSwitchNonGUI(int time, bool useUndo=false)
int updatePatchWidth() const
int updatePatchHeight() const
bool enableProgressReporting(bool requestDefault=false) const
KisSelectionMaskSP deselectedGlobalSelection() const
Definition kis_image.cc:305
void setDeselectedGlobalSelection(KisSelectionMaskSP selectionMask)
Definition kis_image.cc:310
KisImageGlobalSelectionManagementInterface(KisImage *image)
Definition kis_image.cc:300
void emitNotification(KisImageSignalType type)
void emitNodeHasBeenAdded(KisNode *parent, int index, KisNodeAdditionFlags flags)
void emitAboutToRemoveANode(KisNode *parent, int index)
void emitNodeChanged(KisNodeSP node)
KisCompositeProgressProxy compositeProgressProxy
Definition kis_image.cc:278
KisLocklessStack< QRect > savedDisabledUIUpdates
Definition kis_image.cc:268
vKisAnnotationSP annotations
Definition kis_image.cc:265
KisSelectionMaskSP deselectedGlobalSelectionMask
Definition kis_image.cc:248
void notifyProjectionUpdatedInPatches(const QRect &rc, QVector< KisRunnableStrokeJobData * > &jobs)
KisImageAnimationInterface * animationInterface
Definition kis_image.cc:274
KisUpdateScheduler scheduler
Definition kis_image.cc:275
KisProofingConfigurationSP proofingConfig
Definition kis_image.cc:245
KisSelectionMaskSP targetOverlaySelectionMask
Definition kis_image.cc:250
KisImageGlobalSelectionManagementInterface globalSelectionInterface
Definition kis_image.cc:247
KisSelectionMaskSP overlaySelectionMask
Definition kis_image.cc:251
void requestProjectionUpdateImpl(KisNode *node, const QVector< QRect > &rects, const QRect &cropRect, KisProjectionUpdateFlags flags)
KisPostExecutionUndoAdapter postExecutionUndoAdapter
Definition kis_image.cc:263
KisLegacyUndoAdapter legacyUndoAdapter
Definition kis_image.cc:262
QAtomicInt disableUIUpdateSignals
Definition kis_image.cc:267
QScopedPointer< KisUndoStore > undoStore
Definition kis_image.cc:261
WrapAroundAxis wrapAroundModeAxis
Definition kis_image.cc:259
const KoColorSpace * colorSpace
Definition kis_image.cc:244
QAtomicInt disableDirtyRequests
Definition kis_image.cc:276
QVector< KisProjectionUpdatesFilterSP > projectionUpdatesFilters
Definition kis_image.cc:271
KisImageSignalRouter signalRouter
Definition kis_image.cc:273
QStack< KisProjectionUpdatesFilterCookie > disabledUpdatesCookies
Definition kis_image.cc:272
void convertImageColorSpaceImpl(const KoColorSpace *dstColorSpace, bool convertLayers, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
QList< KisLayerCompositionSP > compositions
Definition kis_image.cc:252
KisGroupLayerSP rootLayer
Definition kis_image.cc:249
KisImagePrivate(KisImage *_q, qint32 w, qint32 h, const KoColorSpace *c, KisUndoStore *undo, KisImageAnimationInterface *_animationInterface)
Definition kis_image.cc:122
void resizeImage(const QRect &newRect)
start asynchronous operation on resizing the image
Definition kis_image.cc:865
KisUndoAdapter * undoAdapter() const
void shearNodes(KisNodeList nodes, double angleX, double angleY, KisSelectionSP selection)
vKisAnnotationSP_it endAnnotations()
void requestStrokeEndActiveNode()
QImage convertToQImage(qint32 x1, qint32 y1, qint32 width, qint32 height, const KoColorProfile *profile)
void sigLayersChangedAsync()
int workingThreadsLimit() const
void invalidateAllFrames() override
Definition kis_image.cc:632
void addAnnotation(KisAnnotationSP annotation)
bool wrapAroundModeActive() const
void sigNodeCollapsedChanged()
KisImagePrivate * m_d
Definition kis_image.h:1275
bool canReselectGlobalSelection()
Definition kis_image.cc:705
KisImageGlobalSelectionManagementInterface * globalSelectionManagementInterface() const
Definition kis_image.cc:710
void scaleNode(KisNodeSP node, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection)
start asynchronous operation on scaling a subtree of nodes starting at node
void nodeChanged(KisNode *node) override
Definition kis_image.cc:626
bool isIsolatingLayer() const
void waitForDone()
void setLodPreferences(const KisLodPreferences &value)
bool startIsolatedMode(KisNodeSP node, bool isolateLayer, bool isolateGroup)
bool hasUpdatesRunning() const override
void disableUIUpdates() override
void setWorkingThreadsLimit(int value)
void refreshGraphAsync(KisNodeSP root, const QVector< QRect > &rects, const QRect &cropRect, KisProjectionUpdateFlags flags=KisProjectionUpdateFlag::None) override
void nodeHasBeenAdded(KisNode *parent, int index, KisNodeAdditionFlags flags) override
Definition kis_image.cc:588
void keyframeChannelAboutToBeRemoved(KisNode *node, KisKeyframeChannel *channel) override
bool cancelStroke(KisStrokeId id) override
KisGroupLayerSP rootLayer() const
void disableDirtyRequests() override
void setWrapAroundModeAxis(WrapAroundAxis value)
void shearImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode, bool resizeImage, double angleX, double angleY, KisSelectionSP selection)
void cropNode(KisNodeSP node, const QRect &newRect, const bool activeFrameOnly=false)
start asynchronous operation on cropping a subtree of nodes starting at node
Definition kis_image.cc:986
void enableDirtyRequests() override
QPointF documentToPixel(const QPointF &documentCoord) const
void keyframeChannelHasBeenAdded(KisNode *node, KisKeyframeChannel *channel) override
UndoResult tryUndoUnfinishedLod0Stroke()
void aboutToRemoveANode(KisNode *parent, int index) override
Definition kis_image.cc:604
qint32 nChildLayers() const
void shear(double angleX, double angleY)
start asynchronous operation on shearing the image
const KoColorSpace * colorSpace() const
QPointF mirrorAxesCenter() const
KisImageAnimationInterface * animationInterface() const
void unlock()
Definition kis_image.cc:805
void shearNode(KisNodeSP node, double angleX, double angleY, KisSelectionSP selection)
start asynchronous operation on shearing a subtree of nodes starting at node
void purgeUnusedData(bool isCancellable)
purge all pixels that have default pixel to free up memory
Definition kis_image.cc:875
KisCompositeProgressProxy * compositeProgressProxy()
Definition kis_image.cc:746
void requestUndoDuringStroke()
void removeComposition(KisLayerCompositionSP composition)
void setProjectionColorSpace(const KoColorSpace *colorSpace)
void setAllowMasksOnRootNode(bool value)
void copyFromImageImpl(const KisImage &rhs, int policy)
Definition kis_image.cc:415
void rotateImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode, double radians, bool resizeImage, KisSelectionSP selection)
void sigStrokeCancellationRequested()
void blockUpdates() override
blockUpdates block updating the image projection
Definition kis_image.cc:818
void sigRedoDuringStrokeRequested()
QString nextLayerName(const QString &baseName="") const
Definition kis_image.cc:715
KisAnnotationSP annotation(const QString &type)
void flattenLayer(KisLayerSP layer)
void sigImageModified()
KisProjectionUpdatesFilterSP removeProjectionUpdatesFilter(KisProjectionUpdatesFilterCookie cookie) override
removes already installed filter from the stack of updates filers
void sigInternalStopIsolatedModeRequested()
void unifyLayersColorSpace()
bool assignImageProfile(const KoColorProfile *profile, bool blockAllUpdates=false)
QRect effectiveLodBounds() const
bool isIdle(bool allowLocked=false)
Definition kis_image.cc:788
void copyFromImage(const KisImage &rhs)
Definition kis_image.cc:410
KisProjectionUpdatesFilterCookie currentProjectionUpdatesFilter() const override
QPointF pixelToDocument(const QPointF &pixelCoord) const
void scaleNodes(KisNodeList nodes, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection)
void convertImageColorSpace(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
KisSelectionMaskSP overlaySelectionMask() const
Definition kis_image.cc:685
void sigResolutionChanged(double xRes, double yRes)
void barrierLock(bool readOnly=false)
Wait until all the queued background jobs are completed and lock the image.
Definition kis_image.cc:756
KisImage * clone(bool exactCopy=false)
Definition kis_image.cc:405
void flatten(KisNodeSP activeNode)
void rotateImage(double radians)
start asynchronous operation on rotating the image
WrapAroundAxis wrapAroundModeAxis() const
void sigColorSpaceChanged(const KoColorSpace *cs)
KisPaintDeviceSP projection() const
qint32 width() const
@ REPLACE
we are replacing the current KisImage with another
Definition kis_image.h:126
@ CONSTRUCT
we are copy-constructing a new KisImage
Definition kis_image.h:125
QSize size() const
Definition kis_image.h:547
void scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy)
start asynchronous operation on scaling the image
void resizeImageImpl(const QRect &newRect, bool cropLayers)
Definition kis_image.cc:834
void immediateLockForReadOnly()
Definition kis_image.cc:793
void aboutToAddANode(KisNode *parent, int index) override
Definition kis_image.cc:582
void setDefaultProjectionColor(const KoColor &color)
void setProofingConfiguration(KisProofingConfigurationSP proofingConfig)
setProofingConfiguration, this sets the image's proofing configuration, and signals the proofingConfi...
void explicitRegenerateLevelOfDetail()
void notifyBatchUpdateEnded() override
void notifyBatchUpdateStarted() override
bool allowMasksOnRootNode() const
void addComposition(KisLayerCompositionSP composition)
KisPostExecutionUndoAdapter * postExecutionUndoAdapter() const override
qint32 nHiddenLayers() const
void requestStrokeCancellation()
KisNode * graphOverlayNode() const override
void moveCompositionUp(KisLayerCompositionSP composition)
void requestTimeSwitch(int time) override
QPoint documentToImagePixelFloored(const QPointF &documentCoord) const
void addJob(KisStrokeId id, KisStrokeJobData *data) override
void setOverlaySelectionMask(KisSelectionMaskSP mask)
Definition kis_image.cc:637
KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace *colorSpace, const QString &name)
colorSpace can be null. In that case, it will be initialised to a default color space.
Definition kis_image.cc:315
void cropImage(const QRect &newRect)
start asynchronous operation on cropping the image
Definition kis_image.cc:870
void setMirrorAxesCenter(const QPointF &value) const
bool assignLayerProfile(KisNodeSP node, const KoColorProfile *profile)
void initialRefreshGraph()
void sigStrokeEndRequested()
void sigUndoDuringStrokeRequested()
void stopIsolatedMode()
void setSize(const QSize &size)
Definition kis_image.cc:828
QVector< QRect > enableUIUpdates() override
void nodeCollapsedChanged(KisNode *node) override
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) override
void notifySelectionChanged() override
bool isIsolatingGroup() const
void notifyAboutToBeDeleted()
void requestStrokeEnd()
KisLodPreferences lodPreferences() const
double xRes() const
double yRes() const
void sigAboutToBeDeleted()
qint32 height() const
void invalidateFrames(const KisTimeSpan &range, const QRect &rect) override
void mergeMultipleLayers(QList< KisNodeSP > mergedLayers, KisNodeSP putAfter)
int currentLevelOfDetail() const
KisSelectionSP globalSelection() const
Definition kis_image.cc:695
void sigStrokeEndRequestedActiveNodeFiltered()
qint32 nlayers() const
KisNodeSP isolationRootNode() const
QList< KisLayerCompositionSP > compositions()
void sigProofingConfigChanged()
void mergeDown(KisLayerSP l, const KisMetaData::MergeStrategy *strategy)
const KUndo2Command * lastExecutedCommand() const override
void removeAnnotation(const QString &type)
KisImageSignalRouter * signalRouter()
QRect bounds() const override
KoColor defaultProjectionColor() const
void convertLayerColorSpace(KisNodeSP node, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
void notifyLayersChanged()
use if the layers have changed completely (eg. when flattening)
friend class KisImageResizeCommand
Definition kis_image.h:1266
bool tryBarrierLock(bool readOnly=false)
Tries to lock the image without waiting for the jobs to finish.
Definition kis_image.cc:771
bool locked() const
Definition kis_image.cc:751
KisUndoStore * undoStore()
void unblockUpdates() override
unblockUpdates unblock updating the image project. This only restarts the scheduler and does not sche...
Definition kis_image.cc:823
void setModifiedWithoutUndo()
bool hasOverlaySelectionMask() const
Definition kis_image.cc:690
KisProjectionUpdatesFilterCookie addProjectionUpdatesFilter(KisProjectionUpdatesFilterSP filter) override
void moveCompositionDown(KisLayerCompositionSP composition)
bool wrapAroundModePermitted() const
void notifyProjectionUpdated(const QRect &rc) override
void sigImageUpdated(const QRect &)
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
void rotateNodes(KisNodeList nodes, double radians, KisSelectionSP selection)
void endStroke(KisStrokeId id) override
~KisImage() override
Definition kis_image.cc:330
void notifyUIUpdateCompleted(const QRect &rc) override
const KoColorProfile * profile() const
vKisAnnotationSP_it beginAnnotations()
void requestRedoDuringStroke()
void sigSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint)
void setResolution(double xres, double yres)
void setRootLayer(KisGroupLayerSP rootLayer)
static KisImageSP fromQImage(const QImage &image, KisUndoStore *undoStore)
Definition kis_image.cc:341
void convertImageProjectionColorSpace(const KoColorSpace *dstColorSpace)
void setWrapAroundModePermitted(bool value)
void requestProjectionUpdate(KisNode *node, const QVector< QRect > &rects, KisProjectionUpdateFlags flags) override
void setUndoStore(KisUndoStore *undoStore)
KisProofingConfigurationSP proofingConfiguration() const
proofingConfiguration
void rotateNode(KisNodeSP node, double radians, KisSelectionSP selection)
start asynchronous operation on rotating a subtree of nodes starting at node
KisKeyframeChannel stores and manages KisKeyframes. Maps units of time to virtual keyframe values....
void setNode(KisNodeWSP node)
static QRect upscaledRect(const QRect &srcRect, int lod)
KisPaintInformation map(KisPaintInformation pi) const
QRect exactBounds() const
KoColor defaultPixel() const
QImage convertToQImage(const KoColorProfile *dstProfile, qint32 x, qint32 y, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent=KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags=KoColorConversionTransformation::internalConversionFlags()) const
void convertFromQImage(const QImage &image, const KoColorProfile *profile, qint32 offsetX=0, qint32 offsetY=0)
static void copyAreaOptimized(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &originalSrcRect)
void setUndoStore(KisUndoStore *undoStore)
void applyVisitor(KisProcessingVisitorSP visitor, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
void applyCommand(KUndo2Command *command, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
void applyVisitorAllFrames(KisProcessingVisitorSP visitor, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
The KisProofingConfiguration struct Little struct that stores the proofing configuration for a given ...
bool requestsOtherStrokesToEnd() const
static QList< KisStrokeJobData * > createSuspendJobsData(KisImageWSP image)
static QList< KisStrokeJobData * > createResumeJobsData(KisImageWSP image)
static QList< KisStrokeJobData * > createJobsData(KisImageWSP image)
static KisTimeSpan infinite(int start)
QTransform transform() const
void setUndoStore(KisUndoStore *undoStore)
virtual const KoColorProfile * profile() const =0
static KoColor createTransparent(const KoColorSpace *cs)
Definition KoColor.cpp:681
A holder for an updater that does nothing.
Definition KoUpdater.h:116
KoUpdater * updater()
const T value(const QString &id) const
QString id() const
Definition KoID.cpp:63
void setProperty(const QString &name, const QVariant &value)
static bool qFuzzyCompare(half p1, half p2)
This file is part of the Krita application in calligra.
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define ppVar(var)
Definition kis_debug.h:155
#define dbgImage
Definition kis_debug.h:46
#define EMIT_IF_NEEDED
#define SANITY_CHECK_LOCKED(name)
Definition kis_image.cc:112
KIS_DECLARE_STATIC_INITIALIZER
Definition kis_image.cc:115
bool checkMasksNeedConversion(KisNodeSP root, const QRect &bounds)
static bool isLayer(KisNodeSP node)
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
QSharedPointer< T > toQShared(T *ptr)
std::pair< KisStrokeStrategy *, QList< KisStrokeJobData * > > KisSuspendResumePair
std::pair< KisStrokeStrategy *, QList< KisStrokeJobData * > > KisLodSyncPair
KisSharedPtr< KisAnnotation > KisAnnotationSP
Definition kis_types.h:179
KisWeakSharedPtr< KisImage > KisImageWSP
Definition kis_types.h:70
QSharedPointer< KisProofingConfiguration > KisProofingConfigurationSP
Definition kis_types.h:311
vKisAnnotationSP::iterator vKisAnnotationSP_it
Definition kis_types.h:181
void * KisProjectionUpdatesFilterCookie
Definition kis_types.h:285
KUndo2MagicString kundo2_i18n(const char *text)
KUndo2MagicString kundo2_noi18n(const QString &text)
KUndo2MagicString kundo2_i18np(const char *sing, const char *plur, const A1 &a1)
void flattenImage(KisImageSP image, KisNodeSP activeNode, MergeFlags flags)
KisNodeSP recursiveFindNode(KisNodeSP node, std::function< bool(KisNodeSP)> func)
void flattenLayer(KisImageSP image, KisLayerSP layer, MergeFlags flags)
void recursiveApplyNodes(NodePointer node, Functor func)
void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy *strategy, MergeFlags flags)
void mergeMultipleNodes(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, MergeFlags flags)
void addJobConcurrent(QVector< Job * > &jobs, Func func)
void makeContainerUnique(C &container)
QMap< QString, KisKeyframeChannel * > keyframeChannels
QUuid uuid() const
virtual KisPaintDeviceSP projection() const =0
virtual QRect exactBounds() const
void setUuid(const QUuid &id)
virtual const KoColorSpace * colorSpace() const =0
KisImageWSP image
bool isAnimated() const
FlipFlopCommand(State initialState, KUndo2Command *parent=0)
void setDefaultProjectionColor(KoColor color)
bool accept(KisNodeVisitor &v) override
void setImage(KisImageWSP image) override
KisPaintDeviceSP original() const override
KoColor defaultProjectionColor() const
SetImageProjectionColorSpace(const KoColorSpace *cs, KisImageWSP image, State initialState, KUndo2Command *parent=0)
KisPaintDeviceSP projection() const override
Definition kis_layer.cc:826
virtual KisSelectionMaskSP selectionMask() const
Definition kis_layer.cc:504
void notifyChildMaskChanged()
Definition kis_layer.cc:499
bool lodSupported() const
bool lodPreferred() const
KisSelectionSP selection
Definition kis_mask.cc:44
KisPaintDeviceSP paintDevice() const override
Definition kis_mask.cc:223
static KisMemoryStatisticsServer * instance()
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
void setRoot(KisNodeSP root)
virtual void aboutToRemoveANode(KisNode *parent, int index)
virtual void aboutToAddANode(KisNode *parent, int index)
virtual void requestProjectionUpdate(KisNode *node, const QVector< QRect > &rects, KisProjectionUpdateFlags flags)
virtual void nodeHasBeenAdded(KisNode *parent, int index, KisNodeAdditionFlags flags)
virtual void nodeChanged(KisNode *node)
KisNodeSP firstChild() const
Definition kis_node.cpp:361
QList< KisNodeSP > childNodes(const QStringList &nodeTypes, const KoProperties &properties) const
Definition kis_node.cpp:439
void setImage(KisImageWSP newImage) override
Definition kis_node.cpp:254
KisProjectionLeafSP projectionLeaf
Definition kis_node.cpp:93
virtual KisNodeSP clone() const =0
@ N_ABOVE_FILTHY
Definition kis_node.h:59
@ N_FILTHY
Definition kis_node.h:61
KisNodeWSP parent
Definition kis_node.cpp:86
void setGraphListener(KisNodeGraphListener *graphListener)
Definition kis_node.cpp:289
KisNodeSP nextSibling() const
Definition kis_node.cpp:408
KisNodeGraphListener * graphListener
Definition kis_node.cpp:87
KisPaintDeviceSP paintDevice
QRect extent() const override
void setDirty(const QVector< QRect > &rects) override
bool hasShapeSelection() const
QRect selectedExactRect() const
Slow, but exact way of determining the rectangle that encloses the selection.
void unlock(bool resetLodLevels=true)
void addJob(KisStrokeId id, KisStrokeJobData *data) override
void setPostSyncLod0GUIPlaneRequestForResumeCallback(const std::function< void()> &callback)
void setProgressProxy(KoProgressProxy *progressProxy)
void setSuspendResumeUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyPairFactory &factory)
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) override
void setLodPreferences(const KisLodPreferences &value)
void setLod0ToNStrokeStrategyFactory(const KisLodSyncStrokeStrategyFactory &factory)
void endStroke(KisStrokeId id) override
KisLodPreferences lodPreferences() const
void setThreadsLimit(int value)
KisPostExecutionUndoAdapter * lodNPostExecutionUndoAdapter() const
void setPurgeRedoStateCallback(const std::function< void()> &callback)
bool cancelStroke(KisStrokeId id) override
void fullRefreshAsync(KisNodeSP root, const QVector< QRect > &rects, const QRect &cropRect, KisProjectionUpdateFlags flags)
static KisUpdateTimeMonitor * instance()
void reportUpdateFinished(const QRect &rect)
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
static KoColorSpaceRegistry * instance()
const KoColorSpace * graya8(const QString &profile=QString())
const KoColorSpace * graya16(const QString &profile=QString())
const KoColorSpace * rgb8(const QString &profileName=QString())
const KoColorSpace * rgb16(const QString &profileName=QString())
const KoColorSpace * alpha8()