File indexing completed on 2024-05-19 04:26:22

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, KisImageWSP image);
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         m_d->selection->setResolutionProxy(m_d->selection->resolutionProxy()->createOrCloneDetached(image));
0122     }
0123 
0124     m_d->safeProjection->setImage(image);
0125 
0126     KisNode::setImage(image);
0127 }
0128 
0129 bool KisMask::allowAsChild(KisNodeSP node) const
0130 {
0131     Q_UNUSED(node);
0132     return false;
0133 }
0134 
0135 const KoColorSpace * KisMask::colorSpace() const
0136 {
0137     KisNodeSP parentNode = parent();
0138     return parentNode ? parentNode->colorSpace() : 0;
0139 }
0140 
0141 const KoCompositeOp * KisMask::compositeOp() const
0142 {
0143     /**
0144      * FIXME: This function duplicates the same function from
0145      * KisLayer. We can't move it to KisBaseNode as it doesn't
0146      * know anything about parent() method of KisNode
0147      * Please think it over...
0148      */
0149 
0150     const KoColorSpace *colorSpace = this->colorSpace();
0151     if (!colorSpace) return 0;
0152 
0153     const KoCompositeOp* op = colorSpace->compositeOp(compositeOpId());
0154     return op ? op : colorSpace->compositeOp(COMPOSITE_OVER);
0155 }
0156 
0157 void KisMask::initSelection(KisSelectionSP copyFrom, KisLayerSP parentLayer)
0158 {
0159     m_d->initSelectionImpl(copyFrom, parentLayer, 0, image());
0160 }
0161 
0162 void KisMask::initSelection(KisPaintDeviceSP copyFromDevice, KisLayerSP parentLayer)
0163 {
0164     m_d->initSelectionImpl(0, parentLayer, copyFromDevice, image());
0165 }
0166 
0167 void KisMask::initSelection(KisLayerSP parentLayer)
0168 {
0169     m_d->initSelectionImpl(0, parentLayer, 0, image());
0170 }
0171 
0172 void KisMask::Private::initSelectionImpl(KisSelectionSP copyFrom, KisLayerSP parentLayer, KisPaintDeviceSP copyFromDevice, KisImageWSP image)
0173 {
0174     Q_ASSERT(parentLayer);
0175 
0176     KisPaintDeviceSP parentPaintDevice = parentLayer->original();
0177 
0178     if (copyFrom) {
0179         /**
0180          * We can't use setSelection as we may not have parent() yet
0181          */
0182         selection = new KisSelection(*copyFrom);
0183         selection->setDefaultBounds(new KisSelectionDefaultBounds(parentPaintDevice));
0184         selection->setResolutionProxy(copyFrom->resolutionProxy()->createOrCloneDetached(image));
0185     } else if (copyFromDevice) {
0186         KritaUtils::DeviceCopyMode copyMode =
0187             q->inherits("KisFilterMask") || q->inherits("KisTransparencyMask") ?
0188             KritaUtils::CopyAllFrames : KritaUtils::CopySnapshot;
0189 
0190         selection = new KisSelection(copyFromDevice, copyMode,
0191                                      new KisSelectionDefaultBounds(parentPaintDevice),
0192                                      toQShared(new KisImageResolutionProxy(image)));
0193 
0194         KisPixelSelectionSP pixelSelection = selection->pixelSelection();
0195         if (pixelSelection->framesInterface()) {
0196             KisRasterKeyframeChannel *keyframeChannel = pixelSelection->keyframeChannel();
0197             keyframeChannel->setFilenameSuffix(".pixelselection");
0198 
0199             q->addKeyframeChannel(keyframeChannel);
0200             q->enableAnimation();
0201         }
0202     } else {
0203         selection = new KisSelection(new KisSelectionDefaultBounds(parentPaintDevice),
0204                                      toQShared(new KisImageResolutionProxy(image)));
0205         selection->pixelSelection()->setDefaultPixel(KoColor(Qt::white, selection->pixelSelection()->colorSpace()));
0206 
0207         if (deferredSelectionOffset) {
0208             selection->setX(deferredSelectionOffset->x());
0209             selection->setY(deferredSelectionOffset->y());
0210             deferredSelectionOffset.reset();
0211         }
0212     }
0213     selection->setParentNode(q);
0214     selection->pixelSelection()->setSupportsWraparoundMode(true);
0215     selection->updateProjection();
0216 }
0217 
0218 KisSelectionSP KisMask::selection() const
0219 {
0220     return m_d->selection;
0221 }
0222 
0223 KisPaintDeviceSP KisMask::paintDevice() const
0224 {
0225     KisSelectionSP selection = this->selection();
0226     return selection ? selection->pixelSelection() : 0;
0227 }
0228 
0229 KisPaintDeviceSP KisMask::original() const
0230 {
0231     return paintDevice();
0232 }
0233 
0234 KisPaintDeviceSP KisMask::projection() const
0235 {
0236     KisPaintDeviceSP originalDevice = original();
0237     KisPaintDeviceSP result = originalDevice;
0238 
0239     KisSelectionSP selection = this->selection();
0240     if (selection && hasTemporaryTarget()) {
0241         result = m_d->safeProjection->getDeviceLazy(selection)->pixelSelection();
0242     }
0243 
0244     return result;
0245 }
0246 
0247 KisAbstractProjectionPlaneSP KisMask::projectionPlane() const
0248 {
0249     return m_d->projectionPlane;
0250 }
0251 
0252 void KisMask::setSelection(KisSelectionSP selection)
0253 {
0254     m_d->selection = selection;
0255     m_d->selection->setDefaultBounds(new KisDefaultBounds(image()));
0256     m_d->selection->setResolutionProxy(toQShared(new KisImageResolutionProxy(image())));
0257     m_d->selection->setParentNode(this);
0258     m_d->selection->pixelSelection()->setSupportsWraparoundMode(true);
0259 }
0260 
0261 void KisMask::select(const QRect & rc, quint8 selectedness)
0262 {
0263     KisSelectionSP sel = selection();
0264     KisPixelSelectionSP psel = sel->pixelSelection();
0265     psel->select(rc, selectedness);
0266     sel->updateProjection(rc);
0267 }
0268 
0269 
0270 QRect KisMask::decorateRect(KisPaintDeviceSP &src,
0271                             KisPaintDeviceSP &dst,
0272                             const QRect & rc,
0273                             PositionToFilthy maskPos) const
0274 {
0275     Q_UNUSED(src);
0276     Q_UNUSED(dst);
0277     Q_UNUSED(maskPos);
0278     Q_ASSERT_X(0, "KisMask::decorateRect", "Should be overridden by successors");
0279     return rc;
0280 }
0281 
0282 bool KisMask::paintsOutsideSelection() const
0283 {
0284     return false;
0285 }
0286 
0287 void KisMask::apply(KisPaintDeviceSP projection, const QRect &applyRect, const QRect &needRect, PositionToFilthy maskPos) const
0288 {
0289     if (selection()) {
0290 
0291         flattenSelectionProjection(m_d->selection, applyRect);
0292 
0293         KisSelectionSP effectiveSelection = m_d->selection;
0294 
0295         {
0296             // Access temporary target under the lock held
0297             KisIndirectPaintingSupport::ReadLocker l(this);
0298 
0299             if (!paintsOutsideSelection()) {
0300                 // extent of m_d->selection should also be accessed under a lock,
0301                 // because it might be being merged in by the temporary target atm
0302                 QRect effectiveExtent = m_d->selection->selectedRect();
0303 
0304                 if (hasTemporaryTarget()) {
0305                     effectiveExtent |= temporaryTarget()->extent();
0306                 }
0307 
0308                 if(!effectiveExtent.intersects(applyRect)) {
0309                     return;
0310                 }
0311             }
0312 
0313             if (hasTemporaryTarget()) {
0314                 effectiveSelection = m_d->safeProjection->getDeviceLazy(m_d->selection);
0315 
0316                 KisPainter::copyAreaOptimized(applyRect.topLeft(),
0317                                               m_d->selection->pixelSelection(),
0318                                               effectiveSelection->pixelSelection(), applyRect);
0319 
0320                 KisPainter gc(effectiveSelection->pixelSelection());
0321                 setupTemporaryPainter(&gc);
0322                 gc.bitBlt(applyRect.topLeft(), temporaryTarget(), applyRect);
0323             } else {
0324                 m_d->safeProjection->releaseDevice();
0325             }
0326 
0327             mergeInMaskInternal(projection, effectiveSelection, applyRect, needRect, maskPos);
0328         }
0329 
0330     } else {
0331         mergeInMaskInternal(projection, 0, applyRect, needRect, maskPos);
0332     }
0333 }
0334 
0335 void KisMask::mergeInMaskInternal(KisPaintDeviceSP projection,
0336                                   KisSelectionSP effectiveSelection,
0337                                   const QRect &applyRect,
0338                                   const QRect &preparedNeedRect,
0339                                   KisNode::PositionToFilthy maskPos) const
0340 {
0341     KisCachedPaintDevice::Guard d1(projection, m_d->paintDeviceCache);
0342     KisPaintDeviceSP cacheDevice = d1.device();
0343 
0344     if (effectiveSelection) {
0345         QRect updatedRect = decorateRect(projection, cacheDevice, applyRect, maskPos);
0346 
0347         // masks don't have any compositing
0348         KisPainter::copyAreaOptimized(updatedRect.topLeft(), cacheDevice, projection, updatedRect, effectiveSelection);
0349 
0350     } else {
0351         cacheDevice->makeCloneFromRough(projection, preparedNeedRect);
0352         projection->clear(preparedNeedRect);
0353 
0354         decorateRect(cacheDevice, projection, applyRect, maskPos);
0355     }
0356 }
0357 
0358 void KisMask::flattenSelectionProjection(KisSelectionSP selection, const QRect &dirtyRect) const
0359 {
0360     selection->updateProjection(dirtyRect);
0361 }
0362 
0363 QRect KisMask::needRect(const QRect &rect,  PositionToFilthy pos) const
0364 {
0365     Q_UNUSED(pos);
0366     QRect resultRect = rect;
0367     if (m_d->selection) {
0368         QRect selectionExtent = m_d->selection->selectedRect();
0369 
0370         // copy for thread safety!
0371         KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
0372 
0373         if (temporaryTarget) {
0374             selectionExtent |= temporaryTarget->extent();
0375         }
0376 
0377         resultRect &= selectionExtent;
0378     }
0379 
0380     return resultRect;
0381 }
0382 
0383 QRect KisMask::changeRect(const QRect &rect, PositionToFilthy pos) const
0384 {
0385     return KisMask::needRect(rect, pos);
0386 }
0387 
0388 QRect KisMask::extent() const
0389 {
0390     QRect resultRect;
0391 
0392     if (m_d->selection) {
0393         resultRect = m_d->selection->selectedRect();
0394 
0395         // copy for thread safety!
0396         KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
0397 
0398         if (temporaryTarget) {
0399             resultRect |= temporaryTarget->extent();
0400         }
0401     } else if (KisNodeSP parent = this->parent()) {
0402         resultRect = parent->extent();
0403     }
0404 
0405     return resultRect;
0406 }
0407 
0408 QRect KisMask::exactBounds() const
0409 {
0410     QRect resultRect;
0411 
0412     if (m_d->selection) {
0413         resultRect = m_d->selection->selectedExactRect();
0414 
0415         // copy for thread safety!
0416         KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
0417 
0418         if (temporaryTarget) {
0419             resultRect |= temporaryTarget->exactBounds();
0420         }
0421     } else if (KisNodeSP parent = this->parent()) {
0422         resultRect = parent->exactBounds();
0423     }
0424 
0425     return resultRect;
0426 }
0427 
0428 qint32 KisMask::x() const
0429 {
0430     return m_d->selection ? m_d->selection->x() :
0431            m_d->deferredSelectionOffset ? m_d->deferredSelectionOffset->x() :
0432            parent() ? parent()->x() : 0;
0433 }
0434 
0435 qint32 KisMask::y() const
0436 {
0437     return m_d->selection ? m_d->selection->y() :
0438            m_d->deferredSelectionOffset ? m_d->deferredSelectionOffset->y() :
0439            parent() ? parent()->y() : 0;
0440 }
0441 
0442 void KisMask::setX(qint32 x)
0443 {
0444     if (m_d->selection) {
0445         m_d->selection->setX(x);
0446     } else if (!m_d->deferredSelectionOffset) {
0447         m_d->deferredSelectionOffset.reset(new QPoint(x, 0));
0448     } else {
0449         m_d->deferredSelectionOffset->rx() = x;
0450     }
0451 }
0452 
0453 void KisMask::setY(qint32 y)
0454 {
0455     if (m_d->selection) {
0456         m_d->selection->setY(y);
0457     } else if (!m_d->deferredSelectionOffset) {
0458         m_d->deferredSelectionOffset.reset(new QPoint(0, y));
0459     } else {
0460         m_d->deferredSelectionOffset->ry() = y;
0461     }
0462 }
0463 
0464 QRect KisMask::nonDependentExtent() const
0465 {
0466     return QRect();
0467 }
0468 
0469 QImage KisMask::createThumbnail(qint32 w, qint32 h, Qt::AspectRatioMode aspectRatioMode)
0470 {
0471     KisPaintDeviceSP originalDevice =
0472         selection() ? selection()->projection() : 0;
0473 
0474     return originalDevice ?
0475            originalDevice->createThumbnail(w, h, aspectRatioMode, 1,
0476                                            KoColorConversionTransformation::internalRenderingIntent(),
0477                                            KoColorConversionTransformation::internalConversionFlags()) : QImage();
0478 }
0479 
0480 int KisMask::thumbnailSeqNo() const
0481 {
0482     KisPaintDeviceSP originalDevice =
0483         selection() ? selection()->projection() : 0;
0484     return originalDevice ? originalDevice->sequenceNumber() : -1;
0485 }
0486 
0487 void KisMask::testingInitSelection(const QRect &rect, KisLayerSP parentLayer)
0488 {
0489     if (parentLayer) {
0490         m_d->selection = new KisSelection(new KisSelectionDefaultBounds(parentLayer->paintDevice()), toQShared(new KisImageResolutionProxy(image())));
0491     } else {
0492         m_d->selection = new KisSelection(new KisSelectionEmptyBounds(), toQShared(new KisImageResolutionProxy(image())));
0493     }
0494 
0495     m_d->selection->pixelSelection()->select(rect, OPACITY_OPAQUE_U8);
0496     m_d->selection->pixelSelection()->setSupportsWraparoundMode(true);
0497     m_d->selection->updateProjection(rect);
0498     m_d->selection->setParentNode(this);
0499 }
0500 
0501 bool KisMask::supportsLodPainting() const
0502 {
0503     return !m_d->selection || !m_d->selection->hasShapeSelection();
0504 }
0505 
0506 KisKeyframeChannel *KisMask::requestKeyframeChannel(const QString &id)
0507 {
0508     if (id == KisKeyframeChannel::Raster.id()) {
0509         KisPaintDeviceSP device = paintDevice();
0510         if (device) {
0511             KisRasterKeyframeChannel *contentChannel = device->createKeyframeChannel(KisKeyframeChannel::Raster);
0512             contentChannel->setFilenameSuffix(".pixelselection");
0513             return contentChannel;
0514        }
0515     }
0516 
0517     return KisNode::requestKeyframeChannel(id);
0518 }
0519 
0520 bool KisMask::supportsKeyframeChannel(const QString &id)
0521 {
0522     if (id == KisKeyframeChannel::Raster.id() && paintDevice()) {
0523         return true;
0524     }
0525 
0526     return KisNode::supportsKeyframeChannel(id);
0527 }
0528 
0529 void KisMask::baseNodeChangedCallback()
0530 {
0531     KisNodeSP up = parent();
0532     KisLayer *layer = dynamic_cast<KisLayer*>(up.data());
0533     if (layer) {
0534         layer->notifyChildMaskChanged();
0535     }
0536     KisNode::baseNodeChangedCallback();
0537 }