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