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