diff --git a/libs/image/kis_async_merger.cpp b/libs/image/kis_async_merger.cpp index 1324d8d1af..2b0a2b5d52 100644 --- a/libs/image/kis_async_merger.cpp +++ b/libs/image/kis_async_merger.cpp @@ -1,370 +1,377 @@ /* Copyright (c) Dmitry Kazakov , 2009 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_async_merger.h" #include #include #include #include #include "kis_node_visitor.h" #include "kis_painter.h" #include "kis_layer.h" #include "kis_group_layer.h" #include "kis_adjustment_layer.h" #include "generator/kis_generator_layer.h" #include "kis_external_layer_iface.h" #include "kis_paint_layer.h" #include "filter/kis_filter.h" #include "filter/kis_filter_configuration.h" #include "filter/kis_filter_registry.h" #include "kis_selection.h" #include "kis_clone_layer.h" #include "kis_processing_information.h" #include "kis_busy_progress_indicator.h" #include "kis_merge_walker.h" #include "kis_refresh_subtree_walker.h" #include "kis_abstract_projection_plane.h" //#define DEBUG_MERGER #ifdef DEBUG_MERGER #define DEBUG_NODE_ACTION(message, type, leaf, rect) \ qDebug() << message << type << ":" << leaf->node()->name() << rect #else #define DEBUG_NODE_ACTION(message, type, leaf, rect) #endif class KisUpdateOriginalVisitor : public KisNodeVisitor { public: KisUpdateOriginalVisitor(const QRect &updateRect, KisPaintDeviceSP projection, const QRect &cropRect) : m_updateRect(updateRect), m_cropRect(cropRect), m_projection(projection) { } ~KisUpdateOriginalVisitor() override { } public: using KisNodeVisitor::visit; bool visit(KisAdjustmentLayer* layer) override { if (!layer->visible()) return true; if (!m_projection) { warnImage << "ObligeChild mechanism has been activated for " "an adjustment layer! Do nothing..."; layer->original()->clear(); return true; } KisPaintDeviceSP originalDevice = layer->original(); originalDevice->clear(m_updateRect); const QRect applyRect = m_updateRect & m_projection->extent(); // If the intersection of the updaterect and the projection extent is // null, we are finish here. if(applyRect.isNull()) return true; KisFilterConfigurationSP filterConfig = layer->filter(); if (!filterConfig) { /** * When an adjustment layer is just created, it may have no * filter inside. Then the layer has work as a pass-through * node. Just copy the merged data to the layer's original. */ KisPainter::copyAreaOptimized(applyRect.topLeft(), m_projection, originalDevice, applyRect); return true; } KisSelectionSP selection = layer->fetchComposedInternalSelection(applyRect); const QRect filterRect = selection ? applyRect & selection->selectedRect() : applyRect; KisFilterSP filter = KisFilterRegistry::instance()->value(filterConfig->name()); if (!filter) return false; KisPaintDeviceSP dstDevice = originalDevice; if (selection) { dstDevice = new KisPaintDevice(originalDevice->colorSpace()); } if (!filterRect.isEmpty()) { KIS_ASSERT_RECOVER_NOOP(layer->busyProgressIndicator()); layer->busyProgressIndicator()->update(); // We do not create a transaction here, as srcDevice != dstDevice filter->process(m_projection, dstDevice, 0, filterRect, filterConfig.data(), 0); } if (selection) { KisPainter::copyAreaOptimized(applyRect.topLeft(), m_projection, originalDevice, applyRect); KisPainter::copyAreaOptimized(filterRect.topLeft(), dstDevice, originalDevice, filterRect, selection); } return true; } bool visit(KisExternalLayer*) override { return true; } bool visit(KisGeneratorLayer*) override { return true; } bool visit(KisPaintLayer*) override { return true; } bool visit(KisGroupLayer*) override { return true; } bool visit(KisCloneLayer *layer) override { QRect emptyRect; KisRefreshSubtreeWalker walker(emptyRect); KisAsyncMerger merger; KisLayerSP srcLayer = layer->copyFrom(); QRect srcRect = m_updateRect.translated(-layer->x(), -layer->y()); QRegion prepareRegion(srcRect); prepareRegion -= m_cropRect; /** * If a clone has complicated masks, we should prepare additional * source area to ensure the rect is prepared. */ QRect needRectOnSource = layer->needRectOnSourceForMasks(srcRect); if (!needRectOnSource.isEmpty()) { prepareRegion += needRectOnSource; } Q_FOREACH (const QRect &rect, prepareRegion.rects()) { walker.collectRects(srcLayer, rect); merger.startMerge(walker, false); } return true; } bool visit(KisNode*) override { return true; } bool visit(KisFilterMask*) override { return true; } bool visit(KisTransformMask*) override { return true; } bool visit(KisTransparencyMask*) override { return true; } bool visit(KisSelectionMask*) override { return true; } bool visit(KisColorizeMask*) override { return true; } private: QRect m_updateRect; QRect m_cropRect; KisPaintDeviceSP m_projection; }; /*********************************************************************/ /* KisAsyncMerger */ /*********************************************************************/ void KisAsyncMerger::startMerge(KisBaseRectsWalker &walker, bool notifyClones) { KisMergeWalker::LeafStack &leafStack = walker.leafStack(); const bool useTempProjections = walker.needRectVaries(); while(!leafStack.isEmpty()) { KisMergeWalker::JobItem item = leafStack.pop(); KisProjectionLeafSP currentLeaf = item.m_leaf; + /** + * In some unidentified cases teh nodes might be removed + * while the updates are still running. We have no proof + * of it yet, so just add a safety assert here. + */ + KIS_SAFE_ASSERT_RECOVER_RETURN(currentLeaf); + KIS_SAFE_ASSERT_RECOVER_RETURN(currentLeaf->node()); + // All the masks should be filtered by the walkers - Q_ASSERT(currentLeaf); KIS_SAFE_ASSERT_RECOVER_RETURN(currentLeaf->isLayer()); QRect applyRect = item.m_applyRect; if (currentLeaf->isRoot()) { currentLeaf->projectionPlane()->recalculate(applyRect, walker.startNode()); continue; } if(item.m_position & KisMergeWalker::N_EXTRA) { // The type of layers that will not go to projection. DEBUG_NODE_ACTION("Updating", "N_EXTRA", currentLeaf, applyRect); KisUpdateOriginalVisitor originalVisitor(applyRect, m_currentProjection, walker.cropRect()); currentLeaf->accept(originalVisitor); currentLeaf->projectionPlane()->recalculate(applyRect, currentLeaf->node()); continue; } if (!m_currentProjection) { setupProjection(currentLeaf, applyRect, useTempProjections); } KisUpdateOriginalVisitor originalVisitor(applyRect, m_currentProjection, walker.cropRect()); if(item.m_position & KisMergeWalker::N_FILTHY) { DEBUG_NODE_ACTION("Updating", "N_FILTHY", currentLeaf, applyRect); if (currentLeaf->visible()) { currentLeaf->accept(originalVisitor); currentLeaf->projectionPlane()->recalculate(applyRect, walker.startNode()); } } else if(item.m_position & KisMergeWalker::N_ABOVE_FILTHY) { DEBUG_NODE_ACTION("Updating", "N_ABOVE_FILTHY", currentLeaf, applyRect); if(currentLeaf->dependsOnLowerNodes()) { if (currentLeaf->visible()) { currentLeaf->accept(originalVisitor); currentLeaf->projectionPlane()->recalculate(applyRect, currentLeaf->node()); } } } else if(item.m_position & KisMergeWalker::N_FILTHY_PROJECTION) { DEBUG_NODE_ACTION("Updating", "N_FILTHY_PROJECTION", currentLeaf, applyRect); if (currentLeaf->visible()) { currentLeaf->projectionPlane()->recalculate(applyRect, walker.startNode()); } } else /*if(item.m_position & KisMergeWalker::N_BELOW_FILTHY)*/ { DEBUG_NODE_ACTION("Updating", "N_BELOW_FILTHY", currentLeaf, applyRect); /* nothing to do */ } compositeWithProjection(currentLeaf, applyRect); if(item.m_position & KisMergeWalker::N_TOPMOST) { writeProjection(currentLeaf, useTempProjections, applyRect); resetProjection(); } // FIXME: remove it from the inner loop and/or change to a warning! Q_ASSERT(currentLeaf->projection()->defaultBounds()->currentLevelOfDetail() == walker.levelOfDetail()); } if(notifyClones) { doNotifyClones(walker); } if(m_currentProjection) { warnImage << "BUG: The walker hasn't reached the root layer!"; warnImage << " Start node:" << walker.startNode() << "Requested rect:" << walker.requestedRect(); warnImage << " An inconsistency in the walkers occurred!"; warnImage << " Please report a bug describing how you got this message."; // reset projection to avoid artifacts in next merges and allow people to work further resetProjection(); } } void KisAsyncMerger::resetProjection() { m_currentProjection = 0; m_finalProjection = 0; } void KisAsyncMerger::setupProjection(KisProjectionLeafSP currentLeaf, const QRect& rect, bool useTempProjection) { KisPaintDeviceSP parentOriginal = currentLeaf->parent()->original(); if (parentOriginal != currentLeaf->projection()) { if (useTempProjection) { if(!m_cachedPaintDevice) m_cachedPaintDevice = new KisPaintDevice(parentOriginal->colorSpace()); m_currentProjection = m_cachedPaintDevice; m_currentProjection->prepareClone(parentOriginal); m_finalProjection = parentOriginal; } else { parentOriginal->clear(rect); m_finalProjection = m_currentProjection = parentOriginal; } } else { /** * It happened so that our parent uses our own projection as * its original. It means obligeChild mechanism works. * We won't initialise m_currentProjection. This will cause * writeProjection() and compositeWithProjection() do nothing * when called. */ /* NOP */ } } void KisAsyncMerger::writeProjection(KisProjectionLeafSP topmostLeaf, bool useTempProjection, const QRect &rect) { Q_UNUSED(useTempProjection); Q_UNUSED(topmostLeaf); if (!m_currentProjection) return; if(m_currentProjection != m_finalProjection) { KisPainter::copyAreaOptimized(rect.topLeft(), m_currentProjection, m_finalProjection, rect); } DEBUG_NODE_ACTION("Writing projection", "", topmostLeaf->parent(), rect); } bool KisAsyncMerger::compositeWithProjection(KisProjectionLeafSP leaf, const QRect &rect) { if (!m_currentProjection) return true; if (!leaf->visible()) return true; KisPainter gc(m_currentProjection); leaf->projectionPlane()->apply(&gc, rect); DEBUG_NODE_ACTION("Compositing projection", "", leaf, rect); return true; } void KisAsyncMerger::doNotifyClones(KisBaseRectsWalker &walker) { KisBaseRectsWalker::CloneNotificationsVector &vector = walker.cloneNotifications(); KisBaseRectsWalker::CloneNotificationsVector::iterator it; for(it = vector.begin(); it != vector.end(); ++it) { (*it).notify(); } } diff --git a/libs/image/kis_projection_leaf.cpp b/libs/image/kis_projection_leaf.cpp index b5634ddd83..816305889f 100644 --- a/libs/image/kis_projection_leaf.cpp +++ b/libs/image/kis_projection_leaf.cpp @@ -1,385 +1,385 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_projection_leaf.h" #include #include "kis_layer.h" #include "kis_mask.h" #include "kis_group_layer.h" #include "kis_selection_mask.h" #include "kis_adjustment_layer.h" #include "krita_utils.h" #include "kis_refresh_subtree_walker.h" #include "kis_async_merger.h" #include "kis_node_graph_listener.h" struct Q_DECL_HIDDEN KisProjectionLeaf::Private { Private(KisNode *_node) : node(_node) {} - KisNode* node; + KisNodeWSP node; bool isTemporaryHidden = false; static bool checkPassThrough(const KisNode *node) { const KisGroupLayer *group = qobject_cast(node); return group && group->passThroughMode(); } static bool isSelectionMask(const KisNode *node) { return qobject_cast(node); } static KisNodeSP skipSelectionMasksForward(KisNodeSP node) { while (node && isSelectionMask(node)) { node = node->nextSibling(); } return node; } static KisNodeSP skipSelectionMasksBackward(KisNodeSP node) { while (node && isSelectionMask(node)) { node = node->prevSibling(); } return node; } bool checkParentPassThrough() { return node->parent() && checkPassThrough(node->parent()); } bool checkThisPassThrough() { return checkPassThrough(node); } KisProjectionLeafSP overlayProjectionLeaf() const { return node && node->graphListener() && node->graphListener()->graphOverlayNode() ? node->graphListener()->graphOverlayNode()->projectionLeaf() : 0; } bool isTopmostNode() const { return !skipSelectionMasksForward(node->nextSibling()) && node->parent() && !node->parent()->parent(); } KisNodeSP findRoot() const { KisNodeSP root = node; while (root->parent()) { root = root->parent(); } return root; } void temporarySetPassThrough(bool value) { - KisGroupLayer *group = qobject_cast(node); + KisGroupLayer *group = qobject_cast(node.data()); if (!group) return; group->setPassThroughMode(value); } }; KisProjectionLeaf::KisProjectionLeaf(KisNode *node) : m_d(new Private(node)) { } KisProjectionLeaf::~KisProjectionLeaf() { } KisProjectionLeafSP KisProjectionLeaf::parent() const { KisNodeSP node; if (Private::isSelectionMask(m_d->node)) { if (m_d->overlayProjectionLeaf() == this) { node = m_d->findRoot(); } } else { node = m_d->node->parent(); } while (node && Private::checkPassThrough(node)) { node = node->parent(); } return node ? node->projectionLeaf() : KisProjectionLeafSP(); } KisProjectionLeafSP KisProjectionLeaf::firstChild() const { KisNodeSP node; if (!m_d->checkThisPassThrough()) { node = m_d->node->firstChild(); node = Private::skipSelectionMasksForward(node); } if (!node && isRoot()) { KisProjectionLeafSP overlayLeaf = m_d->overlayProjectionLeaf(); if (overlayLeaf) { return overlayLeaf; } } return node ? node->projectionLeaf() : KisProjectionLeafSP(); } KisProjectionLeafSP KisProjectionLeaf::lastChild() const { KisNodeSP node; if (isRoot()) { KisProjectionLeafSP overlayLeaf = m_d->overlayProjectionLeaf(); if (overlayLeaf) { return overlayLeaf; } } if (!m_d->checkThisPassThrough()) { node = m_d->node->lastChild(); node = Private::skipSelectionMasksBackward(node); } return node ? node->projectionLeaf() : KisProjectionLeafSP(); } KisProjectionLeafSP KisProjectionLeaf::prevSibling() const { if (Private::isSelectionMask(m_d->node)) { KisProjectionLeafSP leaf; if (m_d->overlayProjectionLeaf() == this) { KisNodeSP node = m_d->findRoot()->lastChild(); node = Private::skipSelectionMasksBackward(node); leaf = node->projectionLeaf(); } return leaf; } KisNodeSP node; if (m_d->checkThisPassThrough()) { node = m_d->node->lastChild(); node = Private::skipSelectionMasksBackward(node); } if (!node) { node = m_d->node->prevSibling(); node = Private::skipSelectionMasksBackward(node); } const KisProjectionLeaf *leaf = this; while (!node && leaf->m_d->checkParentPassThrough()) { leaf = leaf->node()->parent()->projectionLeaf().data(); node = leaf->node()->prevSibling(); node = Private::skipSelectionMasksBackward(node); } return node ? node->projectionLeaf() : KisProjectionLeafSP(); } KisProjectionLeafSP KisProjectionLeaf::nextSibling() const { if (Private::isSelectionMask(m_d->node)) { return KisProjectionLeafSP(); } KisProjectionLeafSP overlayLeaf = m_d->overlayProjectionLeaf(); if (overlayLeaf && m_d->isTopmostNode()) { return overlayLeaf; } KisNodeSP node = m_d->node->nextSibling(); node = Private::skipSelectionMasksForward(node); while (node && Private::checkPassThrough(node) && node->firstChild()) { node = node->firstChild(); node = Private::skipSelectionMasksForward(node); } if (!node && m_d->checkParentPassThrough()) { node = m_d->node->parent(); node = Private::skipSelectionMasksForward(node); } return node ? node->projectionLeaf() : KisProjectionLeafSP(); } KisNodeSP KisProjectionLeaf::node() const { return m_d->node; } KisAbstractProjectionPlaneSP KisProjectionLeaf::projectionPlane() const { return m_d->node->projectionPlane(); } bool KisProjectionLeaf::accept(KisNodeVisitor &visitor) { return m_d->node->accept(visitor); } KisPaintDeviceSP KisProjectionLeaf::original() { return m_d->node->original(); } KisPaintDeviceSP KisProjectionLeaf::projection() { return m_d->node->projection(); } bool KisProjectionLeaf::isRoot() const { return (bool)!m_d->node->parent(); } bool KisProjectionLeaf::isLayer() const { - return (bool)qobject_cast(m_d->node); + return (bool)qobject_cast(m_d->node.data()); } bool KisProjectionLeaf::isMask() const { - return (bool)qobject_cast(m_d->node); + return (bool)qobject_cast(m_d->node.data()); } bool KisProjectionLeaf::canHaveChildLayers() const { - return (bool)qobject_cast(m_d->node); + return (bool)qobject_cast(m_d->node.data()); } bool KisProjectionLeaf::dependsOnLowerNodes() const { - return (bool)qobject_cast(m_d->node); + return (bool)qobject_cast(m_d->node.data()); } bool KisProjectionLeaf::visible() const { if (m_d->isTemporaryHidden) return false; // TODO: check opacity as well! bool hiddenByParentPassThrough = false; KisNodeSP node = m_d->node->parent(); while (node && node->projectionLeaf()->m_d->checkThisPassThrough()) { hiddenByParentPassThrough |= !node->visible(); node = node->parent(); } return m_d->node->visible(false) && !m_d->checkThisPassThrough() && !hiddenByParentPassThrough; } quint8 KisProjectionLeaf::opacity() const { quint8 resultOpacity = m_d->node->opacity(); if (m_d->checkParentPassThrough()) { quint8 parentOpacity = m_d->node->parent()->projectionLeaf()->opacity(); resultOpacity = KritaUtils::mergeOpacity(resultOpacity, parentOpacity); } return resultOpacity; } QBitArray KisProjectionLeaf::channelFlags() const { QBitArray channelFlags; - KisLayer *layer = qobject_cast(m_d->node); + KisLayer *layer = qobject_cast(m_d->node.data()); if (!layer) return channelFlags; channelFlags = layer->channelFlags(); if (m_d->checkParentPassThrough()) { QBitArray parentChannelFlags; if (*m_d->node->colorSpace() == *m_d->node->parent()->colorSpace()) { KisLayer *parentLayer = qobject_cast(m_d->node->parent().data()); parentChannelFlags = parentLayer->channelFlags(); } channelFlags = KritaUtils::mergeChannelFlags(channelFlags, parentChannelFlags); } return channelFlags; } bool KisProjectionLeaf::isStillInGraph() const { return (bool)m_d->node->graphListener(); } bool KisProjectionLeaf::isDroppedMask() const { - return qobject_cast(m_d->node) && + return qobject_cast(m_d->node.data()) && m_d->checkParentPassThrough(); } bool KisProjectionLeaf::isOverlayProjectionLeaf() const { return this == m_d->overlayProjectionLeaf(); } void KisProjectionLeaf::setTemporaryHiddenFromRendering(bool value) { m_d->isTemporaryHidden = value; } bool KisProjectionLeaf::isTemporaryHiddenFromRendering() const { return m_d->isTemporaryHidden; } /** * This method is rather slow and dangerous. It should be executes in * exclusive environment only. */ void KisProjectionLeaf::explicitlyRegeneratePassThroughProjection() { if (!m_d->checkThisPassThrough()) return; m_d->temporarySetPassThrough(false); const QRect updateRect = projection()->defaultBounds()->bounds(); KisRefreshSubtreeWalker walker(updateRect); walker.collectRects(m_d->node, updateRect); KisAsyncMerger merger; merger.startMerge(walker); m_d->temporarySetPassThrough(true); }