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

0001 /*
0002  *  SPDX-FileCopyrightText: 2005 C. Boemann <cbo@boemann.dk>
0003  *  SPDX-FileCopyrightText: 2006 Bart Coppens <kde@bartcoppens.be>
0004  *  SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
0005  *  SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
0006  *
0007  *  SPDX-License-Identifier: GPL-2.0-or-later
0008  */
0009 
0010 #include "kis_paint_layer.h"
0011 
0012 #include <kis_debug.h>
0013 #include <klocalizedstring.h>
0014 
0015 #include <KoIcon.h>
0016 #include <kis_icon.h>
0017 #include <KoColorSpace.h>
0018 #include <KoColorProfile.h>
0019 #include <KoCompositeOpRegistry.h>
0020 #include <KoProperties.h>
0021 
0022 #include "kis_image.h"
0023 #include "kis_painter.h"
0024 #include "kis_paint_device.h"
0025 #include "kis_node_visitor.h"
0026 #include "kis_processing_visitor.h"
0027 #include "kis_default_bounds.h"
0028 
0029 #include "kis_onion_skin_compositor.h"
0030 #include "kis_raster_keyframe_channel.h"
0031 
0032 #include "kis_signal_auto_connection.h"
0033 #include "kis_layer_properties_icons.h"
0034 
0035 #include "KisFrameChangeUpdateRecipe.h"
0036 #include "kis_onion_skin_cache.h"
0037 #include "kis_time_span.h"
0038 
0039 
0040 struct Q_DECL_HIDDEN KisPaintLayer::Private
0041 {
0042 public:
0043     Private(KisPaintLayer *_q) : q(_q), contentChannel(0) {}
0044 
0045     KisPaintLayer *q;
0046 
0047     KisPaintDeviceSP paintDevice;
0048     QBitArray        paintChannelFlags;
0049 
0050     // the real pointer is owned by the paint device
0051     KisRasterKeyframeChannel *contentChannel;
0052 
0053     KisSignalAutoConnectionsStore onionSkinConnection;
0054     KisOnionSkinCache onionSkinCache;
0055 
0056     bool onionSkinVisibleOverride = true;
0057 
0058     [[nodiscard]]
0059     KisFrameChangeUpdateRecipe
0060     handleRasterKeyframeChannelUpdateImpl(const KisKeyframeChannel *channel, int time);
0061 };
0062 
0063 KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity, KisPaintDeviceSP dev)
0064     : KisLayer(image, name, opacity)
0065     , m_d(new Private(this))
0066 {
0067     Q_ASSERT(dev);
0068 
0069     m_d->paintDevice = dev;
0070     m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image));
0071     m_d->paintDevice->setSupportsWraparoundMode(true);
0072     m_d->paintDevice->setParentNode(this);
0073 }
0074 
0075 
0076 KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity)
0077     : KisLayer(image, name, opacity)
0078     , m_d(new Private(this))
0079 {
0080     Q_ASSERT(image);
0081 
0082     m_d->paintDevice = new KisPaintDevice(this, image->colorSpace(), new KisDefaultBounds(image));
0083     m_d->paintDevice->setSupportsWraparoundMode(true);
0084 }
0085 
0086 KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity, const KoColorSpace * colorSpace)
0087     : KisLayer(image, name, opacity)
0088     , m_d(new Private(this))
0089 {
0090     if (!colorSpace) {
0091         Q_ASSERT(image);
0092         colorSpace = image->colorSpace();
0093     }
0094     Q_ASSERT(colorSpace);
0095     m_d->paintDevice = new KisPaintDevice(this, colorSpace, new KisDefaultBounds(image));
0096     m_d->paintDevice->setSupportsWraparoundMode(true);
0097 }
0098 
0099 KisPaintLayer::KisPaintLayer(const KisPaintLayer& rhs)
0100     : KisLayer(rhs)
0101     , KisIndirectPaintingSupport()
0102     , m_d(new Private(this))
0103 {
0104     const bool copyFrames = (rhs.m_d->contentChannel != 0);
0105     if (!copyFrames) {
0106         m_d->paintDevice = new KisPaintDevice(*rhs.m_d->paintDevice.data(), KritaUtils::CopySnapshot, this);
0107         m_d->paintDevice->setSupportsWraparoundMode(true);
0108         m_d->paintChannelFlags = rhs.m_d->paintChannelFlags;
0109     } else {
0110         m_d->paintDevice = new KisPaintDevice(*rhs.m_d->paintDevice.data(), KritaUtils::CopyAllFrames, this);
0111         m_d->paintDevice->setSupportsWraparoundMode(true);
0112         m_d->paintChannelFlags = rhs.m_d->paintChannelFlags;
0113 
0114         m_d->contentChannel = m_d->paintDevice->keyframeChannel();
0115         addKeyframeChannel(m_d->contentChannel);
0116 
0117         m_d->contentChannel->setOnionSkinsEnabled(rhs.onionSkinEnabled());
0118 
0119         KisLayer::enableAnimation();
0120     }
0121 }
0122 
0123 KisPaintLayer::~KisPaintLayer()
0124 {
0125     delete m_d;
0126 }
0127 
0128 bool KisPaintLayer::allowAsChild(KisNodeSP node) const
0129 {
0130     return node->inherits("KisMask");
0131 }
0132 
0133 KisPaintDeviceSP KisPaintLayer::original() const
0134 {
0135     return m_d->paintDevice;
0136 }
0137 
0138 KisPaintDeviceSP KisPaintLayer::paintDevice() const
0139 {
0140     return m_d->paintDevice;
0141 }
0142 
0143 bool KisPaintLayer::needProjection() const
0144 {
0145     return hasTemporaryTarget() || (isAnimated() && onionSkinEnabled());
0146 }
0147 
0148 void KisPaintLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
0149                                              KisPaintDeviceSP projection,
0150                                              const QRect& rect) const
0151 {
0152     KisIndirectPaintingSupport::ReadLocker l(this);
0153 
0154     KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect);
0155 
0156     if (hasTemporaryTarget()) {
0157         KisPainter gc(projection);
0158         setupTemporaryPainter(&gc);
0159         gc.bitBlt(rect.topLeft(), temporaryTarget(), rect);
0160     }
0161 
0162     if (m_d->contentChannel &&
0163             m_d->contentChannel->keyframeCount() > 1 &&
0164             onionSkinEnabled() &&
0165             m_d->onionSkinVisibleOverride &&
0166             !m_d->paintDevice->defaultBounds()->externalFrameActive()) {
0167 
0168         KisPaintDeviceSP skins = m_d->onionSkinCache.projection(m_d->paintDevice);
0169 
0170         KisPainter gcDest(projection);
0171         gcDest.setCompositeOpId(COMPOSITE_BEHIND);
0172         gcDest.bitBlt(rect.topLeft(), skins, rect);
0173         gcDest.end();
0174     }
0175 
0176     if (!m_d->contentChannel ||
0177         (m_d->contentChannel->keyframeCount() <= 1) || !onionSkinEnabled()) {
0178         m_d->onionSkinCache.reset();
0179     }
0180 }
0181 
0182 QIcon KisPaintLayer::icon() const
0183 {
0184     return KisIconUtils::loadIcon("paintLayer");
0185 }
0186 
0187 void KisPaintLayer::setImage(KisImageWSP image)
0188 {
0189     m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image));
0190     KisLayer::setImage(image);
0191 }
0192 
0193 KisBaseNode::PropertyList KisPaintLayer::sectionModelProperties() const
0194 {
0195     KisBaseNode::PropertyList l = KisLayer::sectionModelProperties();
0196 
0197     l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::alphaLocked, alphaLocked());
0198 
0199     if (isAnimated()) {
0200         l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::onionSkins, onionSkinEnabled());
0201     }
0202 
0203     return l;
0204 }
0205 
0206 void KisPaintLayer::setSectionModelProperties(const KisBaseNode::PropertyList &properties)
0207 {
0208     Q_FOREACH (const KisBaseNode::Property &property, properties) {
0209         if (property.name == i18n("Alpha Locked")) {
0210             setAlphaLocked(property.state.toBool());
0211         }
0212         else if (property.name == i18n("Onion Skins")) {
0213             setOnionSkinEnabled(property.state.toBool());
0214         }
0215     }
0216 
0217     KisLayer::setSectionModelProperties(properties);
0218 }
0219 
0220 bool KisPaintLayer::accept(KisNodeVisitor &v)
0221 {
0222     return v.visit(this);
0223 }
0224 
0225 void KisPaintLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
0226 {
0227     return visitor.visit(this, undoAdapter);
0228 }
0229 
0230 void KisPaintLayer::setChannelLockFlags(const QBitArray& channelFlags)
0231 {
0232     Q_ASSERT(((quint32)channelFlags.count() == colorSpace()->channelCount() || channelFlags.isEmpty()));
0233     m_d->paintChannelFlags = channelFlags;
0234 }
0235 
0236 const QBitArray& KisPaintLayer::channelLockFlags() const
0237 {
0238     return m_d->paintChannelFlags;
0239 }
0240 
0241 QRect KisPaintLayer::extent() const
0242 {
0243     KisPaintDeviceSP t = temporaryTarget();
0244     QRect rect = t ? t->extent() : QRect();
0245     if (onionSkinEnabled() && m_d->onionSkinVisibleOverride) rect |= KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice);
0246     return rect | KisLayer::extent();
0247 }
0248 
0249 QRect KisPaintLayer::exactBounds() const
0250 {
0251     KisPaintDeviceSP t = temporaryTarget();
0252     QRect rect = t ? t->extent() : QRect();
0253     if (onionSkinEnabled() && m_d->onionSkinVisibleOverride) rect |= KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice);
0254     return rect | KisLayer::exactBounds();
0255 }
0256 
0257 bool KisPaintLayer::alphaLocked() const
0258 {
0259     QBitArray flags = colorSpace()->channelFlags(false, true) & m_d->paintChannelFlags;
0260     return flags.count(true) == 0 && !m_d->paintChannelFlags.isEmpty();
0261 }
0262 
0263 void KisPaintLayer::setAlphaLocked(bool lock)
0264 {
0265     if(m_d->paintChannelFlags.isEmpty())
0266         m_d->paintChannelFlags = colorSpace()->channelFlags(true, true);
0267 
0268     if(lock)
0269         m_d->paintChannelFlags &= colorSpace()->channelFlags(true, false);
0270     else
0271         m_d->paintChannelFlags |= colorSpace()->channelFlags(false, true);
0272 
0273     baseNodeChangedCallback();
0274 }
0275 
0276 bool KisPaintLayer::onionSkinEnabled() const
0277 {
0278     return nodeProperties().boolProperty("onionskin", false);
0279 }
0280 
0281 void KisPaintLayer::setOnionSkinEnabled(bool state)
0282 {
0283     const auto oldState = onionSkinEnabled();
0284     if (oldState == state) return;
0285 
0286     if (!state && oldState) {
0287         // FIXME: change ordering! race condition possible!
0288 
0289         // Turning off onionskins shrinks our extent. Let's clean up the onion skins first
0290         setDirty(KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice));
0291     }
0292 
0293     if (state) {
0294         m_d->onionSkinConnection.addConnection(KisOnionSkinCompositor::instance(),
0295                                                SIGNAL(sigOnionSkinChanged()),
0296                                                this,
0297                                                SLOT(slotExternalUpdateOnionSkins()));
0298     } else {
0299         m_d->onionSkinConnection.clear();
0300     }
0301 
0302     if (m_d->contentChannel) {
0303         m_d->contentChannel->setOnionSkinsEnabled(state);
0304     }
0305 
0306     setNodeProperty("onionskin", state);
0307 }
0308 
0309 void KisPaintLayer::flushOnionSkinCache() {
0310     m_d->onionSkinCache.reset();
0311 }
0312 
0313 void KisPaintLayer::slotExternalUpdateOnionSkins()
0314 {
0315     if (!onionSkinEnabled()) return;
0316 
0317     const QRect dirtyRect =
0318             KisOnionSkinCompositor::instance()->calculateFullExtent(m_d->paintDevice);
0319 
0320     setDirty(dirtyRect);
0321 }
0322 
0323 KisKeyframeChannel *KisPaintLayer::requestKeyframeChannel(const QString &id)
0324 {
0325     if (id == KisKeyframeChannel::Raster.id()) {
0326         m_d->contentChannel = m_d->paintDevice->createKeyframeChannel(KisKeyframeChannel::Raster);
0327         m_d->contentChannel->setOnionSkinsEnabled(onionSkinEnabled());
0328 
0329         enableAnimation();
0330         return m_d->contentChannel;
0331     }
0332 
0333     return KisLayer::requestKeyframeChannel(id);
0334 }
0335 
0336 KisFrameChangeUpdateRecipe KisPaintLayer::Private::handleRasterKeyframeChannelUpdateImpl(const KisKeyframeChannel *channel, int time)
0337 {
0338     KisFrameChangeUpdateRecipe recipe;
0339 
0340     recipe.affectedRange = channel->affectedFrames(time);
0341     recipe.affectedRect = channel->affectedRect(time);
0342 
0343     KisImageWSP image = q->image();
0344     if (image) {
0345         KisDefaultBoundsSP bounds(new KisDefaultBounds(image));
0346         if (recipe.affectedRange.contains(bounds->currentTime())) {
0347             recipe.totalDirtyRect = recipe.affectedRect;
0348         }
0349     }
0350 
0351     if (contentChannel->onionSkinsEnabled()) {
0352         recipe.totalDirtyRect |= KisOnionSkinCompositor::instance()->updateExtentOnAddition(paintDevice, time);
0353     }
0354 
0355     return recipe;
0356 }
0357 
0358 void KisPaintLayer::handleKeyframeChannelFrameChange(const KisKeyframeChannel *channel, int time)
0359 {
0360     if (channel->id() == KisKeyframeChannel::Raster.id()) {
0361         KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "raster channel is not supposed to emit sigKeyframeChanged");
0362     } else {
0363         KisLayer::handleKeyframeChannelFrameChange(channel, time);
0364     }
0365 }
0366 
0367 void KisPaintLayer::handleKeyframeChannelFrameAdded(const KisKeyframeChannel *channel, int time)
0368 {
0369     if (channel->id() == KisKeyframeChannel::Raster.id()) {
0370         m_d->handleRasterKeyframeChannelUpdateImpl(channel, time).notify(this);
0371     } else {
0372         KisLayer::handleKeyframeChannelFrameAdded(channel, time);
0373     }
0374 }
0375 
0376 KisFrameChangeUpdateRecipe KisPaintLayer::handleKeyframeChannelFrameAboutToBeRemovedImpl(const KisKeyframeChannel *channel, int time)
0377 {
0378     if (channel->id() == KisKeyframeChannel::Raster.id()) {
0379         return m_d->handleRasterKeyframeChannelUpdateImpl(channel, time);
0380     } else {
0381         return KisLayer::handleKeyframeChannelFrameAboutToBeRemovedImpl(channel, time);
0382     }
0383 }
0384 
0385 bool KisPaintLayer::supportsKeyframeChannel(const QString &id)
0386 {
0387      if (id == KisKeyframeChannel::Raster.id()) {
0388          return true;
0389      }
0390 
0391      return KisLayer::supportsKeyframeChannel(id);
0392 }
0393 
0394 KisPaintDeviceList KisPaintLayer::getLodCapableDevices() const
0395 {
0396     KisPaintDeviceList list = KisLayer::getLodCapableDevices();
0397 
0398     KisPaintDeviceSP onionSkinsDevice = m_d->onionSkinCache.lodCapableDevice();
0399     if (onionSkinsDevice) {
0400         list << onionSkinsDevice;
0401     }
0402 
0403     return list;
0404 }
0405 
0406 bool KisPaintLayer::decorationsVisible() const
0407 {
0408     return m_d->onionSkinVisibleOverride;
0409 }
0410 
0411 void KisPaintLayer::setDecorationsVisible(bool value, bool update)
0412 {
0413     if (value == decorationsVisible()) return;
0414 
0415     const QRect oldExtent = extent();
0416 
0417     m_d->onionSkinVisibleOverride = value;
0418 
0419     if (update && onionSkinEnabled()) {
0420         setDirty(oldExtent | extent());
0421     }
0422 }