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