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