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 }