File indexing completed on 2024-05-19 04:26:32

0001 /*
0002  *  SPDX-FileCopyrightText: 2010 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef __KIS_REFRESH_SUBTREE_WALKER_H
0008 #define __KIS_REFRESH_SUBTREE_WALKER_H
0009 
0010 #include "kis_types.h"
0011 #include "kis_base_rects_walker.h"
0012 
0013 
0014 class KRITAIMAGE_EXPORT KisRefreshSubtreeWalker : public virtual KisBaseRectsWalker
0015 {
0016 public:
0017     enum Flag {
0018         None = 0x0,
0019         SkipNonRenderableNodes = 0x1,
0020         NoFilthyMode = 0x2,
0021         DontAdjustChangeRect = 0x4
0022     };
0023 
0024     Q_DECLARE_FLAGS(Flags, Flag);
0025 
0026 public:
0027     KisRefreshSubtreeWalker(QRect cropRect, Flags flags = None)
0028         : m_flags(flags)
0029     {
0030         setCropRect(cropRect);
0031     }
0032 
0033     UpdateType type() const override {
0034         return UNSUPPORTED;
0035     }
0036 
0037     ~KisRefreshSubtreeWalker() override
0038     {
0039     }
0040 
0041     Flags flags() const {
0042         return m_flags;
0043     }
0044 
0045 protected:
0046     KisRefreshSubtreeWalker() {}
0047 
0048 
0049     QRect calculateChangeRect(KisProjectionLeafSP startWith,
0050                               const QRect &requestedRect) {
0051 
0052         if(!startWith->isLayer())
0053             return requestedRect;
0054 
0055         QRect childrenRect;
0056         QRect tempRect = requestedRect;
0057         bool changeRectVaries = false;
0058 
0059         KisProjectionLeafSP currentLeaf = startWith->firstChild();
0060         KisProjectionLeafSP prevLeaf;
0061         KisProjectionLeafSP nextLeaf;
0062 
0063         while(currentLeaf) {
0064             nextLeaf = currentLeaf->nextSibling();
0065 
0066             if(currentLeaf->isLayer() && currentLeaf->shouldBeRendered()) {
0067                 tempRect |= calculateChangeRect(currentLeaf, requestedRect);
0068 
0069                 if(!changeRectVaries)
0070                     changeRectVaries = tempRect != requestedRect;
0071 
0072                 childrenRect = tempRect;
0073                 prevLeaf = currentLeaf;
0074             }
0075 
0076             currentLeaf = nextLeaf;
0077         }
0078 
0079         tempRect |= startWith->projectionPlane()->changeRect(requestedRect | childrenRect);
0080 
0081         if(!changeRectVaries)
0082             changeRectVaries = tempRect != requestedRect;
0083 
0084         setExplicitChangeRect(tempRect, changeRectVaries);
0085 
0086         return tempRect;
0087     }
0088 
0089     void startTrip(KisProjectionLeafSP startWith) override {
0090         if (!m_flags.testFlag(DontAdjustChangeRect)) {
0091             setExplicitChangeRect(requestedRect(), false);
0092         }
0093 
0094         if (isStartLeaf(startWith)) {
0095             KisProjectionLeafSP extraUpdateLeaf = startWith;
0096 
0097             if (startWith->isMask()) {
0098                 /**
0099                  * When the mask is the root of the update, update
0100                  * its parent projection using N_EXTRA method.
0101                  *
0102                  * This special update is necessary because the following
0103                  * wolker will work in N_ABOVE_FILTHY mode only
0104                  */
0105 
0106                 extraUpdateLeaf = startWith->parent();
0107             }
0108 
0109             /**
0110              * Sometimes it may happen that the mask is placed outside layers hierarchy
0111              * (e.g. inactive selection mask), then the projection leafs will not point
0112              * to anywhere
0113              */
0114             if (extraUpdateLeaf) {
0115                 NodePosition pos = N_EXTRA | calculateNodePosition(extraUpdateLeaf);
0116                 registerNeedRect(extraUpdateLeaf, pos);
0117 
0118                 /**
0119                  * In normal walkers we register notifications
0120                  * in the change-rect pass to avoid regeneration
0121                  * of the nodes that are below filthy. In the subtree
0122                  * walker there is no change-rect pass and all the
0123                  * nodes are considered as filthy, so we should do
0124                  * that explicitly.
0125                  */
0126                 registerCloneNotification(extraUpdateLeaf->node(), pos);
0127             }
0128         }
0129 
0130         /**
0131          * If the node is not renderable and we don't care about hidden groups,
0132          * e.g. when generating animation frames, then just skip the entire group.
0133          */
0134         if (m_flags & SkipNonRenderableNodes && !startWith->shouldBeRendered()) return;
0135 
0136 
0137         KisProjectionLeafSP currentLeaf = startWith->lastChild();
0138         while(currentLeaf) {
0139             NodePosition pos = (m_flags & NoFilthyMode ? N_ABOVE_FILTHY : N_FILTHY) |
0140                 calculateNodePosition(currentLeaf);
0141             registerNeedRect(currentLeaf, pos);
0142 
0143             // see a comment above
0144             registerCloneNotification(currentLeaf->node(), pos);
0145             currentLeaf = currentLeaf->prevSibling();
0146         }
0147 
0148         /**
0149          * In no-filthy mode we just recompose the root layer
0150          * without entering any subgroups
0151          */
0152         if (m_flags & NoFilthyMode) return;
0153 
0154         currentLeaf = startWith->lastChild();
0155         while(currentLeaf) {
0156             if(currentLeaf->canHaveChildLayers()) {
0157                 startTrip(currentLeaf);
0158             }
0159             currentLeaf = currentLeaf->prevSibling();
0160         }
0161     }
0162 
0163 private:
0164     Flags m_flags = None;
0165 };
0166 
0167 Q_DECLARE_OPERATORS_FOR_FLAGS(KisRefreshSubtreeWalker::Flags);
0168 
0169 #endif /* __KIS_REFRESH_SUBTREE_WALKER_H */
0170