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#include <memory>
92
93#include "kis_time_span.h"
94
99
100#include "KisBusyWaitBroker.h"
101#include <KisStaticInitializer.h>
103
104
105// #define SANITY_CHECKS
106
107#ifdef SANITY_CHECKS
108#define SANITY_CHECK_LOCKED(name) \
109 if (!locked()) warnKrita() << "Locking policy failed:" << name \
110 << "has been called without the image" \
111 "being locked";
112#else
113#define SANITY_CHECK_LOCKED(name)
114#endif
115
117 qRegisterMetaType<KisImageSP>("KisImageSP");
118}
119
121{
122public:
123 KisImagePrivate(KisImage *_q, qint32 w, qint32 h,
124 const KoColorSpace *c,
125 KisUndoStore *undo,
126 KisImageAnimationInterface *_animationInterface)
127 : q(_q)
128 , lockedForReadOnly(false)
129 , width(w)
130 , height(h)
131 , colorSpace(c ? c : KoColorSpaceRegistry::instance()->rgb8())
133 , isolateLayer(false)
134 , isolateGroup(false)
135 , undoStore(undo ? undo : new KisDumbUndoStore())
136 , legacyUndoAdapter(undoStore.data(), _q)
138 , signalRouter(_q)
139 , animationInterface(_animationInterface)
140 , scheduler(_q, _q)
141 , axesCenter(QPointF(0.5, 0.5))
142 {
143 {
144 KisImageConfig cfg(true);
145 if (cfg.enableProgressReporting()) {
147 }
148
149 // Each of these lambdas defines a new factory function.
151 [=](bool forgettable) {
152 return KisLodSyncPair(
155 });
156
158 [=]() {
160
165
166 return std::make_pair(suspend, resume);
167 });
168
170 [this] () {
171 undoStore->purgeRedoState();
172 });
173
175 [this] () {
176
178
179 bool addedUIUpdateRequestSuccessfully = false;
180
181 for (auto it = std::make_reverse_iterator(projectionUpdatesFilters.end());
182 it != std::make_reverse_iterator(projectionUpdatesFilters.begin());
183 ++it) {
184
187
188 if (iface) {
190 addedUIUpdateRequestSuccessfully = true;
191 break;
192 }
193 }
194
195 KIS_SAFE_ASSERT_RECOVER_NOOP(addedUIUpdateRequestSuccessfully);
196 });
197 }
198
199 connect(q, SIGNAL(sigImageModified()), KisMemoryStatisticsServer::instance(), SLOT(notifyImageChanged()));
200 connect(undoStore.data(), SIGNAL(historyStateChanged()), &signalRouter, SLOT(emitImageModifiedNotification()));
201 }
202
211
218 if (rootLayer->image() == q) {
220 }
221
222 if (rootLayer->graphListener() == q) {
224 }
225
227
231 delete animationInterface;
232 }
233
235
236 quint32 lockCount = 0;
238
239 qint32 width;
240 qint32 height;
241
242 double xres = 1.0;
243 double yres = 1.0;
244
247
250 KisGroupLayerSP rootLayer; // The layers are contained in here
251 KisSelectionMaskSP targetOverlaySelectionMask; // the overlay switching stroke will try to switch into this mask
254
258
261
262 QScopedPointer<KisUndoStore> undoStore;
265
267
270
271 // filters are applied in a reversed way, from rbegin() to rend()
278
280
281 QPointF axesCenter;
283
285 const QVector<QRect> &rects,
286 const QRect &cropRect,
287 KisProjectionUpdateFlags flags);
288
290
292
293 void convertImageColorSpaceImpl(const KoColorSpace *dstColorSpace,
294 bool convertLayers,
296 KoColorConversionTransformation::ConversionFlags conversionFlags);
297
299};
300
305
310
315
316KisImage::KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace *colorSpace, const QString& name)
317 : QObject(0)
318 , KisShared()
319 , m_d(new KisImagePrivate(this, width, height,
320 colorSpace, undoStore,
322{
323 // make sure KisImage belongs to the GUI thread
324 moveToThread(qApp->thread());
325 connect(this, SIGNAL(sigInternalStopIsolatedModeRequested()), SLOT(stopIsolatedMode()));
326
327 setObjectName(name);
328 setRootLayer(new KisGroupLayer(this, "root", OPACITY_OPAQUE_U8));
329}
330
332{
336 waitForDone();
337
338 delete m_d;
339 disconnect(); // in case Qt gets confused
340}
341
342KisImageSP KisImage::fromQImage(const QImage &image, KisUndoStore *undoStore)
343{
344 const KoColorSpace *colorSpace = 0;
345
346 switch (image.format()) {
347 case QImage::Format_Invalid:
348 case QImage::Format_Mono:
349 case QImage::Format_MonoLSB:
351 break;
352 case QImage::Format_Indexed8:
353 case QImage::Format_RGB32:
354 case QImage::Format_ARGB32:
355 case QImage::Format_ARGB32_Premultiplied:
357 break;
358 case QImage::Format_RGB16:
360 break;
361 case QImage::Format_ARGB8565_Premultiplied:
362 case QImage::Format_RGB666:
363 case QImage::Format_ARGB6666_Premultiplied:
364 case QImage::Format_RGB555:
365 case QImage::Format_ARGB8555_Premultiplied:
366 case QImage::Format_RGB888:
367 case QImage::Format_RGB444:
368 case QImage::Format_ARGB4444_Premultiplied:
369 case QImage::Format_RGBX8888:
370 case QImage::Format_RGBA8888:
371 case QImage::Format_RGBA8888_Premultiplied:
373 break;
374 case QImage::Format_BGR30:
375 case QImage::Format_A2BGR30_Premultiplied:
376 case QImage::Format_RGB30:
377 case QImage::Format_A2RGB30_Premultiplied:
379 break;
380 case QImage::Format_Alpha8:
382 break;
383 case QImage::Format_Grayscale8:
385 break;
386 case QImage::Format_Grayscale16:
388 break;
389 case QImage::Format_RGBX64:
390 case QImage::Format_RGBA64:
391 case QImage::Format_RGBA64_Premultiplied:
393 break;
394 default:
395 colorSpace = 0;
396 }
397
398 KisImageSP img = new KisImage(undoStore, image.width(), image.height(), colorSpace, i18n("Imported Image"));
399 KisPaintLayerSP layer = new KisPaintLayer(img, img->nextLayerName(), 255);
400 layer->paintDevice()->convertFromQImage(image, 0, 0, 0);
401 img->addNode(layer.data(), img->rootLayer().data());
402
403 return img;
404}
405
406KisImage *KisImage::clone(bool exactCopy)
407{
408 return new KisImage(*this, 0, exactCopy);
409}
410
412{
414}
415
416void KisImage::copyFromImageImpl(const KisImage &rhs, int policy)
417{
418 // make sure we choose exactly one from REPLACE and CONSTRUCT
419 KIS_ASSERT_RECOVER_RETURN(bool(policy & REPLACE) != bool(policy & CONSTRUCT));
420
433 const bool sizeChanged = m_d->width != rhs.width() || m_d->height != rhs.height();
434 const bool colorSpaceChanged = *m_d->colorSpace != *rhs.colorSpace();
435 const bool resolutionChanged = m_d->xres != rhs.m_d->xres || m_d->yres != rhs.m_d->yres;
436
437 if (sizeChanged) {
438 m_d->width = rhs.width();
439 m_d->height = rhs.height();
440 }
441
442 if (colorSpaceChanged) {
443 m_d->colorSpace = rhs.colorSpace();
444 }
445
446 if (resolutionChanged) {
447 m_d->xres = rhs.m_d->xres;
448 m_d->yres = rhs.m_d->yres;
449 }
450
451 // from KisImage::KisImage(const KisImage &, KisUndoStore *, bool)
452 setObjectName(rhs.objectName());
453
454 KisNodeSP oldRoot = this->root();
455 KisNodeSP newRoot = rhs.root()->clone();
456 newRoot->setGraphListener(this);
457 newRoot->setImage(this);
458
459 m_d->rootLayer = dynamic_cast<KisGroupLayer*>(newRoot.data());
460 setRoot(newRoot);
461
462 if (oldRoot) {
463 oldRoot->setImage(0);
464 oldRoot->setGraphListener(0);
465 oldRoot->disconnect();
466 }
467
468 // only when replacing do we need to Q_EMIT signals
469#define EMIT_IF_NEEDED if (!(policy & REPLACE)) {} else emit
470
471 if (sizeChanged) {
472 EMIT_IF_NEEDED sigSizeChanged(QPointF(), QPointF());
473 }
474 if (colorSpaceChanged) {
476 }
477 if (resolutionChanged) {
479 }
480
482
483 if (rhs.m_d->proofingConfig) {
485 if (policy & REPLACE) {
486 setProofingConfiguration(proofingConfig);
487 } else {
488 m_d->proofingConfig = proofingConfig;
489 }
490 }
491
492 bool exactCopy = policy & EXACT_COPY;
493
494 if (exactCopy || rhs.m_d->isolationRootNode || rhs.m_d->overlaySelectionMask) {
497
498 QQueue<KisNodeSP> linearizedNodes;
500 [&linearizedNodes](KisNodeSP node) {
501 linearizedNodes.enqueue(node);
502 });
504 [&linearizedNodes, exactCopy, &rhs, this](KisNodeSP node) {
505 KisNodeSP refNode = linearizedNodes.dequeue();
506
507 if (exactCopy) {
508 node->setUuid(refNode->uuid());
509 }
510
511 if (rhs.m_d->isolationRootNode &&
512 rhs.m_d->isolationRootNode == refNode) {
513 m_d->isolationRootNode = node;
514 }
515
516 if (rhs.m_d->overlaySelectionMask &&
517 KisNodeSP(rhs.m_d->overlaySelectionMask) == refNode) {
518 m_d->targetOverlaySelectionMask = dynamic_cast<KisSelectionMask*>(node.data());
521 }
522
523
524 // Re-establish DefaultBounds Instances for Existing Nodes
525 // This is a workaround for copy-constructors failing to pass
526 // proper DefaultBounds due to either lacking image data on construction
527 // We should change the way "DefaultBounds" works to try to make it
528 // safer for threading races.
529 using KeyframeChannelContainer = QMap<QString, KisKeyframeChannel*>;
530 KeyframeChannelContainer keyframeChannels = node->keyframeChannels();
531 for (KeyframeChannelContainer::iterator i = keyframeChannels.begin();
532 i != keyframeChannels.end(); i++) {
533 keyframeChannels[i.key()]->setNode(node);
534 }
535 });
536 }
537
539 [](KisNodeSP node) {
540 dbgImage << "Node: " << (void *)node.data();
541 });
542
543
544
545 m_d->compositions.clear();
546
547 Q_FOREACH (KisLayerCompositionSP comp, rhs.m_d->compositions) {
548 m_d->compositions << toQShared(new KisLayerComposition(*comp, this));
549 }
550
552
553 vKisAnnotationSP newAnnotations;
554 Q_FOREACH (KisAnnotationSP annotation, rhs.m_d->annotations) {
555 newAnnotations << annotation->clone();
556 }
557 m_d->annotations = newAnnotations;
558
562
563#undef EMIT_IF_NEEDED
564}
565
566KisImage::KisImage(const KisImage& rhs, KisUndoStore *undoStore, bool exactCopy)
567 : KisNodeFacade(),
569 KisShared(),
570 m_d(new KisImagePrivate(this,
571 rhs.width(), rhs.height(),
572 rhs.colorSpace(),
573 undoStore ? undoStore : new KisDumbUndoStore(),
574 new KisImageAnimationInterface(*rhs.animationInterface(), this)))
575{
576 // make sure KisImage belongs to the GUI thread
577 moveToThread(qApp->thread());
578 connect(this, SIGNAL(sigInternalStopIsolatedModeRequested()), SLOT(stopIsolatedMode()));
579
580 copyFromImageImpl(rhs, CONSTRUCT | (exactCopy ? EXACT_COPY : 0));
581}
582
583void KisImage::aboutToAddANode(KisNode *parent, int index)
584{
586 SANITY_CHECK_LOCKED("aboutToAddANode");
587}
588
589void KisImage::nodeHasBeenAdded(KisNode *parent, int index, KisNodeAdditionFlags flags)
590{
591 KisNodeGraphListener::nodeHasBeenAdded(parent, index, flags);
592
594 QMap<QString, KisKeyframeChannel*> chans = node->keyframeChannels();
595 Q_FOREACH(KisKeyframeChannel* chan, chans.values()) {
596 chan->setNode(node);
597 this->keyframeChannelHasBeenAdded(node.data(), chan);
598 }
599 });
600
601 SANITY_CHECK_LOCKED("nodeHasBeenAdded");
602 m_d->signalRouter.emitNodeHasBeenAdded(parent, index, flags);
603}
604
605void KisImage::aboutToRemoveANode(KisNode *parent, int index)
606{
607 KisNodeSP deletedNode = parent->at(index);
608 if (!dynamic_cast<KisSelectionMask*>(deletedNode.data()) &&
609 deletedNode == m_d->isolationRootNode) {
610
612 }
613
615 QMap<QString, KisKeyframeChannel*> chans = node->keyframeChannels();
616 Q_FOREACH(KisKeyframeChannel* chan, chans.values()) {
617 this->keyframeChannelAboutToBeRemoved(node.data(), chan);
618 }
619 });
620
622
623 SANITY_CHECK_LOCKED("aboutToRemoveANode");
625}
626
632
637
639{
640 if (m_d->targetOverlaySelectionMask == mask) return;
641
643
644 struct UpdateOverlaySelectionStroke : public KisSimpleStrokeStrategy {
645 UpdateOverlaySelectionStroke(KisImageSP image)
646 : KisSimpleStrokeStrategy(QLatin1String("update-overlay-selection-mask"), kundo2_noi18n("update-overlay-selection-mask")),
647 m_image(image)
648 {
649 this->enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
650 setClearsRedoOnStart(false);
651 }
652
653 void initStrokeCallback() override {
654 KisSelectionMaskSP oldMask = m_image->m_d->overlaySelectionMask;
655 KisSelectionMaskSP newMask = m_image->m_d->targetOverlaySelectionMask;
656 if (oldMask == newMask) return;
657
658 KIS_SAFE_ASSERT_RECOVER_RETURN(!newMask || static_cast<KisImage*>(newMask->graphListener()) == m_image);
659
660 m_image->m_d->overlaySelectionMask = newMask;
661
662 if (oldMask || newMask) {
663 m_image->m_d->rootLayer->notifyChildMaskChanged();
664 }
665
666 if (oldMask) {
667 const QRect oldMaskRect = oldMask->graphListener() ? oldMask->extent() : m_image->bounds();
668 m_image->m_d->rootLayer->setDirtyDontResetAnimationCache(oldMaskRect);
669 }
670
671 if (newMask) {
672 newMask->setDirty();
673 }
674
675 m_image->undoAdapter()->emitSelectionChanged();
676 }
677
678 private:
679 KisImageSP m_image;
680 };
681
682 KisStrokeId id = startStroke(new UpdateOverlaySelectionStroke(this));
683 endStroke(id);
684}
685
690
695
697{
698 KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
699 if (selectionMask) {
700 return selectionMask->selection();
701 } else {
702 return 0;
703 }
704}
705
710
715
716QString KisImage::nextLayerName(const QString &_baseName) const
717{
718 QString baseName = _baseName;
719
720 int numLayers = 0;
721 int maxLayerIndex = 0;
722 QRegularExpression numberedLayerRegexp(".* (\\d+)$");
724 [&numLayers, &maxLayerIndex, &numberedLayerRegexp] (KisNodeSP node) {
725 if (node->inherits("KisLayer")) {
726 QRegularExpressionMatch match = numberedLayerRegexp.match(node->name());
727
728 if (match.hasMatch()) {
729 maxLayerIndex = qMax(maxLayerIndex, match.captured(1).toInt());
730 }
731 numLayers++;
732 }
733 });
734
735 // special case if there is only root node
736 if (numLayers == 1) {
737 return i18nc("Name for the bottom-most layer in the layerstack", "Background");
738 }
739
740 if (baseName.isEmpty()) {
741 baseName = i18n("Paint Layer");
742 }
743
744 return QString("%1 %2").arg(baseName).arg(maxLayerIndex + 1);
745}
746
751
753{
754 return m_d->lockCount != 0;
755}
756
757void KisImage::barrierLock(bool readOnly)
758{
759 if (!locked()) {
764 m_d->lockedForReadOnly = readOnly;
765 } else {
766 m_d->lockedForReadOnly &= readOnly;
767 }
768
769 m_d->lockCount++;
770}
771
772bool KisImage::tryBarrierLock(bool readOnly)
773{
774 bool result = true;
775
776 if (!locked()) {
777 result = m_d->scheduler.tryBarrierLock();
778 m_d->lockedForReadOnly = readOnly;
779 }
780
781 if (result) {
782 m_d->lockCount++;
783 m_d->lockedForReadOnly &= readOnly;
784 }
785
786 return result;
787}
788
789bool KisImage::isIdle(bool allowLocked)
790{
791 return (allowLocked || !locked()) && m_d->scheduler.isIdle();
792}
793
805
807{
808 Q_ASSERT(locked());
809
810 if (locked()) {
811 m_d->lockCount--;
812
813 if (m_d->lockCount == 0) {
815 }
816 }
817}
818
823
828
829void KisImage::setSize(const QSize& size)
830{
831 m_d->width = size.width();
832 m_d->height = size.height();
833}
834
835void KisImage::resizeImageImpl(const QRect& newRect, bool cropLayers)
836{
837 if (newRect == bounds() && !cropLayers) return;
838
839 KUndo2MagicString actionName = cropLayers ?
840 kundo2_i18n("Crop Image") :
841 kundo2_i18n("Resize Image");
842
843 KisImageSignalVector emitSignals;
844 emitSignals << ComplexSizeChangedSignal(newRect, newRect.size());
845
846 KisCropSavedExtraData *extraData =
847 new KisCropSavedExtraData(cropLayers ?
850 newRect);
851
852 KisProcessingApplicator applicator(this, m_d->rootLayer,
855 emitSignals, actionName, extraData);
856
857 if (cropLayers || !newRect.topLeft().isNull()) {
858 KisProcessingVisitorSP visitor =
859 new KisCropProcessingVisitor(newRect, cropLayers, true);
860 applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
861 }
862 applicator.applyCommand(new KisImageResizeCommand(this, newRect.size()));
863 applicator.end();
864}
865
866void KisImage::resizeImage(const QRect& newRect)
867{
868 resizeImageImpl(newRect, false);
869}
870
871void KisImage::cropImage(const QRect& newRect)
872{
873 resizeImageImpl(newRect, true);
874}
875
876void KisImage::purgeUnusedData(bool isCancellable)
877{
884 struct PurgeUnusedDataStroke : public KisRunnableBasedStrokeStrategy {
885 PurgeUnusedDataStroke(KisImageSP image, bool isCancellable)
886 : KisRunnableBasedStrokeStrategy(QLatin1String("purge-unused-data"),
887 kundo2_i18n("Purge Unused Data")),
888 m_image(image),
889 m_finalCommand(new KUndo2Command(this->name()))
890
891 {
892 this->enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
893 this->enableJob(JOB_DOSTROKE, true);
894 this->enableJob(JOB_FINISH, true, KisStrokeJobData::SEQUENTIAL);
895
896 setClearsRedoOnStart(!isCancellable);
897 setRequestsOtherStrokesToEnd(!isCancellable);
898 setCanForgetAboutMe(isCancellable);
899 }
900
901 void initStrokeCallback() override
902 {
903 KisPaintDeviceList paintDevicesList;
904 KisPaintDeviceList projectionsList;
906
908 [&paintDevicesList, &projectionsList, this](KisNodeSP node) {
909 KisPaintDeviceList deviceList = node->getLodCapableDevices();
910
911 Q_FOREACH (KisPaintDeviceSP dev, deviceList) {
912 if (!dev) continue;
913
914 // we do **not** strip paint devices in the forgettable
915 // mode, since we should handle transactions for them
916 if (dev == node->paintDevice() && !canForgetAboutMe()) {
917 paintDevicesList << dev;
918 } else {
919 projectionsList << dev;
920 }
921 }
922 });
923
926 KritaUtils::makeContainerUnique(paintDevicesList);
927 KritaUtils::makeContainerUnique(projectionsList);
928
929 Q_FOREACH(KisPaintDeviceSP dev, paintDevicesList) {
930 projectionsList.removeAll(dev);
931
932 // all transactions will be linked to the final command via the
933 // parent-child relationship
934 m_transactions.emplace_back(dev, m_finalCommand.get(), -1, nullptr, KisTransaction::None);
935 }
936
937 // now, when the transactions are started, we can merge the two lists
938 paintDevicesList << projectionsList;
939 projectionsList.clear();
940
941 Q_FOREACH (KisPaintDeviceSP device, paintDevicesList) {
943 [device] () {
944 const_cast<KisPaintDevice*>(device.data())->purgeDefaultPixels();
945 });
946 }
947
948 addMutatedJobs(jobsData);
949 }
950
951 void finishStrokeCallback() override {
952 for (auto it = m_transactions.begin(); it != m_transactions.end(); ++it) {
953 std::unique_ptr<KUndo2Command> cmd(it->endAndTake());
954
955 // verify the transaction command is linked to m_finalCommand,
956 // if not, just delete on return
957 KIS_SAFE_ASSERT_RECOVER(cmd->hasParent()) { continue; }
958
959 // if has a parent, release...
960 (void)cmd.release();
961 }
962
963 m_transactions.clear();
964
965 m_finalCommand->redo();
966 m_image->postExecutionUndoAdapter()->addCommand(toQShared(m_finalCommand.release()));
967
968 // now reset the thumbnail generation limitation
970 [](KisNodeSP node) {
971 if (node->preferredThumbnailBoundsMode() != KisThumbnailBoundsMode::Precise) {
972 node->setPreferredThumbnailBoundsMode(KisThumbnailBoundsMode::Precise);
973 }
974 });
975 }
976
977 private:
978 KisImageSP m_image;
979 std::unique_ptr<KUndo2Command> m_finalCommand;
980 std::vector<KisTransaction> m_transactions;
981 };
982
983 KisStrokeId id = startStroke(new PurgeUnusedDataStroke(this, isCancellable));
984 endStroke(id);
985}
986
987void KisImage::cropNode(KisNodeSP node, const QRect& newRect, const bool activeFrameOnly)
988{
989 const bool isLayer = qobject_cast<KisLayer*>(node.data());
990 KUndo2MagicString actionName = isLayer ?
991 kundo2_i18n("Crop Layer") :
992 kundo2_i18n("Crop Mask");
993
994 KisImageSignalVector emitSignals;
995
996 KisCropSavedExtraData *extraData =
998 newRect, node);
999
1000 KisProcessingApplicator applicator(this, node,
1002 emitSignals, actionName, extraData);
1003
1004 KisProcessingVisitorSP visitor =
1005 new KisCropProcessingVisitor(newRect, true, false);
1006
1007 if (node->isAnimated() && activeFrameOnly) {
1008 // Crop active frame..
1009 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1010 } else {
1011 // Crop all frames..
1013 }
1014 applicator.end();
1015}
1016
1017void KisImage::scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy)
1018{
1019 bool resolutionChanged = !qFuzzyCompare(xRes(), xres) || !qFuzzyCompare(yRes(), yres);
1020 bool sizeChanged = size != this->size();
1021
1022 if (!resolutionChanged && !sizeChanged) return;
1023
1024 KisImageSignalVector emitSignals;
1025 if (resolutionChanged) emitSignals << ResolutionChangedSignal;
1026 if (sizeChanged) emitSignals << ComplexSizeChangedSignal(bounds(), size);
1027
1028 KUndo2MagicString actionName = sizeChanged ?
1029 kundo2_i18n("Scale Image") :
1030 kundo2_i18n("Change Image Resolution");
1031
1032 KisProcessingApplicator::ProcessingFlags signalFlags =
1033 (resolutionChanged || sizeChanged) ?
1036
1037 KisProcessingApplicator applicator(this, m_d->rootLayer,
1039 emitSignals, actionName);
1040
1041 qreal sx = qreal(size.width()) / this->size().width();
1042 qreal sy = qreal(size.height()) / this->size().height();
1043
1044 QTransform shapesCorrection;
1045
1046 if (resolutionChanged) {
1047 shapesCorrection = QTransform::fromScale(xRes() / xres, yRes() / yres);
1048 }
1049
1050 KisProcessingVisitorSP visitor =
1052 0, 0,
1053 0,
1054 0, 0,
1055 filterStrategy,
1056 shapesCorrection);
1057
1059
1060 if (resolutionChanged) {
1061 KUndo2Command *parent =
1063 new KisImageSetResolutionCommand(this, xres, yres, parent);
1064 applicator.applyCommand(parent);
1065 }
1066
1067 if (sizeChanged) {
1068 applicator.applyCommand(new KisImageResizeCommand(this, size));
1069 }
1070
1071 applicator.end();
1072}
1073
1074void KisImage::scaleNode(KisNodeSP node, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection)
1075{
1076 scaleNodes(KisNodeList{node}, center, scaleX, scaleY, filterStrategy, selection);
1077}
1078void KisImage::scaleNodes(KisNodeList nodes, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection)
1079{
1080 KUndo2MagicString actionName(kundo2_i18np("Scale Layer", "Scale %1 Layers", nodes.size()));
1081 KisImageSignalVector emitSignals;
1082
1083 QPointF offset;
1084 {
1085 KisTransformWorker worker(0,
1086 scaleX, scaleY,
1087 0, 0,
1088 0.0,
1089 0, 0, 0, 0);
1090 QTransform transform = worker.transform();
1091
1092 offset = center - transform.map(center);
1093 }
1094
1095 KisProcessingApplicator applicator(this, nodes,
1097 emitSignals, actionName);
1098
1100 new KisTransformProcessingVisitor(scaleX, scaleY,
1101 0, 0,
1102 0,
1103 offset.x(), offset.y(),
1104 filterStrategy);
1105
1106 visitor->setSelection(selection);
1107
1108 if (selection) {
1109 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1110 } else {
1112 }
1113
1114 applicator.end();
1115}
1116
1118 KisNodeSP rootNode,
1119 double radians,
1120 bool resizeImage,
1121 KisSelectionSP selection)
1122{
1123 rotateImpl(actionName, KisNodeList{rootNode}, radians, resizeImage, selection);
1124}
1126 KisNodeList nodes,
1127 double radians,
1128 bool resizeImage,
1129 KisSelectionSP selection)
1130{
1131 // we can either transform (and resize) the whole image or
1132 // transform a selection, we cannot do both at the same time
1133 KIS_SAFE_ASSERT_RECOVER(!(bool(selection) && resizeImage)) {
1134 selection = 0;
1135 }
1136
1137 QRect baseBounds;
1138 if (resizeImage) {
1139 baseBounds = bounds();
1140 }
1141 else if (selection) {
1142 baseBounds = selection->selectedExactRect();
1143 }
1144 else {
1145 Q_FOREACH(KisNodeSP node, nodes) {
1146 baseBounds = baseBounds.united(node->exactBounds());
1147 }
1148 }
1149
1150 QPointF offset;
1151 QSize newSize;
1152
1153 {
1154 KisTransformWorker worker(0,
1155 1.0, 1.0,
1156 0, 0,
1157 radians,
1158 0, 0, 0, 0);
1159 QTransform transform = worker.transform();
1160
1161 if (resizeImage) {
1162 QRect newRect = transform.mapRect(baseBounds);
1163 newSize = newRect.size();
1164 offset = -newRect.topLeft();
1165 }
1166 else {
1167 QPointF origin = QRectF(baseBounds).center();
1168
1169 newSize = size();
1170 offset = -(transform.map(origin) - origin);
1171 }
1172 }
1173
1174 bool sizeChanged = resizeImage &&
1175 (newSize.width() != baseBounds.width() ||
1176 newSize.height() != baseBounds.height());
1177
1178 // These signals will be emitted after processing is done
1179 KisImageSignalVector emitSignals;
1180 if (sizeChanged) emitSignals << ComplexSizeChangedSignal(baseBounds, newSize);
1181
1182 // These flags determine whether updates are transferred to the UI during processing
1183 KisProcessingApplicator::ProcessingFlags signalFlags =
1184 sizeChanged ?
1187
1188
1189 KisProcessingApplicator applicator(this, nodes,
1191 emitSignals, actionName);
1192
1194
1196 new KisTransformProcessingVisitor(1.0, 1.0, 0.0, 0.0,
1197 radians,
1198 offset.x(), offset.y(),
1199 filter);
1200 if (selection) {
1201 visitor->setSelection(selection);
1202 }
1203
1204 if (selection) {
1205 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1206 } else {
1208 }
1209
1210 if (sizeChanged) {
1211 applicator.applyCommand(new KisImageResizeCommand(this, newSize));
1212 }
1213 applicator.end();
1214}
1215
1216
1217void KisImage::rotateImage(double radians)
1218{
1219 rotateImpl(kundo2_i18n("Rotate Image"), root(), radians, true, 0);
1220}
1221
1222void KisImage::rotateNode(KisNodeSP node, double radians, KisSelectionSP selection)
1223{
1224 rotateNodes(KisNodeList{node}, radians, selection);
1225}
1226void KisImage::rotateNodes(KisNodeList nodes, double radians, KisSelectionSP selection)
1227{
1228 if (nodes.size() == 1 && nodes[0]->inherits("KisMask")) {
1229 rotateImpl(kundo2_i18n("Rotate Mask"), nodes, radians, false, selection);
1230 }
1231 else {
1232 rotateImpl(kundo2_i18np("Rotate Layer", "Rotate %1 Layers", nodes.size()), nodes, radians, false, selection);
1233 }
1234}
1235
1237 KisNodeSP rootNode,
1238 bool resizeImage,
1239 double angleX, double angleY,
1240 KisSelectionSP selection)
1241{
1242 shearImpl(actionName, KisNodeList{rootNode}, resizeImage, angleX, angleY, selection);
1243}
1245 KisNodeList nodes,
1246 bool resizeImage,
1247 double angleX, double angleY,
1248 KisSelectionSP selection)
1249{
1250 QRect baseBounds;
1251 if (resizeImage) {
1252 baseBounds = bounds();
1253 }
1254 else if (selection) {
1255 baseBounds = selection->selectedExactRect();
1256 }
1257 else {
1258 Q_FOREACH(KisNodeSP node, nodes) {
1259 baseBounds = baseBounds.united(node->exactBounds());
1260 }
1261 }
1262 const QPointF origin = QRectF(baseBounds).center();
1263
1264 //angleX, angleY are in degrees
1265 const qreal pi = 3.1415926535897932385;
1266 const qreal deg2rad = pi / 180.0;
1267
1268 qreal tanX = tan(angleX * deg2rad);
1269 qreal tanY = tan(angleY * deg2rad);
1270
1271 QPointF offset;
1272 QSize newSize;
1273
1274 {
1275 KisTransformWorker worker(0,
1276 1.0, 1.0,
1277 tanX, tanY,
1278 0,
1279 0, 0, 0, 0);
1280
1281 QRect newRect = worker.transform().mapRect(baseBounds);
1282 newSize = newRect.size();
1283 if (resizeImage) offset = -newRect.topLeft();
1284 else offset = origin - worker.transform().map(origin);
1285 }
1286
1287 if (newSize == baseBounds.size()) return;
1288
1289 KisImageSignalVector emitSignals;
1290 if (resizeImage) emitSignals << ComplexSizeChangedSignal(baseBounds, newSize);
1291
1292 KisProcessingApplicator::ProcessingFlags signalFlags =
1295
1296 KisProcessingApplicator applicator(this, nodes,
1297 signalFlags,
1298 emitSignals, actionName);
1299
1301
1303 new KisTransformProcessingVisitor(1.0, 1.0,
1304 tanX, tanY,
1305 0,
1306 offset.x(), offset.y(),
1307 filter);
1308
1309 if (selection) {
1310 visitor->setSelection(selection);
1311 }
1312
1313 if (selection) {
1314 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1315 } else {
1317 }
1318
1319 if (resizeImage) {
1320 applicator.applyCommand(new KisImageResizeCommand(this, newSize));
1321 }
1322
1323 applicator.end();
1324}
1325
1326void KisImage::shearNode(KisNodeSP node, double angleX, double angleY, KisSelectionSP selection)
1327{
1328 shearNodes(KisNodeList{node}, angleX, angleY, selection);
1329}
1330void KisImage::shearNodes(KisNodeList nodes, double angleX, double angleY, KisSelectionSP selection)
1331{
1332 if (nodes.size() == 1 && nodes[0]->inherits("KisMask")) {
1333 shearImpl(kundo2_i18n("Shear Mask"), nodes, false,
1334 angleX, angleY, selection);
1335 }
1336 else {
1337 shearImpl(kundo2_i18np("Shear Layer", "Shear %1 Layers", nodes.size()), nodes, false,
1338 angleX, angleY, selection);
1339 }
1340}
1341
1342void KisImage::shear(double angleX, double angleY)
1343{
1344 shearImpl(kundo2_i18n("Shear Image"), m_d->rootLayer, true,
1345 angleX, angleY, 0);
1346}
1347
1349 const KoColorSpace *dstColorSpace,
1351 KoColorConversionTransformation::ConversionFlags conversionFlags)
1352{
1353 if (!node->projectionLeaf()->isLayer()) return;
1354 // must not be an image root, use convertImageColorSpace() for that:
1355 KIS_SAFE_ASSERT_RECOVER_RETURN(!node->image() || (node.data() != node->image()->rootLayer().data()));
1356
1357 const KoColorSpace *srcColorSpace = node->colorSpace();
1358
1359 if (!dstColorSpace || *srcColorSpace == *dstColorSpace) return;
1360
1361 KUndo2MagicString actionName =
1362 kundo2_i18n("Convert Layer Color Space");
1363
1364 KisImageSignalVector emitSignals;
1365
1366 KisProcessingApplicator applicator(this, node,
1368 emitSignals, actionName);
1369
1370 applicator.applyVisitor(
1372 srcColorSpace, dstColorSpace,
1373 renderingIntent, conversionFlags),
1375
1376 applicator.end();
1377}
1378
1380{
1382 State initialState, KUndo2Command *parent = 0)
1383 : KisCommandUtils::FlipFlopCommand(initialState, parent),
1384 m_cs(cs),
1385 m_image(image)
1386 {
1387 }
1388
1389 void partA() override {
1390 KisImageSP image = m_image;
1391
1392 if (image) {
1394 }
1395 }
1396
1397private:
1400};
1401
1403 bool convertLayers,
1405 KoColorConversionTransformation::ConversionFlags conversionFlags)
1406{
1407 const KoColorSpace *srcColorSpace = this->colorSpace;
1408
1409 if (!dstColorSpace || *srcColorSpace == *dstColorSpace) return;
1410
1411 const KUndo2MagicString actionName =
1412 convertLayers ?
1413 kundo2_i18n("Convert Image Color Space") :
1414 kundo2_i18n("Convert Projection Color Space");
1415
1416 KisImageSignalVector emitSignals;
1417 emitSignals << ColorSpaceChangedSignal;
1418
1419 KisProcessingApplicator::ProcessingFlags flags = KisProcessingApplicator::NO_UI_UPDATES;
1420 if (convertLayers) {
1422 }
1423
1424 KisProcessingApplicator applicator(q, this->rootLayer,
1425 flags,
1426 emitSignals, actionName);
1427
1428 applicator.applyCommand(
1430 KisImageWSP(q),
1433
1434 applicator.applyVisitor(
1436 srcColorSpace, dstColorSpace,
1437 renderingIntent, conversionFlags),
1439
1440 applicator.applyCommand(
1442 KisImageWSP(q),
1445
1446
1447 applicator.end();
1448}
1449
1452 KoColorConversionTransformation::ConversionFlags conversionFlags)
1453{
1454 m_d->convertImageColorSpaceImpl(dstColorSpace, true, renderingIntent, conversionFlags);
1455}
1456
1463
1465{
1466 const KUndo2MagicString actionName = kundo2_i18n("Unify Layers Color Space");
1467
1468 KisImageSignalVector emitSignals;
1469
1470 KisProcessingApplicator::ProcessingFlags flags =
1472
1473 KisProcessingApplicator applicator(this, m_d->rootLayer,
1474 flags,
1475 emitSignals, actionName);
1476
1477 // src and dst color spaces coincide, since we should just unify
1478 // all our layers
1479 applicator.applyVisitor(
1485
1486 applicator.end();
1487}
1488
1490{
1491 const KoColorSpace *srcColorSpace = node->colorSpace();
1492
1493 if (!node->projectionLeaf()->isLayer()) return false;
1494 if (!profile || *srcColorSpace->profile() == *profile) return false;
1495
1496 KUndo2MagicString actionName = kundo2_i18n("Assign Profile to Layer");
1497
1498 KisImageSignalVector emitSignals;
1499
1500 const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
1501 if (!dstColorSpace) return false;
1502
1503 KisProcessingApplicator applicator(this, node,
1506 emitSignals, actionName);
1507
1508 applicator.applyVisitor(
1510 srcColorSpace, dstColorSpace),
1512
1513 applicator.end();
1514
1515 return true;
1516}
1517
1518
1519bool KisImage::assignImageProfile(const KoColorProfile *profile, bool blockAllUpdates)
1520{
1521 if (!profile) return false;
1522
1523 const KoColorSpace *srcColorSpace = m_d->colorSpace;
1524 bool imageProfileIsSame = *srcColorSpace->profile() == *profile;
1525
1526 imageProfileIsSame &=
1528 [profile] (KisNodeSP node) {
1529 return *node->colorSpace()->profile() != *profile;
1530 });
1531
1532 if (imageProfileIsSame) {
1533 dbgImage << "Trying to set the same image profile again" << ppVar(srcColorSpace->profile()->name()) << ppVar(profile->name());
1534 return true;
1535 }
1536
1537 KUndo2MagicString actionName = kundo2_i18n("Assign Profile");
1538
1539 KisImageSignalVector emitSignals;
1540 emitSignals << ProfileChangedSignal;
1541
1542 const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
1543 if (!dstColorSpace) return false;
1544
1545 KisProcessingApplicator applicator(this, m_d->rootLayer,
1547 (!blockAllUpdates ?
1550 emitSignals, actionName);
1551
1552 applicator.applyCommand(
1554 KisImageWSP(this),
1557
1558 applicator.applyVisitor(
1560 srcColorSpace, dstColorSpace),
1562
1563 applicator.applyCommand(
1565 KisImageWSP(this),
1568
1569
1570 applicator.end();
1571
1572 return true;
1573}
1574
1579
1581{
1582 return m_d->colorSpace;
1583}
1584
1586{
1587 return colorSpace()->profile();
1588}
1589
1590double KisImage::xRes() const
1591{
1592 return m_d->xres;
1593}
1594
1595double KisImage::yRes() const
1596{
1597 return m_d->yres;
1598}
1599
1600void KisImage::setResolution(double xres, double yres)
1601{
1602 if (xres > 0) {
1603 m_d->xres = xres;
1604 } else {
1605 qWarning() << "WARNING: Ignoring attempt to set image x resolution <= 0 (" << xres << ")!";
1606 }
1607
1608 if (yres > 0) {
1609 m_d->yres = yres;
1610 } else {
1611 qWarning() << "WARNING: Ignoring attempt to set image y resolution <= 0 (" << yres << ")!";
1612 }
1613}
1614
1615QPointF KisImage::documentToPixel(const QPointF &documentCoord) const
1616{
1617 return QPointF(documentCoord.x() * xRes(), documentCoord.y() * yRes());
1618}
1619
1620QPoint KisImage::documentToImagePixelFloored(const QPointF &documentCoord) const
1621{
1622 QPointF pixelCoord = documentToPixel(documentCoord);
1623 return QPoint(qFloor(pixelCoord.x()), qFloor(pixelCoord.y()));
1624}
1625
1626QRectF KisImage::documentToPixel(const QRectF &documentRect) const
1627{
1628 return QRectF(documentToPixel(documentRect.topLeft()), documentToPixel(documentRect.bottomRight()));
1629}
1630
1631QPointF KisImage::pixelToDocument(const QPointF &pixelCoord) const
1632{
1633 return QPointF(pixelCoord.x() / xRes(), pixelCoord.y() / yRes());
1634}
1635
1636QPointF KisImage::pixelToDocument(const QPoint &pixelCoord) const
1637{
1638 return QPointF((pixelCoord.x() + 0.5) / xRes(), (pixelCoord.y() + 0.5) / yRes());
1639}
1640
1641QRectF KisImage::pixelToDocument(const QRectF &pixelCoord) const
1642{
1643 return QRectF(pixelToDocument(pixelCoord.topLeft()), pixelToDocument(pixelCoord.bottomRight()));
1644}
1645
1646qint32 KisImage::width() const
1647{
1648 return m_d->width;
1649}
1650
1651qint32 KisImage::height() const
1652{
1653 return m_d->height;
1654}
1655
1657{
1658 Q_ASSERT(m_d->rootLayer);
1659 return m_d->rootLayer;
1660}
1661
1663{
1664 if (m_d->isolationRootNode) {
1665 return m_d->isolationRootNode->projection();
1666 }
1667
1668 Q_ASSERT(m_d->rootLayer);
1670 Q_ASSERT(projection);
1671 return projection;
1672}
1673
1674qint32 KisImage::nlayers() const
1675{
1676 QStringList list;
1677 list << "KisLayer";
1678
1679 KisCountVisitor visitor(list, KoProperties());
1680 m_d->rootLayer->accept(visitor);
1681 return visitor.count();
1682}
1683
1685{
1686 QStringList list;
1687 list << "KisLayer";
1688 KoProperties properties;
1689 properties.setProperty("visible", false);
1690 KisCountVisitor visitor(list, properties);
1691 m_d->rootLayer->accept(visitor);
1692
1693 return visitor.count();
1694}
1695
1697{
1698 const QStringList list = {"KisLayer"};
1699
1700 KoProperties koProperties;
1701 KisCountVisitor visitor(list, koProperties);
1702 const QList<KisNodeSP> childNodes = m_d->rootLayer->childNodes(list, koProperties);
1703 for (KisNodeSP childNode: childNodes) {
1704 childNode->accept(visitor);
1705 }
1706 return visitor.count();
1707}
1708
1710{
1711 KisLayerUtils::flattenImage(this, activeNode);
1712}
1713
1715{
1716 KisLayerUtils::mergeMultipleNodes(this, mergedNodes, putAfter);
1717}
1718
1720{
1721 KisLayerUtils::mergeDown(this, layer, strategy);
1722}
1723
1725{
1726 KisLayerUtils::flattenLayer(this, layer);
1727}
1728
1734
1735QImage KisImage::convertToQImage(QRect imageRect,
1736 const KoColorProfile * profile)
1737{
1738 qint32 x;
1739 qint32 y;
1740 qint32 w;
1741 qint32 h;
1742 imageRect.getRect(&x, &y, &w, &h);
1743 return convertToQImage(x, y, w, h, profile);
1744}
1745
1747 qint32 y,
1748 qint32 w,
1749 qint32 h,
1750 const KoColorProfile * profile)
1751{
1753 if (!dev) return QImage();
1754 QImage image = dev->convertToQImage(const_cast<KoColorProfile*>(profile), x, y, w, h,
1757
1758 return image;
1759}
1760
1761
1762
1763QImage KisImage::convertToQImage(const QSize& scaledImageSize, const KoColorProfile *profile)
1764{
1765 if (scaledImageSize.isEmpty()) {
1766 return QImage();
1767 }
1768
1770 KisPainter gc;
1771 gc.copyAreaOptimized(QPoint(0, 0), projection(), dev, bounds());
1772 gc.end();
1773 double scaleX = qreal(scaledImageSize.width()) / width();
1774 double scaleY = qreal(scaledImageSize.height()) / height();
1775
1776
1777 if (scaleX < 1.0/256 || scaleY < 1.0/256) {
1778 // quick checking if we're not trying to scale too much
1779 // convertToQImage uses KisFixedPoint values, which means that the scale cannot be smaller than 1/2^8
1780 // BUG:432182
1781 // FIXME: would be best to extend KisFixedPoint instead
1782 return convertToQImage(size(), profile).scaled(scaledImageSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
1783 }
1784
1785 KoDummyUpdaterHolder updaterHolder;
1786 QPointer<KoUpdater> updater = updaterHolder.updater();
1787
1788 KisTransformWorker worker(dev, scaleX, scaleY, 0.0, 0.0, 0.0, 0.0, 0.0, updater, KisFilterStrategyRegistry::instance()->value("Bicubic"));
1789 worker.run();
1790
1791 return dev->convertToQImage(profile);
1792}
1797
1798QRect KisImage::bounds() const
1799{
1800 return QRect(0, 0, width(), height());
1801}
1802
1804{
1805 QRect boundRect = bounds();
1806
1807 const int lod = currentLevelOfDetail();
1808 if (lod > 0) {
1809 KisLodTransform t(lod);
1810 boundRect = t.map(boundRect);
1811 }
1812
1813 return boundRect;
1814}
1815
1823
1825{
1826 return m_d->undoStore->presentCommand();
1827}
1828
1830{
1831 disconnect(m_d->undoStore.data(), SIGNAL(historyStateChanged()), &m_d->signalRouter, SLOT(emitImageModifiedNotification()));
1832
1835 m_d->undoStore.reset(undoStore);
1836
1837 connect(m_d->undoStore.data(), SIGNAL(historyStateChanged()), &m_d->signalRouter, SLOT(emitImageModifiedNotification()));
1838
1839}
1840
1842{
1843 return m_d->undoStore.data();
1844}
1845
1847{
1848 return &m_d->legacyUndoAdapter;
1849}
1850
1856
1865
1867{
1869
1871
1872 if (m_d->rootLayer) {
1874 m_d->rootLayer->setImage(0);
1875 m_d->rootLayer->disconnect();
1876
1877 KisPaintDeviceSP original = m_d->rootLayer->original();
1879 }
1880
1882 m_d->rootLayer->disconnect();
1884 m_d->rootLayer->setImage(this);
1885
1887 this->setDefaultProjectionColor(defaultProjectionColor);
1888}
1889
1891{
1892 // Find the icc annotation, if there is one
1893 vKisAnnotationSP_it it = m_d->annotations.begin();
1894 while (it != m_d->annotations.end()) {
1895 if ((*it)->type() == annotation->type()) {
1896 *it = annotation;
1898 return;
1899 }
1900 ++it;
1901 }
1902 m_d->annotations.push_back(annotation);
1904}
1905
1907{
1908 vKisAnnotationSP_it it = m_d->annotations.begin();
1909 while (it != m_d->annotations.end()) {
1910 if ((*it) && (*it)->type() == type) {
1911 return *it;
1912 }
1913 else if (!*it) {
1914 qWarning() << "Skipping deleted annotation";
1915 }
1916 ++it;
1917 }
1918 return KisAnnotationSP(0);
1919}
1920
1921void KisImage::removeAnnotation(const QString& type)
1922{
1923 vKisAnnotationSP_it it = m_d->annotations.begin();
1924 while (it != m_d->annotations.end()) {
1925 if ((*it)->type() == type) {
1926 m_d->annotations.erase(it);
1928 return;
1929 }
1930 ++it;
1931 }
1932}
1933
1938
1943
1948
1953
1961
1963{
1971 if (strokeStrategy->requestsOtherStrokesToEnd()) {
1973 }
1974
1975 return m_d->scheduler.startStroke(strokeStrategy);
1976}
1977
1979{
1980 KisImageConfig imageConfig(true);
1981 int patchWidth = imageConfig.updatePatchWidth();
1982 int patchHeight = imageConfig.updatePatchHeight();
1983
1984 for (int y = 0; y < rc.height(); y += patchHeight) {
1985 for (int x = 0; x < rc.width(); x += patchWidth) {
1986 QRect patchRect(x, y, patchWidth, patchHeight);
1987 patchRect &= rc;
1988
1989 KritaUtils::addJobConcurrent(jobs, std::bind(&KisImage::notifyProjectionUpdated, q, patchRect));
1990 }
1991 }
1992}
1993
1994bool KisImage::startIsolatedMode(KisNodeSP node, bool isolateLayer, bool isolateGroup)
1995{
1996 m_d->isolateLayer = isolateLayer;
1997 m_d->isolateGroup = isolateGroup;
1998 if ((isolateLayer || isolateGroup) == false) return false;
1999
2004 if (!node->projection()) return false;
2005
2006 struct StartIsolatedModeStroke : public KisRunnableBasedStrokeStrategy {
2007 StartIsolatedModeStroke(KisNodeSP node, KisImageSP image, bool isolateLayer, bool isolateGroup)
2008 : KisRunnableBasedStrokeStrategy(QLatin1String("start-isolated-mode"),
2009 kundo2_noi18n("start-isolated-mode")),
2010 m_newRoot(node),
2011 m_image(image),
2012 m_isolateLayer(isolateLayer),
2013 m_isolateGroup(isolateGroup)
2014 {
2015 this->enableJob(JOB_INIT, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
2016 this->enableJob(JOB_DOSTROKE, true);
2017 this->enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
2018 setClearsRedoOnStart(false);
2019 }
2020
2021 void initStrokeCallback() override {
2022 if (m_isolateLayer == false && m_isolateGroup == true) {
2023 // Isolate parent node unless node is the root note.
2024 m_newRoot = m_newRoot->parent() ? m_newRoot->parent() : m_newRoot;
2025 }
2026 // pass-though node don't have any projection prepared, so we should
2027 // explicitly regenerate it before activating isolated mode.
2028 m_newRoot->projectionLeaf()->explicitlyRegeneratePassThroughProjection();
2029 m_prevRoot = m_image->m_d->isolationRootNode;
2030
2031 const bool beforeVisibility = m_newRoot->projectionLeaf()->visible();
2032 const bool prevRootBeforeVisibility = m_prevRoot ? m_prevRoot->projectionLeaf()->visible() : false;
2033
2034 m_image->m_d->isolationRootNode = m_newRoot;
2035 Q_EMIT m_image->sigIsolatedModeChanged();
2036
2037 const bool afterVisibility = m_newRoot->projectionLeaf()->visible();
2038 const bool prevRootAfterVisibility = m_prevRoot ? m_prevRoot->projectionLeaf()->visible() : false;
2039
2040 m_newRootNeedsFullRefresh = beforeVisibility != afterVisibility;
2041 m_prevRootNeedsFullRefresh = prevRootBeforeVisibility != prevRootAfterVisibility;
2042 }
2043
2044 void finishStrokeCallback() override {
2045 // the GUI uses our thread to do the color space conversion so we
2046 // need to Q_EMIT this signal in multiple threads
2047
2048 if (m_prevRoot && m_prevRootNeedsFullRefresh) {
2049 m_image->refreshGraphAsync(m_prevRoot);
2050 }
2051
2052 if (m_newRootNeedsFullRefresh) {
2053 m_image->refreshGraphAsync(m_newRoot);
2054 }
2055
2056 if (!m_prevRootNeedsFullRefresh && !m_newRootNeedsFullRefresh) {
2058 m_image->m_d->notifyProjectionUpdatedInPatches(m_image->bounds(), jobs);
2059 this->runnableJobsInterface()->addRunnableJobs(jobs);
2060 }
2061
2062 m_image->invalidateAllFrames();
2063 }
2064
2065 private:
2066 KisNodeSP m_newRoot;
2067 KisNodeSP m_prevRoot;
2068 KisImageSP m_image;
2069 bool m_newRootNeedsFullRefresh = false;
2070 bool m_prevRootNeedsFullRefresh = false;
2071
2072 bool m_isolateLayer;
2073 bool m_isolateGroup;
2074 };
2075
2076 KisStrokeId id = startStroke(new StartIsolatedModeStroke(node, this, isolateLayer, isolateGroup));
2077 endStroke(id);
2078
2079 return true;
2080}
2081
2083{
2084 if (!m_d->isolationRootNode) return;
2085
2086 struct StopIsolatedModeStroke : public KisRunnableBasedStrokeStrategy {
2087 StopIsolatedModeStroke(KisImageSP image)
2088 : KisRunnableBasedStrokeStrategy(QLatin1String("stop-isolated-mode"), kundo2_noi18n("stop-isolated-mode")),
2089 m_image(image),
2090 m_oldRootNode(nullptr),
2091 m_oldNodeNeedsRefresh(false)
2092 {
2093 this->enableJob(JOB_INIT);
2094 this->enableJob(JOB_DOSTROKE, true);
2095 this->enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
2096 setClearsRedoOnStart(false);
2097 }
2098
2099 void initStrokeCallback() override {
2100 if (!m_image->m_d->isolationRootNode) return;
2101
2102 m_oldRootNode = m_image->m_d->isolationRootNode;
2103
2104 const bool beforeVisibility = m_oldRootNode->projectionLeaf()->visible();
2105 m_image->m_d->isolationRootNode = 0;
2106 m_image->m_d->isolateLayer = false;
2107 m_image->m_d->isolateGroup = false;
2108 Q_EMIT m_image->sigIsolatedModeChanged();
2109 const bool afterVisibility = m_oldRootNode->projectionLeaf()->visible();
2110
2111 m_oldNodeNeedsRefresh = (beforeVisibility != afterVisibility);
2112 }
2113
2114 void finishStrokeCallback() override {
2115
2116 m_image->invalidateAllFrames();
2117
2118 if (m_oldNodeNeedsRefresh){
2119 m_oldRootNode->setDirty(m_image->bounds());
2120 } else {
2121 // TODO: Substitute notifyProjectionUpdated() with this code
2122 // when update optimization is implemented
2123 //
2124 // QRect updateRect = bounds() | oldRootNode->extent();
2125 //oldRootNode->setDirty(updateRect);
2126
2128 m_image->m_d->notifyProjectionUpdatedInPatches(m_image->bounds(), jobs);
2129 this->runnableJobsInterface()->addRunnableJobs(jobs);
2130 }
2131 }
2132
2133 private:
2134 KisImageSP m_image;
2135 KisNodeSP m_oldRootNode;
2136 bool m_oldNodeNeedsRefresh;
2137 };
2138
2139 KisStrokeId id = startStroke(new StopIsolatedModeStroke(this));
2140 endStroke(id);
2141}
2142
2146
2148{
2149 return m_d->isolateLayer;
2150}
2151
2153{
2154 return m_d->isolateGroup;
2155}
2156
2162
2167
2169{
2170 return m_d->scheduler.cancelStroke(id);
2171}
2172
2174{
2175 return scheduler.tryCancelCurrentStrokeAsync();
2176}
2177
2182
2187
2194
2199
2205
2210
2212{
2218 refreshGraphAsync(0, bounds(), QRect());
2219 waitForDone();
2220}
2221
2222void KisImage::refreshGraphAsync(KisNodeSP root, const QVector<QRect> &rects, const QRect &cropRect, KisProjectionUpdateFlags flags)
2223{
2224 if (!root) root = m_d->rootLayer;
2225
2226 QVector<QRect> requestedRects = rects;
2227
2228 KisGroupLayer *group = dynamic_cast<KisGroupLayer*>(root.data());
2229 if (group && group->passThroughMode()) {
2240 QVector<QRect> changeRects = requestedRects;
2241 KisProjectionLeafSP leaf = root->projectionLeaf()->nextSibling();
2242 while (leaf) {
2243 if (leaf->shouldBeRendered()) {
2244 for (auto it = changeRects.begin(); it != changeRects.end(); ++it) {
2245 *it = leaf->projectionPlane()->changeRect(*it, leaf->node() == root ? KisNode::N_FILTHY : KisNode::N_ABOVE_FILTHY);
2246 }
2247 }
2248
2249 leaf = leaf->nextSibling();
2250 }
2251
2252 std::swap(requestedRects, changeRects);
2253 root = group->parent();
2254
2256 }
2257
2262 for (auto it = m_d->projectionUpdatesFilters.rbegin();
2263 it != m_d->projectionUpdatesFilters.rend();
2264 ++it) {
2265
2266 KIS_SAFE_ASSERT_RECOVER(*it) { continue; }
2267
2268 if ((*it)->filterRefreshGraph(this, root.data(), requestedRects, cropRect, flags)) {
2269 return;
2270 }
2271 }
2272
2273 if (!flags.testFlag(KisProjectionUpdateFlag::DontInvalidateFrames)) {
2274 m_d->animationInterface->notifyNodeChanged(root.data(), requestedRects, true);
2275 }
2276
2277 m_d->scheduler.fullRefreshAsync(root, requestedRects, cropRect, flags);
2278}
2279
2281{
2282 m_d->scheduler.addSpontaneousJob(spontaneousJob);
2283}
2284
2286{
2288}
2289
2298
2313
2320
2326
2332
2337
2342
2347
2349{
2351}
2352
2354{
2355 m_d->disableUIUpdateSignals.deref();
2356
2357 QRect rect;
2358 QVector<QRect> postponedUpdates;
2359
2361 postponedUpdates.append(rect);
2362 }
2363
2364 return postponedUpdates;
2365}
2366
2368{
2370
2372 int lod = currentLevelOfDetail();
2373 QRect dirtyRect = !lod ? rc : KisLodTransform::upscaledRect(rc, lod);
2374
2375 if (dirtyRect.isEmpty()) return;
2376
2377 Q_EMIT sigImageUpdated(dirtyRect);
2378 } else {
2380 }
2381}
2382
2387
2389{
2390 return m_d->scheduler.threadsLimit();
2391}
2392
2413
2416 const QVector<QRect> &rects,
2417 const QRect &cropRect,
2418 KisProjectionUpdateFlags flags)
2419{
2420 if (rects.isEmpty()) return;
2421
2422 scheduler.updateProjection(node, rects, cropRect, flags);
2423}
2424
2425void KisImage::requestProjectionUpdate(KisNode *node, const QVector<QRect> &rects, KisProjectionUpdateFlags flags)
2426{
2431 for (auto it = m_d->projectionUpdatesFilters.rbegin();
2432 it != m_d->projectionUpdatesFilters.rend();
2433 ++it) {
2434
2435 KIS_SAFE_ASSERT_RECOVER(*it) { continue; }
2436
2437 if ((*it)->filter(this, node, rects, flags)) {
2438 return;
2439 }
2440 }
2441
2442 if (!flags.testFlag(KisProjectionUpdateFlag::DontInvalidateFrames)) {
2443 m_d->animationInterface->notifyNodeChanged(node, rects, false);
2444 }
2445
2455 QVector<QRect> allSplitRects;
2456
2457 const QRect boundRect = effectiveLodBounds();
2458 Q_FOREACH (const QRect &rc, rects) {
2459 KisWrappedRect splitRect(rc, boundRect, m_d->wrapAroundModeAxis);
2460 allSplitRects.append(splitRect);
2461 }
2462
2463 m_d->requestProjectionUpdateImpl(node, allSplitRects, boundRect, flags);
2464
2465 } else {
2466 m_d->requestProjectionUpdateImpl(node, rects, bounds(), flags);
2467 }
2468
2470}
2471
2472void KisImage::invalidateFrames(const KisTimeSpan &range, const QRect &rect)
2473{
2475}
2476
2481
2486
2488{
2489 Q_UNUSED(node);
2490
2491 channel->connect(channel, SIGNAL(sigAddedKeyframe(const KisKeyframeChannel*, int)), m_d->animationInterface, SIGNAL(sigKeyframeAdded(const KisKeyframeChannel*, int)), Qt::UniqueConnection);
2492 channel->connect(channel, SIGNAL(sigKeyframeHasBeenRemoved(const KisKeyframeChannel*,int)), m_d->animationInterface, SIGNAL(sigKeyframeRemoved(const KisKeyframeChannel*, int)), Qt::UniqueConnection);
2493}
2494
2496{
2497 Q_UNUSED(node);
2498
2499 channel->disconnect(channel, SIGNAL(sigAddedKeyframe(const KisKeyframeChannel*, int)), m_d->animationInterface, SIGNAL(sigKeyframeAdded(const KisKeyframeChannel*, int)));
2500 channel->disconnect(channel, SIGNAL(sigKeyframeHasBeenRemoved(const KisKeyframeChannel*, int)), m_d->animationInterface, SIGNAL(sigKeyframeRemoved(const KisKeyframeChannel*, int)));
2501}
2502
2507
2509{
2510 m_d->compositions.append(composition);
2511}
2512
2514{
2515 m_d->compositions.removeAll(composition);
2516}
2517
2519{
2520 int index = m_d->compositions.indexOf(composition);
2521 if (index <= 0) {
2522 return;
2523 }
2524 m_d->compositions.move(index, index - 1);
2525}
2526
2528{
2529 int index = m_d->compositions.indexOf(composition);
2530 if (index >= m_d->compositions.size() -1) {
2531 return;
2532 }
2533 m_d->compositions.move(index, index + 1);
2534}
2535
2537{
2538 KisSelectionMask *mask = dynamic_cast<KisSelectionMask*>(root.data());
2539 if (mask &&
2540 (!bounds.contains(mask->paintDevice()->exactBounds()) ||
2541 mask->selection()->hasShapeSelection())) {
2542
2543 return true;
2544 }
2545
2546 KisNodeSP node = root->firstChild();
2547
2548 while (node) {
2549 if (checkMasksNeedConversion(node, bounds)) {
2550 return true;
2551 }
2552
2553 node = node->nextSibling();
2554 }
2555
2556 return false;
2557}
2558
2560{
2563 }
2564
2566
2569
2570 KisProcessingApplicator applicator(this, root(),
2573 kundo2_i18n("Crop Selections"));
2574
2575 KisProcessingVisitorSP visitor =
2577
2578 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
2579 applicator.end();
2580 }
2581}
2582
2587
2592
2593
2598
2604
2609
2618
2623
2628
2630{
2631 Q_UNUSED(node);
2632 Q_EMIT sigNodeCollapsedChanged();
2633}
2634
2639
2641{
2642 const bool changed = bool(m_d->proofingConfig) != bool(proofingConfig) ||
2643 (m_d->proofingConfig && proofingConfig && *m_d->proofingConfig != *proofingConfig);
2644
2645 // we still assign even when unchanged since they can be different
2646 // shared pointer objects
2647 m_d->proofingConfig = proofingConfig;
2648
2649 if (changed) {
2650 Q_EMIT sigProofingConfigChanged();
2651 }
2652}
2653
2661
2663{
2664 return m_d->axesCenter;
2665}
2666
2667void KisImage::setMirrorAxesCenter(const QPointF &value) const
2668{
2669 m_d->axesCenter = value;
2670}
2671
2676
2678{
2679 return m_d->allowMasksOnRootNode;
2680}
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:306
void setDeselectedGlobalSelection(KisSelectionMaskSP selectionMask)
Definition kis_image.cc:311
KisImageGlobalSelectionManagementInterface(KisImage *image)
Definition kis_image.cc:301
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:279
KisLocklessStack< QRect > savedDisabledUIUpdates
Definition kis_image.cc:269
vKisAnnotationSP annotations
Definition kis_image.cc:266
KisSelectionMaskSP deselectedGlobalSelectionMask
Definition kis_image.cc:249
void notifyProjectionUpdatedInPatches(const QRect &rc, QVector< KisRunnableStrokeJobData * > &jobs)
KisImageAnimationInterface * animationInterface
Definition kis_image.cc:275
KisUpdateScheduler scheduler
Definition kis_image.cc:276
KisProofingConfigurationSP proofingConfig
Definition kis_image.cc:246
KisSelectionMaskSP targetOverlaySelectionMask
Definition kis_image.cc:251
KisImageGlobalSelectionManagementInterface globalSelectionInterface
Definition kis_image.cc:248
KisSelectionMaskSP overlaySelectionMask
Definition kis_image.cc:252
void requestProjectionUpdateImpl(KisNode *node, const QVector< QRect > &rects, const QRect &cropRect, KisProjectionUpdateFlags flags)
KisPostExecutionUndoAdapter postExecutionUndoAdapter
Definition kis_image.cc:264
KisLegacyUndoAdapter legacyUndoAdapter
Definition kis_image.cc:263
QAtomicInt disableUIUpdateSignals
Definition kis_image.cc:268
QScopedPointer< KisUndoStore > undoStore
Definition kis_image.cc:262
WrapAroundAxis wrapAroundModeAxis
Definition kis_image.cc:260
const KoColorSpace * colorSpace
Definition kis_image.cc:245
QAtomicInt disableDirtyRequests
Definition kis_image.cc:277
QVector< KisProjectionUpdatesFilterSP > projectionUpdatesFilters
Definition kis_image.cc:272
KisImageSignalRouter signalRouter
Definition kis_image.cc:274
QStack< KisProjectionUpdatesFilterCookie > disabledUpdatesCookies
Definition kis_image.cc:273
void convertImageColorSpaceImpl(const KoColorSpace *dstColorSpace, bool convertLayers, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
QList< KisLayerCompositionSP > compositions
Definition kis_image.cc:253
KisGroupLayerSP rootLayer
Definition kis_image.cc:250
KisImagePrivate(KisImage *_q, qint32 w, qint32 h, const KoColorSpace *c, KisUndoStore *undo, KisImageAnimationInterface *_animationInterface)
Definition kis_image.cc:123
void resizeImage(const QRect &newRect)
start asynchronous operation on resizing the image
Definition kis_image.cc:866
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:633
void addAnnotation(KisAnnotationSP annotation)
bool wrapAroundModeActive() const
void sigNodeCollapsedChanged()
KisImagePrivate * m_d
Definition kis_image.h:1275
bool canReselectGlobalSelection()
Definition kis_image.cc:706
KisImageGlobalSelectionManagementInterface * globalSelectionManagementInterface() const
Definition kis_image.cc:711
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:627
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:589
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:987
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:605
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:806
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:876
KisCompositeProgressProxy * compositeProgressProxy()
Definition kis_image.cc:747
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:416
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:819
void sigRedoDuringStrokeRequested()
QString nextLayerName(const QString &baseName="") const
Definition kis_image.cc:716
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:789
void copyFromImage(const KisImage &rhs)
Definition kis_image.cc:411
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:686
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:757
KisImage * clone(bool exactCopy=false)
Definition kis_image.cc:406
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:835
void immediateLockForReadOnly()
Definition kis_image.cc:794
void aboutToAddANode(KisNode *parent, int index) override
Definition kis_image.cc:583
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:638
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:316
void cropImage(const QRect &newRect)
start asynchronous operation on cropping the image
Definition kis_image.cc:871
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:829
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:696
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:772
bool locked() const
Definition kis_image.cc:752
KisUndoStore * undoStore()
void unblockUpdates() override
unblockUpdates unblock updating the image project. This only restarts the scheduler and does not sche...
Definition kis_image.cc:824
void setModifiedWithoutUndo()
bool hasOverlaySelectionMask() const
Definition kis_image.cc:691
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:331
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:342
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:682
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:113
KIS_DECLARE_STATIC_INITIALIZER
Definition kis_image.cc:116
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()