Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_paint_layer.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2005 C. Boemann <cbo@boemann.dk>
3 * SPDX-FileCopyrightText: 2006 Bart Coppens <kde@bartcoppens.be>
4 * SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
5 * SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10#include "kis_paint_layer.h"
11
12#include <kis_debug.h>
13#include <klocalizedstring.h>
14
15#include <KoIcon.h>
16#include <kis_icon.h>
17#include <KoColorSpace.h>
18#include <KoColorProfile.h>
20#include <KoProperties.h>
21
22#include "kis_image.h"
23#include "kis_painter.h"
24#include "kis_paint_device.h"
25#include "kis_node_visitor.h"
27#include "kis_default_bounds.h"
28
31
34
37#include "kis_time_span.h"
38
39
40struct Q_DECL_HIDDEN KisPaintLayer::Private
41{
42public:
43 Private(KisPaintLayer *_q) : q(_q), contentChannel(0) {}
44
46
49
50 // the real pointer is owned by the paint device
52
55
56 bool onionSkinVisibleOverride = true;
57
58 [[nodiscard]]
61};
62
63KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity, KisPaintDeviceSP dev)
64 : KisLayer(image, name, opacity)
65 , m_d(new Private(this))
66{
67 Q_ASSERT(dev);
68
69 m_d->paintDevice = dev;
70 m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image));
71 m_d->paintDevice->setSupportsWraparoundMode(true);
72 m_d->paintDevice->setParentNode(this);
73}
74
75
76KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity)
77 : KisLayer(image, name, opacity)
78 , m_d(new Private(this))
79{
80 Q_ASSERT(image);
81
82 m_d->paintDevice = new KisPaintDevice(this, image->colorSpace(), new KisDefaultBounds(image));
83 m_d->paintDevice->setSupportsWraparoundMode(true);
84}
85
86KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity, const KoColorSpace * colorSpace)
87 : KisLayer(image, name, opacity)
88 , m_d(new Private(this))
89{
90 if (!colorSpace) {
91 Q_ASSERT(image);
93 }
94 Q_ASSERT(colorSpace);
95 m_d->paintDevice = new KisPaintDevice(this, colorSpace, new KisDefaultBounds(image));
96 m_d->paintDevice->setSupportsWraparoundMode(true);
97}
98
100 : KisLayer(rhs)
102 , m_d(new Private(this))
103{
104 const bool copyFrames = (rhs.m_d->contentChannel != 0);
105 if (!copyFrames) {
106 m_d->paintDevice = new KisPaintDevice(*rhs.m_d->paintDevice.data(), KritaUtils::CopySnapshot, this);
107 m_d->paintDevice->setSupportsWraparoundMode(true);
108 m_d->paintChannelFlags = rhs.m_d->paintChannelFlags;
109 } else {
110 m_d->paintDevice = new KisPaintDevice(*rhs.m_d->paintDevice.data(), KritaUtils::CopyAllFrames, this);
111 m_d->paintDevice->setSupportsWraparoundMode(true);
112 m_d->paintChannelFlags = rhs.m_d->paintChannelFlags;
113
114 m_d->contentChannel = m_d->paintDevice->keyframeChannel();
115 addKeyframeChannel(m_d->contentChannel);
116
117 m_d->contentChannel->setOnionSkinsEnabled(rhs.onionSkinEnabled());
118
120 }
121}
122
124{
125 delete m_d;
126}
127
129{
130 return node->inherits("KisMask");
131}
132
134{
135 return m_d->paintDevice;
136}
137
139{
140 return m_d->paintDevice;
141}
142
144{
146}
147
149 KisPaintDeviceSP projection,
150 const QRect& rect) const
151{
153
155
156 if (hasTemporaryTarget()) {
159 gc.bitBlt(rect.topLeft(), temporaryTarget(), rect);
160 }
161
162 if (m_d->contentChannel &&
163 m_d->contentChannel->keyframeCount() > 1 &&
165 m_d->onionSkinVisibleOverride &&
166 !m_d->paintDevice->defaultBounds()->externalFrameActive()) {
167
168 KisPaintDeviceSP skins = m_d->onionSkinCache.projection(m_d->paintDevice);
169
170 KisPainter gcDest(projection);
172 gcDest.bitBlt(rect.topLeft(), skins, rect);
173 gcDest.end();
174 }
175
176 if (!m_d->contentChannel ||
177 (m_d->contentChannel->keyframeCount() <= 1) || !onionSkinEnabled()) {
178 m_d->onionSkinCache.reset();
179 }
180}
181
183{
184 return KisIconUtils::loadIcon("paintLayer");
185}
186
188{
189 m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image));
191}
192
211
213{
214 Q_FOREACH (const KisBaseNode::Property &property, properties) {
215 if (property.name == i18n("Alpha Locked")) {
216 setAlphaLocked(property.state.toBool());
217 }
218 else if (property.name == i18n("Onion Skins")) {
219 setOnionSkinEnabled(property.state.toBool());
220 }
221 }
222
224}
225
227{
228 return v.visit(this);
229}
230
232{
233 return visitor.visit(this, undoAdapter);
234}
235
236void KisPaintLayer::setChannelLockFlags(const QBitArray& channelFlags)
237{
238 Q_ASSERT(((quint32)channelFlags.count() == colorSpace()->channelCount() || channelFlags.isEmpty()));
239 m_d->paintChannelFlags = channelFlags;
240}
241
242const QBitArray& KisPaintLayer::channelLockFlags() const
243{
244 return m_d->paintChannelFlags;
245}
246
248{
250 QRect rect = t ? t->extent() : QRect();
251 if (onionSkinEnabled() && m_d->onionSkinVisibleOverride) rect |= KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice);
252 return rect | KisLayer::extent();
253}
254
256{
258 QRect rect = t ? t->extent() : QRect();
259 if (onionSkinEnabled() && m_d->onionSkinVisibleOverride) rect |= KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice);
260 return rect | KisLayer::exactBounds();
261}
262
264{
265 QBitArray flags = colorSpace()->channelFlags(false, true) & m_d->paintChannelFlags;
266 return flags.count(true) == 0 && !m_d->paintChannelFlags.isEmpty();
267}
268
270{
271 const bool oldAlphaLocked = alphaLocked();
272 if (oldAlphaLocked == lock) return;
273
274 if(m_d->paintChannelFlags.isEmpty())
275 m_d->paintChannelFlags = colorSpace()->channelFlags(true, true);
276
277 if(lock)
278 m_d->paintChannelFlags &= colorSpace()->channelFlags(true, false);
279 else
280 m_d->paintChannelFlags |= colorSpace()->channelFlags(false, true);
281
283}
284
286{
287 return nodeProperties().boolProperty("onionskin", false);
288}
289
291{
292 const auto oldState = onionSkinEnabled();
293 if (oldState == state) return;
294
295 if (!state && oldState) {
296 // FIXME: change ordering! race condition possible!
297
298 // Turning off onionskins shrinks our extent. Let's clean up the onion skins first
299 setDirty(KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice));
300 }
301
302 if (state) {
303 m_d->onionSkinConnection.addConnection(KisOnionSkinCompositor::instance(),
304 SIGNAL(sigOnionSkinChanged()),
305 this,
307 } else {
308 m_d->onionSkinConnection.clear();
309 }
310
311 if (m_d->contentChannel) {
312 m_d->contentChannel->setOnionSkinsEnabled(state);
313 }
314
315 setNodeProperty("onionskin", state);
316}
317
319 m_d->onionSkinCache.reset();
320}
321
323{
324 if (!onionSkinEnabled()) return;
325
326 const QRect dirtyRect =
328
329 setDirty(dirtyRect);
330}
331
333{
334 if (id == KisKeyframeChannel::Raster.id()) {
335 m_d->contentChannel = m_d->paintDevice->createKeyframeChannel(KisKeyframeChannel::Raster);
336 m_d->contentChannel->setOnionSkinsEnabled(onionSkinEnabled());
337
339 return m_d->contentChannel;
340 }
341
343}
344
345KisFrameChangeUpdateRecipe KisPaintLayer::Private::handleRasterKeyframeChannelUpdateImpl(const KisKeyframeChannel *channel, int time)
346{
348
349 recipe.affectedRange = channel->affectedFrames(time);
350 recipe.affectedRect = channel->affectedRect(time);
351
352 KisImageWSP image = q->image();
353 if (image) {
355 if (recipe.affectedRange.contains(bounds->currentTime())) {
356 recipe.totalDirtyRect = recipe.affectedRect;
357 }
358 }
359
360 if (contentChannel->onionSkinsEnabled()) {
362 }
363
364 return recipe;
365}
366
368{
369 if (channel->id() == KisKeyframeChannel::Raster.id()) {
370 KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "raster channel is not supposed to Q_EMIT sigKeyframeChanged");
371 } else {
373 }
374}
375
377{
378 if (channel->id() == KisKeyframeChannel::Raster.id()) {
379 m_d->handleRasterKeyframeChannelUpdateImpl(channel, time).notify(this);
380 } else {
382 }
383}
384
386{
387 if (channel->id() == KisKeyframeChannel::Raster.id()) {
388 return m_d->handleRasterKeyframeChannelUpdateImpl(channel, time);
389 } else {
391 }
392}
393
395{
396 if (id == KisKeyframeChannel::Raster.id()) {
397 return true;
398 }
399
401}
402
404{
406
407 KisPaintDeviceSP onionSkinsDevice = m_d->onionSkinCache.lodCapableDevice();
408 if (onionSkinsDevice) {
409 list << onionSkinsDevice;
410 }
411
412 return list;
413}
414
416{
417 return m_d->onionSkinVisibleOverride;
418}
419
421{
422 if (value == decorationsVisible()) return;
423
424 const QRect oldExtent = extent();
425
426 m_d->onionSkinVisibleOverride = value;
427
428 if (update && onionSkinEnabled()) {
429 setDirty(oldExtent | extent());
430 }
431}
float value(const T *src, size_t ch)
qreal v
const QString COMPOSITE_BEHIND
const KoColorSpace * colorSpace() const
KisKeyframeChannel stores and manages KisKeyframes. Maps units of time to virtual keyframe values....
static const KoID Raster
virtual KisTimeSpan affectedFrames(int time) const
Get the set of frames affected by any changes to the value or content of the active keyframe at the g...
virtual QRect affectedRect(int time) const =0
static KisBaseNode::Property getColorSpaceMismatchProperty(const KoColorSpace *cs)
static KisBaseNode::Property getProperty(const KoID &id, bool state)
QRect calculateFullExtent(const KisPaintDeviceSP device)
QRect calculateExtent(const KisPaintDeviceSP device, int time)
QRect updateExtentOnAddition(const KisPaintDeviceSP device, int addedTime)
static KisOnionSkinCompositor * instance()
QRect extent() const
static void copyAreaOptimized(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &originalSrcRect)
void bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
void setCompositeOpId(const KoCompositeOp *op)
virtual void visit(KisNode *node, KisUndoAdapter *undoAdapter)=0
The KisRasterKeyframeChannel is a concrete KisKeyframeChannel subclass that stores and manages KisRas...
bool contains(int time) const
QBitArray channelFlags(bool color=true, bool alpha=false) const
QString id() const
Definition KoID.cpp:63
bool boolProperty(const QString &name, bool defaultValue=false) const
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define bounds(x, a, b)
QIcon loadIcon(const QString &name)
void setNodeProperty(const QString &name, const QVariant &value)
virtual KisKeyframeChannel * requestKeyframeChannel(const QString &id)
const KoProperties & nodeProperties() const
KoProperties properties
KisImageWSP image
bool isAnimated() const
void enableAnimation()
virtual bool supportsKeyframeChannel(const QString &id)
void setupTemporaryPainter(KisPainter *painter) const
KisPaintDeviceSP projection() const override
Definition kis_layer.cc:820
void setImage(KisImageWSP image) override
Definition kis_layer.cc:378
QRect exactBounds() const override
QRect extent() const override
const KoColorSpace * colorSpace() const override
returns the image's colorSpace or null, if there is no image
Definition kis_layer.cc:225
void setSectionModelProperties(const KisBaseNode::PropertyList &properties) override
Definition kis_layer.cc:297
KisBaseNode::PropertyList sectionModelProperties() const override
Definition kis_layer.cc:272
virtual void handleKeyframeChannelFrameAdded(const KisKeyframeChannel *channel, int time)
Definition kis_node.cpp:647
void baseNodeChangedCallback() override
Definition kis_node.cpp:329
virtual KisPaintDeviceList getLodCapableDevices() const
Definition kis_node.cpp:682
void addKeyframeChannel(KisKeyframeChannel *channel) override
Definition kis_node.cpp:351
virtual KisFrameChangeUpdateRecipe handleKeyframeChannelFrameAboutToBeRemovedImpl(const KisKeyframeChannel *channel, int time)
Definition kis_node.cpp:652
virtual void handleKeyframeChannelFrameChange(const KisKeyframeChannel *channel, int time)
Definition kis_node.cpp:642
virtual void setDirty()
Definition kis_node.cpp:577
const QBitArray & channelLockFlags() const
Private *const m_d
bool alphaLocked() const
void setChannelLockFlags(const QBitArray &channelFlags)
bool supportsKeyframeChannel(const QString &id) override
QBitArray paintChannelFlags
KisRasterKeyframeChannel * contentChannel
void handleKeyframeChannelFrameAdded(const KisKeyframeChannel *channel, int time) override
KisBaseNode::PropertyList sectionModelProperties() const override
QRect extent() const override
KisKeyframeChannel * requestKeyframeChannel(const QString &id) override
KisFrameChangeUpdateRecipe handleRasterKeyframeChannelUpdateImpl(const KisKeyframeChannel *channel, int time)
void slotExternalUpdateOnionSkins()
bool accept(KisNodeVisitor &v) override
KisPaintDeviceList getLodCapableDevices() const override
bool needProjection() const override
void copyOriginalToProjection(const KisPaintDeviceSP original, KisPaintDeviceSP projection, const QRect &rect) const override
KisSignalAutoConnectionsStore onionSkinConnection
KisPaintLayer(KisImageWSP image, const QString &name, quint8 opacity, KisPaintDeviceSP dev)
KisFrameChangeUpdateRecipe handleKeyframeChannelFrameAboutToBeRemovedImpl(const KisKeyframeChannel *channel, int time) override
~KisPaintLayer() override
QRect exactBounds() const override
void setDecorationsVisible(bool value, bool update) override
void handleKeyframeChannelFrameChange(const KisKeyframeChannel *channel, int time) override
bool decorationsVisible() const override
KisPaintDeviceSP original() const override
void setImage(KisImageWSP image) override
KisOnionSkinCache onionSkinCache
KisPaintLayer * q
void setSectionModelProperties(const KisBaseNode::PropertyList &properties) override
QIcon icon() const override
void setOnionSkinEnabled(bool state)
KisPaintDeviceSP paintDevice
bool onionSkinEnabled() const
Private(KisPaintLayer *_q)
bool allowAsChild(KisNodeSP) const override
void setAlphaLocked(bool lock)