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
200 // return a reference for efficiency reasons
202 return m_cloneNotifications;
203 }
204
205 inline QRect accessRect() const {
206 return m_resultAccessRect;
207 }
208
209 inline QRect changeRect() const {
210 return m_resultChangeRect;
211 }
212
213 inline QRect uncroppedChangeRect() const {
214 return m_resultUncroppedChangeRect;
215 }
216
217 inline bool needRectVaries() const {
218 return m_needRectVaries;
219 }
220
221 inline bool changeRectVaries() const {
222 return m_changeRectVaries;
223 }
224
225 inline KisNodeSP startNode() const {
226 return m_startNode;
227 }
228
229 inline QRect requestedRect() const {
230 return m_requestedRect;
231 }
232
233 inline int levelOfDetail() const {
234 return m_levelOfDetail;
235 }
236
237 virtual UpdateType type() const = 0;
238
239protected:
240
245 virtual void startTrip(KisProjectionLeafSP startWith) = 0;
246
247protected:
248
249 static inline qint32 getGraphPosition(qint32 position) {
250 return position & GRAPH_POSITION_MASK;
251 }
252
253 static inline bool hasClones(KisNodeSP node) {
254 KisLayer *layer = qobject_cast<KisLayer*>(node.data());
255 return layer && layer->hasClones();
256 }
257
259 KisProjectionLeafSP nextLeaf = leaf->nextSibling();
260 while(nextLeaf && !nextLeaf->isLayer()) nextLeaf = nextLeaf->nextSibling();
261 if (!nextLeaf) return N_TOPMOST;
262
263 KisProjectionLeafSP prevLeaf = leaf->prevSibling();
264 while(prevLeaf && !prevLeaf->isLayer()) prevLeaf = prevLeaf->prevSibling();
265 if (!prevLeaf) return N_BOTTOMMOST;
266
267 return N_NORMAL;
268 }
269
270 inline bool isStartLeaf(KisProjectionLeafSP leaf) const {
271 return leaf->node() == m_startNode;
272 }
273
274 inline void clear() {
275 m_resultAccessRect = m_resultNeedRect = /*m_resultChangeRect =*/
276 m_childNeedRect = m_lastNeedRect = QRect();
277
278 m_needRectVaries = m_changeRectVaries = false;
279 m_mergeTask.clear();
280 m_cloneNotifications.clear();
281
282 // Not needed really. Think over removing.
283 //m_startNode = 0;
284 //m_requestedRect = QRect();
285 }
286
287 inline void pushJob(KisProjectionLeafSP leaf, NodePosition position, QRect applyRect, KisRenderPassFlags flags) {
288 JobItem item = {leaf, position, applyRect, flags};
289 m_mergeTask.push(item);
290 }
291
292 inline QRect cropThisRect(const QRect& rect, const QRect &cropRect) {
293 return cropRect.isValid() ? rect & cropRect : rect;
294 }
295
299 inline void setExplicitChangeRect(const QRect &changeRect, bool changeRectVaries) {
300 m_resultChangeRect = changeRect;
301 m_resultUncroppedChangeRect = changeRect;
302 m_changeRectVaries = changeRectVaries;
303 }
304
309 // We do not work with masks here. It is KisLayer's job.
310 if(!leaf->isLayer()) return;
311 if(!(position & N_FILTHY) && !leaf->visible()) return;
312
313 QRect currentChangeRect = leaf->projectionPlane()->changeRect(m_resultChangeRect,
314 convertPositionToFilthy(position));
315 currentChangeRect = cropThisRect(currentChangeRect, m_cropRect);
316
317 if(!m_changeRectVaries)
318 m_changeRectVaries = currentChangeRect != m_resultChangeRect;
319
320 m_resultChangeRect = currentChangeRect;
321
322 m_resultUncroppedChangeRect = leaf->projectionPlane()->changeRect(m_resultUncroppedChangeRect,
323 convertPositionToFilthy(position));
324 registerCloneNotification(leaf->node(), position);
325 }
326
336 if(hasClones(node) && position & (N_FILTHY | N_FILTHY_PROJECTION | N_EXTRA)) {
337 m_cloneNotifications.append(
338 CloneNotification(node, m_resultUncroppedChangeRect, m_clonesDontInvalidateFrames));
339 }
340 }
341
342 void registerNeedRect(KisProjectionLeafSP leaf, NodePosition position, KisRenderPassFlags flags) {
343 registerNeedRect(leaf, position, flags, m_cropRect);
344 }
345
350 KisRenderPassFlags flags, const QRect &cropRect) {
351 // We do not work with masks here. It is KisLayer's job.
352 if(!leaf->isLayer()) return;
353
354 if(m_mergeTask.isEmpty())
355 m_resultAccessRect = m_resultNeedRect = m_childNeedRect =
356 m_lastNeedRect = m_resultChangeRect;
357
358 if (leaf->parent() && position & N_TOPMOST) {
359 bool parentNeedRectFound = false;
360 QRect parentNeedRect;
361
362 for (auto it = std::make_reverse_iterator(m_mergeTask.end());
363 it != std::make_reverse_iterator(m_mergeTask.begin());
364 ++it) {
365
366 if (it->m_leaf == leaf->parent()) {
367 parentNeedRect =
368 it->m_leaf->projectionPlane()->needRectForOriginal(it->m_applyRect);
369 parentNeedRectFound = true;
370 break;
371 }
372 }
373
374 // TODO: check if we can put this requirement
375 // KIS_SAFE_ASSERT_RECOVER_NOOP(parentNeedRectFound);
376
377 if (parentNeedRectFound) {
378 m_lastNeedRect = parentNeedRect;
379 } else {
380 // legacy way of fetching parent need rect, just
381 // takes need rect of the last visited filthy node
382 m_lastNeedRect = m_childNeedRect;
383 }
384 }
385
386 if (!leaf->shouldBeRendered()) {
387 if (!m_lastNeedRect.isEmpty()) {
388 // push a dumb job to fit state machine requirements
389 pushJob(leaf, position, m_lastNeedRect, flags);
390 }
391 } else if(position & (N_FILTHY | N_ABOVE_FILTHY | N_EXTRA)) {
392 if(!m_lastNeedRect.isEmpty())
393 pushJob(leaf, position, m_lastNeedRect, flags);
394 //else /* Why push empty rect? */;
395
396 m_resultAccessRect |= leaf->projectionPlane()->accessRect(m_lastNeedRect,
397 convertPositionToFilthy(position));
398
399 m_lastNeedRect = leaf->projectionPlane()->needRect(m_lastNeedRect,
400 convertPositionToFilthy(position));
401 m_lastNeedRect = cropThisRect(m_lastNeedRect, cropRect);
402 m_childNeedRect = m_lastNeedRect;
403 }
404 else if(position & (N_BELOW_FILTHY | N_FILTHY_PROJECTION)) {
405 if(!m_lastNeedRect.isEmpty()) {
406 pushJob(leaf, position, m_lastNeedRect, flags);
407
408 m_resultAccessRect |= leaf->projectionPlane()->accessRect(m_lastNeedRect,
409 convertPositionToFilthy(position));
410
411 m_lastNeedRect = leaf->projectionPlane()->needRect(m_lastNeedRect,
412 convertPositionToFilthy(position));
413 m_lastNeedRect = cropThisRect(m_lastNeedRect, cropRect);
414 }
415 }
416 else {
417 // N_FILTHY_ORIGINAL is not used so it goes there
418 qFatal("KisBaseRectsWalker: node position(%d) is out of range", position);
419 }
420
421 if(!m_needRectVaries)
422 m_needRectVaries = m_resultNeedRect != m_lastNeedRect;
423 m_resultNeedRect |= m_lastNeedRect;
424 }
425
427 KisProjectionLeafSP currentLeaf = firstMask;
428
429 while (currentLeaf) {
434 do {
435 currentLeaf = currentLeaf->nextSibling();
436 } while (currentLeaf &&
437 (!currentLeaf->isMask() || !currentLeaf->visible()));
438
439 if(currentLeaf) {
440 QRect changeRect = currentLeaf->projectionPlane()->changeRect(m_resultChangeRect);
441 m_changeRectVaries |= changeRect != m_resultChangeRect;
442 m_resultChangeRect = changeRect;
443 m_resultUncroppedChangeRect = changeRect;
444 }
445 }
446
447 KisProjectionLeafSP parentLayer = firstMask->parent();
449
450 registerCloneNotification(parentLayer->node(), N_FILTHY_PROJECTION);
451 }
452
453 static qint32 calculateChecksum(KisProjectionLeafSP leaf, const QRect &requestedRect) {
454 qint32 checksum = 0;
455 qint32 x, y, w, h;
456 QRect tempRect;
457
458 tempRect = leaf->projectionPlane()->changeRect(requestedRect);
459 tempRect.getRect(&x, &y, &w, &h);
460 checksum += -x - y + w + h;
461
462 tempRect = leaf->projectionPlane()->needRect(requestedRect);
463 tempRect.getRect(&x, &y, &w, &h);
464 checksum += -x - y + w + h;
465
466// errKrita << leaf << requestedRect << "-->" << checksum;
467
468 return checksum;
469 }
470
471 void addCloneSourceRegenerationJobs();
472 void visitSubtreeTopToBottom(KisProjectionLeafSP startWith, SubtreeVisitFlags flags, KisRenderPassFlags renderFlags, const QRect &cropRect);
473
474private:
476 while (leaf && !leaf->projection()) {
477 leaf = leaf->parent();
478 }
479
480 if (!leaf || !leaf->projection()) {
485 qWarning() << "WARNING: KisBaseRectsWalker::getNodeLevelOfDetail() "
486 "failed to fetch currentLevelOfDetail() from the node. "
487 "Perhaps the node was removed from the image in the meantime.";
488 return 0;
489 }
490
491 return leaf->projection()->defaultBounds()->currentLevelOfDetail();
492 }
493
494private:
504 bool m_needRectVaries {false};
505 bool m_changeRectVaries {false};
508
514
520 qint32 m_nodeChecksum {0};
521
527 qint32 m_graphChecksum {0};
528
533
536
537 int m_levelOfDetail {0};
538
539 bool m_clonesDontInvalidateFrames {false};
540};
541
542Q_DECLARE_OPERATORS_FOR_FLAGS(KisBaseRectsWalker::SubtreeVisitFlags);
543
544#endif /* __KIS_BASE_RECTS_WALKER_H */
545
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:483
KisProjectionLeafSP projectionLeaf
Definition kis_node.cpp:93
int graphSequenceNumber() const
Definition kis_node.cpp:279
PositionToFilthy
Definition kis_node.h:58