Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_base_rects_walker.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#ifndef __KIS_BASE_RECTS_WALKER_H
8#define __KIS_BASE_RECTS_WALKER_H
9
10#include <QStack>
11
12#include "kis_layer.h"
13
15#include "kis_projection_leaf.h"
16
17
20
21class KRITAIMAGE_EXPORT KisBaseRectsWalker : public KisShared
22{
23public:
31
32
33 typedef qint32 NodePosition;
41 N_NORMAL = 0x00,
42 N_TOPMOST = 0x01,
43 N_BOTTOMMOST = 0x02,
44 N_EXTRA = 0x04,
45
46 N_ABOVE_FILTHY = 0x08,
47 N_FILTHY_ORIGINAL = 0x10, // not used actually
48 N_FILTHY_PROJECTION = 0x20,
49 N_FILTHY = 0x40,
50 N_BELOW_FILTHY = 0x80
51 };
52
53 #define GRAPH_POSITION_MASK 0x07
54
56 static const int positionToFilthyMask =
57 N_ABOVE_FILTHY |
58 N_FILTHY_PROJECTION |
59 N_FILTHY |
60 N_BELOW_FILTHY;
61
62 qint32 positionToFilthy = position & N_EXTRA ? N_FILTHY : position & positionToFilthyMask;
63 // We do not use N_FILTHY_ORIGINAL yet, so...
64 Q_ASSERT(positionToFilthy);
65
66 return static_cast<KisNode::PositionToFilthy>(positionToFilthy);
67 }
68
71 CloneNotification(KisNodeSP node, const QRect &dirtyRect,
72 bool dontInvalidateFrames)
73 : m_layer(qobject_cast<KisLayer*>(node.data())),
74 m_dirtyRect(dirtyRect),
75 m_dontInvalidateFrames(dontInvalidateFrames) {}
76
77 void notify() {
78 Q_ASSERT(m_layer); // clones are possible for layers only
79 m_layer->updateClones(m_dirtyRect, m_dontInvalidateFrames);
80 }
81
82 private:
83 friend class KisWalkersTest;
84
87 bool m_dontInvalidateFrames {false};
88 };
89
91
92 struct JobItem {
95
103
104 KisRenderPassFlags m_renderFlags = KisRenderPassFlag::None;
105 };
106
108
110 None = 0x0,
111 SkipNonRenderableNodes = 0x1,
112 NoFilthyMode = 0x2,
113 DontNotifyClones = 0x4
114 };
115
117
118public:
120 : m_levelOfDetail(0)
121 {
122 }
123
125 }
126
127 void collectRects(KisNodeSP node, const QRect& requestedRect) {
128 clear();
129
130 KisProjectionLeafSP startLeaf = node->projectionLeaf();
131
132 m_nodeChecksum = calculateChecksum(startLeaf, requestedRect);
133 m_graphChecksum = node->graphSequenceNumber();
134 m_resultChangeRect = requestedRect;
135 m_resultUncroppedChangeRect = requestedRect;
136 m_requestedRect = requestedRect;
137 m_startNode = node;
138 m_levelOfDetail = getNodeLevelOfDetail(startLeaf);
139 startTrip(startLeaf);
140 addCloneSourceRegenerationJobs();
141 }
142
143 inline void recalculate(const QRect& requestedRect) {
145
146 KisProjectionLeafSP startLeaf = m_startNode->projectionLeaf();
147
148 int calculatedLevelOfDetail = getNodeLevelOfDetail(startLeaf);
149
150 if (m_levelOfDetail != calculatedLevelOfDetail) {
151 qWarning() << "WARNING: KisBaseRectsWalker::recalculate()"
152 << "The levelOfDetail has changes with time,"
153 << "which couldn't have happened!"
154 << ppVar(m_levelOfDetail)
155 << ppVar(calculatedLevelOfDetail);
156
157 m_levelOfDetail = calculatedLevelOfDetail;
158 }
159
160 if(startLeaf->isStillInGraph()) {
161 collectRects(m_startNode, requestedRect);
162 }
163 else {
164 clear();
165 m_nodeChecksum = calculateChecksum(startLeaf, requestedRect);
166 m_graphChecksum = m_startNode->graphSequenceNumber();
167 m_resultChangeRect = QRect();
168 m_resultUncroppedChangeRect = QRect();
169 }
170 }
171
173 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_startNode, false);
174 return
175 m_nodeChecksum == calculateChecksum(m_startNode->projectionLeaf(), m_requestedRect) &&
176 m_graphChecksum == m_startNode->graphSequenceNumber();
177 }
178
180 m_clonesDontInvalidateFrames = value;
181 }
182
183 inline bool clonesDontInvalidateFrames() const {
184 return m_clonesDontInvalidateFrames;
185 }
186
187 inline void setCropRect(QRect cropRect) {
188 m_cropRect = cropRect;
189 }
190
191 inline QRect cropRect() const{
192 return m_cropRect;
193 }
194
195 // return a reference for efficiency reasons
197 return m_mergeTask;
198 }
199
205 inline bool isEmpty() const {
206 return m_mergeTask.isEmpty() && m_cloneNotifications.isEmpty();
207 }
208
209 // return a reference for efficiency reasons
211 return m_cloneNotifications;
212 }
213
214 inline QRect accessRect() const {
215 return m_resultAccessRect;
216 }
217
218 inline QRect changeRect() const {
219 return m_resultChangeRect;
220 }
221
222 inline QRect uncroppedChangeRect() const {
223 return m_resultUncroppedChangeRect;
224 }
225
226 inline bool needRectVaries() const {
227 return m_needRectVaries;
228 }
229
230 inline bool changeRectVaries() const {
231 return m_changeRectVaries;
232 }
233
234 inline KisNodeSP startNode() const {
235 return m_startNode;
236 }
237
238 inline QRect requestedRect() const {
239 return m_requestedRect;
240 }
241
242 inline int levelOfDetail() const {
243 return m_levelOfDetail;
244 }
245
246 virtual UpdateType type() const = 0;
247
248protected:
249
254 virtual void startTrip(KisProjectionLeafSP startWith) = 0;
255
256protected:
257
258 static inline qint32 getGraphPosition(qint32 position) {
259 return position & GRAPH_POSITION_MASK;
260 }
261
262 static inline bool hasClones(KisNodeSP node) {
263 KisLayer *layer = qobject_cast<KisLayer*>(node.data());
264 return layer && layer->hasClones();
265 }
266
268 KisProjectionLeafSP nextLeaf = leaf->nextSibling();
269 while(nextLeaf && !nextLeaf->isLayer()) nextLeaf = nextLeaf->nextSibling();
270 if (!nextLeaf) return N_TOPMOST;
271
272 KisProjectionLeafSP prevLeaf = leaf->prevSibling();
273 while(prevLeaf && !prevLeaf->isLayer()) prevLeaf = prevLeaf->prevSibling();
274 if (!prevLeaf) return N_BOTTOMMOST;
275
276 return N_NORMAL;
277 }
278
279 inline bool isStartLeaf(KisProjectionLeafSP leaf) const {
280 return leaf->node() == m_startNode;
281 }
282
283 inline void clear() {
284 m_resultAccessRect = m_resultNeedRect = /*m_resultChangeRect =*/
285 m_childNeedRect = m_lastNeedRect = QRect();
286
287 m_needRectVaries = m_changeRectVaries = false;
288 m_mergeTask.clear();
289 m_cloneNotifications.clear();
290
291 // Not needed really. Think over removing.
292 //m_startNode = 0;
293 //m_requestedRect = QRect();
294 }
295
296 inline void pushJob(KisProjectionLeafSP leaf, NodePosition position, QRect applyRect, KisRenderPassFlags flags) {
297 JobItem item = {leaf, position, applyRect, flags};
298 m_mergeTask.push(item);
299 }
300
301 inline QRect cropThisRect(const QRect& rect, const QRect &cropRect) {
302 return cropRect.isValid() ? rect & cropRect : rect;
303 }
304
308 inline void setExplicitChangeRect(const QRect &changeRect, bool changeRectVaries) {
309 m_resultChangeRect = cropThisRect(changeRect, cropRect());
310 m_resultUncroppedChangeRect = changeRect;
311 m_changeRectVaries = changeRectVaries;
312 }
313
318 // We do not work with masks here. It is KisLayer's job.
319 if(!leaf->isLayer()) return;
320 if(!(position & N_FILTHY) && !leaf->visible()) return;
321
322 QRect currentChangeRect = leaf->projectionPlane()->changeRect(m_resultChangeRect,
323 convertPositionToFilthy(position));
324 currentChangeRect = cropThisRect(currentChangeRect, m_cropRect);
325
326 if(!m_changeRectVaries)
327 m_changeRectVaries = currentChangeRect != m_resultChangeRect;
328
329 m_resultChangeRect = currentChangeRect;
330
331 m_resultUncroppedChangeRect = leaf->projectionPlane()->changeRect(m_resultUncroppedChangeRect,
332 convertPositionToFilthy(position));
333 registerCloneNotification(leaf->node(), position);
334 }
335
345 if(hasClones(node) && position & (N_FILTHY | N_FILTHY_PROJECTION | N_EXTRA)) {
346 m_cloneNotifications.append(
347 CloneNotification(node, m_resultUncroppedChangeRect, m_clonesDontInvalidateFrames));
348 }
349 }
350
351 void registerNeedRect(KisProjectionLeafSP leaf, NodePosition position, KisRenderPassFlags flags) {
352 registerNeedRect(leaf, position, flags, m_cropRect);
353 }
354
359 KisRenderPassFlags flags, const QRect &cropRect) {
360 // We do not work with masks here. It is KisLayer's job.
361 if(!leaf->isLayer()) return;
362
363 if(m_mergeTask.isEmpty())
364 m_resultAccessRect = m_resultNeedRect = m_childNeedRect =
365 m_lastNeedRect = m_resultChangeRect;
366
367 if (leaf->parent() && position & N_TOPMOST) {
368 bool parentNeedRectFound = false;
369 QRect parentNeedRect;
370
371 for (auto it = std::make_reverse_iterator(m_mergeTask.end());
372 it != std::make_reverse_iterator(m_mergeTask.begin());
373 ++it) {
374
375 if (it->m_leaf == leaf->parent()) {
376 parentNeedRect =
377 it->m_leaf->projectionPlane()->needRectForOriginal(it->m_applyRect);
378 parentNeedRectFound = true;
379 break;
380 }
381 }
382
383 // TODO: check if we can put this requirement
384 // KIS_SAFE_ASSERT_RECOVER_NOOP(parentNeedRectFound);
385
386 if (parentNeedRectFound) {
387 m_lastNeedRect = parentNeedRect;
388 } else {
389 // legacy way of fetching parent need rect, just
390 // takes need rect of the last visited filthy node
391 m_lastNeedRect = m_childNeedRect;
392 }
393 }
394
395 if (!leaf->shouldBeRendered()) {
396 if (!m_lastNeedRect.isEmpty()) {
397 // push a dumb job to fit state machine requirements
398 pushJob(leaf, position, m_lastNeedRect, flags);
399 }
400 } else if(position & (N_FILTHY | N_ABOVE_FILTHY | N_EXTRA)) {
401 if(!m_lastNeedRect.isEmpty())
402 pushJob(leaf, position, m_lastNeedRect, flags);
403 //else /* Why push empty rect? */;
404
405 m_resultAccessRect |= leaf->projectionPlane()->accessRect(m_lastNeedRect,
406 convertPositionToFilthy(position));
407
408 m_lastNeedRect = leaf->projectionPlane()->needRect(m_lastNeedRect,
409 convertPositionToFilthy(position));
410 m_lastNeedRect = cropThisRect(m_lastNeedRect, cropRect);
411 m_childNeedRect = m_lastNeedRect;
412 }
413 else if(position & (N_BELOW_FILTHY | N_FILTHY_PROJECTION)) {
414 if(!m_lastNeedRect.isEmpty()) {
415 pushJob(leaf, position, m_lastNeedRect, flags);
416
417 m_resultAccessRect |= leaf->projectionPlane()->accessRect(m_lastNeedRect,
418 convertPositionToFilthy(position));
419
420 m_lastNeedRect = leaf->projectionPlane()->needRect(m_lastNeedRect,
421 convertPositionToFilthy(position));
422 m_lastNeedRect = cropThisRect(m_lastNeedRect, cropRect);
423 }
424 }
425 else {
426 // N_FILTHY_ORIGINAL is not used so it goes there
427 qFatal("KisBaseRectsWalker: node position(%d) is out of range", position);
428 }
429
430 if(!m_needRectVaries)
431 m_needRectVaries = m_resultNeedRect != m_lastNeedRect;
432 m_resultNeedRect |= m_lastNeedRect;
433 }
434
436 KisProjectionLeafSP currentLeaf = firstMask;
437
438 while (currentLeaf) {
443 do {
444 currentLeaf = currentLeaf->nextSibling();
445 } while (currentLeaf &&
446 (!currentLeaf->isMask() || !currentLeaf->visible()));
447
448 if(currentLeaf) {
449 QRect changeRect = currentLeaf->projectionPlane()->changeRect(m_resultChangeRect);
450 m_changeRectVaries |= changeRect != m_resultChangeRect;
451 m_resultChangeRect = changeRect;
452 m_resultUncroppedChangeRect = changeRect;
453 }
454 }
455
456 KisProjectionLeafSP parentLayer = firstMask->parent();
458
459 registerCloneNotification(parentLayer->node(), N_FILTHY_PROJECTION);
460 }
461
462 static qint32 calculateChecksum(KisProjectionLeafSP leaf, const QRect &requestedRect) {
463 qint32 checksum = 0;
464 qint32 x, y, w, h;
465 QRect tempRect;
466
467 tempRect = leaf->projectionPlane()->changeRect(requestedRect);
468 tempRect.getRect(&x, &y, &w, &h);
469 checksum += -x - y + w + h;
470
471 tempRect = leaf->projectionPlane()->needRect(requestedRect);
472 tempRect.getRect(&x, &y, &w, &h);
473 checksum += -x - y + w + h;
474
475// errKrita << leaf << requestedRect << "-->" << checksum;
476
477 return checksum;
478 }
479
480 void addCloneSourceRegenerationJobs();
481 void visitSubtreeTopToBottom(KisProjectionLeafSP startWith, SubtreeVisitFlags flags, KisRenderPassFlags renderFlags, const QRect &cropRect);
482
483private:
485 while (leaf && !leaf->projection()) {
486 leaf = leaf->parent();
487 }
488
489 if (!leaf || !leaf->projection()) {
494 qWarning() << "WARNING: KisBaseRectsWalker::getNodeLevelOfDetail() "
495 "failed to fetch currentLevelOfDetail() from the node. "
496 "Perhaps the node was removed from the image in the meantime.";
497 return 0;
498 }
499
500 return leaf->projection()->defaultBounds()->currentLevelOfDetail();
501 }
502
503private:
513 bool m_needRectVaries {false};
514 bool m_changeRectVaries {false};
517
523
529 qint32 m_nodeChecksum {0};
530
536 qint32 m_graphChecksum {0};
537
542
545
546 int m_levelOfDetail {0};
547
548 bool m_clonesDontInvalidateFrames {false};
549};
550
551Q_DECLARE_OPERATORS_FOR_FLAGS(KisBaseRectsWalker::SubtreeVisitFlags);
552
553#endif /* __KIS_BASE_RECTS_WALKER_H */
554
float value(const T *src, size_t ch)
virtual void registerNeedRect(KisProjectionLeafSP leaf, NodePosition position, KisRenderPassFlags flags, const QRect &cropRect)
CloneNotificationsVector m_cloneNotifications
static NodePosition calculateNodePosition(KisProjectionLeafSP leaf)
void registerCloneNotification(KisNodeSP node, NodePosition position)
QRect cropThisRect(const QRect &rect, const QRect &cropRect)
QStack< JobItem > LeafStack
void collectRects(KisNodeSP node, const QRect &requestedRect)
QRect uncroppedChangeRect() const
Q_DECLARE_FLAGS(SubtreeVisitFlags, SubtreeVisitFlag)
bool clonesDontInvalidateFrames() const
bool isStartLeaf(KisProjectionLeafSP leaf) const
static qint32 getGraphPosition(qint32 position)
void recalculate(const QRect &requestedRect)
void setExplicitChangeRect(const QRect &changeRect, bool changeRectVaries)
virtual UpdateType type() const =0
static qint32 calculateChecksum(KisProjectionLeafSP leaf, const QRect &requestedRect)
void pushJob(KisProjectionLeafSP leaf, NodePosition position, QRect applyRect, KisRenderPassFlags flags)
void setClonesDontInvalidateFrames(bool value)
virtual void registerChangeRect(KisProjectionLeafSP leaf, NodePosition position)
static bool hasClones(KisNodeSP node)
virtual void adjustMasksChangeRect(KisProjectionLeafSP firstMask)
QVector< CloneNotification > CloneNotificationsVector
KisNodeSP startNode() const
void setCropRect(QRect cropRect)
CloneNotificationsVector & cloneNotifications()
int getNodeLevelOfDetail(KisProjectionLeafSP leaf)
void registerNeedRect(KisProjectionLeafSP leaf, NodePosition position, KisRenderPassFlags flags)
static KisNode::PositionToFilthy convertPositionToFilthy(NodePosition position)
virtual void startTrip(KisProjectionLeafSP startWith)=0
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define GRAPH_POSITION_MASK
Q_DECLARE_OPERATORS_FOR_FLAGS(KisBaseRectsWalker::SubtreeVisitFlags)
KisSharedPtr< KisBaseRectsWalker > KisBaseRectsWalkerSP
#define ppVar(var)
Definition kis_debug.h:155
QRect collectRects(const KisNode *node, MetricPolicy policy)
CloneNotification(KisNodeSP node, const QRect &dirtyRect, bool dontInvalidateFrames)
bool hasClones() const
Definition kis_layer.cc:489
KisProjectionLeafSP projectionLeaf
Definition kis_node.cpp:93
int graphSequenceNumber() const
Definition kis_node.cpp:279
PositionToFilthy
Definition kis_node.h:58