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

0001 /* SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
0002  *
0003  * SPDX-License-Identifier: LGPL-2.0-or-later
0004  */
0005 
0006 #include "kis_async_merger.h"
0007 
0008 
0009 #include <kis_debug.h>
0010 #include <QBitArray>
0011 
0012 #include <KoChannelInfo.h>
0013 #include <KoCompositeOpRegistry.h>
0014 
0015 #include "kis_node_visitor.h"
0016 #include "kis_painter.h"
0017 #include "kis_layer.h"
0018 #include "kis_group_layer.h"
0019 #include "kis_adjustment_layer.h"
0020 #include "generator/kis_generator_layer.h"
0021 #include "kis_external_layer_iface.h"
0022 #include "kis_paint_layer.h"
0023 #include "filter/kis_filter.h"
0024 #include "filter/kis_filter_configuration.h"
0025 #include "filter/kis_filter_registry.h"
0026 #include "kis_selection.h"
0027 #include "kis_clone_layer.h"
0028 #include "kis_processing_information.h"
0029 #include "kis_busy_progress_indicator.h"
0030 
0031 
0032 #include "kis_merge_walker.h"
0033 #include "kis_refresh_subtree_walker.h"
0034 
0035 #include "kis_abstract_projection_plane.h"
0036 
0037 
0038 //#define DEBUG_MERGER
0039 
0040 #ifdef DEBUG_MERGER
0041 #define DEBUG_NODE_ACTION(message, type, leaf, rect)            \
0042     qDebug() << message << type << ":" << leaf->node()->name() << rect << "rendered:" << leaf->shouldBeRendered()
0043 #else
0044 #define DEBUG_NODE_ACTION(message, type, leaf, rect)
0045 #endif
0046 
0047 
0048 class KisUpdateOriginalVisitor : public KisNodeVisitor
0049 {
0050 public:
0051     KisUpdateOriginalVisitor(const QRect &updateRect, KisPaintDeviceSP projection, const QRect &cropRect)
0052         : m_updateRect(updateRect),
0053           m_cropRect(cropRect),
0054           m_projection(projection)
0055         {
0056         }
0057 
0058     ~KisUpdateOriginalVisitor() override {
0059     }
0060 
0061 public:
0062     using KisNodeVisitor::visit;
0063 
0064     bool visit(KisAdjustmentLayer* layer) override {
0065         if (!layer->visible()) return true;
0066 
0067         if (!m_projection) {
0068             warnImage << "ObligeChild mechanism has been activated for "
0069                 "an adjustment layer! Do nothing...";
0070             layer->original()->clear();
0071             return true;
0072         }
0073 
0074         const QRect originalUpdateRect =
0075             layer->projectionPlane()->needRectForOriginal(m_updateRect);
0076 
0077         KisPaintDeviceSP originalDevice = layer->original();
0078         originalDevice->clear(originalUpdateRect);
0079 
0080         const QRect applyRect = originalUpdateRect & m_projection->extent();
0081 
0082         // If the intersection of the updaterect and the projection extent is
0083         //      null, we are finish here.
0084         if(applyRect.isNull()) return true;
0085 
0086         KisFilterConfigurationSP filterConfig = layer->filter();
0087         if (!filterConfig) {
0088             /**
0089              * When an adjustment layer is just created, it may have no
0090              * filter inside. Then the layer has work as a pass-through
0091              * node. Just copy the merged data to the layer's original.
0092              */
0093             KisPainter::copyAreaOptimized(applyRect.topLeft(), m_projection, originalDevice, applyRect);
0094             return true;
0095         }
0096 
0097         KisSelectionSP selection = layer->fetchComposedInternalSelection(applyRect);
0098         const QRect filterRect = selection ? applyRect & selection->selectedRect() : applyRect;
0099 
0100         KisFilterSP filter = KisFilterRegistry::instance()->value(filterConfig->name());
0101         if (!filter) return false;
0102 
0103         KisPaintDeviceSP dstDevice = originalDevice;
0104 
0105         if (selection) {
0106             dstDevice = new KisPaintDevice(originalDevice->colorSpace());
0107         }
0108 
0109         if (!filterRect.isEmpty()) {
0110             KIS_ASSERT_RECOVER_NOOP(layer->busyProgressIndicator());
0111             layer->busyProgressIndicator()->update();
0112 
0113             // We do not create a transaction here, as srcDevice != dstDevice
0114             filter->process(m_projection, dstDevice, 0, filterRect, filterConfig.data(), 0);
0115         }
0116 
0117         if (selection) {
0118             KisPainter::copyAreaOptimized(applyRect.topLeft(), m_projection, originalDevice, applyRect);
0119             KisPainter::copyAreaOptimized(filterRect.topLeft(), dstDevice, originalDevice, filterRect, selection);
0120         }
0121 
0122         return true;
0123     }
0124 
0125     bool visit(KisExternalLayer*) override {
0126         return true;
0127     }
0128 
0129     bool visit(KisGeneratorLayer*) override {
0130         return true;
0131     }
0132 
0133     bool visit(KisPaintLayer*) override {
0134         return true;
0135     }
0136 
0137     bool visit(KisGroupLayer*) override {
0138         return true;
0139     }
0140 
0141     bool visit(KisCloneLayer *layer) override {
0142         QRect emptyRect;
0143         KisRefreshSubtreeWalker walker(emptyRect);
0144         KisAsyncMerger merger;
0145 
0146         KisLayerSP srcLayer = layer->copyFrom();
0147         QRect srcRect = m_updateRect.translated(-layer->x(), -layer->y());
0148 
0149         QRegion prepareRegion(srcRect);
0150         prepareRegion -= m_cropRect;
0151 
0152 
0153         /**
0154          * If a clone has complicated masks, we should prepare additional
0155          * source area to ensure the rect is prepared.
0156          */
0157         QRect needRectOnSource = layer->needRectOnSourceForMasks(srcRect);
0158         if (!needRectOnSource.isEmpty()) {
0159             prepareRegion += needRectOnSource;
0160         }
0161 
0162         if (srcLayer.isNull()) {
0163             return true;
0164         }
0165 
0166         auto rect = prepareRegion.begin();
0167         while (rect != prepareRegion.end()) {
0168             walker.collectRects(srcLayer,*rect);
0169             merger.startMerge(walker, false);
0170             rect++;
0171         }
0172 
0173         return true;
0174     }
0175 
0176     bool visit(KisNode*) override {
0177         return true;
0178     }
0179     bool visit(KisFilterMask*) override {
0180         return true;
0181     }
0182     bool visit(KisTransformMask*) override {
0183         return true;
0184     }
0185     bool visit(KisTransparencyMask*) override {
0186         return true;
0187     }
0188     bool visit(KisSelectionMask*) override {
0189         return true;
0190     }
0191     bool visit(KisColorizeMask*) override {
0192         return true;
0193     }
0194 
0195 private:
0196     QRect m_updateRect;
0197     QRect m_cropRect;
0198     KisPaintDeviceSP m_projection;
0199 };
0200 
0201 
0202 /*********************************************************************/
0203 /*                     KisAsyncMerger                                */
0204 /*********************************************************************/
0205 
0206 void KisAsyncMerger::startMerge(KisBaseRectsWalker &walker, bool notifyClones) {
0207     KisMergeWalker::LeafStack &leafStack = walker.leafStack();
0208 
0209     const bool useTempProjections = walker.needRectVaries();
0210 
0211     while(!leafStack.isEmpty()) {
0212         KisMergeWalker::JobItem item = leafStack.pop();
0213         KisProjectionLeafSP currentLeaf = item.m_leaf;
0214 
0215         /**
0216          * In some unidentified cases teh nodes might be removed
0217          * while the updates are still running. We have no proof
0218          * of it yet, so just add a safety assert here.
0219          */
0220         KIS_SAFE_ASSERT_RECOVER_RETURN(currentLeaf);
0221         KIS_SAFE_ASSERT_RECOVER_RETURN(currentLeaf->node());
0222 
0223         // All the masks should be filtered by the walkers
0224         KIS_SAFE_ASSERT_RECOVER_RETURN(currentLeaf->isLayer());
0225 
0226         QRect applyRect = item.m_applyRect;
0227 
0228         if (currentLeaf->isRoot()) {
0229             currentLeaf->projectionPlane()->recalculate(applyRect, walker.startNode());
0230             continue;
0231         }
0232 
0233         if(item.m_position & KisMergeWalker::N_EXTRA) {
0234             // The type of layers that will not go to projection.
0235 
0236             DEBUG_NODE_ACTION("Updating", "N_EXTRA", currentLeaf, applyRect);
0237             KisUpdateOriginalVisitor originalVisitor(applyRect,
0238                                                      m_currentProjection,
0239                                                      walker.cropRect());
0240             currentLeaf->accept(originalVisitor);
0241             currentLeaf->projectionPlane()->recalculate(applyRect, currentLeaf->node());
0242 
0243             continue;
0244         }
0245 
0246 
0247         if (!m_currentProjection) {
0248             setupProjection(currentLeaf, applyRect, useTempProjections);
0249         }
0250 
0251         KisUpdateOriginalVisitor originalVisitor(applyRect,
0252                                                  m_currentProjection,
0253                                                  walker.cropRect());
0254 
0255         if(item.m_position & KisMergeWalker::N_FILTHY) {
0256             DEBUG_NODE_ACTION("Updating", "N_FILTHY", currentLeaf, applyRect);
0257             if (currentLeaf->shouldBeRendered()) {
0258                 currentLeaf->accept(originalVisitor);
0259                 currentLeaf->projectionPlane()->recalculate(applyRect, walker.startNode());
0260             }
0261         }
0262         else if(item.m_position & KisMergeWalker::N_ABOVE_FILTHY) {
0263             DEBUG_NODE_ACTION("Updating", "N_ABOVE_FILTHY", currentLeaf, applyRect);
0264             if(currentLeaf->dependsOnLowerNodes()) {
0265                 if (currentLeaf->shouldBeRendered()) {
0266                     currentLeaf->accept(originalVisitor);
0267                     currentLeaf->projectionPlane()->recalculate(applyRect, currentLeaf->node());
0268                 }
0269             }
0270         }
0271         else if(item.m_position & KisMergeWalker::N_FILTHY_PROJECTION) {
0272             DEBUG_NODE_ACTION("Updating", "N_FILTHY_PROJECTION", currentLeaf, applyRect);
0273             if (currentLeaf->shouldBeRendered()) {
0274                 currentLeaf->projectionPlane()->recalculate(applyRect, walker.startNode());
0275             }
0276         }
0277         else /*if(item.m_position & KisMergeWalker::N_BELOW_FILTHY)*/ {
0278             DEBUG_NODE_ACTION("Updating", "N_BELOW_FILTHY", currentLeaf, applyRect);
0279             /* nothing to do */
0280         }
0281 
0282         compositeWithProjection(currentLeaf, applyRect);
0283 
0284         if(item.m_position & KisMergeWalker::N_TOPMOST) {
0285             writeProjection(currentLeaf, useTempProjections, applyRect);
0286             resetProjection();
0287         }
0288 
0289         // FIXME: remove it from the inner loop and/or change to a warning!
0290         Q_ASSERT(currentLeaf->projection()->defaultBounds()->currentLevelOfDetail() ==
0291                  walker.levelOfDetail());
0292     }
0293 
0294     if(notifyClones) {
0295         doNotifyClones(walker);
0296     }
0297 
0298     if(m_currentProjection) {
0299         warnImage << "BUG: The walker hasn't reached the root layer!";
0300         warnImage << "     Start node:" << walker.startNode() << "Requested rect:" << walker.requestedRect();
0301         warnImage << "     An inconsistency in the walkers occurred!";
0302         warnImage << "     Please report a bug describing how you got this message.";
0303         // reset projection to avoid artifacts in next merges and allow people to work further
0304         resetProjection();
0305     }
0306 }
0307 
0308 void KisAsyncMerger::resetProjection() {
0309     m_currentProjection = 0;
0310     m_finalProjection = 0;
0311 }
0312 
0313 void KisAsyncMerger::setupProjection(KisProjectionLeafSP currentLeaf, const QRect& rect, bool useTempProjection) {
0314     KisPaintDeviceSP parentOriginal = currentLeaf->parent()->lazyDestinationForSubtreeComposition();
0315 
0316     if (parentOriginal) {
0317         if (useTempProjection) {
0318             if(!m_cachedPaintDevice)
0319                 m_cachedPaintDevice = new KisPaintDevice(parentOriginal->colorSpace());
0320             m_currentProjection = m_cachedPaintDevice;
0321             m_currentProjection->prepareClone(parentOriginal);
0322             m_finalProjection = parentOriginal;
0323         }
0324         else {
0325             parentOriginal->clear(rect);
0326             m_finalProjection = m_currentProjection = parentOriginal;
0327         }
0328     }
0329     else {
0330         /**
0331          * It happened so that our parent uses our own projection as
0332          * its original. It means obligeChild mechanism works.
0333          * We won't initialise m_currentProjection. This will cause
0334          * writeProjection() and compositeWithProjection() do nothing
0335          * when called.
0336          */
0337         /* NOP */
0338     }
0339 }
0340 
0341 void KisAsyncMerger::writeProjection(KisProjectionLeafSP topmostLeaf, bool useTempProjection, const QRect &rect) {
0342     Q_UNUSED(useTempProjection);
0343     Q_UNUSED(topmostLeaf);
0344     if (!m_currentProjection) return;
0345 
0346     if(m_currentProjection != m_finalProjection) {
0347         KisPainter::copyAreaOptimized(rect.topLeft(), m_currentProjection, m_finalProjection, rect);
0348     }
0349     DEBUG_NODE_ACTION("Writing projection", "", topmostLeaf->parent(), rect);
0350 }
0351 
0352 bool KisAsyncMerger::compositeWithProjection(KisProjectionLeafSP leaf, const QRect &rect) {
0353 
0354     if (!m_currentProjection) return true;
0355     if (!leaf->visible()) return true;
0356 
0357     KisPainter gc(m_currentProjection);
0358     leaf->projectionPlane()->apply(&gc, rect);
0359 
0360     DEBUG_NODE_ACTION("Compositing projection", "", leaf, rect);
0361     return true;
0362 }
0363 
0364 void KisAsyncMerger::doNotifyClones(KisBaseRectsWalker &walker) {
0365     KisBaseRectsWalker::CloneNotificationsVector &vector =
0366         walker.cloneNotifications();
0367 
0368     KisBaseRectsWalker::CloneNotificationsVector::iterator it;
0369 
0370     for(it = vector.begin(); it != vector.end(); ++it) {
0371         (*it).notify();
0372     }
0373 }