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 }