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

0001 /*
0002  *  SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
0003  *  SPDX-FileCopyrightText: 2005 C. Boemann <cbo@boemann.dk>
0004  *  SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "kis_selection_based_layer.h"
0010 
0011 #include <klocalizedstring.h>
0012 #include "kis_debug.h"
0013 
0014 #include <KoCompositeOpRegistry.h>
0015 
0016 #include "kis_image.h"
0017 #include "kis_painter.h"
0018 #include "kis_default_bounds.h"
0019 
0020 #include "kis_selection.h"
0021 #include "kis_pixel_selection.h"
0022 #include "filter/kis_filter_configuration.h"
0023 #include "filter/kis_filter_registry.h"
0024 #include "filter/kis_filter.h"
0025 #include "kis_signal_auto_connection.h"
0026 
0027 #include "kis_raster_keyframe_channel.h"
0028 
0029 
0030 struct Q_DECL_HIDDEN KisSelectionBasedLayer::Private
0031 {
0032 public:
0033     Private() : useSelectionInProjection(true) {}
0034     Private(const Private &rhs) : useSelectionInProjection(rhs.useSelectionInProjection) {}
0035 
0036     KisSelectionSP selection;
0037     KisPaintDeviceSP paintDevice;
0038     bool useSelectionInProjection;
0039     KisSignalAutoConnectionsStore imageConnections;
0040 };
0041 
0042 
0043 KisSelectionBasedLayer::KisSelectionBasedLayer(KisImageWSP image,
0044         const QString &name,
0045         KisSelectionSP selection,
0046         KisFilterConfigurationSP filterConfig)
0047         : KisLayer(image.data(), name, OPACITY_OPAQUE_U8),
0048           KisNodeFilterInterface(filterConfig),
0049           m_d(new Private())
0050 {
0051     if (!selection) {
0052         initSelection();
0053     } else {
0054         setInternalSelection(selection);
0055     }
0056 
0057     KisImageSP imageSP = image.toStrongRef();
0058     if (!imageSP) {
0059         return;
0060     }
0061     m_d->paintDevice = KisPaintDeviceSP(new KisPaintDevice(this, imageSP->colorSpace(), KisDefaultBoundsSP(new KisDefaultBounds(image))));
0062     m_d->imageConnections.addConnection(imageSP.data(), SIGNAL(sigSizeChanged(QPointF,QPointF)), this, SLOT(slotImageSizeChanged()));
0063 }
0064 
0065 KisSelectionBasedLayer::KisSelectionBasedLayer(const KisSelectionBasedLayer& rhs)
0066         : KisLayer(rhs)
0067         , KisIndirectPaintingSupport()
0068         , KisNodeFilterInterface(rhs)
0069         , m_d(new Private(*rhs.m_d))
0070 {
0071     setInternalSelection(rhs.m_d->selection);
0072 
0073     m_d->paintDevice = new KisPaintDevice(*rhs.m_d->paintDevice.data());
0074 }
0075 
0076 
0077 KisSelectionBasedLayer::~KisSelectionBasedLayer()
0078 {
0079     delete m_d;
0080 }
0081 
0082 void KisSelectionBasedLayer::initSelection()
0083 {
0084     m_d->selection = KisSelectionSP(new KisSelection(KisDefaultBoundsSP(new KisDefaultBounds(image()))));
0085     m_d->selection->pixelSelection()->setDefaultPixel(KoColor(Qt::white, m_d->selection->pixelSelection()->colorSpace()));
0086     m_d->selection->setParentNode(this);
0087     m_d->selection->updateProjection();
0088 }
0089 
0090 void KisSelectionBasedLayer::slotImageSizeChanged()
0091 {
0092     if (m_d->selection) {
0093         /**
0094          * Make sure exactBounds() of the selection got recalculated after
0095          * the image changed
0096          */
0097         m_d->selection->pixelSelection()->setDirty();
0098         setDirty();
0099     }
0100 }
0101 
0102 void KisSelectionBasedLayer::setImage(KisImageWSP image)
0103 {
0104     m_d->imageConnections.clear();
0105     m_d->paintDevice->setDefaultBounds(KisDefaultBoundsSP(new KisDefaultBounds(image)));
0106     m_d->selection->pixelSelection()->setDefaultBounds(KisDefaultBoundsSP(new KisDefaultBounds(image)));
0107     KisLayer::setImage(image);
0108 
0109     if (image) {
0110         m_d->imageConnections.addConnection(image.data(), SIGNAL(sigSizeChanged(QPointF,QPointF)), this, SLOT(slotImageSizeChanged()));
0111     }
0112 }
0113 
0114 bool KisSelectionBasedLayer::allowAsChild(KisNodeSP node) const
0115 {
0116     return node->inherits("KisMask");
0117 }
0118 
0119 
0120 KisPaintDeviceSP KisSelectionBasedLayer::original() const
0121 {
0122     return m_d->paintDevice;
0123 }
0124 KisPaintDeviceSP KisSelectionBasedLayer::paintDevice() const
0125 {
0126     return m_d->selection->pixelSelection();
0127 }
0128 
0129 
0130 bool KisSelectionBasedLayer::needProjection() const
0131 {
0132     return m_d->selection;
0133 }
0134 
0135 void KisSelectionBasedLayer::setUseSelectionInProjection(bool value) const
0136 {
0137     m_d->useSelectionInProjection = value;
0138 }
0139 
0140 KisSelectionSP KisSelectionBasedLayer::fetchComposedInternalSelection(const QRect &rect) const
0141 {
0142     if (!m_d->selection) return KisSelectionSP();
0143     m_d->selection->updateProjection(rect);
0144 
0145     KisSelectionSP tempSelection = m_d->selection;
0146 
0147     KisIndirectPaintingSupport::ReadLocker l(this);
0148 
0149     if (hasTemporaryTarget()) {
0150         /**
0151          * WARNING: we don't try to clone the selection entirely, because
0152          *          it might be unsafe for shape selections.
0153          *
0154          * TODO: make cloning of vector selections safe! See a comment in
0155          *       KisShapeSelection::clone().
0156          */
0157         tempSelection = new KisSelection();
0158 
0159         KisPainter::copyAreaOptimized(rect.topLeft(), m_d->selection->pixelSelection(), tempSelection->pixelSelection(), rect);
0160 
0161         KisPainter gc2(tempSelection->pixelSelection());
0162         setupTemporaryPainter(&gc2);
0163         gc2.bitBlt(rect.topLeft(), temporaryTarget(), rect);
0164     }
0165 
0166     return tempSelection;
0167 }
0168 
0169 void KisSelectionBasedLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
0170         KisPaintDeviceSP projection,
0171         const QRect& rect) const
0172 {
0173     KisSelectionSP tempSelection;
0174 
0175     if (m_d->useSelectionInProjection) {
0176         tempSelection = fetchComposedInternalSelection(rect);
0177 
0178         /**
0179          * When we paint with a selection, the deselected areas will *not* be
0180          * overwritten by copyAreaOptimized(), so we need to clear them beforehand
0181          */
0182         projection->clear(rect);
0183     }
0184 
0185     KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect, tempSelection);
0186 }
0187 
0188 QRect KisSelectionBasedLayer::cropChangeRectBySelection(const QRect &rect) const
0189 {
0190     return m_d->selection ?
0191         rect & m_d->selection->selectedRect() :
0192         rect;
0193 }
0194 
0195 QRect KisSelectionBasedLayer::needRect(const QRect &rect, PositionToFilthy pos) const
0196 {
0197     Q_UNUSED(pos);
0198     return rect;
0199 }
0200 
0201 void KisSelectionBasedLayer::resetCache()
0202 {
0203     KisImageSP imageSP = image().toStrongRef();
0204     if (!imageSP) {
0205         return;
0206     }
0207 
0208     if (!m_d->paintDevice) {
0209         m_d->paintDevice = KisPaintDeviceSP(new KisPaintDevice(KisNodeWSP(this), imageSP->colorSpace(), new KisDefaultBounds(image())));
0210     } else if (*m_d->paintDevice->colorSpace() != *imageSP->colorSpace()) {
0211         m_d->paintDevice->clear();
0212         m_d->paintDevice->convertTo(imageSP->colorSpace());
0213     } else {
0214         m_d->paintDevice->clear();
0215     }
0216 }
0217 
0218 KisSelectionSP KisSelectionBasedLayer::internalSelection() const
0219 {
0220     return m_d->selection;
0221 }
0222 
0223 void KisSelectionBasedLayer::setInternalSelection(KisSelectionSP selection)
0224 {
0225     if (selection) {
0226         m_d->selection = new KisSelection(*selection.data());
0227         m_d->selection->setParentNode(this);
0228         m_d->selection->setDefaultBounds(new KisDefaultBounds(image()));
0229         m_d->selection->updateProjection();
0230 
0231         KisPixelSelectionSP pixelSelection = m_d->selection->pixelSelection();
0232         if (pixelSelection->framesInterface()) {
0233             addKeyframeChannel(pixelSelection->keyframeChannel());
0234             enableAnimation();
0235         }
0236 
0237         KisImageSP imageSP = image().toStrongRef();
0238         if (imageSP) {
0239             /**
0240              * Sanity check for the case when the image is present
0241              */
0242             if (m_d->selection->pixelSelection()->defaultBounds()->bounds() != imageSP->bounds()) {
0243                 qWarning() << "WARNING: KisSelectionBasedLayer::setInternalSelection"
0244                            << "New selection has suspicious default bounds";
0245                 qWarning() << "WARNING:" << ppVar(m_d->selection->pixelSelection()->defaultBounds()->bounds());
0246                 qWarning() << "WARNING:" << ppVar(imageSP->bounds());
0247             }
0248         }
0249 
0250     } else {
0251         m_d->selection = 0;
0252     }
0253 }
0254 
0255 qint32 KisSelectionBasedLayer::x() const
0256 {
0257     return m_d->selection ? m_d->selection->x() : 0;
0258 }
0259 
0260 qint32 KisSelectionBasedLayer::y() const
0261 {
0262     return m_d->selection ? m_d->selection->y() : 0;
0263 }
0264 
0265 void KisSelectionBasedLayer::setX(qint32 x)
0266 {
0267     if (m_d->selection) {
0268         m_d->selection->setX(x);
0269     }
0270 }
0271 
0272 void KisSelectionBasedLayer::setY(qint32 y)
0273 {
0274     if (m_d->selection) {
0275         m_d->selection->setY(y);
0276     }
0277 }
0278 
0279 bool KisSelectionBasedLayer::supportsLodPainting() const
0280 {
0281     return !m_d->selection || !m_d->selection->hasShapeSelection();
0282 }
0283 
0284 KisKeyframeChannel *KisSelectionBasedLayer::requestKeyframeChannel(const QString &id)
0285 {
0286     if (id == KisKeyframeChannel::Raster.id()) {
0287         KisRasterKeyframeChannel *contentChannel = m_d->selection->pixelSelection()->createKeyframeChannel(KisKeyframeChannel::Raster);
0288         contentChannel->setFilenameSuffix(".pixelselection");
0289         return contentChannel;
0290     }
0291 
0292     return KisLayer::requestKeyframeChannel(id);
0293 }
0294 
0295 bool KisSelectionBasedLayer::supportsKeyframeChannel(const QString &id)
0296 {
0297     if (id == KisKeyframeChannel::Raster.id()) {
0298         return true;
0299     }
0300 
0301     return KisLayer::supportsKeyframeChannel(id);
0302 }
0303 
0304 void KisSelectionBasedLayer::setDirty()
0305 {
0306     Q_ASSERT(image());
0307     KisImageSP imageSP = image().toStrongRef();
0308     if (!imageSP) {
0309         return;
0310     }
0311     setDirty(imageSP->bounds());
0312 }
0313 
0314 QRect KisSelectionBasedLayer::extent() const
0315 {
0316     QRect resultRect;
0317 
0318     if (m_d->selection) {
0319         resultRect = m_d->selection->selectedRect();
0320 
0321         // copy for thread safety!
0322         KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
0323 
0324         if (temporaryTarget) {
0325             resultRect |= temporaryTarget->extent();
0326         }
0327 
0328     } else {
0329         KisImageSP image = this->image().toStrongRef();
0330         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(image, QRect());
0331         resultRect = image->bounds();
0332     }
0333 
0334     return resultRect;
0335 }
0336 
0337 QRect KisSelectionBasedLayer::exactBounds() const
0338 {
0339     QRect resultRect;
0340 
0341     if (m_d->selection) {
0342         resultRect = m_d->selection->selectedExactRect();
0343 
0344         // copy for thread safety!
0345         KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
0346 
0347         if (temporaryTarget) {
0348             resultRect |= temporaryTarget->exactBounds();
0349         }
0350 
0351     } else {
0352         KisImageSP image = this->image().toStrongRef();
0353         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(image, QRect());
0354         resultRect = image->bounds();
0355     }
0356 
0357     return resultRect;
0358 }
0359 
0360 QImage KisSelectionBasedLayer::createThumbnail(qint32 w, qint32 h, Qt::AspectRatioMode aspectRatioMode)
0361 {
0362     KisSelectionSP originalSelection = internalSelection();
0363     KisPaintDeviceSP originalDevice = original();
0364 
0365     return originalDevice && originalSelection ?
0366            originalDevice->createThumbnail(w, h, aspectRatioMode, 1,
0367                                            KoColorConversionTransformation::internalRenderingIntent(),
0368                                            KoColorConversionTransformation::internalConversionFlags()) :
0369            QImage();
0370 }
0371