File indexing completed on 2024-05-12 15:58:27
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); 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 } 0122 0123 m_d->safeProjection->setImage(image); 0124 0125 KisNode::setImage(image); 0126 } 0127 0128 bool KisMask::allowAsChild(KisNodeSP node) const 0129 { 0130 Q_UNUSED(node); 0131 return false; 0132 } 0133 0134 const KoColorSpace * KisMask::colorSpace() const 0135 { 0136 KisNodeSP parentNode = parent(); 0137 return parentNode ? parentNode->colorSpace() : 0; 0138 } 0139 0140 const KoCompositeOp * KisMask::compositeOp() const 0141 { 0142 /** 0143 * FIXME: This function duplicates the same function from 0144 * KisLayer. We can't move it to KisBaseNode as it doesn't 0145 * know anything about parent() method of KisNode 0146 * Please think it over... 0147 */ 0148 0149 const KoColorSpace *colorSpace = this->colorSpace(); 0150 if (!colorSpace) return 0; 0151 0152 const KoCompositeOp* op = colorSpace->compositeOp(compositeOpId()); 0153 return op ? op : colorSpace->compositeOp(COMPOSITE_OVER); 0154 } 0155 0156 void KisMask::initSelection(KisSelectionSP copyFrom, KisLayerSP parentLayer) 0157 { 0158 m_d->initSelectionImpl(copyFrom, parentLayer, 0); 0159 } 0160 0161 void KisMask::initSelection(KisPaintDeviceSP copyFromDevice, KisLayerSP parentLayer) 0162 { 0163 m_d->initSelectionImpl(0, parentLayer, copyFromDevice); 0164 } 0165 0166 void KisMask::initSelection(KisLayerSP parentLayer) 0167 { 0168 m_d->initSelectionImpl(0, parentLayer, 0); 0169 } 0170 0171 void KisMask::Private::initSelectionImpl(KisSelectionSP copyFrom, KisLayerSP parentLayer, KisPaintDeviceSP copyFromDevice) 0172 { 0173 Q_ASSERT(parentLayer); 0174 0175 KisPaintDeviceSP parentPaintDevice = parentLayer->original(); 0176 0177 if (copyFrom) { 0178 /** 0179 * We can't use setSelection as we may not have parent() yet 0180 */ 0181 selection = new KisSelection(*copyFrom); 0182 selection->setDefaultBounds(new KisSelectionDefaultBounds(parentPaintDevice)); 0183 } else if (copyFromDevice) { 0184 KritaUtils::DeviceCopyMode copyMode = 0185 q->inherits("KisFilterMask") || q->inherits("KisTransparencyMask") ? 0186 KritaUtils::CopyAllFrames : KritaUtils::CopySnapshot; 0187 0188 selection = new KisSelection(copyFromDevice, copyMode, new KisSelectionDefaultBounds(parentPaintDevice)); 0189 0190 KisPixelSelectionSP pixelSelection = selection->pixelSelection(); 0191 if (pixelSelection->framesInterface()) { 0192 KisRasterKeyframeChannel *keyframeChannel = pixelSelection->keyframeChannel(); 0193 keyframeChannel->setFilenameSuffix(".pixelselection"); 0194 0195 q->addKeyframeChannel(keyframeChannel); 0196 q->enableAnimation(); 0197 } 0198 } else { 0199 selection = new KisSelection(new KisSelectionDefaultBounds(parentPaintDevice)); 0200 selection->pixelSelection()->setDefaultPixel(KoColor(Qt::white, selection->pixelSelection()->colorSpace())); 0201 0202 if (deferredSelectionOffset) { 0203 selection->setX(deferredSelectionOffset->x()); 0204 selection->setY(deferredSelectionOffset->y()); 0205 deferredSelectionOffset.reset(); 0206 } 0207 } 0208 selection->setParentNode(q); 0209 selection->updateProjection(); 0210 } 0211 0212 KisSelectionSP KisMask::selection() const 0213 { 0214 return m_d->selection; 0215 } 0216 0217 KisPaintDeviceSP KisMask::paintDevice() const 0218 { 0219 KisSelectionSP selection = this->selection(); 0220 return selection ? selection->pixelSelection() : 0; 0221 } 0222 0223 KisPaintDeviceSP KisMask::original() const 0224 { 0225 return paintDevice(); 0226 } 0227 0228 KisPaintDeviceSP KisMask::projection() const 0229 { 0230 KisPaintDeviceSP originalDevice = original(); 0231 KisPaintDeviceSP result = originalDevice; 0232 0233 KisSelectionSP selection = this->selection(); 0234 if (selection && hasTemporaryTarget()) { 0235 result = m_d->safeProjection->getDeviceLazy(selection)->pixelSelection(); 0236 } 0237 0238 return result; 0239 } 0240 0241 KisAbstractProjectionPlaneSP KisMask::projectionPlane() const 0242 { 0243 return m_d->projectionPlane; 0244 } 0245 0246 void KisMask::setSelection(KisSelectionSP selection) 0247 { 0248 m_d->selection = selection; 0249 if (parent()) { 0250 const KisLayer *parentLayer = qobject_cast<const KisLayer*>(parent()); 0251 m_d->selection->setDefaultBounds(new KisDefaultBounds(parentLayer->image())); 0252 } 0253 m_d->selection->setParentNode(this); 0254 } 0255 0256 void KisMask::select(const QRect & rc, quint8 selectedness) 0257 { 0258 KisSelectionSP sel = selection(); 0259 KisPixelSelectionSP psel = sel->pixelSelection(); 0260 psel->select(rc, selectedness); 0261 sel->updateProjection(rc); 0262 } 0263 0264 0265 QRect KisMask::decorateRect(KisPaintDeviceSP &src, 0266 KisPaintDeviceSP &dst, 0267 const QRect & rc, 0268 PositionToFilthy maskPos) const 0269 { 0270 Q_UNUSED(src); 0271 Q_UNUSED(dst); 0272 Q_UNUSED(maskPos); 0273 Q_ASSERT_X(0, "KisMask::decorateRect", "Should be overridden by successors"); 0274 return rc; 0275 } 0276 0277 bool KisMask::paintsOutsideSelection() const 0278 { 0279 return false; 0280 } 0281 0282 void KisMask::apply(KisPaintDeviceSP projection, const QRect &applyRect, const QRect &needRect, PositionToFilthy maskPos) const 0283 { 0284 if (selection()) { 0285 0286 flattenSelectionProjection(m_d->selection, applyRect); 0287 0288 KisSelectionSP effectiveSelection = m_d->selection; 0289 0290 { 0291 // Access temporary target under the lock held 0292 KisIndirectPaintingSupport::ReadLocker l(this); 0293 0294 if (!paintsOutsideSelection()) { 0295 // extent of m_d->selection should also be accessed under a lock, 0296 // because it might be being merged in by the temporary target atm 0297 QRect effectiveExtent = m_d->selection->selectedRect(); 0298 0299 if (hasTemporaryTarget()) { 0300 effectiveExtent |= temporaryTarget()->extent(); 0301 } 0302 0303 if(!effectiveExtent.intersects(applyRect)) { 0304 return; 0305 } 0306 } 0307 0308 if (hasTemporaryTarget()) { 0309 effectiveSelection = m_d->safeProjection->getDeviceLazy(m_d->selection); 0310 0311 KisPainter::copyAreaOptimized(applyRect.topLeft(), 0312 m_d->selection->pixelSelection(), 0313 effectiveSelection->pixelSelection(), applyRect); 0314 0315 KisPainter gc(effectiveSelection->pixelSelection()); 0316 setupTemporaryPainter(&gc); 0317 gc.bitBlt(applyRect.topLeft(), temporaryTarget(), applyRect); 0318 } else { 0319 m_d->safeProjection->releaseDevice(); 0320 } 0321 0322 mergeInMaskInternal(projection, effectiveSelection, applyRect, needRect, maskPos); 0323 } 0324 0325 } else { 0326 mergeInMaskInternal(projection, 0, applyRect, needRect, maskPos); 0327 } 0328 } 0329 0330 void KisMask::mergeInMaskInternal(KisPaintDeviceSP projection, 0331 KisSelectionSP effectiveSelection, 0332 const QRect &applyRect, 0333 const QRect &preparedNeedRect, 0334 KisNode::PositionToFilthy maskPos) const 0335 { 0336 KisCachedPaintDevice::Guard d1(projection, m_d->paintDeviceCache); 0337 KisPaintDeviceSP cacheDevice = d1.device(); 0338 0339 if (effectiveSelection) { 0340 QRect updatedRect = decorateRect(projection, cacheDevice, applyRect, maskPos); 0341 0342 // masks don't have any compositioning 0343 KisPainter::copyAreaOptimized(updatedRect.topLeft(), cacheDevice, projection, updatedRect, effectiveSelection); 0344 0345 } else { 0346 cacheDevice->makeCloneFromRough(projection, preparedNeedRect); 0347 projection->clear(preparedNeedRect); 0348 0349 decorateRect(cacheDevice, projection, applyRect, maskPos); 0350 } 0351 } 0352 0353 void KisMask::flattenSelectionProjection(KisSelectionSP selection, const QRect &dirtyRect) const 0354 { 0355 selection->updateProjection(dirtyRect); 0356 } 0357 0358 QRect KisMask::needRect(const QRect &rect, PositionToFilthy pos) const 0359 { 0360 Q_UNUSED(pos); 0361 QRect resultRect = rect; 0362 if (m_d->selection) { 0363 QRect selectionExtent = m_d->selection->selectedRect(); 0364 0365 // copy for thread safety! 0366 KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); 0367 0368 if (temporaryTarget) { 0369 selectionExtent |= temporaryTarget->extent(); 0370 } 0371 0372 resultRect &= selectionExtent; 0373 } 0374 0375 return resultRect; 0376 } 0377 0378 QRect KisMask::changeRect(const QRect &rect, PositionToFilthy pos) const 0379 { 0380 return KisMask::needRect(rect, pos); 0381 } 0382 0383 QRect KisMask::extent() const 0384 { 0385 QRect resultRect; 0386 0387 if (m_d->selection) { 0388 resultRect = m_d->selection->selectedRect(); 0389 0390 // copy for thread safety! 0391 KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); 0392 0393 if (temporaryTarget) { 0394 resultRect |= temporaryTarget->extent(); 0395 } 0396 } else if (KisNodeSP parent = this->parent()) { 0397 resultRect = parent->extent(); 0398 } 0399 0400 return resultRect; 0401 } 0402 0403 QRect KisMask::exactBounds() const 0404 { 0405 QRect resultRect; 0406 0407 if (m_d->selection) { 0408 resultRect = m_d->selection->selectedExactRect(); 0409 0410 // copy for thread safety! 0411 KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); 0412 0413 if (temporaryTarget) { 0414 resultRect |= temporaryTarget->exactBounds(); 0415 } 0416 } else if (KisNodeSP parent = this->parent()) { 0417 resultRect = parent->exactBounds(); 0418 } 0419 0420 return resultRect; 0421 } 0422 0423 qint32 KisMask::x() const 0424 { 0425 return m_d->selection ? m_d->selection->x() : 0426 m_d->deferredSelectionOffset ? m_d->deferredSelectionOffset->x() : 0427 parent() ? parent()->x() : 0; 0428 } 0429 0430 qint32 KisMask::y() const 0431 { 0432 return m_d->selection ? m_d->selection->y() : 0433 m_d->deferredSelectionOffset ? m_d->deferredSelectionOffset->y() : 0434 parent() ? parent()->y() : 0; 0435 } 0436 0437 void KisMask::setX(qint32 x) 0438 { 0439 if (m_d->selection) { 0440 m_d->selection->setX(x); 0441 } else if (!m_d->deferredSelectionOffset) { 0442 m_d->deferredSelectionOffset.reset(new QPoint(x, 0)); 0443 } else { 0444 m_d->deferredSelectionOffset->rx() = x; 0445 } 0446 } 0447 0448 void KisMask::setY(qint32 y) 0449 { 0450 if (m_d->selection) { 0451 m_d->selection->setY(y); 0452 } else if (!m_d->deferredSelectionOffset) { 0453 m_d->deferredSelectionOffset.reset(new QPoint(0, y)); 0454 } else { 0455 m_d->deferredSelectionOffset->ry() = y; 0456 } 0457 } 0458 0459 QRect KisMask::nonDependentExtent() const 0460 { 0461 return QRect(); 0462 } 0463 0464 QImage KisMask::createThumbnail(qint32 w, qint32 h, Qt::AspectRatioMode aspectRatioMode) 0465 { 0466 KisPaintDeviceSP originalDevice = 0467 selection() ? selection()->projection() : 0; 0468 0469 return originalDevice ? 0470 originalDevice->createThumbnail(w, h, aspectRatioMode, 1, 0471 KoColorConversionTransformation::internalRenderingIntent(), 0472 KoColorConversionTransformation::internalConversionFlags()) : QImage(); 0473 } 0474 0475 void KisMask::testingInitSelection(const QRect &rect, KisLayerSP parentLayer) 0476 { 0477 if (parentLayer) { 0478 m_d->selection = new KisSelection(new KisSelectionDefaultBounds(parentLayer->paintDevice())); 0479 } else { 0480 m_d->selection = new KisSelection(); 0481 } 0482 0483 m_d->selection->pixelSelection()->select(rect, OPACITY_OPAQUE_U8); 0484 m_d->selection->updateProjection(rect); 0485 m_d->selection->setParentNode(this); 0486 } 0487 0488 bool KisMask::supportsLodPainting() const 0489 { 0490 return !m_d->selection || !m_d->selection->hasShapeSelection(); 0491 } 0492 0493 KisKeyframeChannel *KisMask::requestKeyframeChannel(const QString &id) 0494 { 0495 if (id == KisKeyframeChannel::Raster.id()) { 0496 KisPaintDeviceSP device = paintDevice(); 0497 if (device) { 0498 KisRasterKeyframeChannel *contentChannel = device->createKeyframeChannel(KisKeyframeChannel::Raster); 0499 contentChannel->setFilenameSuffix(".pixelselection"); 0500 return contentChannel; 0501 } 0502 } 0503 0504 return KisNode::requestKeyframeChannel(id); 0505 } 0506 0507 bool KisMask::supportsKeyframeChannel(const QString &id) 0508 { 0509 if (id == KisKeyframeChannel::Raster.id() && paintDevice()) { 0510 return true; 0511 } 0512 0513 return KisNode::supportsKeyframeChannel(id); 0514 } 0515 0516 void KisMask::baseNodeChangedCallback() 0517 { 0518 KisNodeSP up = parent(); 0519 KisLayer *layer = dynamic_cast<KisLayer*>(up.data()); 0520 if (layer) { 0521 layer->notifyChildMaskChanged(); 0522 } 0523 KisNode::baseNodeChangedCallback(); 0524 }