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

0001 /*
0002  *  SPDX-FileCopyrightText: 2006 Boudewijn Rempt <boud@valdyas.org>
0003  *  SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "kis_mask.h"
0009 
0010 
0011 #include <kis_debug.h>
0012 
0013 // to prevent incomplete class types on "delete selection->flatten();"
0014 #include <kundo2command.h>
0015 
0016 #include <QScopedPointer>
0017 
0018 #include <KoColor.h>
0019 #include <KoColorSpace.h>
0020 #include <KoCompositeOpRegistry.h>
0021 
0022 #include "kis_paint_device.h"
0023 #include "kis_selection.h"
0024 #include "kis_pixel_selection.h"
0025 #include "kis_painter.h"
0026 
0027 #include "kis_image.h"
0028 #include "kis_layer.h"
0029 
0030 #include "kis_cached_paint_device.h"
0031 #include "kis_mask_projection_plane.h"
0032 
0033 #include "kis_raster_keyframe_channel.h"
0034 #include "KisSafeNodeProjectionStore.h"
0035 
0036 
0037 struct Q_DECL_HIDDEN KisMask::Private {
0038     Private(KisMask *_q)
0039         : q(_q),
0040           projectionPlane(new KisMaskProjectionPlane(q))
0041     {
0042     }
0043 
0044     mutable KisSelectionSP selection;
0045     KisCachedPaintDevice paintDeviceCache;
0046     KisMask *q;
0047 
0048     /**
0049      * Due to the design of the Kra format the X,Y offset of the paint
0050      * device belongs to the node, but not to the device itself.  So
0051      * the offset is set when the node is created, but not when the
0052      * selection is initialized. This causes the X,Y values to be
0053      * lost, since the selection does not exist at the moment. That is
0054      * why we save it separately.
0055      */
0056     QScopedPointer<QPoint> deferredSelectionOffset;
0057 
0058     KisAbstractProjectionPlaneSP projectionPlane;
0059     KisSafeSelectionNodeProjectionStoreSP safeProjection;
0060 
0061     void initSelectionImpl(KisSelectionSP copyFrom, KisLayerSP parentLayer, KisPaintDeviceSP copyFromDevice);
0062 };
0063 
0064 KisMask::KisMask(KisImageWSP image, const QString &name)
0065         : KisNode(image)
0066         , m_d(new Private(this))
0067 {
0068     setName(name);
0069     m_d->safeProjection = new KisSafeSelectionNodeProjectionStore();
0070     m_d->safeProjection->setImage(image);
0071 }
0072 
0073 KisMask::KisMask(const KisMask& rhs)
0074         : KisNode(rhs)
0075         , KisIndirectPaintingSupport()
0076         , m_d(new Private(this))
0077 {
0078     setName(rhs.name());
0079 
0080     m_d->safeProjection = new KisSafeSelectionNodeProjectionStore(*rhs.m_d->safeProjection);
0081 
0082     if (rhs.m_d->selection) {
0083         m_d->selection = new KisSelection(*rhs.m_d->selection.data());
0084         m_d->selection->setParentNode(this);
0085 
0086         KisPixelSelectionSP pixelSelection = m_d->selection->pixelSelection();
0087         if (pixelSelection->framesInterface()) {
0088             addKeyframeChannel(pixelSelection->keyframeChannel());
0089             enableAnimation();
0090         }
0091     }
0092 }
0093 
0094 KisMask::~KisMask()
0095 {
0096     if (m_d->selection) {
0097         m_d->selection->setParentNode(0);
0098     }
0099 
0100     delete m_d;
0101 }
0102 
0103 void KisMask::setImage(KisImageWSP image)
0104 {
0105     KisPaintDeviceSP parentPaintDevice = parent() ? parent()->original() : 0;
0106     KisDefaultBoundsBaseSP defaultBounds;
0107 
0108     if (parentPaintDevice) {
0109         defaultBounds = new KisSelectionDefaultBounds(parentPaintDevice);
0110     } else {
0111         if (image) {
0112             qWarning() << "WARNING: KisMask::setImage() was called without any parent layer being set";
0113         }
0114 
0115         // just a fallback solution
0116         defaultBounds = new KisDefaultBounds(image);
0117     }
0118 
0119     if (m_d->selection) {
0120         m_d->selection->setDefaultBounds(defaultBounds);
0121     }
0122 
0123     m_d->safeProjection->setImage(image);
0124 
0125     KisNode::setImage(image);
0126 }
0127 
0128 bool KisMask::allowAsChild(KisNodeSP node) const
0129 {
0130     Q_UNUSED(node);
0131     return false;
0132 }
0133 
0134 const KoColorSpace * KisMask::colorSpace() const
0135 {
0136     KisNodeSP parentNode = parent();
0137     return parentNode ? parentNode->colorSpace() : 0;
0138 }
0139 
0140 const KoCompositeOp * KisMask::compositeOp() const
0141 {
0142     /**
0143      * FIXME: This function duplicates the same function from
0144      * KisLayer. We can't move it to KisBaseNode as it doesn't
0145      * know anything about parent() method of KisNode
0146      * Please think it over...
0147      */
0148 
0149     const KoColorSpace *colorSpace = this->colorSpace();
0150     if (!colorSpace) return 0;
0151 
0152     const KoCompositeOp* op = colorSpace->compositeOp(compositeOpId());
0153     return op ? op : colorSpace->compositeOp(COMPOSITE_OVER);
0154 }
0155 
0156 void KisMask::initSelection(KisSelectionSP copyFrom, KisLayerSP parentLayer)
0157 {
0158     m_d->initSelectionImpl(copyFrom, parentLayer, 0);
0159 }
0160 
0161 void KisMask::initSelection(KisPaintDeviceSP copyFromDevice, KisLayerSP parentLayer)
0162 {
0163     m_d->initSelectionImpl(0, parentLayer, copyFromDevice);
0164 }
0165 
0166 void KisMask::initSelection(KisLayerSP parentLayer)
0167 {
0168     m_d->initSelectionImpl(0, parentLayer, 0);
0169 }
0170 
0171 void KisMask::Private::initSelectionImpl(KisSelectionSP copyFrom, KisLayerSP parentLayer, KisPaintDeviceSP copyFromDevice)
0172 {
0173     Q_ASSERT(parentLayer);
0174 
0175     KisPaintDeviceSP parentPaintDevice = parentLayer->original();
0176 
0177     if (copyFrom) {
0178         /**
0179          * We can't use setSelection as we may not have parent() yet
0180          */
0181         selection = new KisSelection(*copyFrom);
0182         selection->setDefaultBounds(new KisSelectionDefaultBounds(parentPaintDevice));
0183     } else if (copyFromDevice) {
0184         KritaUtils::DeviceCopyMode copyMode =
0185             q->inherits("KisFilterMask") || q->inherits("KisTransparencyMask") ?
0186             KritaUtils::CopyAllFrames : KritaUtils::CopySnapshot;
0187 
0188         selection = new KisSelection(copyFromDevice, copyMode, new KisSelectionDefaultBounds(parentPaintDevice));
0189 
0190         KisPixelSelectionSP pixelSelection = selection->pixelSelection();
0191         if (pixelSelection->framesInterface()) {
0192             KisRasterKeyframeChannel *keyframeChannel = pixelSelection->keyframeChannel();
0193             keyframeChannel->setFilenameSuffix(".pixelselection");
0194 
0195             q->addKeyframeChannel(keyframeChannel);
0196             q->enableAnimation();
0197         }
0198     } else {
0199         selection = new KisSelection(new KisSelectionDefaultBounds(parentPaintDevice));
0200         selection->pixelSelection()->setDefaultPixel(KoColor(Qt::white, selection->pixelSelection()->colorSpace()));
0201 
0202         if (deferredSelectionOffset) {
0203             selection->setX(deferredSelectionOffset->x());
0204             selection->setY(deferredSelectionOffset->y());
0205             deferredSelectionOffset.reset();
0206         }
0207     }
0208     selection->setParentNode(q);
0209     selection->updateProjection();
0210 }
0211 
0212 KisSelectionSP KisMask::selection() const
0213 {
0214     return m_d->selection;
0215 }
0216 
0217 KisPaintDeviceSP KisMask::paintDevice() const
0218 {
0219     KisSelectionSP selection = this->selection();
0220     return selection ? selection->pixelSelection() : 0;
0221 }
0222 
0223 KisPaintDeviceSP KisMask::original() const
0224 {
0225     return paintDevice();
0226 }
0227 
0228 KisPaintDeviceSP KisMask::projection() const
0229 {
0230     KisPaintDeviceSP originalDevice = original();
0231     KisPaintDeviceSP result = originalDevice;
0232 
0233     KisSelectionSP selection = this->selection();
0234     if (selection && hasTemporaryTarget()) {
0235         result = m_d->safeProjection->getDeviceLazy(selection)->pixelSelection();
0236     }
0237 
0238     return result;
0239 }
0240 
0241 KisAbstractProjectionPlaneSP KisMask::projectionPlane() const
0242 {
0243     return m_d->projectionPlane;
0244 }
0245 
0246 void KisMask::setSelection(KisSelectionSP selection)
0247 {
0248     m_d->selection = selection;
0249     if (parent()) {
0250         const KisLayer *parentLayer = qobject_cast<const KisLayer*>(parent());
0251         m_d->selection->setDefaultBounds(new KisDefaultBounds(parentLayer->image()));
0252     }
0253     m_d->selection->setParentNode(this);
0254 }
0255 
0256 void KisMask::select(const QRect & rc, quint8 selectedness)
0257 {
0258     KisSelectionSP sel = selection();
0259     KisPixelSelectionSP psel = sel->pixelSelection();
0260     psel->select(rc, selectedness);
0261     sel->updateProjection(rc);
0262 }
0263 
0264 
0265 QRect KisMask::decorateRect(KisPaintDeviceSP &src,
0266                             KisPaintDeviceSP &dst,
0267                             const QRect & rc,
0268                             PositionToFilthy maskPos) const
0269 {
0270     Q_UNUSED(src);
0271     Q_UNUSED(dst);
0272     Q_UNUSED(maskPos);
0273     Q_ASSERT_X(0, "KisMask::decorateRect", "Should be overridden by successors");
0274     return rc;
0275 }
0276 
0277 bool KisMask::paintsOutsideSelection() const
0278 {
0279     return false;
0280 }
0281 
0282 void KisMask::apply(KisPaintDeviceSP projection, const QRect &applyRect, const QRect &needRect, PositionToFilthy maskPos) const
0283 {
0284     if (selection()) {
0285 
0286         flattenSelectionProjection(m_d->selection, applyRect);
0287 
0288         KisSelectionSP effectiveSelection = m_d->selection;
0289 
0290         {
0291             // Access temporary target under the lock held
0292             KisIndirectPaintingSupport::ReadLocker l(this);
0293 
0294             if (!paintsOutsideSelection()) {
0295                 // extent of m_d->selection should also be accessed under a lock,
0296                 // because it might be being merged in by the temporary target atm
0297                 QRect effectiveExtent = m_d->selection->selectedRect();
0298 
0299                 if (hasTemporaryTarget()) {
0300                     effectiveExtent |= temporaryTarget()->extent();
0301                 }
0302 
0303                 if(!effectiveExtent.intersects(applyRect)) {
0304                     return;
0305                 }
0306             }
0307 
0308             if (hasTemporaryTarget()) {
0309                 effectiveSelection = m_d->safeProjection->getDeviceLazy(m_d->selection);
0310 
0311                 KisPainter::copyAreaOptimized(applyRect.topLeft(),
0312                                               m_d->selection->pixelSelection(),
0313                                               effectiveSelection->pixelSelection(), applyRect);
0314 
0315                 KisPainter gc(effectiveSelection->pixelSelection());
0316                 setupTemporaryPainter(&gc);
0317                 gc.bitBlt(applyRect.topLeft(), temporaryTarget(), applyRect);
0318             } else {
0319                 m_d->safeProjection->releaseDevice();
0320             }
0321 
0322             mergeInMaskInternal(projection, effectiveSelection, applyRect, needRect, maskPos);
0323         }
0324 
0325     } else {
0326         mergeInMaskInternal(projection, 0, applyRect, needRect, maskPos);
0327     }
0328 }
0329 
0330 void KisMask::mergeInMaskInternal(KisPaintDeviceSP projection,
0331                                   KisSelectionSP effectiveSelection,
0332                                   const QRect &applyRect,
0333                                   const QRect &preparedNeedRect,
0334                                   KisNode::PositionToFilthy maskPos) const
0335 {
0336     KisCachedPaintDevice::Guard d1(projection, m_d->paintDeviceCache);
0337     KisPaintDeviceSP cacheDevice = d1.device();
0338 
0339     if (effectiveSelection) {
0340         QRect updatedRect = decorateRect(projection, cacheDevice, applyRect, maskPos);
0341 
0342         // masks don't have any compositioning
0343         KisPainter::copyAreaOptimized(updatedRect.topLeft(), cacheDevice, projection, updatedRect, effectiveSelection);
0344 
0345     } else {
0346         cacheDevice->makeCloneFromRough(projection, preparedNeedRect);
0347         projection->clear(preparedNeedRect);
0348 
0349         decorateRect(cacheDevice, projection, applyRect, maskPos);
0350     }
0351 }
0352 
0353 void KisMask::flattenSelectionProjection(KisSelectionSP selection, const QRect &dirtyRect) const
0354 {
0355     selection->updateProjection(dirtyRect);
0356 }
0357 
0358 QRect KisMask::needRect(const QRect &rect,  PositionToFilthy pos) const
0359 {
0360     Q_UNUSED(pos);
0361     QRect resultRect = rect;
0362     if (m_d->selection) {
0363         QRect selectionExtent = m_d->selection->selectedRect();
0364 
0365         // copy for thread safety!
0366         KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
0367 
0368         if (temporaryTarget) {
0369             selectionExtent |= temporaryTarget->extent();
0370         }
0371 
0372         resultRect &= selectionExtent;
0373     }
0374 
0375     return resultRect;
0376 }
0377 
0378 QRect KisMask::changeRect(const QRect &rect, PositionToFilthy pos) const
0379 {
0380     return KisMask::needRect(rect, pos);
0381 }
0382 
0383 QRect KisMask::extent() const
0384 {
0385     QRect resultRect;
0386 
0387     if (m_d->selection) {
0388         resultRect = m_d->selection->selectedRect();
0389 
0390         // copy for thread safety!
0391         KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
0392 
0393         if (temporaryTarget) {
0394             resultRect |= temporaryTarget->extent();
0395         }
0396     } else if (KisNodeSP parent = this->parent()) {
0397         resultRect = parent->extent();
0398     }
0399 
0400     return resultRect;
0401 }
0402 
0403 QRect KisMask::exactBounds() const
0404 {
0405     QRect resultRect;
0406 
0407     if (m_d->selection) {
0408         resultRect = m_d->selection->selectedExactRect();
0409 
0410         // copy for thread safety!
0411         KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
0412 
0413         if (temporaryTarget) {
0414             resultRect |= temporaryTarget->exactBounds();
0415         }
0416     } else if (KisNodeSP parent = this->parent()) {
0417         resultRect = parent->exactBounds();
0418     }
0419 
0420     return resultRect;
0421 }
0422 
0423 qint32 KisMask::x() const
0424 {
0425     return m_d->selection ? m_d->selection->x() :
0426            m_d->deferredSelectionOffset ? m_d->deferredSelectionOffset->x() :
0427            parent() ? parent()->x() : 0;
0428 }
0429 
0430 qint32 KisMask::y() const
0431 {
0432     return m_d->selection ? m_d->selection->y() :
0433            m_d->deferredSelectionOffset ? m_d->deferredSelectionOffset->y() :
0434            parent() ? parent()->y() : 0;
0435 }
0436 
0437 void KisMask::setX(qint32 x)
0438 {
0439     if (m_d->selection) {
0440         m_d->selection->setX(x);
0441     } else if (!m_d->deferredSelectionOffset) {
0442         m_d->deferredSelectionOffset.reset(new QPoint(x, 0));
0443     } else {
0444         m_d->deferredSelectionOffset->rx() = x;
0445     }
0446 }
0447 
0448 void KisMask::setY(qint32 y)
0449 {
0450     if (m_d->selection) {
0451         m_d->selection->setY(y);
0452     } else if (!m_d->deferredSelectionOffset) {
0453         m_d->deferredSelectionOffset.reset(new QPoint(0, y));
0454     } else {
0455         m_d->deferredSelectionOffset->ry() = y;
0456     }
0457 }
0458 
0459 QRect KisMask::nonDependentExtent() const
0460 {
0461     return QRect();
0462 }
0463 
0464 QImage KisMask::createThumbnail(qint32 w, qint32 h, Qt::AspectRatioMode aspectRatioMode)
0465 {
0466     KisPaintDeviceSP originalDevice =
0467         selection() ? selection()->projection() : 0;
0468 
0469     return originalDevice ?
0470            originalDevice->createThumbnail(w, h, aspectRatioMode, 1,
0471                                            KoColorConversionTransformation::internalRenderingIntent(),
0472                                            KoColorConversionTransformation::internalConversionFlags()) : QImage();
0473 }
0474 
0475 void KisMask::testingInitSelection(const QRect &rect, KisLayerSP parentLayer)
0476 {
0477     if (parentLayer) {
0478         m_d->selection = new KisSelection(new KisSelectionDefaultBounds(parentLayer->paintDevice()));
0479     } else {
0480         m_d->selection = new KisSelection();
0481     }
0482 
0483     m_d->selection->pixelSelection()->select(rect, OPACITY_OPAQUE_U8);
0484     m_d->selection->updateProjection(rect);
0485     m_d->selection->setParentNode(this);
0486 }
0487 
0488 bool KisMask::supportsLodPainting() const
0489 {
0490     return !m_d->selection || !m_d->selection->hasShapeSelection();
0491 }
0492 
0493 KisKeyframeChannel *KisMask::requestKeyframeChannel(const QString &id)
0494 {
0495     if (id == KisKeyframeChannel::Raster.id()) {
0496         KisPaintDeviceSP device = paintDevice();
0497         if (device) {
0498             KisRasterKeyframeChannel *contentChannel = device->createKeyframeChannel(KisKeyframeChannel::Raster);
0499             contentChannel->setFilenameSuffix(".pixelselection");
0500             return contentChannel;
0501        }
0502     }
0503 
0504     return KisNode::requestKeyframeChannel(id);
0505 }
0506 
0507 bool KisMask::supportsKeyframeChannel(const QString &id)
0508 {
0509     if (id == KisKeyframeChannel::Raster.id() && paintDevice()) {
0510         return true;
0511     }
0512 
0513     return KisNode::supportsKeyframeChannel(id);
0514 }
0515 
0516 void KisMask::baseNodeChangedCallback()
0517 {
0518     KisNodeSP up = parent();
0519     KisLayer *layer = dynamic_cast<KisLayer*>(up.data());
0520     if (layer) {
0521         layer->notifyChildMaskChanged();
0522     }
0523     KisNode::baseNodeChangedCallback();
0524 }