10#include <QMutexLocker>
11#include <QElapsedTimer>
16#include <klocalizedstring.h>
50#include <boost/optional.hpp>
144 const QString &filterId,
158 m_d->filterId = filterId;
159 m_d->forceReset = forceReset;
160 m_d->rootNodes = rootNodes;
161 m_d->selection = selection;
162 m_d->externalSource = externalSource;
163 m_d->updatesFacade = updatesFacade;
165 m_d->imageRoot = imageRoot;
166 m_d->forceLodMode = forceLodMode;
167 m_d->commandUpdatesBlockerCookie.reset(
new boost::none_t(boost::none));
188 m_d->pendingUpdateArgs = upd->args;
190 }
else if (
m_d->selection) {
199 m_d->selection->pixelSelection(), &helper);
216 if (!
m_d->convexHullHasBeenCalculated) {
217 m_d->convexHullHasBeenCalculated =
true;
219 if (!hull.isEmpty()) {
232 if (!
m_d->pendingUpdateArgs)
return;
235 (
m_d->updateTimer.elapsed() >
m_d->updateInterval &&
236 !
m_d->updatesFacade->hasUpdatesRunning())) {
244 if (!
m_d->pendingUpdateArgs)
return;
247 (
m_d->updateTimer.elapsed() <
m_d->updateInterval ||
248 m_d->updatesFacade->hasUpdatesRunning())) {
256 m_d->pendingUpdateArgs = boost::none;
261 m_d->currentTransformArgs = args;
262 m_d->updateTimer.restart();
276 const int maxSize = 2000;
279 const qreal zoom = qMax(1.0, qreal(maxDimension) / maxSize);
281 const int calculatedLod = qCeil(std::log2(zoom));
291 m_d->currentTransformArgs,
295 m_d->overriddenCommand);
305 m_d->externalSource ?
m_d->externalSource :
306 m_d->initialTransformArgs.externalSource() ?
307 m_d->initialTransformArgs.externalSource() : 0;
310 if (externalSource) {
312 }
else if (
m_d->selection) {
315 int numContributions = 0;
317 if (node->inherits(
"KisGroupLayer"))
continue;
325 device = extLayer->projection();
333 QMutexLocker l(&
m_d->devicesCacheMutex);
334 if (
m_d->devicesCacheHash.contains(device.
data())) {
335 toUse =
m_d->devicesCacheHash[device.
data()];
346 const bool isConvertedSelection =
352 QPolygon polygon = isConvertedSelection ?
358 numContributions += 1;
366 if (numContributions > 1) {
370 return QPolygon(points);
382 if (
m_d->selection) {
383 m_d->selection->setVisible(
false);
384 m_d->deactivatedSelections.append(
m_d->selection);
390 if (overlaySelectionMask && node !=
KisNodeSP(overlaySelectionMask)) {
392 m_d->deactivatedOverlaySelectionMasks.append(overlaySelectionMask);
396 if (
m_d->rootNodes.size() == 1) {
400 if (rootNode->inherits(
"KisTransformMask") && rootNode->
projectionLeaf()->isDroppedNode()) {
402 m_d->processedNodes.clear();
413 bool argsAreInitialized =
false;
421 if (!
m_d->forceReset && !
m_d->externalSource) {
428 &lastCommandUndoJobs,
429 &
m_d->overriddenCommand)) {
430 argsAreInitialized =
true;
432 argsAreInitialized =
true;
438 m_d->prevDirtyRects.addUpdate(node, node->
projectionPlane()->tightUserVisibleBounds());
441 m_d->initialUpdatesBeforeClear =
m_d->prevDirtyRects.compressed();
448 extraInitJobs << lastCommandUndoJobs;
450 if (!lastCommandUndoJobs.isEmpty()) {
465 *
m_d->updateDataForUndo =
m_d->initialUpdatesBeforeClear;
472 m_d->pendingUpdateArgs =
m_d->initialTransformArgs;
491 if (autoKeyframeCommand) {
525 m_d->disabledDecoratedNodes << decoratedNode;
532 argsAreInitialized]()
mutable {
536 m_d->externalSource ?
m_d->externalSource :
537 (argsAreInitialized &&
m_d->initialTransformArgs.externalSource()) ?
538 m_d->initialTransformArgs.externalSource() : 0;
540 if (externalSource) {
543 }
else if (
m_d->selection) {
544 srcRect =
m_d->selection->selectedExactRect();
550 if (node->inherits(
"KisGroupLayer"))
continue;
553 srcRect |= mask->sourceDataBounds();
555 srcRect |= mask->selection()->selectedExactRect();
557 srcRect |= mask->selection()->selectedExactRect();
563 if (mask->paintDevice()) {
564 srcRect |= mask->paintDevice()->nonDefaultPixelArea();
575 if (!argsAreInitialized) {
578 m_d->externalSource.clear();
580 const QRect imageBoundsRect =
m_d->imageRoot->projection()->defaultBounds()->bounds();
583 if (
m_d->previewLevelOfDetail > 0) {
584 for (
auto it =
m_d->prevDirtyRects.begin(); it !=
m_d->prevDirtyRects.end(); ++it) {
586 m_d->prevDirtyPreviewRects.addUpdate(it->first, t.
map(it->second));
591 m_d->convexHullHasBeenCalculated =
true;
604 m_d->disabledDecoratedNodes.clear();
614 QMutexLocker l(&
m_d->dirtyRectsMutex);
618 m_d->updateTimer.start();
621 if (!lastCommandUndoJobs.isEmpty()) {
624 for (
auto it = extraInitJobs.begin(); it != extraInitJobs.end(); ++it) {
625 (*it)->setCancellable(
false);
630 if (
m_d->previewLevelOfDetail > 0) {
631 QVector<KisStrokeJobData*> lodSyncJobs;
633 KisSyncLodCacheStrokeStrategy::createJobsData(lodSyncJobs,
636 m_d->previewLevelOfDetail,
637 m_d->devicesCacheHash.values() +
638 m_d->transformMaskCacheHash.values());
640 for (auto it = lodSyncJobs.begin(); it != lodSyncJobs.end(); ++it) {
641 (*it)->setLevelOfDetailOverride(m_d->previewLevelOfDetail);
648 addMutatedJobs(extraInitJobs);
657 if (!mutatedJobs.isEmpty()) {
668 if (!mutatedJobs.isEmpty()) {
690 QMutexLocker l(&
m_d->commandsMutex);
693 m_d->commands.append({group, sharedCommand, seq});
698 for (
auto it =
m_d->commands.begin(); it !=
m_d->commands.end(); ++it) {
699 if (it->commandGroup ==
Clear) {
706 for (
auto it =
m_d->commands.begin(); it !=
m_d->commands.end(); ++it) {
715 for (
auto it = std::make_reverse_iterator(
m_d->commands.end());
716 it != std::make_reverse_iterator(
m_d->commands.begin());
722 m_d->commands.clear();
727 for (
auto it = std::make_reverse_iterator(
m_d->commands.end());
728 it != std::make_reverse_iterator(
m_d->commands.begin());) {
730 if ((levelOfDetail > 0 &&
732 (levelOfDetail <= 0 &&
736 it = std::make_reverse_iterator(
m_d->commands.erase(std::next(it).base()));
748 *updateData = (prevDirtyRects | dirtyRects).compressed();
752 if (levelOfDetail > 0) {
754 for (
auto it = savedUndoRects.begin(); it != savedUndoRects.end(); ++it) {
759 *
m_d->updateDataForUndo = (
m_d->initialUpdatesBeforeClear | savedUndoRects).compressed();
760 prevDirtyRects.clear();
761 dirtyRects.swap(prevDirtyRects);
776 extLayer->supportsPerspectiveTransform())) {
778 if (levelOfDetail <= 0) {
779 const QRect oldDirtyRect = extLayer->projectionPlane()->tightUserVisibleBounds() | extLayer->theoreticalBoundingRect();
782 QTransform t = w.transform();
790 shapeLayer->forceUpdateTimedNode();
798 const QRect theoreticalNewDirtyRect =
803 theoreticalNewDirtyRect |
804 extLayer->projectionPlane()->tightUserVisibleBounds() |
805 extLayer->theoreticalBoundingRect(), 0);
821 QMutexLocker l(&
m_d->devicesCacheMutex);
822 cachedPortion =
m_d->devicesCacheHash[device.
data()];
839 const QRect oldDirtyRect = transformMask->extent();
841 if (levelOfDetail > 0) {
845 QMutexLocker l(&
m_d->devicesCacheMutex);
846 cachedPortion =
m_d->transformMaskCacheHash[transformMask];
858 transformMask->overrideStaticCacheDevice(dst);
865 m_d->commandUpdatesBlockerCookie);
867 addDirtyRect(node, oldDirtyRect | transformMask->extent(), levelOfDetail);
883 extLayer->supportsPerspectiveTransform())) {
892 QMutexLocker l(&
m_d->devicesCacheMutex);
893 if (!
m_d->transformMaskCacheHash.contains(mask)) {
908 QMutexLocker l(&
m_d->devicesCacheMutex);
910 if (!
m_d->devicesCacheHash.contains(device.
data())) {
915 if (
m_d->initialTransformArgs.externalSource()) {
917 }
else if (
m_d->selection) {
918 QRect srcRect =
m_d->selection->selectedExactRect();
923 gc.
bitBlt(srcRect.topLeft(), device, srcRect);
928 m_d->devicesCacheHash.insert(device.
data(), cache);
933 if (
m_d->initialTransformArgs.externalSource())
return;
936 if (
m_d->selection) {
939 QRect oldExtent = device->
extent();
953 if (levelOfDetail > 0) {
963 [
this, args, levelOfDetail, updateData, useHoldUI, commandGroup]() {
977 [
this, node, args, levelOfDetail]() {
983 [
this, levelOfDetail, updateData, useHoldUI, commandGroup]() {
999 for (
auto it = updateData->begin(); it != updateData->end(); ++it) {
1002 if (transformMask &&
1003 ((levelOfDetail <= 0 && !transformMask->transformParams()->isAffine()) ||
1004 (levelOfDetail <= 0 &&
m_d->previewLevelOfDetail > 0))) {
1008 m_d->updatesFacade->refreshGraphAsync(it->first, it->second);
1022 Q_FOREACH(
KisSelectionMaskSP deactivatedOverlaySelectionMask,
m_d->deactivatedOverlaySelectionMasks) {
1024 deactivatedOverlaySelectionMask->
setDirty();
1027 m_d->commandUpdatesBlockerCookie.reset();
1034 m_d->commands.clear();
1045 Q_FOREACH (
const QRect &rc, finalDirtyRects) {
1061 if (
m_d->currentTransformArgs.isUnchanging() &&
1062 m_d->transformMaskCacheHash.isEmpty() &&
1063 !
m_d->overriddenCommand) {
1069 if (
m_d->previewLevelOfDetail > 0) {
1078 if (!
m_d->transformMaskCacheHash.isEmpty()) {
1104 if (
m_d->pendingUpdateArgs) {
1114 m_d->strokeCompletionHasBeenStarted =
true;
1124 for (
auto it = nonCancellableFinishJobs.begin(); it != nonCancellableFinishJobs.end(); ++it) {
1125 (*it)->setCancellable(
false);
1140 if (
m_d->strokeCompletionHasBeenStarted)
return;
1144 (
m_d->transformMaskCacheHash.size() == 1 &&
m_d->processedNodes.size() == 1));
1146 const bool isChangingTransformMask = !
m_d->transformMaskCacheHash.isEmpty();
1148 if (
m_d->initialTransformArgs.isIdentity()) {
1157 m_d->commandUpdatesBlockerCookie.reset();
1162 if (
m_d->previewLevelOfDetail > 0) {
1194 if (
m_d->previewLevelOfDetail > 0) {
1215 if (
m_d->overriddenCommand) {
1217 m_d->currentTransformArgs =
m_d->initialTransformArgs;
1229 QMutexLocker l(&
m_d->dirtyRectsMutex);
1230 m_d->effectiveDirtyRects(levelOfDetail).addUpdate(node,
rect);
const KoID AlphaColorModelID("A", ki18n("Alpha mask"))
KisBatchNodeUpdate compressed() const
void addUpdate(KisNodeSP node, const QRect &rc)
virtual bool decorationsVisible() const =0
virtual void setDecorationsVisible(bool value, bool update)=0
void setDirty(const QRect &rc)
KisPaintDeviceSP createCompositionSourceDevice() const
virtual const KoColorSpace * compositionSourceColorSpace() const
QRect exactBounds() const
const KoColorSpace * colorSpace() const
void clearSelection(KisSelectionSP selection)
void prepareClone(KisPaintDeviceSP src)
void setSelection(KisSelectionSP selection)
void bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
virtual void postProcessToplevelCommand(KUndo2Command *command)
Applies some modifications (e.g. assigning extra data) to the toplevel command.
void setMacroId(int value)
void cancelStrokeCallback() override
void initStrokeCallback() override
void notifyCommandDone(KUndo2CommandSP command, KisStrokeJobData::Sequentiality sequentiality, KisStrokeJobData::Exclusivity exclusivity)
void runAndSaveCommand(KUndo2CommandSP command, KisStrokeJobData::Sequentiality sequentiality, KisStrokeJobData::Exclusivity exclusivity)
void finishStrokeCallback() override
void doStrokeCallback(KisStrokeJobData *data) override
KisStrokeUndoFacade * undoFacade() const
void executeCommand(KUndo2CommandSP command, bool undo)
KisLodPreferences currentLodPreferences() const
void addMutatedJobs(const QVector< KisStrokeJobData * > list)
void addMutatedJob(KisStrokeJobData *data)
void setNeedsExplicitCancel(bool value)
KUndo2Command * endAndTake()
virtual void notifyUIUpdateCompleted(const QRect &rc)=0
virtual KoID colorModelId() const =0
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
T kisGrowRect(const T &rect, U offset)
QSharedPointer< T > toQShared(T *ptr)
QSharedPointer< KUndo2Command > KUndo2CommandSP
KisSharedPtr< KisNode > KisNodeSP
KUndo2MagicString kundo2_i18n(const char *text)
auto maxDimension(Size size) -> decltype(size.width())
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)
void forceAllHiddenOriginalsUpdate(KisNodeSP root)
void forceAllDelayedNodesUpdate(KisNodeSP root)
void addJobConcurrent(QVector< Job * > &jobs, Func func)
void addJobSequential(QVector< Job * > &jobs, Func func)
void addJobBarrier(QVector< Job * > &jobs, Func func)
QVector< QRect > splitRectIntoPatchesTight(const QRect &rc, const QSize &patchSize)
virtual KisPaintDeviceSP projection() const =0
virtual QRect exactBounds() const
virtual KisPaintDeviceSP paintDevice() const =0
bool hasEditablePaintDevice() const
int desiredLevelOfDetail() const
bool lodSupported() const
bool lodPreferred() const
virtual KisNode * graphOverlayNode() const
virtual KisAbstractProjectionPlaneSP projectionPlane() const
KisProjectionLeafSP projectionLeaf
KisNodeGraphListener * graphListener
void setDirty(const QVector< QRect > &rects) override
void setDecorationsVisible(bool value, bool update) override
void setVisible(bool visible)