File indexing completed on 2024-05-12 15:58:32

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 "kis_onion_skin_cache.h"
0036 
0037 struct Q_DECL_HIDDEN KisPaintLayer::Private
0038 {
0039 public:
0040     Private() : contentChannel(0) {}
0041 
0042     KisPaintDeviceSP paintDevice;
0043     QBitArray        paintChannelFlags;
0044 
0045     // the real pointer is owned by the paint device
0046     KisRasterKeyframeChannel *contentChannel;
0047 
0048     KisSignalAutoConnectionsStore onionSkinConnection;
0049     KisOnionSkinCache onionSkinCache;
0050 
0051     bool onionSkinVisibleOverride = true;
0052 };
0053 
0054 KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity, KisPaintDeviceSP dev)
0055     : KisLayer(image, name, opacity)
0056     , m_d(new Private())
0057 {
0058     Q_ASSERT(dev);
0059 
0060     init(dev);
0061     m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image));
0062 }
0063 
0064 
0065 KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity)
0066     : KisLayer(image, name, opacity)
0067     , m_d(new Private())
0068 {
0069     Q_ASSERT(image);
0070 
0071     init(new KisPaintDevice(this, image->colorSpace(), new KisDefaultBounds(image)));
0072 }
0073 
0074 KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity, const KoColorSpace * colorSpace)
0075     : KisLayer(image, name, opacity)
0076     , m_d(new Private())
0077 {
0078     if (!colorSpace) {
0079         Q_ASSERT(image);
0080         colorSpace = image->colorSpace();
0081     }
0082     Q_ASSERT(colorSpace);
0083     init(new KisPaintDevice(this, colorSpace, new KisDefaultBounds(image)));
0084 }
0085 
0086 KisPaintLayer::KisPaintLayer(const KisPaintLayer& rhs)
0087     : KisLayer(rhs)
0088     , KisIndirectPaintingSupport()
0089     , m_d(new Private)
0090 {
0091     const bool copyFrames = (rhs.m_d->contentChannel != 0);
0092     if (!copyFrames) {
0093         init(new KisPaintDevice(*rhs.m_d->paintDevice.data()), rhs.m_d->paintChannelFlags);
0094     } else {
0095         init(new KisPaintDevice(*rhs.m_d->paintDevice.data(), KritaUtils::CopyAllFrames), rhs.m_d->paintChannelFlags);
0096 
0097         m_d->contentChannel = m_d->paintDevice->keyframeChannel();
0098         addKeyframeChannel(m_d->contentChannel);
0099 
0100         m_d->contentChannel->setOnionSkinsEnabled(rhs.onionSkinEnabled());
0101 
0102         KisLayer::enableAnimation();
0103     }
0104 }
0105 
0106 void KisPaintLayer::init(KisPaintDeviceSP paintDevice, const QBitArray &paintChannelFlags)
0107 {
0108     m_d->paintDevice = paintDevice;
0109     m_d->paintDevice->setParentNode(this);
0110 
0111     m_d->paintChannelFlags = paintChannelFlags;
0112 }
0113 
0114 KisPaintLayer::~KisPaintLayer()
0115 {
0116     delete m_d;
0117 }
0118 
0119 bool KisPaintLayer::allowAsChild(KisNodeSP node) const
0120 {
0121     return node->inherits("KisMask");
0122 }
0123 
0124 KisPaintDeviceSP KisPaintLayer::original() const
0125 {
0126     return m_d->paintDevice;
0127 }
0128 
0129 KisPaintDeviceSP KisPaintLayer::paintDevice() const
0130 {
0131     return m_d->paintDevice;
0132 }
0133 
0134 bool KisPaintLayer::needProjection() const
0135 {
0136     return hasTemporaryTarget() || (isAnimated() && onionSkinEnabled());
0137 }
0138 
0139 void KisPaintLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
0140                                              KisPaintDeviceSP projection,
0141                                              const QRect& rect) const
0142 {
0143     KisIndirectPaintingSupport::ReadLocker l(this);
0144 
0145     KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect);
0146 
0147     if (hasTemporaryTarget()) {
0148         KisPainter gc(projection);
0149         setupTemporaryPainter(&gc);
0150         gc.bitBlt(rect.topLeft(), temporaryTarget(), rect);
0151     }
0152 
0153     if (m_d->contentChannel &&
0154             m_d->contentChannel->keyframeCount() > 1 &&
0155             onionSkinEnabled() &&
0156             m_d->onionSkinVisibleOverride &&
0157             !m_d->paintDevice->defaultBounds()->externalFrameActive()) {
0158 
0159         KisPaintDeviceSP skins = m_d->onionSkinCache.projection(m_d->paintDevice);
0160 
0161         KisPainter gcDest(projection);
0162         gcDest.setCompositeOpId(COMPOSITE_BEHIND);
0163         gcDest.bitBlt(rect.topLeft(), skins, rect);
0164         gcDest.end();
0165     }
0166 
0167     if (!m_d->contentChannel ||
0168         (m_d->contentChannel->keyframeCount() <= 1) || !onionSkinEnabled()) {
0169         m_d->onionSkinCache.reset();
0170     }
0171 }
0172 
0173 QIcon KisPaintLayer::icon() const
0174 {
0175     return KisIconUtils::loadIcon("paintLayer");
0176 }
0177 
0178 void KisPaintLayer::setImage(KisImageWSP image)
0179 {
0180     m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image));
0181     KisLayer::setImage(image);
0182 }
0183 
0184 KisBaseNode::PropertyList KisPaintLayer::sectionModelProperties() const
0185 {
0186     KisBaseNode::PropertyList l = KisLayer::sectionModelProperties();
0187 
0188     l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::alphaLocked, alphaLocked());
0189 
0190     if (isAnimated()) {
0191         l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::onionSkins, onionSkinEnabled());
0192     }
0193 
0194     return l;
0195 }
0196 
0197 void KisPaintLayer::setSectionModelProperties(const KisBaseNode::PropertyList &properties)
0198 {
0199     Q_FOREACH (const KisBaseNode::Property &property, properties) {
0200         if (property.name == i18n("Alpha Locked")) {
0201             setAlphaLocked(property.state.toBool());
0202         }
0203         else if (property.name == i18n("Onion Skins")) {
0204             setOnionSkinEnabled(property.state.toBool());
0205         }
0206     }
0207 
0208     KisLayer::setSectionModelProperties(properties);
0209 }
0210 
0211 bool KisPaintLayer::accept(KisNodeVisitor &v)
0212 {
0213     return v.visit(this);
0214 }
0215 
0216 void KisPaintLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
0217 {
0218     return visitor.visit(this, undoAdapter);
0219 }
0220 
0221 void KisPaintLayer::setChannelLockFlags(const QBitArray& channelFlags)
0222 {
0223     Q_ASSERT(((quint32)channelFlags.count() == colorSpace()->channelCount() || channelFlags.isEmpty()));
0224     m_d->paintChannelFlags = channelFlags;
0225 }
0226 
0227 const QBitArray& KisPaintLayer::channelLockFlags() const
0228 {
0229     return m_d->paintChannelFlags;
0230 }
0231 
0232 QRect KisPaintLayer::extent() const
0233 {
0234     KisPaintDeviceSP t = temporaryTarget();
0235     QRect rect = t ? t->extent() : QRect();
0236     if (onionSkinEnabled() && m_d->onionSkinVisibleOverride) rect |= KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice);
0237     return rect | KisLayer::extent();
0238 }
0239 
0240 QRect KisPaintLayer::exactBounds() const
0241 {
0242     KisPaintDeviceSP t = temporaryTarget();
0243     QRect rect = t ? t->extent() : QRect();
0244     if (onionSkinEnabled() && m_d->onionSkinVisibleOverride) rect |= KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice);
0245     return rect | KisLayer::exactBounds();
0246 }
0247 
0248 bool KisPaintLayer::alphaLocked() const
0249 {
0250     QBitArray flags = colorSpace()->channelFlags(false, true) & m_d->paintChannelFlags;
0251     return flags.count(true) == 0 && !m_d->paintChannelFlags.isEmpty();
0252 }
0253 
0254 void KisPaintLayer::setAlphaLocked(bool lock)
0255 {
0256     if(m_d->paintChannelFlags.isEmpty())
0257         m_d->paintChannelFlags = colorSpace()->channelFlags(true, true);
0258 
0259     if(lock)
0260         m_d->paintChannelFlags &= colorSpace()->channelFlags(true, false);
0261     else
0262         m_d->paintChannelFlags |= colorSpace()->channelFlags(false, true);
0263 
0264     baseNodeChangedCallback();
0265 }
0266 
0267 bool KisPaintLayer::onionSkinEnabled() const
0268 {
0269     return nodeProperties().boolProperty("onionskin", false);
0270 }
0271 
0272 void KisPaintLayer::setOnionSkinEnabled(bool state)
0273 {
0274     const auto oldState = onionSkinEnabled();
0275     if (oldState == state) return;
0276 
0277     if (!state && oldState) {
0278         // FIXME: change ordering! race condition possible!
0279 
0280         // Turning off onionskins shrinks our extent. Let's clean up the onion skins first
0281         setDirty(KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice));
0282     }
0283 
0284     if (state) {
0285         m_d->onionSkinConnection.addConnection(KisOnionSkinCompositor::instance(),
0286                                                SIGNAL(sigOnionSkinChanged()),
0287                                                this,
0288                                                SLOT(slotExternalUpdateOnionSkins()));
0289     } else {
0290         m_d->onionSkinConnection.clear();
0291     }
0292 
0293     if (m_d->contentChannel) {
0294         m_d->contentChannel->setOnionSkinsEnabled(state);
0295     }
0296 
0297     setNodeProperty("onionskin", state);
0298 }
0299 
0300 void KisPaintLayer::flushOnionSkinCache() {
0301     m_d->onionSkinCache.reset();
0302 }
0303 
0304 void KisPaintLayer::slotExternalUpdateOnionSkins()
0305 {
0306     if (!onionSkinEnabled()) return;
0307 
0308     const QRect dirtyRect =
0309             KisOnionSkinCompositor::instance()->calculateFullExtent(m_d->paintDevice);
0310 
0311     setDirty(dirtyRect);
0312 }
0313 
0314 KisKeyframeChannel *KisPaintLayer::requestKeyframeChannel(const QString &id)
0315 {
0316     if (id == KisKeyframeChannel::Raster.id()) {
0317         m_d->contentChannel = m_d->paintDevice->createKeyframeChannel(KisKeyframeChannel::Raster);
0318         m_d->contentChannel->setOnionSkinsEnabled(onionSkinEnabled());
0319         enableAnimation();
0320         return m_d->contentChannel;
0321     }
0322 
0323     return KisLayer::requestKeyframeChannel(id);
0324 }
0325 
0326 bool KisPaintLayer::supportsKeyframeChannel(const QString &id)
0327 {
0328      if (id == KisKeyframeChannel::Raster.id()) {
0329          return true;
0330      }
0331 
0332      return KisLayer::supportsKeyframeChannel(id);
0333 }
0334 
0335 KisPaintDeviceList KisPaintLayer::getLodCapableDevices() const
0336 {
0337     KisPaintDeviceList list = KisLayer::getLodCapableDevices();
0338 
0339     KisPaintDeviceSP onionSkinsDevice = m_d->onionSkinCache.lodCapableDevice();
0340     if (onionSkinsDevice) {
0341         list << onionSkinsDevice;
0342     }
0343 
0344     return list;
0345 }
0346 
0347 bool KisPaintLayer::decorationsVisible() const
0348 {
0349     return m_d->onionSkinVisibleOverride;
0350 }
0351 
0352 void KisPaintLayer::setDecorationsVisible(bool value, bool update)
0353 {
0354     if (value == decorationsVisible()) return;
0355 
0356     const QRect oldExtent = extent();
0357 
0358     m_d->onionSkinVisibleOverride = value;
0359 
0360     if (update && onionSkinEnabled()) {
0361         setDirty(oldExtent | extent());
0362     }
0363 }