File indexing completed on 2024-05-12 15:58:39

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