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

0001 /*
0002  *  SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include <KoIcon.h>
0008 #include <kis_icon.h>
0009 #include <KoCompositeOpRegistry.h>
0010 
0011 #include "kis_layer.h"
0012 #include "kis_filter_mask.h"
0013 #include "filter/kis_filter.h"
0014 #include "filter/kis_filter_configuration.h"
0015 #include "filter/kis_filter_registry.h"
0016 #include "kis_selection.h"
0017 #include "kis_processing_information.h"
0018 #include "kis_node.h"
0019 #include "kis_node_visitor.h"
0020 #include "kis_processing_visitor.h"
0021 #include "kis_busy_progress_indicator.h"
0022 #include "kis_transaction.h"
0023 #include "kis_painter.h"
0024 
0025 KisFilterMask::KisFilterMask(KisImageWSP image, const QString &name)
0026     : KisEffectMask(image, name),
0027       KisNodeFilterInterface(0)
0028 {
0029     setCompositeOpId(COMPOSITE_COPY);
0030 }
0031 
0032 KisFilterMask::~KisFilterMask()
0033 {
0034 }
0035 
0036 KisFilterMask::KisFilterMask(const KisFilterMask& rhs)
0037         : KisEffectMask(rhs)
0038         , KisNodeFilterInterface(rhs)
0039 {
0040 }
0041 
0042 QIcon KisFilterMask::icon() const
0043 {
0044     return KisIconUtils::loadIcon("filterMask");
0045 }
0046 
0047 void KisFilterMask::setFilter(KisFilterConfigurationSP  filterConfig, bool checkCompareConfig)
0048 {
0049     KisNodeFilterInterface::setFilter(filterConfig, checkCompareConfig);
0050 }
0051 
0052 QRect KisFilterMask::decorateRect(KisPaintDeviceSP &src,
0053                                   KisPaintDeviceSP &dst,
0054                                   const QRect & rc,
0055                                   PositionToFilthy maskPos) const
0056 {
0057     Q_UNUSED(maskPos);
0058 
0059     KisFilterConfigurationSP filterConfig = filter();
0060 
0061     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(nodeProgressProxy(), rc);
0062     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(
0063         src != dst &&
0064             "KisFilterMask::decorateRect: "
0065             "src must be != dst, because we can't create transactions "
0066             "during merge, as it breaks reentrancy",
0067         rc);
0068 
0069     if (!filterConfig) {
0070         return QRect();
0071     }
0072 
0073     KisFilterSP filter =
0074         KisFilterRegistry::instance()->value(filterConfig->name());
0075 
0076     if (!filter) {
0077         warnKrita << "Could not retrieve filter \"" << filterConfig->name() << "\"";
0078         return QRect();
0079     }
0080 
0081     KIS_ASSERT_RECOVER_NOOP(this->busyProgressIndicator());
0082     this->busyProgressIndicator()->update();
0083 
0084     filter->process(src, dst, 0, rc, filterConfig.data(), 0);
0085 
0086     QRect r = filter->changedRect(rc, filterConfig.data(), dst->defaultBounds()->currentLevelOfDetail());
0087     return r;
0088 }
0089 
0090 bool KisFilterMask::accept(KisNodeVisitor &v)
0091 {
0092     return v.visit(this);
0093 }
0094 
0095 void KisFilterMask::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
0096 {
0097     return visitor.visit(this, undoAdapter);
0098 }
0099 
0100 /**
0101  * FIXME: try to cache filter pointer inside a Private block
0102  */
0103 QRect KisFilterMask::changeRect(const QRect &rect, PositionToFilthy pos) const
0104 {
0105     /**
0106      * FIXME: This check of the emptiness should be done
0107      * on the higher/lower level
0108      */
0109     if(rect.isEmpty()) return rect;
0110 
0111     QRect filteredRect = rect;
0112 
0113     KisFilterConfigurationSP filterConfig = filter();
0114     if (filterConfig) {
0115         KisNodeSP parent = this->parent();
0116         const int lod = parent && parent->projection() ?
0117             parent->projection()->defaultBounds()->currentLevelOfDetail() : 0;
0118 
0119         KisFilterSP filter = KisFilterRegistry::instance()->value(filterConfig->name());
0120         filteredRect = filter->changedRect(rect, filterConfig.data(), lod);
0121     }
0122 
0123     /**
0124      * We can't paint outside a selection, that is why we call
0125      * KisMask::changeRect to crop actual change area in the end
0126      */
0127     filteredRect = KisMask::changeRect(filteredRect, pos);
0128     /**
0129      * FIXME: Think over this solution
0130      * Union of rects means that changeRect returns NOT the rect
0131      * changed by this very layer, but an accumulated rect changed
0132      * by all underlying layers. Just take into account and change
0133      * documentation accordingly
0134      */
0135     return rect | filteredRect;
0136 }
0137 
0138 QRect KisFilterMask::needRect(const QRect& rect, PositionToFilthy pos) const
0139 {
0140     Q_UNUSED(pos);
0141 
0142     /**
0143      * FIXME: This check of the emptiness should be done
0144      * on the higher/lower level
0145      */
0146 
0147     if(rect.isEmpty()) return rect;
0148 
0149     KisFilterConfigurationSP filterConfig = filter();
0150     if (!filterConfig) return rect;
0151 
0152     KisNodeSP parent = this->parent();
0153     const int lod = parent && parent->projection() ?
0154         parent->projection()->defaultBounds()->currentLevelOfDetail() : 0;
0155 
0156     KisFilterSP filter = KisFilterRegistry::instance()->value(filterConfig->name());
0157 
0158     /**
0159      * If we need some additional pixels even outside of a selection
0160      * for accurate layer filtering, we'll get them!
0161      * And no KisMask::needRect will prevent us from doing this! ;)
0162      * That's why simply we do not call KisMask::needRect here :)
0163      */
0164     return filter->neededRect(rect, filterConfig.data(), lod);
0165 }
0166