File indexing completed on 2024-05-12 15:58:10
0001 /* 0002 * SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #ifndef __KIS_BASE_RECTS_WALKER_H 0008 #define __KIS_BASE_RECTS_WALKER_H 0009 0010 #include <QStack> 0011 0012 #include "kis_layer.h" 0013 0014 #include "kis_abstract_projection_plane.h" 0015 #include "kis_projection_leaf.h" 0016 0017 0018 class KisBaseRectsWalker; 0019 typedef KisSharedPtr<KisBaseRectsWalker> KisBaseRectsWalkerSP; 0020 0021 class KRITAIMAGE_EXPORT KisBaseRectsWalker : public KisShared 0022 { 0023 public: 0024 enum UpdateType { 0025 UPDATE, 0026 UPDATE_NO_FILTHY, 0027 FULL_REFRESH, 0028 FULL_REFRESH_NO_FILTHY, 0029 UNSUPPORTED 0030 }; 0031 0032 0033 typedef qint32 NodePosition; 0034 enum NodePositionValues { 0035 /** 0036 * There are two different sets of values. 0037 * The first describes the position of the node to the graph, 0038 * the second shows the position to the filthy node 0039 */ 0040 0041 N_NORMAL = 0x00, 0042 N_TOPMOST = 0x01, 0043 N_BOTTOMMOST = 0x02, 0044 N_EXTRA = 0x04, 0045 0046 N_ABOVE_FILTHY = 0x08, 0047 N_FILTHY_ORIGINAL = 0x10, // not used actually 0048 N_FILTHY_PROJECTION = 0x20, 0049 N_FILTHY = 0x40, 0050 N_BELOW_FILTHY = 0x80 0051 }; 0052 0053 #define GRAPH_POSITION_MASK 0x07 0054 0055 static inline KisNode::PositionToFilthy convertPositionToFilthy(NodePosition position) { 0056 static const int positionToFilthyMask = 0057 N_ABOVE_FILTHY | 0058 N_FILTHY_PROJECTION | 0059 N_FILTHY | 0060 N_BELOW_FILTHY; 0061 0062 qint32 positionToFilthy = position & N_EXTRA ? N_FILTHY : position & positionToFilthyMask; 0063 // We do not use N_FILTHY_ORIGINAL yet, so... 0064 Q_ASSERT(positionToFilthy); 0065 0066 return static_cast<KisNode::PositionToFilthy>(positionToFilthy); 0067 } 0068 0069 struct CloneNotification { 0070 CloneNotification() {} 0071 CloneNotification(KisNodeSP node, const QRect &dirtyRect) 0072 : m_layer(qobject_cast<KisLayer*>(node.data())), 0073 m_dirtyRect(dirtyRect) {} 0074 0075 void notify() { 0076 Q_ASSERT(m_layer); // clones are possible for layers only 0077 m_layer->updateClones(m_dirtyRect); 0078 } 0079 0080 private: 0081 friend class KisWalkersTest; 0082 0083 KisLayerSP m_layer; 0084 QRect m_dirtyRect; 0085 }; 0086 0087 typedef QVector<CloneNotification> CloneNotificationsVector; 0088 0089 struct JobItem { 0090 KisProjectionLeafSP m_leaf; 0091 NodePosition m_position; 0092 0093 /** 0094 * The rect that should be prepared on this node. 0095 * E.g. area where the filter applies on filter layer 0096 * or an area of a paint layer that will be copied to 0097 * the projection. 0098 */ 0099 QRect m_applyRect; 0100 }; 0101 0102 typedef QStack<JobItem> LeafStack; 0103 0104 public: 0105 KisBaseRectsWalker() 0106 : m_levelOfDetail(0) 0107 { 0108 } 0109 0110 virtual ~KisBaseRectsWalker() { 0111 } 0112 0113 void collectRects(KisNodeSP node, const QRect& requestedRect) { 0114 clear(); 0115 0116 KisProjectionLeafSP startLeaf = node->projectionLeaf(); 0117 0118 m_nodeChecksum = calculateChecksum(startLeaf, requestedRect); 0119 m_graphChecksum = node->graphSequenceNumber(); 0120 m_resultChangeRect = requestedRect; 0121 m_resultUncroppedChangeRect = requestedRect; 0122 m_requestedRect = requestedRect; 0123 m_startNode = node; 0124 m_levelOfDetail = getNodeLevelOfDetail(startLeaf); 0125 startTrip(startLeaf); 0126 } 0127 0128 inline void recalculate(const QRect& requestedRect) { 0129 KIS_SAFE_ASSERT_RECOVER_RETURN(m_startNode); 0130 0131 KisProjectionLeafSP startLeaf = m_startNode->projectionLeaf(); 0132 0133 int calculatedLevelOfDetail = getNodeLevelOfDetail(startLeaf); 0134 0135 if (m_levelOfDetail != calculatedLevelOfDetail) { 0136 qWarning() << "WARNING: KisBaseRectsWalker::recalculate()" 0137 << "The levelOfDetail has changes with time," 0138 << "which couldn't have happened!" 0139 << ppVar(m_levelOfDetail) 0140 << ppVar(calculatedLevelOfDetail); 0141 0142 m_levelOfDetail = calculatedLevelOfDetail; 0143 } 0144 0145 if(startLeaf->isStillInGraph()) { 0146 collectRects(m_startNode, requestedRect); 0147 } 0148 else { 0149 clear(); 0150 m_nodeChecksum = calculateChecksum(startLeaf, requestedRect); 0151 m_graphChecksum = m_startNode->graphSequenceNumber(); 0152 m_resultChangeRect = QRect(); 0153 m_resultUncroppedChangeRect = QRect(); 0154 } 0155 } 0156 0157 bool checksumValid() { 0158 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_startNode, false); 0159 return 0160 m_nodeChecksum == calculateChecksum(m_startNode->projectionLeaf(), m_requestedRect) && 0161 m_graphChecksum == m_startNode->graphSequenceNumber(); 0162 } 0163 0164 inline void setCropRect(QRect cropRect) { 0165 m_cropRect = cropRect; 0166 } 0167 0168 inline QRect cropRect() const{ 0169 return m_cropRect; 0170 } 0171 0172 // return a reference for efficiency reasons 0173 inline LeafStack& leafStack() { 0174 return m_mergeTask; 0175 } 0176 0177 // return a reference for efficiency reasons 0178 inline CloneNotificationsVector& cloneNotifications() { 0179 return m_cloneNotifications; 0180 } 0181 0182 inline QRect accessRect() const { 0183 return m_resultAccessRect; 0184 } 0185 0186 inline QRect changeRect() const { 0187 return m_resultChangeRect; 0188 } 0189 0190 inline QRect uncroppedChangeRect() const { 0191 return m_resultUncroppedChangeRect; 0192 } 0193 0194 inline bool needRectVaries() const { 0195 return m_needRectVaries; 0196 } 0197 0198 inline bool changeRectVaries() const { 0199 return m_changeRectVaries; 0200 } 0201 0202 inline KisNodeSP startNode() const { 0203 return m_startNode; 0204 } 0205 0206 inline QRect requestedRect() const { 0207 return m_requestedRect; 0208 } 0209 0210 inline int levelOfDetail() const { 0211 return m_levelOfDetail; 0212 } 0213 0214 virtual UpdateType type() const = 0; 0215 0216 protected: 0217 0218 /** 0219 * Initiates collecting of rects. 0220 * Should be implemented in derived classes 0221 */ 0222 virtual void startTrip(KisProjectionLeafSP startWith) = 0; 0223 0224 protected: 0225 0226 static inline qint32 getGraphPosition(qint32 position) { 0227 return position & GRAPH_POSITION_MASK; 0228 } 0229 0230 static inline bool hasClones(KisNodeSP node) { 0231 KisLayer *layer = qobject_cast<KisLayer*>(node.data()); 0232 return layer && layer->hasClones(); 0233 } 0234 0235 static inline NodePosition calculateNodePosition(KisProjectionLeafSP leaf) { 0236 KisProjectionLeafSP nextLeaf = leaf->nextSibling(); 0237 while(nextLeaf && !nextLeaf->isLayer()) nextLeaf = nextLeaf->nextSibling(); 0238 if (!nextLeaf) return N_TOPMOST; 0239 0240 KisProjectionLeafSP prevLeaf = leaf->prevSibling(); 0241 while(prevLeaf && !prevLeaf->isLayer()) prevLeaf = prevLeaf->prevSibling(); 0242 if (!prevLeaf) return N_BOTTOMMOST; 0243 0244 return N_NORMAL; 0245 } 0246 0247 inline bool isStartLeaf(KisProjectionLeafSP leaf) const { 0248 return leaf->node() == m_startNode; 0249 } 0250 0251 inline void clear() { 0252 m_resultAccessRect = m_resultNeedRect = /*m_resultChangeRect =*/ 0253 m_childNeedRect = m_lastNeedRect = QRect(); 0254 0255 m_needRectVaries = m_changeRectVaries = false; 0256 m_mergeTask.clear(); 0257 m_cloneNotifications.clear(); 0258 0259 // Not needed really. Think over removing. 0260 //m_startNode = 0; 0261 //m_requestedRect = QRect(); 0262 } 0263 0264 inline void pushJob(KisProjectionLeafSP leaf, NodePosition position, QRect applyRect) { 0265 JobItem item = {leaf, position, applyRect}; 0266 m_mergeTask.push(item); 0267 } 0268 0269 inline QRect cropThisRect(const QRect& rect) { 0270 return m_cropRect.isValid() ? rect & m_cropRect : rect; 0271 } 0272 0273 /** 0274 * Used by KisFullRefreshWalker as it has a special changeRect strategy 0275 */ 0276 inline void setExplicitChangeRect(const QRect &changeRect, bool changeRectVaries) { 0277 m_resultChangeRect = changeRect; 0278 m_resultUncroppedChangeRect = changeRect; 0279 m_changeRectVaries = changeRectVaries; 0280 } 0281 0282 /** 0283 * Called for every node we meet on a forward way of the trip. 0284 */ 0285 virtual void registerChangeRect(KisProjectionLeafSP leaf, NodePosition position) { 0286 // We do not work with masks here. It is KisLayer's job. 0287 if(!leaf->isLayer()) return; 0288 if(!(position & N_FILTHY) && !leaf->visible()) return; 0289 0290 QRect currentChangeRect = leaf->projectionPlane()->changeRect(m_resultChangeRect, 0291 convertPositionToFilthy(position)); 0292 currentChangeRect = cropThisRect(currentChangeRect); 0293 0294 if(!m_changeRectVaries) 0295 m_changeRectVaries = currentChangeRect != m_resultChangeRect; 0296 0297 m_resultChangeRect = currentChangeRect; 0298 0299 m_resultUncroppedChangeRect = leaf->projectionPlane()->changeRect(m_resultUncroppedChangeRect, 0300 convertPositionToFilthy(position)); 0301 registerCloneNotification(leaf->node(), position); 0302 } 0303 0304 void registerCloneNotification(KisNodeSP node, NodePosition position) { 0305 /** 0306 * Note, we do not check for (N_ABOVE_FILTHY && 0307 * dependOnLowerNodes(node)) because it may lead to an 0308 * infinite loop with filter layer. Activate it when it is 0309 * guaranteed that it is not possible to create a filter layer 0310 * avobe its own clone 0311 */ 0312 0313 if(hasClones(node) && position & (N_FILTHY | N_FILTHY_PROJECTION | N_EXTRA)) { 0314 m_cloneNotifications.append( 0315 CloneNotification(node, m_resultUncroppedChangeRect)); 0316 } 0317 } 0318 0319 /** 0320 * Called for every node we meet on a backward way of the trip. 0321 */ 0322 virtual void registerNeedRect(KisProjectionLeafSP leaf, NodePosition position) { 0323 // We do not work with masks here. It is KisLayer's job. 0324 if(!leaf->isLayer()) return; 0325 0326 if(m_mergeTask.isEmpty()) 0327 m_resultAccessRect = m_resultNeedRect = m_childNeedRect = 0328 m_lastNeedRect = m_resultChangeRect; 0329 0330 if (leaf->parent() && position & N_TOPMOST) { 0331 bool parentNeedRectFound = false; 0332 QRect parentNeedRect; 0333 0334 Q_FOREACH(const JobItem &job, m_mergeTask) { 0335 if (job.m_leaf == leaf->parent()) { 0336 parentNeedRect = 0337 job.m_leaf->projectionPlane()->needRectForOriginal(job.m_applyRect); 0338 parentNeedRectFound = true; 0339 } 0340 } 0341 0342 // TODO: check if we can put this requirement 0343 // KIS_SAFE_ASSERT_RECOVER_NOOP(parentNeedRectFound); 0344 0345 if (parentNeedRectFound) { 0346 m_lastNeedRect = parentNeedRect; 0347 } else { 0348 // legacy way of fetching parent need rect, just 0349 // takes need rect of the last visited filthy node 0350 m_lastNeedRect = m_childNeedRect; 0351 } 0352 } 0353 0354 if (!leaf->shouldBeRendered()) { 0355 if (!m_lastNeedRect.isEmpty()) { 0356 // push a dumb job to fit state machine requirements 0357 pushJob(leaf, position, m_lastNeedRect); 0358 } 0359 } else if(position & (N_FILTHY | N_ABOVE_FILTHY | N_EXTRA)) { 0360 if(!m_lastNeedRect.isEmpty()) 0361 pushJob(leaf, position, m_lastNeedRect); 0362 //else /* Why push empty rect? */; 0363 0364 m_resultAccessRect |= leaf->projectionPlane()->accessRect(m_lastNeedRect, 0365 convertPositionToFilthy(position)); 0366 0367 m_lastNeedRect = leaf->projectionPlane()->needRect(m_lastNeedRect, 0368 convertPositionToFilthy(position)); 0369 m_lastNeedRect = cropThisRect(m_lastNeedRect); 0370 m_childNeedRect = m_lastNeedRect; 0371 } 0372 else if(position & (N_BELOW_FILTHY | N_FILTHY_PROJECTION)) { 0373 if(!m_lastNeedRect.isEmpty()) { 0374 pushJob(leaf, position, m_lastNeedRect); 0375 0376 m_resultAccessRect |= leaf->projectionPlane()->accessRect(m_lastNeedRect, 0377 convertPositionToFilthy(position)); 0378 0379 m_lastNeedRect = leaf->projectionPlane()->needRect(m_lastNeedRect, 0380 convertPositionToFilthy(position)); 0381 m_lastNeedRect = cropThisRect(m_lastNeedRect); 0382 } 0383 } 0384 else { 0385 // N_FILTHY_ORIGINAL is not used so it goes there 0386 qFatal("KisBaseRectsWalker: node position(%d) is out of range", position); 0387 } 0388 0389 if(!m_needRectVaries) 0390 m_needRectVaries = m_resultNeedRect != m_lastNeedRect; 0391 m_resultNeedRect |= m_lastNeedRect; 0392 } 0393 0394 virtual void adjustMasksChangeRect(KisProjectionLeafSP firstMask) { 0395 KisProjectionLeafSP currentLeaf = firstMask; 0396 0397 while (currentLeaf) { 0398 /** 0399 * ATTENTION: we miss the first mask 0400 */ 0401 0402 do { 0403 currentLeaf = currentLeaf->nextSibling(); 0404 } while (currentLeaf && 0405 (!currentLeaf->isMask() || !currentLeaf->visible())); 0406 0407 if(currentLeaf) { 0408 QRect changeRect = currentLeaf->projectionPlane()->changeRect(m_resultChangeRect); 0409 m_changeRectVaries |= changeRect != m_resultChangeRect; 0410 m_resultChangeRect = changeRect; 0411 m_resultUncroppedChangeRect = changeRect; 0412 } 0413 } 0414 0415 KisProjectionLeafSP parentLayer = firstMask->parent(); 0416 KIS_SAFE_ASSERT_RECOVER_RETURN(parentLayer); 0417 0418 registerCloneNotification(parentLayer->node(), N_FILTHY_PROJECTION); 0419 } 0420 0421 static qint32 calculateChecksum(KisProjectionLeafSP leaf, const QRect &requestedRect) { 0422 qint32 checksum = 0; 0423 qint32 x, y, w, h; 0424 QRect tempRect; 0425 0426 tempRect = leaf->projectionPlane()->changeRect(requestedRect); 0427 tempRect.getRect(&x, &y, &w, &h); 0428 checksum += -x - y + w + h; 0429 0430 tempRect = leaf->projectionPlane()->needRect(requestedRect); 0431 tempRect.getRect(&x, &y, &w, &h); 0432 checksum += -x - y + w + h; 0433 0434 // errKrita << leaf << requestedRect << "-->" << checksum; 0435 0436 return checksum; 0437 } 0438 0439 private: 0440 inline int getNodeLevelOfDetail(KisProjectionLeafSP leaf) { 0441 while (leaf && !leaf->projection()) { 0442 leaf = leaf->parent(); 0443 } 0444 0445 if (!leaf || !leaf->projection()) { 0446 /** 0447 * Such errors may happen during undo or too quick node removal, 0448 * they shouldn't cause any real problems in Krita work. 0449 */ 0450 qWarning() << "WARNING: KisBaseRectsWalker::getNodeLevelOfDetail() " 0451 "failed to fetch currentLevelOfDetail() from the node. " 0452 "Perhaps the node was removed from the image in the meantime."; 0453 return 0; 0454 } 0455 0456 return leaf->projection()->defaultBounds()->currentLevelOfDetail(); 0457 } 0458 0459 private: 0460 /** 0461 * The result variables. 0462 * By the end of a recursion they will store a complete 0463 * data for a successful merge operation. 0464 */ 0465 QRect m_resultAccessRect; 0466 QRect m_resultNeedRect; 0467 QRect m_resultChangeRect; 0468 QRect m_resultUncroppedChangeRect; 0469 bool m_needRectVaries {false}; 0470 bool m_changeRectVaries {false}; 0471 LeafStack m_mergeTask; 0472 CloneNotificationsVector m_cloneNotifications; 0473 0474 /** 0475 * Used by update optimization framework 0476 */ 0477 KisNodeSP m_startNode; 0478 QRect m_requestedRect; 0479 0480 /** 0481 * Used for getting know whether the start node 0482 * properties have changed since the walker was 0483 * calculated 0484 */ 0485 qint32 m_nodeChecksum {0}; 0486 0487 /** 0488 * Used for getting know whether the structure of 0489 * the graph has changed since the walker was 0490 * calculated 0491 */ 0492 qint32 m_graphChecksum {0}; 0493 0494 /** 0495 * Temporary variables 0496 */ 0497 QRect m_cropRect; 0498 0499 QRect m_childNeedRect; 0500 QRect m_lastNeedRect; 0501 0502 int m_levelOfDetail {0}; 0503 }; 0504 0505 #endif /* __KIS_BASE_RECTS_WALKER_H */ 0506