Krita Source Code Documentation
Loading...
Searching...
No Matches
transform_stroke_strategy.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2013 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QMutexLocker>
11
13
14#include <klocalizedstring.h>
15#include <kis_node.h>
16#include <kis_group_layer.h>
18#include <kis_transaction.h>
19#include <kis_painter.h>
21#include <kis_transform_mask.h>
23#include "kis_transform_utils.h"
24#include "kis_convex_hull.h"
27#include "kis_lod_transform.h"
28
29#include "kis_projection_leaf.h"
31
33#include "KisAnimAutoKey.h"
35#include "kis_selection_mask.h"
37#include "kis_filter_mask.h"
38#include "kis_image_config.h"
39#include "kis_layer_utils.h"
40#include <QQueue>
46#include "kis_command_ids.h"
52#include "kis_layer_utils.h"
53#include "KisAnimAutoKey.h"
54
55
57 const QString &filterId,
58 bool forceReset,
59 KisNodeList rootNodes,
60 KisSelectionSP selection,
61 KisStrokeUndoFacade *undoFacade,
62 KisUpdatesFacade *updatesFacade)
63 : KisStrokeStrategyUndoCommandBased(kundo2_i18n("Transform"), false, undoFacade),
64 m_updatesFacade(updatesFacade),
65 m_mode(mode),
66 m_filterId(filterId),
67 m_forceReset(forceReset),
68 m_selection(selection)
69{
70 if (selection) {
71 Q_FOREACH(KisNodeSP node, rootNodes) {
72 KIS_SAFE_ASSERT_RECOVER_NOOP(!dynamic_cast<KisTransformMask*>(node.data()));
73 }
74 }
75
76 m_rootNodes = rootNodes;
78}
79
83
85{
86 KisPaintDeviceSP cache;
87
88 if (m_selection) {
89 QRect srcRect = m_selection->selectedExactRect();
90
91 cache = dev->createCompositionSourceDevice();
92 KisPainter gc(cache);
94 gc.bitBlt(srcRect.topLeft(), dev, srcRect);
95 } else {
96 cache = dev->createCompositionSourceDevice(dev);
97 }
98
99 return cache;
100}
101
103{
104 QMutexLocker l(&m_devicesCacheMutex);
105 return m_devicesCacheHash.contains(src.data());
106}
107
109{
110 QMutexLocker l(&m_devicesCacheMutex);
111 m_devicesCacheHash.insert(src.data(), cache);
112}
113
115{
116 QMutexLocker l(&m_devicesCacheMutex);
117 KisPaintDeviceSP cache = m_devicesCacheHash.value(src.data());
118 if (!cache) {
119 warnKrita << "WARNING: Transform Stroke: the device is absent in cache!";
120 }
121
122 return cache;
123}
124
126{
127 return m_selection &&
128 (device == m_selection->pixelSelection().data() ||
129 device == m_selection->projection().data());
130}
131
133{
134 TransformData *td = dynamic_cast<TransformData*>(data);
135 ClearSelectionData *csd = dynamic_cast<ClearSelectionData*>(data);
136 PreparePreviewData *ppd = dynamic_cast<PreparePreviewData*>(data);
137 TransformAllData *runAllData = dynamic_cast<TransformAllData*>(data);
138 CalculateConvexHullData *cch = dynamic_cast<CalculateConvexHullData*>(data);
139
140
141 if (runAllData) {
142 // here we only save the passed args, actual
143 // transformation will be performed during
144 // finish job
145 m_savedTransformArgs = runAllData->config;
146 } else if (ppd) {
147 KisNodeSP rootNode = m_rootNodes[0];
148 KisNodeList processedNodes = m_processedNodes;
149 KisPaintDeviceSP previewDevice;
150
151
152 if (processedNodes.size() > 1) {
153 const QRect bounds = rootNode->image()->bounds();
154 const int desiredAnimTime = rootNode->image()->animationInterface()->currentTime();
155
156 KisImageSP clonedImage = new KisImage(0,
157 bounds.width(),
158 bounds.height(),
159 rootNode->image()->colorSpace(),
160 "transformed_image");
161
162 // BUG: 413968
163 // Workaround: Group layers wouldn't properly render the right frame
164 // since `clonedImage` would always have a time value of 0.
165 clonedImage->animationInterface()->explicitlySetCurrentTime(desiredAnimTime);
166
167 KisNodeSP cloneRoot = clonedImage->rootLayer();
168
169 Q_FOREACH(KisNodeSP node, processedNodes) {
170 // Masks with unselected parents can't be added.
171 if (!node->inherits("KisMask")) {
172 clonedImage->addNode(node->clone().data(), cloneRoot);
173 }
174 }
175
176 clonedImage->refreshGraphAsync();
177 KisLayerUtils::refreshHiddenAreaAsync(clonedImage, cloneRoot, clonedImage->bounds());
178
180 clonedImage->waitForDone();
181
182 previewDevice = createDeviceCache(clonedImage->projection());
183 previewDevice->setDefaultBounds(cloneRoot->projection()->defaultBounds());
184
185 // we delete the cloned image in GUI thread to ensure
186 // no signals are still pending
187 makeKisDeleteLaterWrapper(clonedImage)->deleteLater();
188 }
189 else if (rootNode->childCount() || !rootNode->paintDevice()) {
190 if (KisTransformMask* tmask =
191 dynamic_cast<KisTransformMask*>(rootNode.data())) {
192 previewDevice = createDeviceCache(tmask->buildPreviewDevice());
193
195 m_selection = 0;
196 }
197
198 } else if (KisGroupLayer *group = dynamic_cast<KisGroupLayer*>(rootNode.data())) {
199 const QRect bounds = group->image()->bounds();
200 const int desiredAnimTime = group->image()->animationInterface()->currentTime();
201
202 KisImageSP clonedImage = new KisImage(0,
203 bounds.width(),
204 bounds.height(),
205 group->colorSpace(),
206 "transformed_image");
207
208 // BUG: 413968
209 // Workaround: Group layers wouldn't properly render the right frame
210 // since `clonedImage` would always have a time value of 0.
211 clonedImage->animationInterface()->explicitlySetCurrentTime(desiredAnimTime);
212
213 KisGroupLayerSP clonedGroup = dynamic_cast<KisGroupLayer*>(group->clone().data());
214
215 // In case the group is pass-through, it needs to be disabled for the preview,
216 // otherwise it will crash (no parent for a preview leaf).
217 // Also it needs to be done before setting the root layer for clonedImage.
218 // Result: preview for pass-through group is the same as for standard group
219 // (i.e. filter layers in the group won't affect the layer stack for a moment).
220 clonedGroup->setPassThroughMode(false);
221 clonedImage->setRootLayer(clonedGroup);
222
223 QQueue<KisNodeSP> linearizedSrcNodes;
224 KisLayerUtils::recursiveApplyNodes(rootNode, [&linearizedSrcNodes] (KisNodeSP node) {
225 linearizedSrcNodes.enqueue(node);
226 });
227
228 KisLayerUtils::recursiveApplyNodes(KisNodeSP(clonedGroup), [&linearizedSrcNodes, processedNodes] (KisNodeSP node) {
229 KisNodeSP srcNode = linearizedSrcNodes.dequeue();
230
231 if (!processedNodes.contains(srcNode)) {
232 node->setVisible(false);
233 }
234 });
235
236 clonedImage->refreshGraphAsync();
237 KisLayerUtils::refreshHiddenAreaAsync(clonedImage, clonedGroup, clonedImage->bounds());
238
240 clonedImage->waitForDone();
241
242 previewDevice = createDeviceCache(clonedImage->projection());
243 previewDevice->setDefaultBounds(group->projection()->defaultBounds());
244
245 // we delete the cloned image in GUI thread to ensure
246 // no signals are still pending
247 makeKisDeleteLaterWrapper(clonedImage)->deleteLater();
248
249 } else {
250 rootNode->projectionLeaf()->explicitlyRegeneratePassThroughProjection();
251 previewDevice = createDeviceCache(rootNode->projection());
252 }
253
254
255
256 } else {
257 KisPaintDeviceSP cacheDevice = createDeviceCache(rootNode->paintDevice());
258
259 if (dynamic_cast<KisSelectionMask*>(rootNode.data())) {
261 cacheDevice->colorSpace()->colorDepthId() == Integer8BitsColorDepthID) {
262
264 }
265
266 previewDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
267 const QRect srcRect = cacheDevice->exactBounds();
268
269 KisSequentialConstIterator srcIt(cacheDevice, srcRect);
270 KisSequentialIterator dstIt(previewDevice, srcRect);
271
272 const int pixelSize = previewDevice->colorSpace()->pixelSize();
273
274
275 KisImageConfig cfg(true);
276 KoColor pixel(cfg.selectionOverlayMaskColor(), previewDevice->colorSpace());
277
278 const qreal coeff = 1.0 / 255.0;
279 const qreal baseOpacity = 0.5;
280
281 while (srcIt.nextPixel() && dstIt.nextPixel()) {
282 qreal gray = srcIt.rawDataConst()[0];
283 qreal alpha = srcIt.rawDataConst()[1];
284
285 pixel.setOpacity(quint8(gray * alpha * baseOpacity * coeff));
286 memcpy(dstIt.rawData(), pixel.data(), pixelSize);
287 }
288
289 } else {
290 previewDevice = cacheDevice;
291 }
292
293 putDeviceCache(rootNode->paintDevice(), cacheDevice);
294 }
295
296 Q_EMIT sigPreviewDeviceReady(previewDevice);
297 }
298 else if (td) {
300 QRect oldExtent = td->node->projectionPlane()->tightUserVisibleBounds();
301 KisPaintDeviceSP device = td->node->paintDevice();
302
303 if (device && !checkBelongsToSelection(device)) {
304 KisPaintDeviceSP cachedPortion = getDeviceCache(device);
305 Q_ASSERT(cachedPortion);
306
307 KisTransaction transaction(device);
308
311 device, &helper);
312
316
317 m_updateData->addUpdate(td->node, cachedPortion->extent() | oldExtent | td->node->projectionPlane()->tightUserVisibleBounds());
318 } else if (KisExternalLayer *extLayer =
319 dynamic_cast<KisExternalLayer*>(td->node.data())) {
320
323 extLayer->supportsPerspectiveTransform())) {
324
325 QRect oldDirtyRect = oldExtent | extLayer->theoreticalBoundingRect();
326
328 QTransform t = w.transform();
329
330 runAndSaveCommand(KUndo2CommandSP(extLayer->transform(t)),
333
339 const QRect theoreticalNewDirtyRect =
340 kisGrowRect(t.mapRect(oldDirtyRect), 1);
341
342 m_updateData->addUpdate(td->node, oldDirtyRect | td->node->projectionPlane()->tightUserVisibleBounds() | extLayer->theoreticalBoundingRect() | theoreticalNewDirtyRect);
343 }
344
345 } else if (KisTransformMask *transformMask =
346 dynamic_cast<KisTransformMask*>(td->node.data())) {
347
349 new KisSimpleModifyTransformMaskCommand(transformMask,
354
355 m_updateData->addUpdate(td->node, oldExtent | td->node->extent());
356 }
357 } else if (m_selection) {
358
364
368 &helper);
369
373 }
374 } else if (csd) {
375 KisPaintDeviceSP device = csd->node->paintDevice();
376
377 if (device && !checkBelongsToSelection(device)) {
378 if (!haveDeviceInCache(device)) {
379 putDeviceCache(device, createDeviceCache(device));
380 }
381 clearSelection(device);
382
386 if (KisSelectionMask *mask = dynamic_cast<KisSelectionMask*>(csd->node.data())) {
387 KisSelectionSP selection = mask->selection();
388 if (selection) {
389 selection->setVisible(false);
390 m_deactivatedSelections.append(selection);
391 }
392 }
393 } else if (KisExternalLayer *externalLayer = dynamic_cast<KisExternalLayer*>(csd->node.data())) {
394 externalLayer->projectionLeaf()->setTemporaryHiddenFromRendering(true);
395 m_hiddenProjectionLeaves.append(csd->node);
396 } else if (KisTransformMask *transformMask =
397 dynamic_cast<KisTransformMask*>(csd->node.data())) {
398
399 KisTransformMaskParamsInterfaceSP params = transformMask->transformParams();
400 params->setHidden(true);
401
403 new KisSimpleModifyTransformMaskCommand(transformMask,
404 params)),
407 }
408 } else if (cch) {
411 QPolygon hull = calculateConvexHull();
412 if (!hull.isEmpty()) {
413 Q_EMIT sigConvexHullCalculated(hull, this);
414 }
415 }
416 } else {
418 }
419}
420
422{
423 KisTransaction transaction(device);
424 if (m_selection) {
426 } else {
427 device->clear();
428 }
432}
433
447
449{
450 // Best effort attempt to calculate the convex hull, mimicking the
451 // approach that computes srcRect in initStrokeCallback below
452 QVector<QPoint> points;
453 if (m_selection) {
455 } else {
456 int numContributions = 0;
457 Q_FOREACH (KisNodeSP node, m_processedNodes) {
458 if (node->inherits("KisGroupLayer")) continue;
459
460 if (dynamic_cast<const KisTransformMask*>(node.data())) {
461 return QPolygon(); // Produce no convex hull if a KisTransformMask is present
462 } else {
463 KisPaintDeviceSP device;
464 if (KisExternalLayer *extLayer = dynamic_cast<KisExternalLayer*>(node.data())) {
465 device = extLayer->projection();
466 } else {
467 device = node->paintDevice();
468 }
469 if (device) {
470 KisPaintDeviceSP toUse;
471 // Use the original device to get the cached device containing the original image data
472 if (haveDeviceInCache(device)) {
473 toUse = getDeviceCache(device);
474 } else {
475 toUse = device;
476 }
477 /* This sometimes does not agree with the original exactBounds
478 because of colorspace changes between the original device
479 and cached. E.g. When the defaultPixel changes as follows it
480 triggers different behavior in calculateExactBounds:
481 KoColor ("ALPHA", "Alpha":0) => KoColor ("GRAYA", "Gray":0, "Alpha":255)
482 */
483 const bool isConvertedSelection =
484 node->paintDevice() &&
486 *toUse->colorSpace() == *node->paintDevice()->compositionSourceColorSpace();
487
488 QPolygon polygon = isConvertedSelection ?
491
492 points += polygon;
493 numContributions += 1;
494 } else {
495 // When can this happen? Should it continue instead?
496 ENTER_FUNCTION() << "Bailing out, device was null" << ppVar(node);
497 return QPolygon();
498 }
499 }
500 }
501 if (numContributions > 1) {
502 points = KisConvexHull::findConvexHull(points);
503 }
504 }
505 return QPolygon(points);
506}
507
509{
511
513
514 if (m_selection) {
515 m_selection->setVisible(false);
517 }
518
519 Q_FOREACH(KisNodeSP node, m_rootNodes) {
520 KisSelectionMaskSP overlaySelectionMask =
521 dynamic_cast<KisSelectionMask*>(node->graphListener()->graphOverlayNode());
522 if (overlaySelectionMask) {
523 overlaySelectionMask->setDecorationsVisible(false);
524 m_deactivatedOverlaySelectionMasks.append(overlaySelectionMask);
525 }
526 }
527
528 if (m_rootNodes.size() == 1){
529 KisNodeSP rootNode = m_rootNodes[0];
531
532 if (rootNode->inherits("KisTransformMask") && rootNode->projectionLeaf()->isDroppedNode()) {
533 rootNode.clear();
534 m_processedNodes.clear();
535
537 Q_EMIT sigTransactionGenerated(transaction, m_initialTransformArgs, this);
538 return;
539 }
540 }
541
542 ToolTransformArgs initialTransformArgs;
543 bool isExternalSourcePresent = false;
545
546 bool argsAreInitialized = false;
547 QVector<KisStrokeJobData *> lastCommandUndoJobs;
548
550 m_mode,
553 undoFacade(),
555 &lastCommandUndoJobs,
557 argsAreInitialized = true;
558 } else if (!m_forceReset && KisTransformUtils::tryInitArgsFromNode(m_rootNodes, &initialTransformArgs)) {
559 argsAreInitialized = true;
560 }
561
562 QVector<KisStrokeJobData *> extraInitJobs;
563
565
566 extraInitJobs << lastCommandUndoJobs;
567
568 KritaUtils::addJobSequential(extraInitJobs, [this]() {
569 // When dealing with animated transform mask layers, create keyframe and save the command for undo.
570 // NOTE: for transform masks we create a keyframe no matter what the user
571 // settings are
572 Q_FOREACH (KisNodeSP node, m_processedNodes) {
573 if (KisTransformMask* transformMask = dynamic_cast<KisTransformMask*>(node.data())) {
576 }
578 node->hasEditablePaintDevice()){
579
580 KUndo2Command *autoKeyframeCommand =
583 if (autoKeyframeCommand) {
585 }
586 }
587 }
588 });
589
590 KritaUtils::addJobSequential(extraInitJobs, [this]() {
594 Q_FOREACH(KisNodeSP node, m_rootNodes) {
596 }
597 });
598
599 KritaUtils::addJobBarrier(extraInitJobs, [this]() {
604 Q_FOREACH(KisNodeSP node, m_rootNodes) {
606 }
607 });
608
612 KritaUtils::addJobBarrier(extraInitJobs, [this]() {
613 Q_FOREACH (KisNodeSP node, m_processedNodes) {
614 KisDecoratedNodeInterface *decoratedNode = dynamic_cast<KisDecoratedNodeInterface*>(node.data());
615 if (decoratedNode && decoratedNode->decorationsVisible()) {
616 decoratedNode->setDecorationsVisible(false);
617 m_disabledDecoratedNodes << decoratedNode;
618 }
619 }
620 });
621
622 KritaUtils::addJobBarrier(extraInitJobs, [this, initialTransformArgs, argsAreInitialized]() mutable {
623 QRect srcRect;
624
625 if (m_selection) {
626 srcRect = m_selection->selectedExactRect();
627 } else {
628 srcRect = QRect();
629 Q_FOREACH (KisNodeSP node, m_processedNodes) {
630 // group layers may have a projection of layers
631 // that are locked and will not be transformed
632 if (node->inherits("KisGroupLayer")) continue;
633
634 if (const KisTransformMask *mask = dynamic_cast<const KisTransformMask*>(node.data())) {
635 srcRect |= mask->sourceDataBounds();
636 } else if (const KisSelectionMask *mask = dynamic_cast<const KisSelectionMask*>(node.data())) {
637 srcRect |= mask->selection()->selectedExactRect();
638 } else if (const KisTransparencyMask *mask = dynamic_cast<const KisTransparencyMask*>(node.data())) {
639 srcRect |= mask->selection()->selectedExactRect();
640 } else if (const KisFilterMask *mask = dynamic_cast<const KisFilterMask*>(node.data())) {
645 if (mask->paintDevice()) {
646 srcRect |= mask->paintDevice()->nonDefaultPixelArea();
647 }
648 } else {
651 srcRect |= node->paintDevice() ? node->paintDevice()->exactBounds() : node->exactBounds();
652 }
653 }
654 }
655
656 TransformTransactionProperties transaction(srcRect, &initialTransformArgs, m_rootNodes, m_processedNodes);
657 if (!argsAreInitialized) {
658 initialTransformArgs = KisTransformUtils::resetArgsForMode(m_mode, m_filterId, transaction, 0);
659 }
660
661 this->m_initialTransformArgs = initialTransformArgs;
662 if (transaction.currentConfig()->boundsRotation() != 0.0) {
664 transaction.setConvexHull(calculateConvexHull());
665 transaction.setConvexHullHasBeenRequested(true);
666 }
667 Q_EMIT this->sigTransactionGenerated(transaction, initialTransformArgs, this);
668 });
669
670 extraInitJobs << new PreparePreviewData();
671
673
674 KritaUtils::addJobBarrier(extraInitJobs, [this, sharedData]() {
676 Q_FOREACH (KisNodeSP root, filteredRoots) {
677 sharedData->addUpdate(root, root->projectionPlane()->tightUserVisibleBounds());
678 }
679 });
680
681 extraInitJobs << new Data(new KisUpdateCommandEx(sharedData, m_updatesFacade, KisUpdateCommandEx::INITIALIZING), false, Data::BARRIER);
682
683 Q_FOREACH (KisNodeSP node, m_processedNodes) {
684 extraInitJobs << new ClearSelectionData(node);
685 }
686
687 extraInitJobs << new Data(new KisUpdateCommandEx(sharedData, m_updatesFacade, KisUpdateCommandEx::FINALIZING), false, Data::BARRIER);
688
690 KritaUtils::addJobBarrier(extraInitJobs, [this]() {
691 Q_FOREACH (KisDecoratedNodeInterface *decoratedNode, m_disabledDecoratedNodes) {
692 decoratedNode->setDecorationsVisible(true);
693 }
695 });
696
698
699 if (!lastCommandUndoJobs.isEmpty()) {
701
702 for (auto it = extraInitJobs.begin(); it != extraInitJobs.end(); ++it) {
703 (*it)->setCancellable(false);
704 }
705 }
706
707 addMutatedJobs(extraInitJobs);
708}
709
711{
722 if (m_finalizingActionsStarted) return;
724
725 QVector<KisStrokeJobData *> mutatedJobs;
726
727 auto restoreTemporaryHiddenNodes = [this] () {
728 Q_FOREACH (KisNodeSP node, m_hiddenProjectionLeaves) {
729 node->projectionLeaf()->setTemporaryHiddenFromRendering(false);
730 if (KisDelayedUpdateNodeInterface *delayedNode = dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data())) {
731 delayedNode->forceUpdateTimedNode();
732 } else {
733 node->setDirty();
734 }
735 }
736 };
737
738 if (applyTransform) {
740
741 m_updateData.reset(new KisBatchNodeUpdate());
742
743 KritaUtils::addJobBarrier(mutatedJobs, [this] () {
745 m_updatesDisabled = true;
747 });
748
749 Q_FOREACH (KisNodeSP node, m_processedNodes) {
751 args,
752 node);
753 }
754 mutatedJobs << new TransformData(TransformData::SELECTION,
755 args,
756 m_rootNodes[0]);
757
758 KritaUtils::addJobBarrier(mutatedJobs, restoreTemporaryHiddenNodes);
759
760 KritaUtils::addJobBarrier(mutatedJobs, [this] () {
762 m_updatesDisabled = false;
763
764 m_updateData->compress();
766 });
767 } else {
768 KritaUtils::addJobBarrier(mutatedJobs, restoreTemporaryHiddenNodes);
769 }
770
771 KritaUtils::addJobBarrier(mutatedJobs, [this, applyTransform]() {
772 Q_FOREACH (KisSelectionSP selection, m_deactivatedSelections) {
773 selection->setVisible(true);
774 }
775
776 Q_FOREACH(KisSelectionMaskSP deactivatedOverlaySelectionMask, m_deactivatedOverlaySelectionMasks) {
777 deactivatedOverlaySelectionMask->selection()->setVisible(true);
778 deactivatedOverlaySelectionMask->setDirty();
779 }
780
781 if (applyTransform) {
783 } else {
785 }
786 });
787
788 for (auto it = mutatedJobs.begin(); it != mutatedJobs.end(); ++it) {
789 (*it)->setCancellable(false);
790 }
791
792 addMutatedJobs(mutatedJobs);
793}
794
804
KisDeleteLaterWrapper< T > * makeKisDeleteLaterWrapper(T value)
const KoID GrayAColorModelID("GRAYA", ki18n("Grayscale/Alpha"))
const KoID AlphaColorModelID("A", ki18n("Alpha mask"))
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
virtual bool decorationsVisible() const =0
virtual void setDecorationsVisible(bool value, bool update)=0
The KisDelayedUpdateNodeInterface class is an interface for nodes that delay their real updates with ...
QColor selectionOverlayMaskColor(bool defaultValue=false) const
void waitForDone()
void refreshGraphAsync(KisNodeSP root, const QVector< QRect > &rects, const QRect &cropRect, KisProjectionUpdateFlags flags=KisProjectionUpdateFlag::None) override
KisGroupLayerSP rootLayer() const
const KoColorSpace * colorSpace() const
KisImageAnimationInterface * animationInterface() const
KisPaintDeviceSP projection() const
QRect bounds() const override
void setRootLayer(KisGroupLayerSP rootLayer)
virtual void clear()
void setDefaultBounds(KisDefaultBoundsBaseSP bounds)
KisPaintDeviceSP createCompositionSourceDevice() const
virtual const KoColorSpace * compositionSourceColorSpace() const
QRect exactBounds() const
QRect extent() const
const KoColorSpace * colorSpace() const
void clearSelection(KisSelectionSP selection)
KisDefaultBoundsBaseSP defaultBounds() const
void convertTo(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent=KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags=KoColorConversionTransformation::internalConversionFlags(), KUndo2Command *parentCommand=nullptr, KoUpdater *progressUpdater=nullptr)
void setSelection(KisSelectionSP selection)
void bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
ALWAYS_INLINE quint8 * rawData()
ALWAYS_INLINE const quint8 * rawDataConst() const
virtual void postProcessToplevelCommand(KUndo2Command *command)
Applies some modifications (e.g. assigning extra data) to the toplevel command.
void runAndSaveCommand(KUndo2CommandSP command, KisStrokeJobData::Sequentiality sequentiality, KisStrokeJobData::Exclusivity exclusivity)
void doStrokeCallback(KisStrokeJobData *data) override
void addMutatedJobs(const QVector< KisStrokeJobData * > list)
KUndo2Command * endAndTake()
static void transformDevice(const ToolTransformArgs &config, KisPaintDeviceSP device, KisProcessingVisitor::ProgressHelper *helper)
static KisTransformWorker createTransformWorker(const ToolTransformArgs &config, KisPaintDeviceSP device, KoUpdaterPtr updater)
static ToolTransformArgs resetArgsForMode(ToolTransformArgs::TransformMode mode, const QString &filterId, const TransformTransactionProperties &transaction, KisPaintDeviceSP externalSource)
static int fetchCurrentImageTime(KisNodeList rootNodes)
static void postProcessToplevelCommand(KUndo2Command *command, const ToolTransformArgs &args, KisNodeList rootNodes, KisNodeList processedNodes, int currentTime, const KisSavedMacroCommand *overriddenCommand)
static QList< KisNodeSP > fetchNodesList(ToolTransformArgs::TransformMode mode, KisNodeList rootNodes, bool isExternalSourcePresent, KisSelectionSP selection)
static KisNodeSP tryOverrideRootToTransformMask(KisNodeSP root)
static void transformAndMergeDevice(const ToolTransformArgs &config, KisPaintDeviceSP src, KisPaintDeviceSP dst, KisProcessingVisitor::ProgressHelper *helper)
static bool tryInitArgsFromNode(KisNodeList rootNodes, ToolTransformArgs *args)
static bool tryFetchArgsFromCommandAndUndo(ToolTransformArgs *outArgs, ToolTransformArgs::TransformMode mode, KisNodeList currentNodes, KisNodeList selectedNodes, KisStrokeUndoFacade *undoFacade, int currentTime, QVector< KisStrokeJobData * > *undoJobs, const KisSavedMacroCommand **overriddenCommand)
virtual void enableDirtyRequests()=0
virtual void disableDirtyRequests()=0
virtual quint32 pixelSize() const =0
virtual KoID colorModelId() const =0
virtual KoID colorDepthId() const =0
void setOpacity(quint8 alpha)
Definition KoColor.cpp:333
quint8 * data()
Definition KoColor.h:144
QString id() const
Definition KoID.cpp:63
double boundsRotation() const
TransformMode mode() const
TransformStrokeStrategy(ToolTransformArgs::TransformMode mode, const QString &filterId, bool forceReset, KisNodeList rootNodes, KisSelectionSP selection, KisStrokeUndoFacade *undoFacade, KisUpdatesFacade *updatesFacade)
const KisSavedMacroCommand * m_overriddenCommand
void postProcessToplevelCommand(KUndo2Command *command) override
Applies some modifications (e.g. assigning extra data) to the toplevel command.
QVector< KisDecoratedNodeInterface * > m_disabledDecoratedNodes
void sigTransactionGenerated(TransformTransactionProperties transaction, ToolTransformArgs args, void *cookie)
QHash< KisPaintDevice *, KisPaintDeviceSP > m_devicesCacheHash
void sigPreviewDeviceReady(KisPaintDeviceSP device)
void doStrokeCallback(KisStrokeJobData *data) override
KisPaintDeviceSP getDeviceCache(KisPaintDeviceSP src)
boost::optional< ToolTransformArgs > m_savedTransformArgs
void sigConvexHullCalculated(QPolygon convexHull, void *cookie)
KisPaintDeviceSP createDeviceCache(KisPaintDeviceSP src)
void clearSelection(KisPaintDeviceSP device)
bool checkBelongsToSelection(KisPaintDeviceSP device) const
bool haveDeviceInCache(KisPaintDeviceSP src)
void finishStrokeImpl(bool applyTransform, const ToolTransformArgs &args)
void putDeviceCache(KisPaintDeviceSP src, KisPaintDeviceSP cache)
QList< KisSelectionMaskSP > m_deactivatedOverlaySelectionMasks
QList< KisNodeSP > m_hiddenProjectionLeaves
ToolTransformArgs::TransformMode m_mode
QList< KisSelectionSP > m_deactivatedSelections
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define bounds(x, a, b)
#define warnKrita
Definition kis_debug.h:87
#define ENTER_FUNCTION()
Definition kis_debug.h:178
#define ppVar(var)
Definition kis_debug.h:155
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
QSharedPointer< T > toQShared(T *ptr)
QSharedPointer< KisTransformMaskParamsInterface > KisTransformMaskParamsInterfaceSP
QSharedPointer< KUndo2Command > KUndo2CommandSP
Definition kis_types.h:262
KisSharedPtr< KisNode > KisNodeSP
Definition kis_types.h:86
KUndo2MagicString kundo2_i18n(const char *text)
KUndo2Command * tryAutoCreateDuplicatedFrame(KisPaintDeviceSP device, AutoCreateKeyframeFlags flags)
create a new duplicated keyframe if auto-keyframe mode is on
QPolygon findConvexHullSelectionLike(KisPaintDeviceSP device)
QPolygon findConvexHull(const QVector< QPoint > &points)
KisNodeList sortAndFilterMergeableInternalNodes(KisNodeList nodes, bool allowMasks)
void recursiveApplyNodes(NodePointer node, Functor func)
void forceAllHiddenOriginalsUpdate(KisNodeSP root)
void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea)
void forceAllDelayedNodesUpdate(KisNodeSP root)
void addJobSequential(QVector< Job * > &jobs, Func func)
void addJobBarrier(QVector< Job * > &jobs, Func func)
virtual KisPaintDeviceSP projection() const =0
virtual QRect exactBounds() const
KisImageWSP image
virtual QRect extent() const
virtual KisPaintDeviceSP paintDevice() const =0
bool hasEditablePaintDevice() const
void setPassThroughMode(bool value)
KisSelectionSP selection
Definition kis_mask.cc:44
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
virtual KisNode * graphOverlayNode() const
virtual KisAbstractProjectionPlaneSP projectionPlane() const
Definition kis_node.cpp:240
KisProjectionLeafSP projectionLeaf
Definition kis_node.cpp:93
virtual KisNodeSP clone() const =0
quint32 childCount() const
Definition kis_node.cpp:414
KisNodeGraphListener * graphListener
Definition kis_node.cpp:87
virtual void setDirty()
Definition kis_node.cpp:577
void setDirty(const QVector< QRect > &rects) override
void setDecorationsVisible(bool value, bool update) override
KisPixelSelectionSP projection() const
KisPixelSelectionSP pixelSelection
void setVisible(bool visible)
QRect selectedExactRect() const
Slow, but exact way of determining the rectangle that encloses the selection.
static KoColorSpaceRegistry * instance()