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

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