File indexing completed on 2024-05-19 04:26:38

0001 /*
0002  *  SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
0003  *
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include <KoIcon.h>
0009 #include <kis_icon.h>
0010 #include <KoCompositeOpRegistry.h>
0011 
0012 #include "kis_layer.h"
0013 #include "kis_transform_mask.h"
0014 #include "filter/kis_filter.h"
0015 #include "filter/kis_filter_configuration.h"
0016 #include "filter/kis_filter_registry.h"
0017 #include "kis_selection.h"
0018 #include "kis_processing_information.h"
0019 #include "kis_node.h"
0020 #include "kis_node_visitor.h"
0021 #include "kis_processing_visitor.h"
0022 #include "kis_node_progress_proxy.h"
0023 #include "kis_painter.h"
0024 
0025 #include "kis_busy_progress_indicator.h"
0026 #include "kis_perspectivetransform_worker.h"
0027 #include "kis_transform_mask_params_interface.h"
0028 #include "kis_transform_mask_params_factory_registry.h"
0029 #include "kis_recalculate_transform_mask_job.h"
0030 #include "kis_thread_safe_signal_compressor.h"
0031 #include "kis_algebra_2d.h"
0032 #include "kis_safe_transform.h"
0033 #include "kis_keyframe_channel.h"
0034 #include "kis_raster_keyframe_channel.h"
0035 #include "kis_scalar_keyframe_channel.h"
0036 
0037 #include "kis_image_config.h"
0038 #include "kis_lod_capable_layer_offset.h"
0039 
0040 #include <QReadWriteLock>
0041 #include "KisTransformMaskTestingInterface.h"
0042 
0043 //#include "kis_paint_device_debug_utils.h"
0044 //#define DEBUG_RENDERING
0045 //#define DUMP_RECT QRect(0,0,512,512)
0046 
0047 namespace {
0048 
0049 class StaticCacheStorage
0050 {
0051 public:
0052     bool isCacheValid(KisTransformMaskParamsInterfaceSP currentParams) const {
0053         QReadLocker l(&m_lock);
0054 
0055         KIS_SAFE_ASSERT_RECOVER_NOOP(!staticCacheValid ||
0056                                      paramsForStaticImage ||
0057                                      staticCacheIsOverridden);
0058 
0059         return staticCacheValid &&
0060             (!paramsForStaticImage ||
0061              paramsForStaticImage->compareTransform(currentParams));
0062     }
0063 
0064     bool isCacheOverridden() const {
0065         QReadLocker l(&m_lock);
0066 
0067         KIS_SAFE_ASSERT_RECOVER_NOOP(!staticCacheIsOverridden || staticCacheValid);
0068 
0069         return staticCacheIsOverridden;
0070     }
0071 
0072     void lazyAllocateStaticCache(const KoColorSpace *cs, KisDefaultBoundsBaseSP defaultBounds) {
0073         QWriteLocker l(&m_lock);
0074 
0075         if (!staticCacheDevice ||
0076             *staticCacheDevice->colorSpace() != *cs) {
0077 
0078             staticCacheDevice = new KisPaintDevice(cs);
0079             staticCacheDevice->setDefaultBounds(defaultBounds);
0080         }
0081     }
0082 
0083     KisPaintDeviceSP device() {
0084         return staticCacheDevice;
0085     }
0086 
0087     void setDeviceCacheValid(KisTransformMaskParamsInterfaceSP currentParams) {
0088         QWriteLocker l(&m_lock);
0089 
0090         paramsForStaticImage = currentParams;
0091         staticCacheValid = true;
0092         KIS_SAFE_ASSERT_RECOVER_NOOP(!staticCacheIsOverridden);
0093     }
0094 
0095     void overrideStaticCacheDevice(KisPaintDeviceSP device)
0096     {
0097         KIS_SAFE_ASSERT_RECOVER_RETURN(staticCacheDevice);
0098 
0099         staticCacheDevice->clear();
0100 
0101         if (device) {
0102             const QRect rc = device->extent();
0103             KisPainter::copyAreaOptimized(rc.topLeft(), device, staticCacheDevice, rc);
0104         }
0105 
0106         {
0107             QWriteLocker l(&m_lock);
0108             paramsForStaticImage.clear();
0109             staticCacheValid = bool(device);
0110             staticCacheIsOverridden = bool(device);
0111         }
0112     }
0113 
0114     void invalidateDeviceCache() {
0115         staticCacheValid = false;
0116         paramsForStaticImage.clear();
0117         KIS_SAFE_ASSERT_RECOVER_NOOP(!staticCacheIsOverridden);
0118     }
0119 
0120 private:
0121     mutable QReadWriteLock m_lock;
0122     bool staticCacheIsOverridden {false};
0123     bool staticCacheValid = {false};
0124     KisPaintDeviceSP staticCacheDevice;
0125     KisTransformMaskParamsInterfaceSP paramsForStaticImage;
0126 };
0127 
0128 struct AccumulatedRectStorage
0129 {
0130     void addRect(const QRect &rc) {
0131         QMutexLocker l(&m_mutex);
0132         m_rect |= rc;
0133     }
0134 
0135     QRect takeRect() {
0136         QMutexLocker l(&m_mutex);
0137 
0138         const QRect rect = m_rect;
0139         m_rect = QRect();
0140 
0141         return rect;
0142     }
0143 
0144 private:
0145     QMutex m_mutex;
0146     QRect m_rect;
0147 };
0148 
0149 }
0150 
0151 #define UPDATE_DELAY 3000 /*ms */
0152 
0153 struct Q_DECL_HIDDEN KisTransformMask::Private
0154 {
0155     Private(KisImageSP image)
0156         : worker(0, QTransform(), true, 0),
0157           paramsHolder(KisTransformMaskParamsFactoryRegistry::instance()->createAnimatedParamsHolder(new KisDefaultBounds(image))),
0158           recalculatingStaticImage(false),
0159           offset(new KisDefaultBounds(image)),
0160           updateSignalCompressor(UPDATE_DELAY, KisSignalCompressor::POSTPONE),
0161           offBoundsReadArea(0.5)
0162     {
0163     }
0164 
0165     Private(const Private &rhs)
0166         : worker(rhs.worker),
0167           paramsHolder(rhs.paramsHolder->clone()),
0168           recalculatingStaticImage(rhs.recalculatingStaticImage),
0169           offset(rhs.offset),
0170           updateSignalCompressor(UPDATE_DELAY, KisSignalCompressor::POSTPONE),
0171           offBoundsReadArea(rhs.offBoundsReadArea)
0172     {
0173     }
0174 
0175     KisPerspectiveTransformWorker worker;
0176     KisAnimatedTransformParamsHolderInterfaceSP paramsHolder;
0177 
0178     StaticCacheStorage staticCache;
0179     bool recalculatingStaticImage;
0180 
0181     AccumulatedRectStorage forcedStaticUpdateExtraUpdateRect;
0182 
0183     KisLodCapableLayerOffset offset;
0184 
0185     KisThreadSafeSignalCompressor updateSignalCompressor;
0186     qreal offBoundsReadArea;
0187 
0188     QScopedPointer<KisTransformMaskTestingInterface> testingInterface;
0189 };
0190 
0191 
0192 KisTransformMask::KisTransformMask(KisImageWSP image, const QString &name)
0193     : KisEffectMask(image, name),
0194       m_d(new Private(image))
0195 {
0196     connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate()));
0197     connect(this, SIGNAL(sigInternalForceStaticImageUpdate()), SLOT(slotInternalForceStaticImageUpdate()));
0198     m_d->offBoundsReadArea = KisImageConfig(true).transformMaskOffBoundsReadArea();
0199     setSupportsLodMoves(false);
0200 }
0201 
0202 KisTransformMask::~KisTransformMask()
0203 {
0204 }
0205 
0206 KisTransformMask::KisTransformMask(const KisTransformMask& rhs)
0207     : KisEffectMask(rhs),
0208       m_d(new Private(*rhs.m_d))
0209 {
0210     connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate()));
0211 
0212 
0213     /**
0214      * The channels has already been cloned inside the params object, just
0215      * relink them to the node
0216      */
0217     const QVector<QString> ids = {KisKeyframeChannel::PositionX.id(),
0218                                   KisKeyframeChannel::PositionY.id(),
0219                                   KisKeyframeChannel::ScaleX.id(),
0220                                   KisKeyframeChannel::ScaleY.id(),
0221                                   KisKeyframeChannel::ShearX.id(),
0222                                   KisKeyframeChannel::ShearY.id(),
0223                                   KisKeyframeChannel::RotationX.id(),
0224                                   KisKeyframeChannel::RotationY.id(),
0225                                   KisKeyframeChannel::RotationZ.id()};
0226 
0227     Q_FOREACH (const QString &id, ids) {
0228         KisKeyframeChannel *channel = m_d->paramsHolder->getKeyframeChannel(id);
0229         if (channel) {
0230             addKeyframeChannel(channel);
0231         }
0232     }
0233 }
0234 
0235 KisPaintDeviceSP KisTransformMask::paintDevice() const
0236 {
0237     return 0;
0238 }
0239 
0240 QIcon KisTransformMask::icon() const
0241 {
0242     return KisIconUtils::loadIcon("transformMask");
0243 }
0244 
0245 void KisTransformMask::setTransformParamsWithUndo(KisTransformMaskParamsInterfaceSP params, KUndo2Command *parentCommand)
0246 {
0247     KIS_SAFE_ASSERT_RECOVER_RETURN(params);
0248 
0249     m_d->paramsHolder->setParamsAtCurrentPosition(params.data(), parentCommand);
0250 }
0251 
0252 void KisTransformMask::setTransformParams(KisTransformMaskParamsInterfaceSP params)
0253 {
0254     KUndo2Command todo_REMOVE;
0255     setTransformParamsWithUndo(params, &todo_REMOVE);
0256     todo_REMOVE.redo();
0257 
0258     m_d->staticCache.invalidateDeviceCache();
0259     m_d->updateSignalCompressor.start();
0260 }
0261 
0262 KisTransformMaskParamsInterfaceSP KisTransformMask::transformParams() const
0263 {
0264     return m_d->paramsHolder->bakeIntoParams();
0265 }
0266 
0267 void KisTransformMask::slotDelayedStaticUpdate()
0268 {
0269     if (m_d->testingInterface) {
0270         m_d->testingInterface->notifySlotDelayedStaticUpdate();
0271     }
0272 
0273     startAsyncRegenerationJob();
0274 }
0275 
0276 void KisTransformMask::forceStartAsyncRegenerationJob()
0277 {
0278     m_d->staticCache.invalidateDeviceCache();
0279     m_d->updateSignalCompressor.stop();
0280     startAsyncRegenerationJob();
0281 }
0282 
0283 void KisTransformMask::startAsyncRegenerationJob()
0284 {
0285     /**
0286      * The mask might have been deleted from the layers stack in the
0287      * meanwhile. Just ignore the updates in the case.
0288      */
0289 
0290     KisLayerSP parentLayer = qobject_cast<KisLayer*>(parent().data());
0291     if (!parentLayer) return;
0292 
0293     KisImageSP image = parentLayer->image();
0294 
0295     if (!image) {
0296         return;
0297     }
0298 
0299     /**
0300      * Don't try to start a regeneration stroke while image
0301      * is locked. It may happen on loading, when all necessary
0302      * conversions are not yet finished.
0303      */
0304     if (image->locked()) {
0305         m_d->updateSignalCompressor.start();
0306         return;
0307     }
0308 
0309 
0310     const QRect extraUpdateRect = m_d->forcedStaticUpdateExtraUpdateRect.takeRect();
0311     image->addSpontaneousJob(new KisRecalculateTransformMaskJob(this, extraUpdateRect));
0312 }
0313 
0314 KisPaintDeviceSP KisTransformMask::buildPreviewDevice()
0315 {
0316     /**
0317      * Note: this function must be called from within the scheduler's
0318      * context. We are accessing parent's updateProjection(), which
0319      * is not entirely safe. The calling job must ensure it is the
0320      * only job running.
0321      */
0322 
0323     KisLayerSP parentLayer = qobject_cast<KisLayer*>(parent().data());
0324     KIS_ASSERT_RECOVER(parentLayer) { return new KisPaintDevice(colorSpace()); }
0325 
0326     KisPaintDeviceSP device =
0327         new KisPaintDevice(parentLayer->original()->colorSpace());
0328     device->setDefaultBounds(parentLayer->original()->defaultBounds());
0329 
0330     QRect requestedRect = parentLayer->original()->exactBounds();
0331     parentLayer->buildProjectionUpToNode(device, this, requestedRect);
0332 
0333     return device;
0334 }
0335 
0336 KisPaintDeviceSP KisTransformMask::buildSourcePreviewDevice()
0337 {
0338     /**
0339      * Note: this function must be called from within the scheduler's
0340      * context. We are accessing parent's updateProjection(), which
0341      * is not entirely safe. The calling job must ensure it is the
0342      * only job running.
0343      */
0344 
0345     KisLayerSP parentLayer = qobject_cast<KisLayer*>(parent().data());
0346     KIS_ASSERT_RECOVER(parentLayer) { return new KisPaintDevice(colorSpace()); }
0347 
0348     KisPaintDeviceSP device =
0349         new KisPaintDevice(parentLayer->original()->colorSpace());
0350     device->setDefaultBounds(parentLayer->original()->defaultBounds());
0351 
0352     QRect requestedRect = parentLayer->original()->exactBounds();
0353 
0354     KisNodeSP prevSibling = this->prevSibling();
0355     if (prevSibling) {
0356         parentLayer->buildProjectionUpToNode(device, prevSibling, requestedRect);
0357     } else {
0358         requestedRect = parentLayer->outgoingChangeRect(requestedRect);
0359         parentLayer->copyOriginalToProjection(parentLayer->original(), device, requestedRect);
0360     }
0361 
0362     return device;
0363 }
0364 
0365 void KisTransformMask::overrideStaticCacheDevice(KisPaintDeviceSP device)
0366 {
0367     // TODO: make sure the device is allocated
0368     m_d->staticCache.overrideStaticCacheDevice(device);
0369 }
0370 
0371 bool KisTransformMask::staticImageCacheIsValid() const
0372 {
0373     return m_d->staticCache.isCacheValid(m_d->paramsHolder->bakeIntoParams());
0374 }
0375 
0376 void KisTransformMask::recalculateStaticImage()
0377 {
0378     if (m_d->testingInterface) {
0379         m_d->testingInterface->notifyRecalculateStaticImage();
0380     }
0381 
0382     /**
0383      * Note: this function must be called from within the scheduler's
0384      * context. We are accessing parent's updateProjection(), which
0385      * is not entirely safe.
0386      */
0387 
0388     KisLayerSP parentLayer = qobject_cast<KisLayer*>(parent().data());
0389     KIS_SAFE_ASSERT_RECOVER_RETURN(parentLayer);
0390 
0391     // It might happen that the mask became invisible in the meantime
0392     // and the projection has become disabled. That mush be "impossible"
0393     // situation, hence assert.
0394     KIS_SAFE_ASSERT_RECOVER_RETURN(parentLayer->projection() != parentLayer->paintDevice());
0395 
0396     m_d->staticCache.lazyAllocateStaticCache(parentLayer->original()->colorSpace(),
0397                                              parentLayer->original()->defaultBounds());
0398 
0399     m_d->recalculatingStaticImage = true;
0400     /**
0401      * updateProjection() is assuming that the requestedRect takes
0402      * into account all the change rects of all the masks. Usually,
0403      * this work is done by the walkers.
0404      */
0405     QRect requestedRect =
0406         parentLayer->changeRect(parentLayer->original()->exactBounds());
0407 
0408 
0409     QRect bounds;
0410 
0411     {
0412         KisNodeSP parentNode = parent();
0413 
0414         if (parentNode) {
0415             bounds = parentNode->original()->defaultBounds()->bounds();
0416         } else {
0417             bounds = QRect(0,0,777,777);
0418             warnKrita << "WARNING: transform mask has no parent (change rect)."
0419                       << "Cannot run safe transformations."
0420                       << "Will limit bounds to" << ppVar(bounds);
0421         }
0422     }
0423 
0424     const QRect limitingRect = KisAlgebra2D::blowRect(bounds, m_d->offBoundsReadArea);
0425 
0426     requestedRect &= limitingRect;
0427 
0428     // force reset parent layer's projection, because we might have changed
0429     // our mask parameters and going to write to some other area
0430     parentLayer->projection()->clear();
0431 
0432     /**
0433      * Here we use updateProjection() to regenerate the projection of
0434      * the layer and after that a special update call (no-filthy) will
0435      * be issued to pass the changed further through the stack.
0436      */
0437     parentLayer->updateProjection(requestedRect, this);
0438     m_d->recalculatingStaticImage = false;
0439 }
0440 
0441 QRect KisTransformMask::decorateRect(KisPaintDeviceSP &src,
0442                                      KisPaintDeviceSP &dst,
0443                                      const QRect & rc,
0444                                      PositionToFilthy maskPos) const
0445 {
0446     Q_ASSERT_X(src != dst, "KisTransformMask::decorateRect",
0447                "src must be != dst, because we can't create transactions "
0448                "during merge, as it breaks reentrancy");
0449 
0450     KisTransformMaskParamsInterfaceSP params = m_d->paramsHolder->bakeIntoParams();
0451 
0452     if (params->isHidden()) return rc;
0453     KIS_ASSERT_RECOVER_NOOP(maskPos == N_FILTHY ||
0454                             maskPos == N_ABOVE_FILTHY ||
0455                             maskPos == N_BELOW_FILTHY);
0456 
0457     /**
0458      * We shouldn't reset or use the static image when rendering the animation
0459      * frames.
0460      *
0461      * TODO: implement proper high-quality rendering for animation frames
0462      */
0463     if (m_d->paramsHolder->defaultBounds()->externalFrameActive()) {
0464 
0465         // no preview for non-affine transforms currently...
0466         if (params->isAffine()) {
0467             m_d->worker.setForceSubPixelTranslation(m_d->paramsHolder->isAnimated());
0468             m_d->worker.setForwardTransform(params->finalAffineTransform());
0469             m_d->worker.runPartialDst(src, dst, rc);
0470 
0471 #ifdef DEBUG_RENDERING
0472             qDebug() << "Partial for external frame" << name() << ppVar(src->exactBounds()) << ppVar(src->extent()) << ppVar(dst->exactBounds()) << ppVar(dst->extent()) << ppVar(rc);
0473             KIS_DUMP_DEVICE_2(src, DUMP_RECT, "partial_ext_src", "dd");
0474             KIS_DUMP_DEVICE_2(dst, DUMP_RECT, "partial_ext_dst", "dd");
0475 #endif /* DEBUG_RENDERING */
0476         }
0477 
0478         return rc;
0479     }
0480 
0481     if (!m_d->staticCache.isCacheOverridden() &&
0482         !m_d->recalculatingStaticImage &&
0483         (maskPos == N_FILTHY || maskPos == N_ABOVE_FILTHY ||
0484          !m_d->staticCache.isCacheValid(params))) {
0485 
0486         if (m_d->testingInterface) {
0487             m_d->testingInterface->notifyDecorateRectTriggeredStaticImageUpdate();
0488         }
0489 
0490         m_d->staticCache.invalidateDeviceCache();
0491         m_d->updateSignalCompressor.start();
0492     }
0493 
0494     if (m_d->recalculatingStaticImage) {
0495         KIS_SAFE_ASSERT_RECOVER_NOOP(!m_d->staticCache.isCacheValid(params));
0496 
0497         KisPaintDeviceSP staticCacheDevice = m_d->staticCache.device();
0498 
0499         staticCacheDevice->clear();
0500         params->transformDevice(const_cast<KisTransformMask*>(this), src,
0501                                 staticCacheDevice, m_d->paramsHolder->isAnimated());
0502         QRect updatedRect = staticCacheDevice->extent();
0503         KisPainter::copyAreaOptimized(updatedRect.topLeft(), staticCacheDevice, dst, updatedRect);
0504 
0505         m_d->staticCache.setDeviceCacheValid(params);
0506 
0507 #ifdef DEBUG_RENDERING
0508         qDebug() << "Recalculate" << name() << ppVar(src->exactBounds()) << ppVar(dst->exactBounds()) << ppVar(rc);
0509         KIS_DUMP_DEVICE_2(src, DUMP_RECT, "recalc_src", "dd");
0510         KIS_DUMP_DEVICE_2(dst, DUMP_RECT, "recalc_dst", "dd");
0511 #endif /* DEBUG_RENDERING */
0512 
0513     // Note: overridden cache is **always** valid
0514     } else if (params->isAffine() && !m_d->staticCache.isCacheValid(params)) {
0515         m_d->worker.setForceSubPixelTranslation(m_d->paramsHolder->isAnimated());
0516         m_d->worker.setForwardTransform(params->finalAffineTransform());
0517         m_d->worker.runPartialDst(src, dst, rc);
0518 
0519 #ifdef DEBUG_RENDERING
0520         qDebug() << "Partial" << name() << ppVar(src->exactBounds()) << ppVar(src->extent()) << ppVar(dst->exactBounds()) << ppVar(dst->extent()) << ppVar(rc);
0521         KIS_DUMP_DEVICE_2(src, DUMP_RECT, "partial_src", "dd");
0522         KIS_DUMP_DEVICE_2(dst, DUMP_RECT, "partial_dst", "dd");
0523 #endif /* DEBUG_RENDERING */
0524 
0525     } else if (m_d->staticCache.isCacheValid(params)) {
0526         KisPainter::copyAreaOptimized(rc.topLeft(), m_d->staticCache.device(), dst, rc);
0527 
0528 #ifdef DEBUG_RENDERING
0529         qDebug() << "Fetch" << name() << ppVar(src->exactBounds()) << ppVar(dst->exactBounds()) << ppVar(rc);
0530         KIS_DUMP_DEVICE_2(src, DUMP_RECT, "fetch_src", "dd");
0531         KIS_DUMP_DEVICE_2(dst, DUMP_RECT, "fetch_dst", "dd");
0532 #endif /* DEBUG_RENDERING */
0533 
0534     }
0535 
0536     KIS_ASSERT_RECOVER_NOOP(this->busyProgressIndicator());
0537     this->busyProgressIndicator()->update();
0538 
0539     return rc;
0540 }
0541 
0542 bool KisTransformMask::accept(KisNodeVisitor &v)
0543 {
0544     return v.visit(this);
0545 }
0546 
0547 void KisTransformMask::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
0548 {
0549     return visitor.visit(this, undoAdapter);
0550 }
0551 
0552 QRect KisTransformMask::changeRect(const QRect &rect, PositionToFilthy pos) const
0553 {
0554     Q_UNUSED(pos);
0555 
0556     /**
0557      * FIXME: This check of the emptiness should be done
0558      * on the higher/lower level
0559      */
0560     if (rect.isEmpty()) return rect;
0561 
0562     KisTransformMaskParamsInterfaceSP params = m_d->paramsHolder->bakeIntoParams();
0563 
0564     QRect changeRect = rect;
0565 
0566     if (params->isAffine()) {
0567         QRect bounds;
0568         QRect interestRect;
0569         KisNodeSP parentNode = parent();
0570 
0571         if (parentNode) {
0572             bounds = parentNode->original()->defaultBounds()->bounds();
0573             interestRect = parentNode->original()->extent();
0574         } else {
0575             bounds = QRect(0,0,777,777);
0576             interestRect = QRect(0,0,888,888);
0577             warnKrita << "WARNING: transform mask has no parent (change rect)."
0578                       << "Cannot run safe transformations."
0579                       << "Will limit bounds to" << ppVar(bounds);
0580         }
0581 
0582         const QRect limitingRect = KisAlgebra2D::blowRect(bounds, m_d->offBoundsReadArea);
0583 
0584         KisSafeTransform transform(params->finalAffineTransform(), limitingRect, interestRect);
0585         changeRect = transform.mapRectForward(rect);
0586     } else {
0587         QRect interestRect;
0588         interestRect = parent() ? parent()->original()->extent() : QRect();
0589 
0590         changeRect = params->nonAffineChangeRect(rect);
0591     }
0592 
0593     return changeRect;
0594 }
0595 
0596 QRect KisTransformMask::needRect(const QRect& rect, PositionToFilthy pos) const
0597 {
0598     Q_UNUSED(pos);
0599 
0600     /**
0601      * FIXME: This check of the emptiness should be done
0602      * on the higher/lower level
0603      */
0604     if (rect.isEmpty()) return rect;
0605 
0606     KisTransformMaskParamsInterfaceSP params = m_d->paramsHolder->bakeIntoParams();
0607 
0608     QRect bounds;
0609     QRect interestRect;
0610     KisNodeSP parentNode = parent();
0611 
0612     if (parentNode) {
0613         bounds = parentNode->original()->defaultBounds()->bounds();
0614         interestRect = parentNode->original()->extent();
0615     } else {
0616         bounds = QRect(0,0,777,777);
0617         interestRect = QRect(0,0,888,888);
0618         warnKrita << "WARNING: transform mask has no parent (need rect)."
0619                    << "Cannot run safe transformations."
0620                    << "Will limit bounds to" << ppVar(bounds);
0621     }
0622 
0623     QRect needRect = rect;
0624 
0625     if (params->isAffine()) {
0626         const QRect limitingRect = KisAlgebra2D::blowRect(bounds, m_d->offBoundsReadArea);
0627 
0628         KisSafeTransform transform(params->finalAffineTransform(), limitingRect, interestRect);
0629         needRect = transform.mapRectBackward(rect);
0630 
0631         /**
0632          * When sampling affine transformations we use KisRandomSubAccessor,
0633          * which uses bilinear interpolation for calculating pixels. Therefore,
0634          * we need to extend the sides of the need rect by one pixel.
0635          */
0636         needRect = kisGrowRect(needRect, 1);
0637 
0638     } else {
0639         needRect = params->nonAffineNeedRect(rect, interestRect);
0640     }
0641 
0642     return needRect;
0643 }
0644 
0645 QRect KisTransformMask::extent() const
0646 {
0647     QRect rc = KisMask::extent();
0648 
0649     QRect partialChangeRect;
0650     QRect existentProjection;
0651     KisLayerSP parentLayer = qobject_cast<KisLayer*>(parent().data());
0652     if (parentLayer) {
0653         partialChangeRect = parentLayer->partialChangeRect(const_cast<KisTransformMask*>(this), rc);
0654         existentProjection = parentLayer->projection()->extent();
0655     }
0656 
0657     return changeRect(partialChangeRect) | existentProjection;
0658 }
0659 
0660 QRect KisTransformMask::exactBounds() const
0661 {
0662     QRect existentProjection;
0663     KisLayerSP parentLayer = qobject_cast<KisLayer*>(parent().data());
0664     if (parentLayer) {
0665         existentProjection = parentLayer->projection()->exactBounds();
0666 
0667         /* Take into account multiple keyframes... */
0668         if (parentLayer->original() && parentLayer->original()->defaultBounds() && parentLayer->original()->keyframeChannel()) {
0669             Q_FOREACH( const int& time, parentLayer->original()->keyframeChannel()->allKeyframeTimes() ) {
0670                 KisRasterKeyframeSP keyframe = parentLayer->original()->keyframeChannel()->keyframeAt<KisRasterKeyframe>(time);
0671                 existentProjection |= keyframe->contentBounds();
0672             }
0673         }
0674     }
0675 
0676     if (isAnimated()) {
0677         existentProjection |= changeRect(image()->bounds());
0678     }
0679 
0680     return changeRect(sourceDataBounds()) | existentProjection;
0681 }
0682 
0683 QRect KisTransformMask::sourceDataBounds() const
0684 {
0685     /// NOTE: we should avoid including parent layer's projection's
0686     ///       extent into the source of changeRect calculation, because
0687     ///       that is exactly what partialChangeRect() calculates.
0688 
0689     QRect partialChangeRect;
0690     KisLayerSP parentLayer = qobject_cast<KisLayer*>(parent().data());
0691     if (parentLayer) {
0692         const QRect rc = parentLayer->original()->exactBounds();
0693         partialChangeRect = parentLayer->partialChangeRect(const_cast<KisTransformMask*>(this), rc);
0694     }
0695 
0696     return partialChangeRect;
0697 }
0698 
0699 void KisTransformMask::setImage(KisImageWSP image)
0700 {
0701     m_d->paramsHolder->setDefaultBounds(new KisDefaultBounds(image));
0702     m_d->offset.setDefaultBounds(new KisDefaultBounds(image));
0703     KisEffectMask::setImage(image);
0704 }
0705 
0706 qint32 KisTransformMask::x() const
0707 {
0708     return m_d->offset->x();
0709 }
0710 
0711 qint32 KisTransformMask::y() const
0712 {
0713     return m_d->offset->y();
0714 }
0715 
0716 void KisTransformMask::setX(qint32 x)
0717 {
0718     KisTransformMaskParamsInterfaceSP params(m_d->paramsHolder->bakeIntoParams());
0719 
0720     params->translateSrcAndDst(QPointF(x - this->x(), 0));
0721 
0722     setTransformParams(params);
0723     m_d->offset->setX(x);
0724 }
0725 
0726 void KisTransformMask::setY(qint32 y)
0727 {
0728     KisTransformMaskParamsInterfaceSP params(m_d->paramsHolder->bakeIntoParams());
0729 
0730     params->translateSrcAndDst(QPointF(0, y - this->y()));
0731 
0732     setTransformParams(params);
0733     m_d->offset->setY(y);
0734 }
0735 
0736 void KisTransformMask::forceUpdateTimedNode()
0737 {
0738     if (m_d->testingInterface) {
0739         m_d->testingInterface->notifyForceUpdateTimedNode();
0740     }
0741 
0742     /**
0743      * When flattening the layer with animated transform mask we should
0744      * actually rerender the static image
0745      */
0746     if (hasPendingTimedUpdates() ||
0747         !m_d->staticCache.isCacheValid(m_d->paramsHolder->bakeIntoParams())) {
0748 
0749         forceStartAsyncRegenerationJob();
0750     }
0751 }
0752 
0753 bool KisTransformMask::hasPendingTimedUpdates() const
0754 {
0755     return m_d->updateSignalCompressor.isActive();
0756 }
0757 
0758 void KisTransformMask::threadSafeForceStaticImageUpdate()
0759 {
0760     threadSafeForceStaticImageUpdate(QRect());
0761 }
0762 
0763 void KisTransformMask::threadSafeForceStaticImageUpdate(const QRect &extraUpdateRect)
0764 {
0765     if (m_d->testingInterface) {
0766         m_d->testingInterface->notifyThreadSafeForceStaticImageUpdate();
0767     }
0768     if (!extraUpdateRect.isEmpty()) {
0769         m_d->forcedStaticUpdateExtraUpdateRect.addRect(extraUpdateRect);
0770     }
0771     emit sigInternalForceStaticImageUpdate();
0772 }
0773 
0774 void KisTransformMask::slotInternalForceStaticImageUpdate()
0775 {
0776     forceStartAsyncRegenerationJob();
0777 }
0778 
0779 void KisTransformMask::syncLodCache()
0780 {
0781     m_d->offset.syncLodCache();
0782     m_d->paramsHolder->syncLodCache();
0783     KisEffectMask::syncLodCache();
0784 }
0785 
0786 KisPaintDeviceList KisTransformMask::getLodCapableDevices() const
0787 {
0788     KisPaintDeviceList devices;
0789     devices += KisEffectMask::getLodCapableDevices();
0790     if (m_d->staticCache.device()) {
0791         devices << m_d->staticCache.device();
0792     }
0793     return devices;
0794 }
0795 
0796 void KisTransformMask::setTestingInterface(KisTransformMaskTestingInterface *interface)
0797 {
0798     m_d->testingInterface.reset(interface);
0799 }
0800 
0801 KisTransformMaskTestingInterface* KisTransformMask::testingInterface() const
0802 {
0803     return m_d->testingInterface.data();
0804 }
0805 
0806 KisKeyframeChannel *KisTransformMask::requestKeyframeChannel(const QString &id)
0807 {
0808     if (id == KisKeyframeChannel::PositionX.id() ||
0809         id == KisKeyframeChannel::PositionY.id() ||
0810         id == KisKeyframeChannel::ScaleX.id() ||
0811         id == KisKeyframeChannel::ScaleY.id() ||
0812         id == KisKeyframeChannel::ShearX.id() ||
0813         id == KisKeyframeChannel::ShearY.id() ||
0814         id == KisKeyframeChannel::RotationX.id() ||
0815         id == KisKeyframeChannel::RotationY.id() ||
0816         id == KisKeyframeChannel::RotationZ.id()) {
0817 
0818         KisKeyframeChannel *channel = m_d->paramsHolder->requestKeyframeChannel(id);
0819         KIS_SAFE_ASSERT_RECOVER_NOOP(channel);
0820         return channel;
0821     }
0822 
0823     return KisEffectMask::requestKeyframeChannel(id);
0824 }
0825 
0826 bool KisTransformMask::supportsKeyframeChannel(const QString &id)
0827 {
0828     if (id == KisKeyframeChannel::PositionX.id() ||
0829         id == KisKeyframeChannel::PositionY.id() ||
0830         id == KisKeyframeChannel::ScaleX.id() ||
0831         id == KisKeyframeChannel::ScaleY.id() ||
0832         id == KisKeyframeChannel::ShearX.id() ||
0833         id == KisKeyframeChannel::ShearY.id() ||
0834         id == KisKeyframeChannel::RotationX.id() ||
0835         id == KisKeyframeChannel::RotationY.id() ||
0836             id == KisKeyframeChannel::RotationZ.id()) {
0837         return true;
0838     }
0839     else if (id == KisKeyframeChannel::Opacity.id()) {
0840         return false;
0841     }
0842 
0843     return KisEffectMask::supportsKeyframeChannel(id);
0844 }
0845