diff --git a/libs/image/kis_cached_paint_device.h b/libs/image/kis_cached_paint_device.h index 1cb038d39b..07388a4349 100644 --- a/libs/image/kis_cached_paint_device.h +++ b/libs/image/kis_cached_paint_device.h @@ -1,73 +1,113 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_CACHED_PAINT_DEVICE_H #define __KIS_CACHED_PAINT_DEVICE_H #include "tiles3/kis_lockless_stack.h" #include "kis_paint_device.h" #include "kis_selection.h" class KisCachedPaintDevice { public: KisPaintDeviceSP getDevice(KisPaintDeviceSP prototype) { KisPaintDeviceSP device; if(!m_stack.pop(device)) { device = new KisPaintDevice(prototype->colorSpace()); } device->prepareClone(prototype); return device; } void putDevice(KisPaintDeviceSP device) { device->clear(); device->setDefaultBounds(new KisDefaultBounds()); m_stack.push(device); } + struct Guard { + Guard(KisPaintDeviceSP prototype, KisCachedPaintDevice &parent) + : m_parent(parent) + { + m_device = m_parent.getDevice(prototype); + } + + ~Guard() { + m_parent.putDevice(m_device); + } + + KisPaintDeviceSP device() const { + return m_device; + } + + private: + KisCachedPaintDevice &m_parent; + KisPaintDeviceSP m_device; + }; + private: KisLocklessStack m_stack; }; class KisCachedSelection { public: KisSelectionSP getSelection() { KisSelectionSP selection; if(!m_stack.pop(selection)) { - selection = new KisSelection(); + selection = new KisSelection(new KisSelectionEmptyBounds(0)); } return selection; } void putSelection(KisSelectionSP selection) { selection->clear(); selection->setDefaultBounds(new KisDefaultBounds()); m_stack.push(selection); } + struct Guard { + Guard(KisCachedSelection &parent) + : m_parent(parent) + { + m_selection = m_parent.getSelection(); + } + + ~Guard() { + m_parent.putSelection(m_selection); + } + + KisSelectionSP selection() const { + return m_selection; + } + + private: + KisCachedSelection &m_parent; + KisSelectionSP m_selection; + }; + private: KisLocklessStack m_stack; }; #endif /* __KIS_CACHED_PAINT_DEVICE_H */ diff --git a/libs/image/kis_mask.cc b/libs/image/kis_mask.cc index b07dd02001..a3ae67b967 100644 --- a/libs/image/kis_mask.cc +++ b/libs/image/kis_mask.cc @@ -1,514 +1,513 @@ /* * Copyright (c) 2006 Boudewijn Rempt * (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_mask.h" #include // to prevent incomplete class types on "delete selection->flatten();" #include #include #include #include #include #include "kis_paint_device.h" #include "kis_selection.h" #include "kis_pixel_selection.h" #include "kis_painter.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_cached_paint_device.h" #include "kis_mask_projection_plane.h" #include "kis_raster_keyframe_channel.h" #include "KisSafeNodeProjectionStore.h" struct Q_DECL_HIDDEN KisMask::Private { Private(KisMask *_q) : q(_q), projectionPlane(new KisMaskProjectionPlane(q)) { } mutable KisSelectionSP selection; KisCachedPaintDevice paintDeviceCache; KisMask *q; /** * Due to the design of the Kra format the X,Y offset of the paint * device belongs to the node, but not to the device itself. So * the offset is set when the node is created, but not when the * selection is initialized. This causes the X,Y values to be * lost, since the selection doen not exist at the moment. That is * why we save it separately. */ QScopedPointer deferredSelectionOffset; KisAbstractProjectionPlaneSP projectionPlane; KisSafeSelectionNodeProjectionStoreSP safeProjection; void initSelectionImpl(KisSelectionSP copyFrom, KisLayerSP parentLayer, KisPaintDeviceSP copyFromDevice); }; KisMask::KisMask(const QString & name) : KisNode(nullptr) , m_d(new Private(this)) { setName(name); m_d->safeProjection = new KisSafeSelectionNodeProjectionStore(); m_d->safeProjection->setImage(image()); } KisMask::KisMask(const KisMask& rhs) : KisNode(rhs) , KisIndirectPaintingSupport() , m_d(new Private(this)) { setName(rhs.name()); m_d->safeProjection = new KisSafeSelectionNodeProjectionStore(*rhs.m_d->safeProjection); if (rhs.m_d->selection) { m_d->selection = new KisSelection(*rhs.m_d->selection.data()); m_d->selection->setParentNode(this); KisPixelSelectionSP pixelSelection = m_d->selection->pixelSelection(); if (pixelSelection->framesInterface()) { addKeyframeChannel(pixelSelection->keyframeChannel()); enableAnimation(); } } } KisMask::~KisMask() { if (m_d->selection) { m_d->selection->setParentNode(0); } delete m_d; } void KisMask::setImage(KisImageWSP image) { KisPaintDeviceSP parentPaintDevice = parent() ? parent()->original() : 0; KisDefaultBoundsBaseSP defaultBounds = new KisSelectionDefaultBounds(parentPaintDevice, image); if (m_d->selection) { m_d->selection->setDefaultBounds(defaultBounds); } m_d->safeProjection->setImage(image); KisNode::setImage(image); } bool KisMask::allowAsChild(KisNodeSP node) const { Q_UNUSED(node); return false; } const KoColorSpace * KisMask::colorSpace() const { KisNodeSP parentNode = parent(); return parentNode ? parentNode->colorSpace() : 0; } const KoCompositeOp * KisMask::compositeOp() const { /** * FIXME: This function duplicates the same function from * KisLayer. We can't move it to KisBaseNode as it doesn't * know anything about parent() method of KisNode * Please think it over... */ const KoColorSpace *colorSpace = this->colorSpace(); if (!colorSpace) return 0; const KoCompositeOp* op = colorSpace->compositeOp(compositeOpId()); return op ? op : colorSpace->compositeOp(COMPOSITE_OVER); } void KisMask::initSelection(KisSelectionSP copyFrom, KisLayerSP parentLayer) { m_d->initSelectionImpl(copyFrom, parentLayer, 0); } void KisMask::initSelection(KisPaintDeviceSP copyFromDevice, KisLayerSP parentLayer) { m_d->initSelectionImpl(0, parentLayer, copyFromDevice); } void KisMask::initSelection(KisLayerSP parentLayer) { m_d->initSelectionImpl(0, parentLayer, 0); } void KisMask::Private::initSelectionImpl(KisSelectionSP copyFrom, KisLayerSP parentLayer, KisPaintDeviceSP copyFromDevice) { Q_ASSERT(parentLayer); KisPaintDeviceSP parentPaintDevice = parentLayer->original(); if (copyFrom) { /** * We can't use setSelection as we may not have parent() yet */ selection = new KisSelection(*copyFrom); selection->setDefaultBounds(new KisSelectionDefaultBounds(parentPaintDevice, parentLayer->image())); if (copyFrom->hasShapeSelection()) { delete selection->flatten(); } } else if (copyFromDevice) { KritaUtils::DeviceCopyMode copyMode = q->inherits("KisFilterMask") || q->inherits("KisTransparencyMask") ? KritaUtils::CopyAllFrames : KritaUtils::CopySnapshot; selection = new KisSelection(copyFromDevice, copyMode, new KisSelectionDefaultBounds(parentPaintDevice, parentLayer->image())); KisPixelSelectionSP pixelSelection = selection->pixelSelection(); if (pixelSelection->framesInterface()) { KisRasterKeyframeChannel *keyframeChannel = pixelSelection->keyframeChannel(); keyframeChannel->setFilenameSuffix(".pixelselection"); q->addKeyframeChannel(keyframeChannel); q->enableAnimation(); } } else { selection = new KisSelection(new KisSelectionDefaultBounds(parentPaintDevice, parentLayer->image())); selection->pixelSelection()->setDefaultPixel(KoColor(Qt::white, selection->pixelSelection()->colorSpace())); if (deferredSelectionOffset) { selection->setX(deferredSelectionOffset->x()); selection->setY(deferredSelectionOffset->y()); deferredSelectionOffset.reset(); } } selection->setParentNode(q); selection->updateProjection(); } KisSelectionSP KisMask::selection() const { return m_d->selection; } KisPaintDeviceSP KisMask::paintDevice() const { KisSelectionSP selection = this->selection(); return selection ? selection->pixelSelection() : 0; } KisPaintDeviceSP KisMask::original() const { return paintDevice(); } KisPaintDeviceSP KisMask::projection() const { KisPaintDeviceSP originalDevice = original(); KisPaintDeviceSP result = originalDevice; KisSelectionSP selection = this->selection(); if (selection && hasTemporaryTarget()) { result = m_d->safeProjection->getDeviceLazy(selection)->pixelSelection(); } return result; } KisAbstractProjectionPlaneSP KisMask::projectionPlane() const { return m_d->projectionPlane; } void KisMask::setSelection(KisSelectionSP selection) { m_d->selection = selection; if (parent()) { const KisLayer *parentLayer = qobject_cast(parent()); m_d->selection->setDefaultBounds(new KisDefaultBounds(parentLayer->image())); } m_d->selection->setParentNode(this); } void KisMask::select(const QRect & rc, quint8 selectedness) { KisSelectionSP sel = selection(); KisPixelSelectionSP psel = sel->pixelSelection(); psel->select(rc, selectedness); sel->updateProjection(rc); } QRect KisMask::decorateRect(KisPaintDeviceSP &src, KisPaintDeviceSP &dst, const QRect & rc, PositionToFilthy maskPos) const { Q_UNUSED(src); Q_UNUSED(dst); Q_UNUSED(maskPos); Q_ASSERT_X(0, "KisMask::decorateRect", "Should be overridden by successors"); return rc; } bool KisMask::paintsOutsideSelection() const { return false; } void KisMask::apply(KisPaintDeviceSP projection, const QRect &applyRect, const QRect &needRect, PositionToFilthy maskPos) const { if (selection()) { flattenSelectionProjection(m_d->selection, applyRect); KisSelectionSP effectiveSelection = m_d->selection; { // Access temporary target under the lock held KisIndirectPaintingSupport::ReadLocker l(this); if (!paintsOutsideSelection()) { // extent of m_d->selection should also be accessed under a lock, // because it might be being merged in by the temporary target atm QRect effectiveExtent = m_d->selection->selectedRect(); if (hasTemporaryTarget()) { effectiveExtent |= temporaryTarget()->extent(); } if(!effectiveExtent.intersects(applyRect)) { return; } } if (hasTemporaryTarget()) { effectiveSelection = m_d->safeProjection->getDeviceLazy(m_d->selection); KisPainter::copyAreaOptimized(applyRect.topLeft(), m_d->selection->pixelSelection(), effectiveSelection->pixelSelection(), applyRect); KisPainter gc(effectiveSelection->pixelSelection()); setupTemporaryPainter(&gc); gc.bitBlt(applyRect.topLeft(), temporaryTarget(), applyRect); } else { m_d->safeProjection->releaseDevice(); } mergeInMaskInternal(projection, effectiveSelection, applyRect, needRect, maskPos); } } else { mergeInMaskInternal(projection, 0, applyRect, needRect, maskPos); } } void KisMask::mergeInMaskInternal(KisPaintDeviceSP projection, KisSelectionSP effectiveSelection, const QRect &applyRect, const QRect &preparedNeedRect, KisNode::PositionToFilthy maskPos) const { - KisPaintDeviceSP cacheDevice = m_d->paintDeviceCache.getDevice(projection); + KisCachedPaintDevice::Guard d1(projection, m_d->paintDeviceCache); + KisPaintDeviceSP cacheDevice = d1.device(); if (effectiveSelection) { QRect updatedRect = decorateRect(projection, cacheDevice, applyRect, maskPos); // masks don't have any compositioning KisPainter::copyAreaOptimized(updatedRect.topLeft(), cacheDevice, projection, updatedRect, effectiveSelection); } else { cacheDevice->makeCloneFromRough(projection, preparedNeedRect); projection->clear(preparedNeedRect); decorateRect(cacheDevice, projection, applyRect, maskPos); } - - m_d->paintDeviceCache.putDevice(cacheDevice); } void KisMask::flattenSelectionProjection(KisSelectionSP selection, const QRect &dirtyRect) const { selection->updateProjection(dirtyRect); } QRect KisMask::needRect(const QRect &rect, PositionToFilthy pos) const { Q_UNUSED(pos); QRect resultRect = rect; if (m_d->selection) { QRect selectionExtent = m_d->selection->selectedRect(); // copy for thread safety! KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); if (temporaryTarget) { selectionExtent |= temporaryTarget->extent(); } resultRect &= selectionExtent; } return resultRect; } QRect KisMask::changeRect(const QRect &rect, PositionToFilthy pos) const { return KisMask::needRect(rect, pos); } QRect KisMask::extent() const { QRect resultRect; if (m_d->selection) { resultRect = m_d->selection->selectedRect(); // copy for thread safety! KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); if (temporaryTarget) { resultRect |= temporaryTarget->extent(); } } else if (KisNodeSP parent = this->parent()) { resultRect = parent->extent(); } return resultRect; } QRect KisMask::exactBounds() const { QRect resultRect; if (m_d->selection) { resultRect = m_d->selection->selectedExactRect(); // copy for thread safety! KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); if (temporaryTarget) { resultRect |= temporaryTarget->exactBounds(); } } else if (KisNodeSP parent = this->parent()) { resultRect = parent->exactBounds(); } return resultRect; } qint32 KisMask::x() const { return m_d->selection ? m_d->selection->x() : m_d->deferredSelectionOffset ? m_d->deferredSelectionOffset->x() : parent() ? parent()->x() : 0; } qint32 KisMask::y() const { return m_d->selection ? m_d->selection->y() : m_d->deferredSelectionOffset ? m_d->deferredSelectionOffset->y() : parent() ? parent()->y() : 0; } void KisMask::setX(qint32 x) { if (m_d->selection) { m_d->selection->setX(x); } else if (!m_d->deferredSelectionOffset) { m_d->deferredSelectionOffset.reset(new QPoint(x, 0)); } else { m_d->deferredSelectionOffset->rx() = x; } } void KisMask::setY(qint32 y) { if (m_d->selection) { m_d->selection->setY(y); } else if (!m_d->deferredSelectionOffset) { m_d->deferredSelectionOffset.reset(new QPoint(0, y)); } else { m_d->deferredSelectionOffset->ry() = y; } } QRect KisMask::nonDependentExtent() const { return QRect(); } QImage KisMask::createThumbnail(qint32 w, qint32 h) { KisPaintDeviceSP originalDevice = selection() ? selection()->projection() : 0; return originalDevice ? originalDevice->createThumbnail(w, h, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) : QImage(); } void KisMask::testingInitSelection(const QRect &rect, KisLayerSP parentLayer) { if (parentLayer) { m_d->selection = new KisSelection(new KisSelectionDefaultBounds(parentLayer->paintDevice(), parentLayer->image())); } else { m_d->selection = new KisSelection(); } m_d->selection->pixelSelection()->select(rect, OPACITY_OPAQUE_U8); m_d->selection->updateProjection(rect); m_d->selection->setParentNode(this); } KisKeyframeChannel *KisMask::requestKeyframeChannel(const QString &id) { if (id == KisKeyframeChannel::Content.id()) { KisPaintDeviceSP device = paintDevice(); if (device) { KisRasterKeyframeChannel *contentChannel = device->createKeyframeChannel(KisKeyframeChannel::Content); contentChannel->setFilenameSuffix(".pixelselection"); return contentChannel; } } return KisNode::requestKeyframeChannel(id); } void KisMask::baseNodeChangedCallback() { KisNodeSP up = parent(); KisLayer *layer = dynamic_cast(up.data()); if (layer) { layer->notifyChildMaskChanged(); } KisNode::baseNodeChangedCallback(); } diff --git a/libs/image/kis_selection_mask.cpp b/libs/image/kis_selection_mask.cpp index 81329a2fa4..6a3474dd26 100644 --- a/libs/image/kis_selection_mask.cpp +++ b/libs/image/kis_selection_mask.cpp @@ -1,323 +1,321 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_selection_mask.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_selection.h" #include #include #include #include "kis_fill_painter.h" #include #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_pixel_selection.h" #include "kis_undo_adapter.h" #include #include #include "kis_thread_safe_signal_compressor.h" #include "kis_layer_properties_icons.h" #include "kis_cached_paint_device.h" #include "kis_image_config.h" #include "KisImageConfigNotifier.h" struct Q_DECL_HIDDEN KisSelectionMask::Private { public: Private(KisSelectionMask *_q) : q(_q) , updatesCompressor(0) , maskColor(Qt::green, KoColorSpaceRegistry::instance()->rgb8()) {} KisSelectionMask *q; KisImageWSP image; KisCachedPaintDevice paintDeviceCache; KisCachedSelection cachedSelection; KisThreadSafeSignalCompressor *updatesCompressor; KoColor maskColor; void slotSelectionChangedCompressed(); void slotConfigChanged(); }; KisSelectionMask::KisSelectionMask(KisImageWSP image) : KisEffectMask() , m_d(new Private(this)) { setName("selection"); setActive(false); setSupportsLodMoves(false); m_d->image = image; m_d->updatesCompressor = new KisThreadSafeSignalCompressor(50, KisSignalCompressor::FIRST_ACTIVE); connect(m_d->updatesCompressor, SIGNAL(timeout()), SLOT(slotSelectionChangedCompressed())); this->moveToThread(image->thread()); connect(KisImageConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); m_d->slotConfigChanged(); } KisSelectionMask::KisSelectionMask(const KisSelectionMask& rhs) : KisEffectMask(rhs) , m_d(new Private(this)) { m_d->image = rhs.image(); m_d->updatesCompressor = new KisThreadSafeSignalCompressor(300, KisSignalCompressor::POSTPONE); connect(m_d->updatesCompressor, SIGNAL(timeout()), SLOT(slotSelectionChangedCompressed())); this->moveToThread(m_d->image->thread()); connect(KisImageConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); m_d->slotConfigChanged(); } KisSelectionMask::~KisSelectionMask() { m_d->updatesCompressor->deleteLater(); delete m_d; } QIcon KisSelectionMask::icon() const { return KisIconUtils::loadIcon("selectionMask"); } void KisSelectionMask::mergeInMaskInternal(KisPaintDeviceSP projection, KisSelectionSP effectiveSelection, const QRect &applyRect, const QRect &preparedNeedRect, KisNode::PositionToFilthy maskPos) const { Q_UNUSED(maskPos); Q_UNUSED(preparedNeedRect); if (!effectiveSelection) return; { KisSelectionSP mainMaskSelection = this->selection(); if (mainMaskSelection && (!mainMaskSelection->isVisible() || mainMaskSelection->pixelSelection()->defaultBounds()->externalFrameActive())) { return; } } - KisPaintDeviceSP fillDevice = m_d->paintDeviceCache.getDevice(projection); + KisCachedPaintDevice::Guard d1(projection, m_d->paintDeviceCache); + KisPaintDeviceSP fillDevice = d1.device(); fillDevice->setDefaultPixel(m_d->maskColor); const QRect selectionExtent = effectiveSelection->selectedRect(); if (selectionExtent.contains(applyRect) || selectionExtent.intersects(applyRect)) { - KisSelectionSP invertedSelection = m_d->cachedSelection.getSelection(); + KisCachedSelection::Guard s1(m_d->cachedSelection); + KisSelectionSP invertedSelection = s1.selection(); invertedSelection->pixelSelection()->makeCloneFromRough(effectiveSelection->pixelSelection(), applyRect); invertedSelection->pixelSelection()->invert(); KisPainter gc(projection); gc.setSelection(invertedSelection); gc.bitBlt(applyRect.topLeft(), fillDevice, applyRect); - m_d->cachedSelection.putSelection(invertedSelection); - } else { KisPainter gc(projection); gc.bitBlt(applyRect.topLeft(), fillDevice, applyRect); } - - m_d->paintDeviceCache.putDevice(fillDevice); } bool KisSelectionMask::paintsOutsideSelection() const { return true; } void KisSelectionMask::setSelection(KisSelectionSP selection) { if (selection) { KisEffectMask::setSelection(selection); } else { KisEffectMask::setSelection(new KisSelection()); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->alpha8(); KisFillPainter gc(KisPaintDeviceSP(this->selection()->pixelSelection().data())); gc.fillRect(image()->bounds(), KoColor(Qt::white, cs), MAX_SELECTED); gc.end(); } setDirty(); } KisImageWSP KisSelectionMask::image() const { return m_d->image; } bool KisSelectionMask::accept(KisNodeVisitor &v) { return v.visit(this); } void KisSelectionMask::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } KisBaseNode::PropertyList KisSelectionMask::sectionModelProperties() const { KisBaseNode::PropertyList l = KisBaseNode::sectionModelProperties(); l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::selectionActive, active()); return l; } void KisSelectionMask::setSectionModelProperties(const KisBaseNode::PropertyList &properties) { KisEffectMask::setSectionModelProperties(properties); setActive(properties.at(2).state.toBool()); } void KisSelectionMask::setVisible(bool visible, bool isLoading) { const bool oldVisible = this->visible(false); setNodeProperty("visible", visible); if (!isLoading && visible != oldVisible) { if (selection()) selection()->setVisible(visible); } } bool KisSelectionMask::active() const { return nodeProperties().boolProperty("active", true); } void KisSelectionMask::setActive(bool active) { KisImageWSP image = this->image(); KisLayerSP parentLayer = qobject_cast(parent().data()); if (active && parentLayer) { KisSelectionMaskSP activeMask = parentLayer->selectionMask(); if (activeMask && activeMask != this) { activeMask->setActive(false); } } const bool oldActive = this->active(); setNodeProperty("active", active); /** * WARNING: we have a direct link to the image here, but we * must not use it for notification until we are a part of * the nore graph! Notifications should be emitted iff we * have graph listener link set up. */ if (graphListener() && image && oldActive != active) { baseNodeChangedCallback(); image->undoAdapter()->emitSelectionChanged(); } } QRect KisSelectionMask::needRect(const QRect &rect, KisNode::PositionToFilthy pos) const { Q_UNUSED(pos); // selection masks just add an overlay, so the needed rect is simply passed through return rect; } QRect KisSelectionMask::changeRect(const QRect &rect, KisNode::PositionToFilthy pos) const { Q_UNUSED(pos); // selection masks just add an overlay, so the changed rect is simply passed through return rect; } QRect KisSelectionMask::extent() const { // since mask overlay is inverted, the mask paints over // the entire image bounds QRect resultRect; KisSelectionSP selection = this->selection(); if (selection) { resultRect = selection->pixelSelection()->defaultBounds()->bounds(); } else if (KisNodeSP parent = this->parent()) { KisPaintDeviceSP dev = parent->projection(); if (dev) { resultRect = dev->defaultBounds()->bounds(); } } return resultRect; } QRect KisSelectionMask::exactBounds() const { return extent(); } void KisSelectionMask::notifySelectionChangedCompressed() { m_d->updatesCompressor->start(); } void KisSelectionMask::flattenSelectionProjection(KisSelectionSP selection, const QRect &dirtyRect) const { Q_UNUSED(selection); Q_UNUSED(dirtyRect); } void KisSelectionMask::Private::slotSelectionChangedCompressed() { KisSelectionSP currentSelection = q->selection(); if (!currentSelection) return; currentSelection->notifySelectionChanged(); } void KisSelectionMask::Private::slotConfigChanged() { const KoColorSpace *cs = image ? image->colorSpace() : KoColorSpaceRegistry::instance()->rgb8(); KisImageConfig cfg(true); maskColor = KoColor(cfg.selectionOverlayMaskColor(), cs); if (image && image->overlaySelectionMask() == q) { q->setDirty(); } } #include "moc_kis_selection_mask.cpp" diff --git a/libs/image/layerstyles/kis_layer_style_filter_environment.cpp b/libs/image/layerstyles/kis_layer_style_filter_environment.cpp index a8cf6e834e..902a88bd8a 100644 --- a/libs/image/layerstyles/kis_layer_style_filter_environment.cpp +++ b/libs/image/layerstyles/kis_layer_style_filter_environment.cpp @@ -1,133 +1,146 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_layer_style_filter_environment.h" #include #include "kis_layer.h" #include "kis_ls_utils.h" #include "kis_selection.h" #include "kis_pixel_selection.h" #include "kis_painter.h" #include "kis_image.h" #include "krita_utils.h" #include #include "kis_random_accessor_ng.h" #include "kis_iterator_ng.h" +#include "kis_cached_paint_device.h" struct Q_DECL_HIDDEN KisLayerStyleFilterEnvironment::Private { KisLayer *sourceLayer; KisPixelSelectionSP cachedRandomSelection; + KisCachedSelection globalCachedSelection; + KisCachedPaintDevice globalCachedPaintDevice; static KisPixelSelectionSP generateRandomSelection(const QRect &rc); }; KisPixelSelectionSP KisLayerStyleFilterEnvironment::Private:: generateRandomSelection(const QRect &rc) { KisPixelSelectionSP selection = new KisPixelSelection(); KisSequentialIterator dstIt(selection, rc); boost::mt11213b uniformSource; if (uniformSource.max() >= 0x00FFFFFF) { while (dstIt.nextPixel()) { int randValue = uniformSource(); *dstIt.rawData() = (quint8) randValue; if (!dstIt.nextPixel()) break; randValue >>= 8; *dstIt.rawData() = (quint8) randValue; if (!dstIt.nextPixel()) break; randValue >>= 8; *dstIt.rawData() = (quint8) randValue; } } else { while (dstIt.nextPixel()) { *dstIt.rawData() = (quint8) uniformSource(); } } return selection; } KisLayerStyleFilterEnvironment::KisLayerStyleFilterEnvironment(KisLayer *sourceLayer) : m_d(new Private) { Q_ASSERT(sourceLayer); m_d->sourceLayer = sourceLayer; } KisLayerStyleFilterEnvironment::~KisLayerStyleFilterEnvironment() { } QRect KisLayerStyleFilterEnvironment::layerBounds() const { return m_d->sourceLayer ? m_d->sourceLayer->projection()->exactBounds() : QRect(); } QRect KisLayerStyleFilterEnvironment::defaultBounds() const { return m_d->sourceLayer ? m_d->sourceLayer->original()->defaultBounds()->bounds() : QRect(); } int KisLayerStyleFilterEnvironment::currentLevelOfDetail() const { return m_d->sourceLayer ? m_d->sourceLayer->original()->defaultBounds()->currentLevelOfDetail() : 0; } void KisLayerStyleFilterEnvironment::setupFinalPainter(KisPainter *gc, quint8 opacity, const QBitArray &channelFlags) const { Q_ASSERT(m_d->sourceLayer); gc->setOpacity(KritaUtils::mergeOpacity(opacity, m_d->sourceLayer->opacity())); gc->setChannelFlags(KritaUtils::mergeChannelFlags(channelFlags, m_d->sourceLayer->channelFlags())); } KisPixelSelectionSP KisLayerStyleFilterEnvironment::cachedRandomSelection(const QRect &requestedRect) const { KisPixelSelectionSP selection = m_d->cachedRandomSelection; QRect existingRect; if (selection) { existingRect = selection->selectedExactRect(); } if (!existingRect.contains(requestedRect)) { m_d->cachedRandomSelection = Private::generateRandomSelection(requestedRect | existingRect); } return m_d->cachedRandomSelection; } + +KisCachedSelection *KisLayerStyleFilterEnvironment::cachedSelection() +{ + return &m_d->globalCachedSelection; +} + +KisCachedPaintDevice *KisLayerStyleFilterEnvironment::cachedPaintDevice() +{ + return &m_d->globalCachedPaintDevice; +} diff --git a/libs/image/layerstyles/kis_layer_style_filter_environment.h b/libs/image/layerstyles/kis_layer_style_filter_environment.h index a54c9475c6..dfaa55cc1f 100644 --- a/libs/image/layerstyles/kis_layer_style_filter_environment.h +++ b/libs/image/layerstyles/kis_layer_style_filter_environment.h @@ -1,55 +1,60 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_LAYER_STYLE_FILTER_ENVIRONMENT_H #define __KIS_LAYER_STYLE_FILTER_ENVIRONMENT_H #include #include #include #include "kis_types.h" class KisPainter; class KisLayer; class QPainterPath; class QBitArray; +class KisCachedPaintDevice; +class KisCachedSelection; class KRITAIMAGE_EXPORT KisLayerStyleFilterEnvironment { public: KisLayerStyleFilterEnvironment(KisLayer *sourceLayer); ~KisLayerStyleFilterEnvironment(); QRect layerBounds() const; QRect defaultBounds() const; int currentLevelOfDetail() const; void setupFinalPainter(KisPainter *gc, quint8 opacity, const QBitArray &channelFlags) const; KisPixelSelectionSP cachedRandomSelection(const QRect &requestedRect) const; + KisCachedSelection* cachedSelection(); + KisCachedPaintDevice* cachedPaintDevice(); + private: struct Private; const QScopedPointer m_d; }; #endif /* __KIS_LAYER_STYLE_FILTER_ENVIRONMENT_H */ diff --git a/libs/image/layerstyles/kis_layer_style_projection_plane.cpp b/libs/image/layerstyles/kis_layer_style_projection_plane.cpp index 93d75d52f7..e89a900cad 100644 --- a/libs/image/layerstyles/kis_layer_style_projection_plane.cpp +++ b/libs/image/layerstyles/kis_layer_style_projection_plane.cpp @@ -1,364 +1,365 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_layer_style_projection_plane.h" #include "kis_global.h" #include "kis_layer_style_filter_projection_plane.h" #include "kis_layer_projection_plane.h" #include "kis_psd_layer_style.h" #include "kis_ls_drop_shadow_filter.h" #include "kis_ls_satin_filter.h" #include "kis_ls_overlay_filter.h" #include "kis_ls_stroke_filter.h" #include "kis_ls_bevel_emboss_filter.h" #include "kis_projection_leaf.h" #include "kis_cached_paint_device.h" #include "kis_painter.h" #include "kis_ls_utils.h" struct Q_DECL_HIDDEN KisLayerStyleProjectionPlane::Private { KisLayerProjectionPlaneWSP sourceProjectionPlane; QVector stylesBefore; QVector stylesAfter; QVector stylesOverlay; KisCachedPaintDevice cachedPaintDevice; + KisCachedSelection cachedSelection; KisLayer *sourceLayer = 0; KisPSDLayerStyleSP style; bool canHaveChildNodes = false; bool dependsOnLowerNodes = false; void initSourcePlane(KisLayer *sourceLayer) { KIS_SAFE_ASSERT_RECOVER_RETURN(sourceLayer); sourceProjectionPlane = sourceLayer->internalProjectionPlane(); canHaveChildNodes = sourceLayer->projectionLeaf()->canHaveChildLayers(); dependsOnLowerNodes = sourceLayer->projectionLeaf()->dependsOnLowerNodes(); this->sourceLayer = sourceLayer; } QVector allStyles() const { return stylesBefore + stylesOverlay + stylesAfter; } bool hasOverlayStylesReady() const { Q_FOREACH (KisLayerStyleFilterProjectionPlaneSP plane, stylesOverlay) { if (!plane->isEmpty()) return true; } return false; } }; KisLayerStyleProjectionPlane::KisLayerStyleProjectionPlane(KisLayer *sourceLayer) : m_d(new Private) { KisPSDLayerStyleSP style = sourceLayer->layerStyle(); KIS_ASSERT_RECOVER(style) { style = toQShared(new KisPSDLayerStyle()); } init(sourceLayer, style); } KisLayerStyleProjectionPlane::KisLayerStyleProjectionPlane(const KisLayerStyleProjectionPlane &rhs, KisLayer *sourceLayer, KisPSDLayerStyleSP clonedStyle) : m_d(new Private) { m_d->initSourcePlane(sourceLayer); m_d->style = clonedStyle; KIS_SAFE_ASSERT_RECOVER(m_d->style) { m_d->style = toQShared(new KisPSDLayerStyle()); } Q_FOREACH (KisLayerStyleFilterProjectionPlaneSP plane, rhs.m_d->allStyles()) { m_d->stylesBefore << toQShared(new KisLayerStyleFilterProjectionPlane(*plane, sourceLayer, m_d->style)); } } // for testing purposes only! KisLayerStyleProjectionPlane::KisLayerStyleProjectionPlane(KisLayer *sourceLayer, KisPSDLayerStyleSP layerStyle) : m_d(new Private) { init(sourceLayer, layerStyle); } void KisLayerStyleProjectionPlane::init(KisLayer *sourceLayer, KisPSDLayerStyleSP style) { KIS_SAFE_ASSERT_RECOVER_RETURN(sourceLayer); m_d->initSourcePlane(sourceLayer); m_d->style = style; { KisLayerStyleFilterProjectionPlane *dropShadow = new KisLayerStyleFilterProjectionPlane(sourceLayer); dropShadow->setStyle(new KisLsDropShadowFilter(KisLsDropShadowFilter::DropShadow), style); m_d->stylesBefore << toQShared(dropShadow); } { KisLayerStyleFilterProjectionPlane *outerGlow = new KisLayerStyleFilterProjectionPlane(sourceLayer); outerGlow->setStyle(new KisLsDropShadowFilter(KisLsDropShadowFilter::OuterGlow), style); m_d->stylesAfter << toQShared(outerGlow); } { KisLayerStyleFilterProjectionPlane *stroke = new KisLayerStyleFilterProjectionPlane(sourceLayer); stroke->setStyle(new KisLsStrokeFilter(), style); m_d->stylesAfter << toQShared(stroke); } { KisLayerStyleFilterProjectionPlane *bevelEmboss = new KisLayerStyleFilterProjectionPlane(sourceLayer); bevelEmboss->setStyle(new KisLsBevelEmbossFilter(), style); m_d->stylesAfter << toQShared(bevelEmboss); } { KisLayerStyleFilterProjectionPlane *patternOverlay = new KisLayerStyleFilterProjectionPlane(sourceLayer); patternOverlay->setStyle(new KisLsOverlayFilter(KisLsOverlayFilter::Pattern), style); m_d->stylesOverlay << toQShared(patternOverlay); } { KisLayerStyleFilterProjectionPlane *gradientOverlay = new KisLayerStyleFilterProjectionPlane(sourceLayer); gradientOverlay->setStyle(new KisLsOverlayFilter(KisLsOverlayFilter::Gradient), style); m_d->stylesOverlay << toQShared(gradientOverlay); } { KisLayerStyleFilterProjectionPlane *colorOverlay = new KisLayerStyleFilterProjectionPlane(sourceLayer); colorOverlay->setStyle(new KisLsOverlayFilter(KisLsOverlayFilter::Color), style); m_d->stylesOverlay << toQShared(colorOverlay); } { KisLayerStyleFilterProjectionPlane *satin = new KisLayerStyleFilterProjectionPlane(sourceLayer); satin->setStyle(new KisLsSatinFilter(), style); m_d->stylesOverlay << toQShared(satin); } { KisLayerStyleFilterProjectionPlane *innerGlow = new KisLayerStyleFilterProjectionPlane(sourceLayer); innerGlow->setStyle(new KisLsDropShadowFilter(KisLsDropShadowFilter::InnerGlow), style); m_d->stylesOverlay << toQShared(innerGlow); } { KisLayerStyleFilterProjectionPlane *innerShadow = new KisLayerStyleFilterProjectionPlane(sourceLayer); innerShadow->setStyle(new KisLsDropShadowFilter(KisLsDropShadowFilter::InnerShadow), style); m_d->stylesOverlay << toQShared(innerShadow); } } KisLayerStyleProjectionPlane::~KisLayerStyleProjectionPlane() { } KisAbstractProjectionPlaneSP KisLayerStyleProjectionPlane::factoryObject(KisLayer *sourceLayer) { Q_ASSERT(sourceLayer); return toQShared(new KisLayerStyleProjectionPlane(sourceLayer)); } QRect KisLayerStyleProjectionPlane::recalculate(const QRect& rect, KisNodeSP filthyNode) { KisAbstractProjectionPlaneSP sourcePlane = m_d->sourceProjectionPlane.toStrongRef(); QRect result = sourcePlane->recalculate(stylesNeedRect(rect), filthyNode); if (m_d->style->isEnabled()) { Q_FOREACH (const KisAbstractProjectionPlaneSP plane, m_d->allStyles()) { plane->recalculate(rect, filthyNode); } } return result; } void KisLayerStyleProjectionPlane::apply(KisPainter *painter, const QRect &rect) { KisLayerProjectionPlaneSP sourcePlane = m_d->sourceProjectionPlane.toStrongRef(); if (m_d->style->isEnabled()) { if (m_d->hasOverlayStylesReady()) { - KisPaintDeviceSP originalClone = m_d->cachedPaintDevice.getDevice(painter->device()); + KisCachedPaintDevice::Guard d1(painter->device(), m_d->cachedPaintDevice); + KisPaintDeviceSP originalClone = d1.device(); originalClone->makeCloneFromRough(painter->device(), rect); Q_FOREACH (const KisAbstractProjectionPlaneSP plane, m_d->stylesBefore) { plane->apply(painter, rect); } - // TODO: cache selection device - KisSelectionSP knockoutSelection = KisLsUtils::selectionFromAlphaChannel(m_d->sourceLayer->projection(), rect); + KisCachedSelection::Guard s1(m_d->cachedSelection); + KisSelectionSP knockoutSelection = s1.selection(); + KisLsUtils::selectionFromAlphaChannel(m_d->sourceLayer->projection(), knockoutSelection, rect); { KisPainter overlayPainter(originalClone); sourcePlane->applyMaxOutAlpha(&overlayPainter, rect); Q_FOREACH (const KisAbstractProjectionPlaneSP plane, m_d->stylesOverlay) { plane->apply(&overlayPainter, rect); } } painter->setOpacity(OPACITY_OPAQUE_U8); painter->setChannelFlags(QBitArray()); painter->setCompositeOp(COMPOSITE_COPY); painter->setSelection(knockoutSelection); painter->bitBlt(rect.topLeft(), originalClone, rect); Q_FOREACH (const KisAbstractProjectionPlaneSP plane, m_d->stylesAfter) { plane->apply(painter, rect); } - m_d->cachedPaintDevice.putDevice(originalClone); - } else { Q_FOREACH (const KisAbstractProjectionPlaneSP plane, m_d->stylesBefore) { plane->apply(painter, rect); } sourcePlane->apply(painter, rect); Q_FOREACH (const KisAbstractProjectionPlaneSP plane, m_d->stylesAfter) { plane->apply(painter, rect); } } } else { sourcePlane->apply(painter, rect); } } KisPaintDeviceList KisLayerStyleProjectionPlane::getLodCapableDevices() const { KisPaintDeviceList list; KisAbstractProjectionPlaneSP sourcePlane = m_d->sourceProjectionPlane.toStrongRef(); if (m_d->style->isEnabled()) { Q_FOREACH (const KisAbstractProjectionPlaneSP plane, m_d->allStyles()) { list << plane->getLodCapableDevices(); } list << sourcePlane->getLodCapableDevices(); } else { list << sourcePlane->getLodCapableDevices(); } return list; } QRect KisLayerStyleProjectionPlane::needRect(const QRect &rect, KisLayer::PositionToFilthy pos) const { /** * Need rect should also be adjust for the layers that generate their 'original' * based on the contents of the underlying layers like KisAdjustmentLayer * * \see bug 390299 */ QRect needRect = rect; const bool adjustmentAboveDirty = m_d->dependsOnLowerNodes && (pos & KisLayer::N_FILTHY || pos & KisLayer::N_ABOVE_FILTHY); if (m_d->style->isEnabled() && adjustmentAboveDirty) { needRect |= stylesNeedRect(rect); } KisAbstractProjectionPlaneSP sourcePlane = m_d->sourceProjectionPlane.toStrongRef(); needRect = sourcePlane->needRect(needRect, pos); return needRect; } QRect KisLayerStyleProjectionPlane::changeRect(const QRect &rect, KisLayer::PositionToFilthy pos) const { KisAbstractProjectionPlaneSP sourcePlane = m_d->sourceProjectionPlane.toStrongRef(); QRect layerChangeRect = sourcePlane->changeRect(rect, pos); QRect changeRect = layerChangeRect; if (m_d->style->isEnabled()) { Q_FOREACH (const KisAbstractProjectionPlaneSP plane, m_d->allStyles()) { changeRect |= plane->changeRect(layerChangeRect, KisLayer::N_ABOVE_FILTHY); } } return changeRect; } QRect KisLayerStyleProjectionPlane::accessRect(const QRect &rect, KisLayer::PositionToFilthy pos) const { KisAbstractProjectionPlaneSP sourcePlane = m_d->sourceProjectionPlane.toStrongRef(); QRect accessRect = sourcePlane->accessRect(rect, pos); if (m_d->style->isEnabled()) { Q_FOREACH (const KisAbstractProjectionPlaneSP plane, m_d->allStyles()) { accessRect |= plane->accessRect(rect, KisLayer::N_ABOVE_FILTHY); } } return accessRect; } QRect KisLayerStyleProjectionPlane::needRectForOriginal(const QRect &rect) const { /** * Need rect should also be adjust for the layers that generate their 'original' * based on the contents of the child layers like KisGroupLayer * * \see bug 366419 */ QRect needRect = rect; if (m_d->style->isEnabled()) { needRect |= stylesNeedRect(needRect); } KisAbstractProjectionPlaneSP sourcePlane = m_d->sourceProjectionPlane.toStrongRef(); needRect = sourcePlane->needRectForOriginal(needRect); return needRect; } QRect KisLayerStyleProjectionPlane::stylesNeedRect(const QRect &rect) const { QRect needRect = rect; Q_FOREACH (const KisAbstractProjectionPlaneSP plane, m_d->allStyles()) { needRect |= plane->needRect(rect, KisLayer::N_ABOVE_FILTHY); } return needRect; } diff --git a/libs/image/layerstyles/kis_ls_bevel_emboss_filter.cpp b/libs/image/layerstyles/kis_ls_bevel_emboss_filter.cpp index 0702cd7e39..79aee34514 100644 --- a/libs/image/layerstyles/kis_ls_bevel_emboss_filter.cpp +++ b/libs/image/layerstyles/kis_ls_bevel_emboss_filter.cpp @@ -1,502 +1,517 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * See LayerFX plugin for Gimp as a reference implementation of this style: * http://registry.gimp.org/node/186 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_ls_bevel_emboss_filter.h" #include #include #include #include #include #include "psd.h" #include "kis_convolution_kernel.h" #include "kis_convolution_painter.h" #include "kis_gaussian_kernel.h" #include "kis_pixel_selection.h" #include "kis_fill_painter.h" #include "kis_gradient_painter.h" #include "kis_iterator_ng.h" #include "kis_random_accessor_ng.h" #include "kis_psd_layer_style.h" #include "kis_layer_style_filter_environment.h" #include "kis_ls_utils.h" #include "gimp_bump_map.h" #include "kis_transaction.h" #include "kis_multiple_projection.h" +#include "kis_cached_paint_device.h" KisLsBevelEmbossFilter::KisLsBevelEmbossFilter() : KisLayerStyleFilter(KoID("lsstroke", i18n("Stroke (style)"))) { } KisLayerStyleFilter *KisLsBevelEmbossFilter::clone() const { return new KisLsBevelEmbossFilter(*this); } KisLsBevelEmbossFilter::KisLsBevelEmbossFilter(const KisLsBevelEmbossFilter &rhs) : KisLayerStyleFilter(rhs) { } void paintBevelSelection(KisPixelSelectionSP srcSelection, KisPixelSelectionSP dstSelection, const QRect &applyRect, int size, int initialSize, - bool invert) + bool invert, + KisLayerStyleFilterEnvironment *env) { - KisSelectionSP tmpBaseSelection = new KisSelection(new KisSelectionEmptyBounds(0)); + KisCachedSelection::Guard s1(*env->cachedSelection()); + KisSelectionSP tmpBaseSelection = s1.selection(); KisPixelSelectionSP tmpSelection = tmpBaseSelection->pixelSelection(); // NOTE: we are not using createCompositionSourceDevice() intentionally, // because the source device doesn't have alpha channel - KisPixelSelectionSP fillDevice = new KisPixelSelection(); + KisCachedSelection::Guard s2(*env->cachedSelection()); + KisPixelSelectionSP fillDevice = s2.selection()->pixelSelection(); KisPainter gc(dstSelection); gc.setCompositeOp(COMPOSITE_COPY); for (int i = 0; i < size; i++) { const int growSize = initialSize - i - 1; quint8 selectedness = invert ? qRound(qreal(size - i - 1) / size * 255.0) : qRound(qreal(i + 1) / size * 255.0); fillDevice->setDefaultPixel(KoColor(&selectedness, fillDevice->colorSpace())); tmpSelection->makeCloneFromRough(srcSelection, srcSelection->selectedRect()); QRect changeRect = KisLsUtils::growSelectionUniform(tmpSelection, growSize, applyRect); gc.setSelection(tmpBaseSelection); gc.bitBlt(changeRect.topLeft(), fillDevice, changeRect); } } struct ContrastOp { static const bool supportsCaching = false; ContrastOp(qreal contrast) : m_contrast(contrast) { } int operator() (int iValue) { qreal value = qreal(iValue - 127) / 127.0; qreal slant = std::tan ((m_contrast + 1) * M_PI_4); value = (value - 0.5) * slant + 0.5; return qRound(value * 255.0); } private: qreal m_contrast; }; struct HighlightsFetchOp { static const bool supportsCaching = true; int operator() (int value) { return qRound(qMax(0, value - 127) * (255.0 / (255 - 127))); } }; struct ShadowsFetchOp { static const bool supportsCaching = true; int operator() (int value) { return 255 - qRound(qMin(value, 127) * (255.0 / 127.0)); } }; template void mapPixelValues(KisPixelSelectionSP srcSelection, KisPixelSelectionSP dstSelection, MapOp mapOp, const QRect &applyRect) { static quint8 mapTable[256]; static bool mapInitialized = false; if (!MapOp::supportsCaching || !mapInitialized) { mapInitialized = true; for (int i = 0; i < 256; i++) { mapTable[i] = mapOp(i); } } KisSequentialConstIterator srcIt(srcSelection, applyRect); KisSequentialIterator dstIt(dstSelection, applyRect); while (srcIt.nextPixel() && dstIt.nextPixel()) { const quint8 *srcPtr = srcIt.rawDataConst(); quint8 *dstPtr = dstIt.rawData(); *dstPtr = mapTable[*srcPtr]; } } template void mapPixelValues(KisPixelSelectionSP dstSelection, MapOp mapOp, const QRect &applyRect) { static quint8 mapTable[256]; static bool mapInitialized = false; if (!MapOp::supportsCaching || !mapInitialized) { mapInitialized = true; for (int i = 0; i < 256; i++) { mapTable[i] = mapOp(i); } } KisSequentialIterator dstIt(dstSelection, applyRect); while (dstIt.nextPixel()) { quint8 *dstPtr = dstIt.rawData(); *dstPtr = mapTable[*dstPtr]; } } struct BevelEmbossRectCalculator { BevelEmbossRectCalculator(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config) { shadowHighlightsFinalRect = applyRect; applyGaussianRect = shadowHighlightsFinalRect; applyGlossContourRect = KisLsUtils::growRectFromRadius(applyGaussianRect, config->soften()); applyBumpmapRect = applyGlossContourRect; applyContourRect = applyBumpmapRect; applyTextureRect = applyContourRect; applyBevelRect = calcBevelNeedRect(applyTextureRect, config); initialFetchRect = kisGrowRect(applyBevelRect, 1); } QRect totalChangeRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config) { QRect changeRect = calcBevelChangeRect(applyRect, config); changeRect = kisGrowRect(changeRect, 1); // bumpmap method changeRect = KisLsUtils::growRectFromRadius(changeRect, config->soften()); return changeRect; } QRect totalNeedRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config) { QRect changeRect = applyRect; changeRect = KisLsUtils::growRectFromRadius(changeRect, config->soften()); changeRect = kisGrowRect(changeRect, 1); // bumpmap method changeRect = calcBevelNeedRect(applyRect, config); return changeRect; } QRect initialFetchRect; QRect applyBevelRect; QRect applyTextureRect; QRect applyContourRect; QRect applyBumpmapRect; QRect applyGlossContourRect; QRect applyGaussianRect; QRect shadowHighlightsFinalRect; private: QRect calcBevelChangeRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config) { const int size = config->size(); int limitingGrowSize = 0; switch (config->style()) { case psd_bevel_outer_bevel: limitingGrowSize = size; break; case psd_bevel_inner_bevel: limitingGrowSize = 0; break; case psd_bevel_emboss: { const int initialSize = std::ceil(qreal(size) / 2.0); limitingGrowSize = initialSize; break; } case psd_bevel_pillow_emboss: { const int halfSizeC = std::ceil(qreal(size) / 2.0); limitingGrowSize = halfSizeC; break; } case psd_bevel_stroke_emboss: warnKrita << "WARNING: Stroke Emboss style is not implemented yet!"; return applyRect; } return kisGrowRect(applyRect, limitingGrowSize); } QRect calcBevelNeedRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config) { const int size = config->size(); int limitingGrowSize = size; return kisGrowRect(applyRect, limitingGrowSize); } }; void KisLsBevelEmbossFilter::applyBevelEmboss(KisPaintDeviceSP srcDevice, KisMultipleProjection *dst, const QRect &applyRect, const psd_layer_effects_bevel_emboss *config, KisLayerStyleFilterEnvironment *env) const { if (applyRect.isEmpty()) return; BevelEmbossRectCalculator d(applyRect, config); - KisSelectionSP baseSelection = KisLsUtils::selectionFromAlphaChannel(srcDevice, d.initialFetchRect); + KisCachedSelection::Guard s1(*env->cachedSelection()); + KisSelectionSP baseSelection = s1.selection(); + KisLsUtils::selectionFromAlphaChannel(srcDevice, baseSelection, d.initialFetchRect); + KisPixelSelectionSP selection = baseSelection->pixelSelection(); //selection->convertToQImage(0, QRect(0,0,300,300)).save("0_selection_initial.png"); const int size = config->size(); int limitingGrowSize = 0; - KisPixelSelectionSP bumpmapSelection = new KisPixelSelection(new KisSelectionEmptyBounds(0)); + KisCachedSelection::Guard s2(*env->cachedSelection()); + KisPixelSelectionSP bumpmapSelection = s2.selection()->pixelSelection(); switch (config->style()) { case psd_bevel_outer_bevel: - paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, size, size, false); + paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, size, size, false, env); limitingGrowSize = size; break; case psd_bevel_inner_bevel: - paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, size, 0, false); + paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, size, 0, false, env); limitingGrowSize = 0; break; case psd_bevel_emboss: { const int initialSize = std::ceil(qreal(size) / 2.0); - paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, size, initialSize, false); + paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, size, initialSize, false, env); limitingGrowSize = initialSize; break; } case psd_bevel_pillow_emboss: { const int halfSizeF = std::floor(qreal(size) / 2.0); const int halfSizeC = std::ceil(qreal(size) / 2.0); // TODO: probably not correct! - paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, halfSizeC, halfSizeC, false); - paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, halfSizeF, 0, true); + paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, halfSizeC, halfSizeC, false, env); + paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, halfSizeF, 0, true, env); limitingGrowSize = halfSizeC; break; } case psd_bevel_stroke_emboss: warnKrita << "WARNING: Stroke Emboss style is not implemented yet!"; return; } - KisPixelSelectionSP limitingSelection = new KisPixelSelection(*selection); + KisCachedSelection::Guard s3(*env->cachedSelection()); + KisPixelSelectionSP limitingSelection = s3.selection()->pixelSelection(); + limitingSelection->makeCloneFromRough(selection, selection->selectedRect()); { QRect changeRectUnused = KisLsUtils::growSelectionUniform(limitingSelection, limitingGrowSize, d.applyBevelRect); Q_UNUSED(changeRectUnused); } //bumpmapSelection->convertToQImage(0, QRect(0,0,300,300)).save("1_selection_xconv.png"); if (config->textureEnabled()) { - KisPixelSelectionSP textureSelection = new KisPixelSelection(new KisSelectionEmptyBounds(0)); + KisCachedSelection::Guard s4(*env->cachedSelection()); + KisPixelSelectionSP textureSelection = s4.selection()->pixelSelection(); KisLsUtils::fillPattern(textureSelection, d.applyTextureRect, env, config->textureScale(), config->texturePattern(), config->textureHorizontalPhase(), config->textureVerticalPhase(), config->textureAlignWithLayer()); int contrastadj = 0; { using namespace std; int tex_depth = config->textureDepth(); if (tex_depth >= 0.0) { if (tex_depth <= 100.0) { contrastadj = int(qRound((1-(tex_depth/100.0)) * -127)); } else { contrastadj = int(qRound(((tex_depth-100.0)/900.0) * 127)); } } else { textureSelection->invert(); if (tex_depth >= -100.0) { contrastadj = int(qRound((1-(abs(tex_depth)/100.0)) * -127)); } else { contrastadj = int(qRound(((abs(tex_depth)-100.0)/900.0) * 127)); } } } qreal contrast = qBound(-1.0, qreal(contrastadj) / 127.0, 1.0); mapPixelValues(textureSelection, ContrastOp(contrast), d.applyTextureRect); { KisPainter gc(bumpmapSelection); gc.setCompositeOp(COMPOSITE_MULT); gc.bitBlt(d.applyTextureRect.topLeft(), textureSelection, d.applyTextureRect); gc.end(); } } //bumpmapSelection->convertToQImage(0, QRect(0,0,300,300)).save("15_selection_texture.png"); if (config->contourEnabled()) { if (config->range() != KisLsUtils::FULL_PERCENT_RANGE) { KisLsUtils::adjustRange(bumpmapSelection, d.applyContourRect, config->range()); } KisLsUtils::applyContourCorrection(bumpmapSelection, d.applyContourRect, config->contourLookupTable(), config->antiAliased(), true); } bumpmap_vals_t bmvals; bmvals.azimuth = config->angle(); bmvals.elevation = config->altitude(); bmvals.depth = config->depth(); bmvals.ambient = 0; bmvals.compensate = true; bmvals.invert = config->direction() == psd_direction_down; bmvals.type = LINEAR; bumpmap(bumpmapSelection, d.applyBumpmapRect, bmvals); //bumpmapSelection->convertToQImage(0, QRect(0,0,300,300)).save("3_selection_bumpmap.png"); { // TODO: optimize! KisLsUtils::applyContourCorrection(bumpmapSelection, d.applyGlossContourRect, config->glossContourLookupTable(), config->glossAntiAliased(), true); } if (config->soften()) { KisLsUtils::applyGaussianWithTransaction(bumpmapSelection, d.applyGaussianRect, config->soften()); } if (config->textureEnabled() && config->textureInvert()) { bumpmapSelection->invert(); } selection->clear(); mapPixelValues(bumpmapSelection, selection, ShadowsFetchOp(), d.shadowHighlightsFinalRect); selection->applySelection(limitingSelection, SELECTION_INTERSECT); //dstDevice->convertToQImage(0, QRect(0,0,300,300)).save("4_dst_before_apply.png"); //selection->convertToQImage(0, QRect(0,0,300,300)).save("4_shadows_sel.png"); { KisPaintDeviceSP dstDevice = dst->getProjection("00_bevel_shadow", config->shadowBlendMode(), config->shadowOpacity(), QBitArray(), srcDevice); const KoColor fillColor(config->shadowColor(), dstDevice->colorSpace()); const QRect &fillRect = d.shadowHighlightsFinalRect; - KisPaintDeviceSP fillDevice = new KisPaintDevice(dstDevice->colorSpace()); + + KisCachedPaintDevice::Guard d1(dstDevice, *env->cachedPaintDevice()); + KisPaintDeviceSP fillDevice = d1.device(); fillDevice->setDefaultPixel(fillColor); KisPainter::copyAreaOptimized(fillRect.topLeft(), fillDevice, dstDevice, fillRect, baseSelection); } selection->clear(); mapPixelValues(bumpmapSelection, selection, HighlightsFetchOp(), d.shadowHighlightsFinalRect); selection->applySelection(limitingSelection, SELECTION_INTERSECT); //selection->convertToQImage(0, QRect(0,0,300,300)).save("5_highlights_sel.png"); { KisPaintDeviceSP dstDevice = dst->getProjection("01_bevel_highlight", config->highlightBlendMode(), config->highlightOpacity(), QBitArray(), srcDevice); const KoColor fillColor(config->highlightColor(), dstDevice->colorSpace()); const QRect &fillRect = d.shadowHighlightsFinalRect; - KisPaintDeviceSP fillDevice = new KisPaintDevice(dstDevice->colorSpace()); + + KisCachedPaintDevice::Guard d1(dstDevice, *env->cachedPaintDevice()); + KisPaintDeviceSP fillDevice = d1.device(); fillDevice->setDefaultPixel(fillColor); KisPainter::copyAreaOptimized(fillRect.topLeft(), fillDevice, dstDevice, fillRect, baseSelection); } } void KisLsBevelEmbossFilter::processDirectly(KisPaintDeviceSP src, KisMultipleProjection *dst, const QRect &applyRect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const { Q_UNUSED(env); KIS_ASSERT_RECOVER_RETURN(style); const psd_layer_effects_bevel_emboss *config = style->bevelAndEmboss(); if (!KisLsUtils::checkEffectEnabled(config, dst)) return; KisLsUtils::LodWrapper w(env->currentLevelOfDetail(), config); applyBevelEmboss(src, dst, applyRect, w.config, env); } QRect KisLsBevelEmbossFilter::neededRect(const QRect &rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const { const psd_layer_effects_bevel_emboss *config = style->bevelAndEmboss(); if (!config->effectEnabled()) return rect; KisLsUtils::LodWrapper w(env->currentLevelOfDetail(), config); BevelEmbossRectCalculator d(rect, w.config); return d.totalNeedRect(rect, w.config); } QRect KisLsBevelEmbossFilter::changedRect(const QRect &rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const { const psd_layer_effects_bevel_emboss *config = style->bevelAndEmboss(); if (!config->effectEnabled()) return rect; KisLsUtils::LodWrapper w(env->currentLevelOfDetail(), config); BevelEmbossRectCalculator d(rect, w.config); return d.totalChangeRect(rect, w.config); } diff --git a/libs/image/layerstyles/kis_ls_drop_shadow_filter.cpp b/libs/image/layerstyles/kis_ls_drop_shadow_filter.cpp index 39581b8951..b0da105efe 100644 --- a/libs/image/layerstyles/kis_ls_drop_shadow_filter.cpp +++ b/libs/image/layerstyles/kis_ls_drop_shadow_filter.cpp @@ -1,294 +1,298 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_ls_drop_shadow_filter.h" #include #include #include #include #include "psd.h" #include "kis_convolution_kernel.h" #include "kis_convolution_painter.h" #include "kis_gaussian_kernel.h" #include "kis_pixel_selection.h" #include "kis_fill_painter.h" #include "kis_iterator_ng.h" #include "kis_random_accessor_ng.h" #include "kis_psd_layer_style.h" #include "kis_multiple_projection.h" #include "kis_ls_utils.h" #include "kis_layer_style_filter_environment.h" +#include "kis_cached_paint_device.h" KisLsDropShadowFilter::KisLsDropShadowFilter(Mode mode) : KisLayerStyleFilter(KoID("lsdropshadow", i18n("Drop Shadow (style)"))) , m_mode(mode) { } KisLsDropShadowFilter::KisLsDropShadowFilter(const KisLsDropShadowFilter &rhs) : KisLayerStyleFilter(rhs), m_mode(rhs.m_mode) { } KisLayerStyleFilter *KisLsDropShadowFilter::clone() const { return new KisLsDropShadowFilter(*this); } struct ShadowRectsData { enum Direction { NEED_RECT, CHANGE_RECT }; ShadowRectsData(const QRect &applyRect, const psd_layer_effects_context *context, const psd_layer_effects_shadow_base *shadow, Direction direction) { spread_size = (shadow->spread() * shadow->size() + 50) / 100; blur_size = shadow->size() - spread_size; offset = shadow->calculateOffset(context); // need rect calculation in reverse order dstRect = applyRect; const int directionCoeff = direction == NEED_RECT ? -1 : 1; srcRect = dstRect.translated(directionCoeff * offset); noiseNeedRect = shadow->noise() > 0 ? kisGrowRect(srcRect, KisLsUtils::noiseNeedBorder) : srcRect; blurNeedRect = blur_size ? KisLsUtils::growRectFromRadius(noiseNeedRect, blur_size) : noiseNeedRect; spreadNeedRect = spread_size ? KisLsUtils::growRectFromRadius(blurNeedRect, spread_size) : blurNeedRect; // dbgKrita << ppVar(dstRect); // dbgKrita << ppVar(srcRect); // dbgKrita << ppVar(noiseNeedRect); // dbgKrita << ppVar(blurNeedRect); // dbgKrita << ppVar(spreadNeedRect); } inline QRect finalNeedRect() const { return spreadNeedRect; } inline QRect finalChangeRect() const { // TODO: is it correct? return spreadNeedRect; } qint32 spread_size; qint32 blur_size; QPoint offset; QRect srcRect; QRect dstRect; QRect noiseNeedRect; QRect blurNeedRect; QRect spreadNeedRect; }; -void applyDropShadow(KisPaintDeviceSP srcDevice, - KisMultipleProjection *dst, - const QRect &applyRect, - const psd_layer_effects_context *context, - const psd_layer_effects_shadow_base *shadow, - const KisLayerStyleFilterEnvironment *env) +void KisLsDropShadowFilter::applyDropShadow(KisPaintDeviceSP srcDevice, + KisMultipleProjection *dst, + const QRect &applyRect, + const psd_layer_effects_context *context, + const psd_layer_effects_shadow_base *shadow, + KisLayerStyleFilterEnvironment *env) const { if (applyRect.isEmpty()) return; ShadowRectsData d(applyRect, context, shadow, ShadowRectsData::NEED_RECT); - KisSelectionSP baseSelection = - KisLsUtils::selectionFromAlphaChannel(srcDevice, d.spreadNeedRect); + KisCachedSelection::Guard s1(*env->cachedSelection()); + KisSelectionSP baseSelection = s1.selection(); + KisLsUtils::selectionFromAlphaChannel(srcDevice, baseSelection, d.spreadNeedRect); KisPixelSelectionSP selection = baseSelection->pixelSelection(); //selection->convertToQImage(0, QRect(0,0,300,300)).save("0_selection_initial.png"); if (shadow->invertsSelection()) { selection->invert(); } /** * Copy selection which will be erased from the original later */ + KisCachedSelection::Guard s2(*env->cachedSelection()); KisPixelSelectionSP knockOutSelection; if (shadow->knocksOut()) { - knockOutSelection = new KisPixelSelection(*selection); + knockOutSelection = s2.selection()->pixelSelection(); + knockOutSelection->makeCloneFromRough(selection, selection->selectedRect()); } if (shadow->technique() == psd_technique_precise) { KisLsUtils::findEdge(selection, d.blurNeedRect, true); } /** * Spread and blur the selection */ if (d.spread_size) { KisLsUtils::applyGaussianWithTransaction(selection, d.blurNeedRect, d.spread_size); // TODO: find out why in libpsd we pass false here. If we do so, // the result is fully black, which is not expected KisLsUtils::findEdge(selection, d.blurNeedRect, true /*shadow->edgeHidden()*/); } //selection->convertToQImage(0, QRect(0,0,300,300)).save("1_selection_spread.png"); if (d.blur_size) { KisLsUtils::applyGaussianWithTransaction(selection, d.noiseNeedRect, d.blur_size); } //selection->convertToQImage(0, QRect(0,0,300,300)).save("2_selection_blur.png"); if (shadow->range() != KisLsUtils::FULL_PERCENT_RANGE) { KisLsUtils::adjustRange(selection, d.noiseNeedRect, shadow->range()); } const psd_layer_effects_inner_glow *iglow = 0; if ((iglow = dynamic_cast(shadow)) && iglow->source() == psd_glow_center) { selection->invert(); } /** * Contour correction */ KisLsUtils::applyContourCorrection(selection, d.noiseNeedRect, shadow->contourLookupTable(), shadow->antiAliased(), shadow->edgeHidden()); //selection->convertToQImage(0, QRect(0,0,300,300)).save("3_selection_contour.png"); /** * Noise */ if (shadow->noise() > 0) { KisLsUtils::applyNoise(selection, d.srcRect, shadow->noise(), context, env); } //selection->convertToQImage(0, QRect(0,0,300,300)).save("4_selection_noise.png"); if (!d.offset.isNull()) { selection->setX(d.offset.x()); selection->setY(d.offset.y()); } /** * Knock-out original outline of the device from the resulting shade */ if (shadow->knocksOut()) { KIS_ASSERT_RECOVER_RETURN(knockOutSelection); QRect knockOutRect = !shadow->invertsSelection() ? d.srcRect : d.spreadNeedRect; knockOutRect &= d.dstRect; KisPainter gc(selection); gc.setCompositeOp(COMPOSITE_ERASE); gc.bitBlt(knockOutRect.topLeft(), knockOutSelection, knockOutRect); } //selection->convertToQImage(0, QRect(0,0,300,300)).save("5_selection_knockout.png"); KisLsUtils::applyFinalSelection(KisMultipleProjection::defaultProjectionId(), baseSelection, srcDevice, dst, d.srcRect, d.dstRect, context, shadow, env); } const psd_layer_effects_shadow_base* KisLsDropShadowFilter::getShadowStruct(KisPSDLayerStyleSP style) const { const psd_layer_effects_shadow_base *config = 0; if (m_mode == DropShadow) { config = style->dropShadow(); } else if (m_mode == InnerShadow) { config = style->innerShadow(); } else if (m_mode == OuterGlow) { config = style->outerGlow(); } else if (m_mode == InnerGlow) { config = style->innerGlow(); } return config; } void KisLsDropShadowFilter::processDirectly(KisPaintDeviceSP src, KisMultipleProjection *dst, const QRect &applyRect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const { KIS_ASSERT_RECOVER_RETURN(style); const psd_layer_effects_shadow_base *config = getShadowStruct(style); if (!KisLsUtils::checkEffectEnabled(config, dst)) return; KisLsUtils::LodWrapper w(env->currentLevelOfDetail(), config); applyDropShadow(src, dst, applyRect, style->context(), w.config, env); } QRect KisLsDropShadowFilter::neededRect(const QRect &rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const { const psd_layer_effects_shadow_base *shadowStruct = getShadowStruct(style); if (!shadowStruct->effectEnabled()) return rect; KisLsUtils::LodWrapper w(env->currentLevelOfDetail(), shadowStruct); ShadowRectsData d(rect, style->context(), w.config, ShadowRectsData::NEED_RECT); return rect | d.finalNeedRect(); } QRect KisLsDropShadowFilter::changedRect(const QRect &rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const { const psd_layer_effects_shadow_base *shadowStruct = getShadowStruct(style); if (!shadowStruct->effectEnabled()) return rect; KisLsUtils::LodWrapper w(env->currentLevelOfDetail(), shadowStruct); ShadowRectsData d(rect, style->context(), w.config, ShadowRectsData::CHANGE_RECT); return style->context()->keep_original ? d.finalChangeRect() : rect | d.finalChangeRect(); } diff --git a/libs/image/layerstyles/kis_ls_drop_shadow_filter.h b/libs/image/layerstyles/kis_ls_drop_shadow_filter.h index 30c5cc23df..ff847950e1 100644 --- a/libs/image/layerstyles/kis_ls_drop_shadow_filter.h +++ b/libs/image/layerstyles/kis_ls_drop_shadow_filter.h @@ -1,62 +1,69 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_LS_DROP_SHADOW_FILTER_H #define KIS_LS_DROP_SHADOW_FILTER_H #include #include "kis_layer_style_filter.h" #include class psd_layer_effects_shadow_base; class KRITAIMAGE_EXPORT KisLsDropShadowFilter : public KisLayerStyleFilter { public: enum Mode { DropShadow, InnerShadow, OuterGlow, InnerGlow }; KisLsDropShadowFilter(Mode mode = DropShadow); KisLayerStyleFilter* clone() const override; void processDirectly(KisPaintDeviceSP src, KisMultipleProjection *dst, const QRect &applyRect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const override; QRect neededRect(const QRect & rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const override; QRect changedRect(const QRect & rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const override; private: KisLsDropShadowFilter(const KisLsDropShadowFilter &rhs); const psd_layer_effects_shadow_base* getShadowStruct(KisPSDLayerStyleSP style) const; + void applyDropShadow(KisPaintDeviceSP srcDevice, + KisMultipleProjection *dst, + const QRect &applyRect, + const psd_layer_effects_context *context, + const psd_layer_effects_shadow_base *shadow, + KisLayerStyleFilterEnvironment *env) const; + private: const Mode m_mode; }; #endif diff --git a/libs/image/layerstyles/kis_ls_satin_filter.cpp b/libs/image/layerstyles/kis_ls_satin_filter.cpp index 3e48427443..cc557cda2c 100644 --- a/libs/image/layerstyles/kis_ls_satin_filter.cpp +++ b/libs/image/layerstyles/kis_ls_satin_filter.cpp @@ -1,222 +1,226 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_ls_satin_filter.h" #include #include #include #include "psd.h" #include "kis_convolution_kernel.h" #include "kis_convolution_painter.h" #include "kis_gaussian_kernel.h" #include "kis_pixel_selection.h" #include "kis_fill_painter.h" #include "kis_iterator_ng.h" #include "kis_random_accessor_ng.h" #include "kis_psd_layer_style.h" #include "kis_multiple_projection.h" #include "kis_ls_utils.h" #include "kis_layer_style_filter_environment.h" +#include "kis_cached_paint_device.h" KisLsSatinFilter::KisLsSatinFilter() : KisLayerStyleFilter(KoID("lssatin", i18n("Satin (style)"))) { } KisLsSatinFilter::KisLsSatinFilter(const KisLsSatinFilter &rhs) : KisLayerStyleFilter(rhs) { } KisLayerStyleFilter *KisLsSatinFilter::clone() const { return new KisLsSatinFilter(*this); } struct SatinRectsData { enum Direction { NEED_RECT, CHANGE_RECT }; SatinRectsData(const QRect &applyRect, const psd_layer_effects_context *context, const psd_layer_effects_satin *shadow, Direction direction) { Q_UNUSED(direction); blur_size = shadow->size(); offset = shadow->calculateOffset(context); // need rect calculation in reverse order dstRect = applyRect; srcRect = dstRect; int xGrow = qAbs(offset.x()); int yGrow = qAbs(offset.y()); satinNeedRect = srcRect.adjusted(-xGrow, -yGrow, xGrow, yGrow); blurNeedRect = blur_size ? KisLsUtils::growRectFromRadius(satinNeedRect, blur_size) : satinNeedRect; } inline QRect finalNeedRect() const { return blurNeedRect; } inline QRect finalChangeRect() const { // TODO: is it correct? return blurNeedRect; } qint32 blur_size; QPoint offset; QRect srcRect; QRect dstRect; QRect satinNeedRect; QRect blurNeedRect; }; void blendAndOffsetSatinSelection(KisPixelSelectionSP dstSelection, KisPixelSelectionSP srcSelection, const bool invert, const QPoint &offset, const QRect &applyRect) { KisSequentialIterator srcIt1(srcSelection, applyRect.translated(offset)); KisSequentialIterator srcIt2(srcSelection, applyRect.translated(-offset)); KisSequentialIterator dstIt(dstSelection, applyRect); while(dstIt.nextPixel() && srcIt1.nextPixel() && srcIt2.nextPixel()) { quint8 *dstPixelPtr = dstIt.rawData(); quint8 *src1PixelPtr = srcIt1.rawData(); quint8 *src2PixelPtr = srcIt2.rawData(); if (!invert) { *dstPixelPtr = qAbs(*src1PixelPtr - *src2PixelPtr); } else { *dstPixelPtr = (255 - qAbs(*src1PixelPtr - *src2PixelPtr)); } } } //#include "kis_paint_device_debug_utils.h" -void applySatin(KisPaintDeviceSP srcDevice, - KisMultipleProjection *dst, - const QRect &applyRect, - const psd_layer_effects_context *context, - const psd_layer_effects_satin *config, - const KisLayerStyleFilterEnvironment *env) +void KisLsSatinFilter::applySatin(KisPaintDeviceSP srcDevice, + KisMultipleProjection *dst, + const QRect &applyRect, + const psd_layer_effects_context *context, + const psd_layer_effects_satin *config, + KisLayerStyleFilterEnvironment *env) const { if (applyRect.isEmpty()) return; SatinRectsData d(applyRect, context, config, SatinRectsData::NEED_RECT); - KisSelectionSP baseSelection = - KisLsUtils::selectionFromAlphaChannel(srcDevice, d.blurNeedRect); + KisCachedSelection::Guard s1(*env->cachedSelection()); + KisSelectionSP baseSelection = s1.selection(); + KisLsUtils::selectionFromAlphaChannel(srcDevice, baseSelection, d.blurNeedRect); KisPixelSelectionSP selection = baseSelection->pixelSelection(); - KisPixelSelectionSP tempSelection = new KisPixelSelection(*selection); + KisCachedSelection::Guard s2(*env->cachedSelection()); + KisPixelSelectionSP tempSelection = s2.selection()->pixelSelection(); + tempSelection->makeCloneFromRough(selection, selection->selectedRect()); //KIS_DUMP_DEVICE_2(tempSelection, QRect(0,0,64,64), "00_selection", "dd"); KisLsUtils::applyGaussianWithTransaction(tempSelection, d.satinNeedRect, d.blur_size); //KIS_DUMP_DEVICE_2(tempSelection, QRect(0,0,64,64), "01_gauss", "dd"); /** * Contour correction */ KisLsUtils::applyContourCorrection(tempSelection, d.satinNeedRect, config->contourLookupTable(), config->antiAliased(), config->edgeHidden()); //KIS_DUMP_DEVICE_2(tempSelection, QRect(0,0,64,64), "02_contour", "dd"); blendAndOffsetSatinSelection(selection, tempSelection, config->invert(), d.offset, d.dstRect); //KIS_DUMP_DEVICE_2(selection, QRect(0,0,64,64), "03_blended", "dd"); KisLsUtils::applyFinalSelection(KisMultipleProjection::defaultProjectionId(), baseSelection, srcDevice, dst, d.srcRect, d.dstRect, context, config, env); } void KisLsSatinFilter::processDirectly(KisPaintDeviceSP src, KisMultipleProjection *dst, const QRect &applyRect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const { KIS_ASSERT_RECOVER_RETURN(style); const psd_layer_effects_satin *config = style->satin(); if (!KisLsUtils::checkEffectEnabled(config, dst)) return; KisLsUtils::LodWrapper w(env->currentLevelOfDetail(), config); applySatin(src, dst, applyRect, style->context(), w.config, env); } QRect KisLsSatinFilter::neededRect(const QRect &rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const { const psd_layer_effects_satin *config = style->satin(); if (!config->effectEnabled()) return rect; KisLsUtils::LodWrapper w(env->currentLevelOfDetail(), config); SatinRectsData d(rect, style->context(), w.config, SatinRectsData::NEED_RECT); return rect | d.finalNeedRect(); } QRect KisLsSatinFilter::changedRect(const QRect &rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const { const psd_layer_effects_satin *config = style->satin(); if (!config->effectEnabled()) return rect; KisLsUtils::LodWrapper w(env->currentLevelOfDetail(), config); SatinRectsData d(rect, style->context(), w.config, SatinRectsData::CHANGE_RECT); return style->context()->keep_original ? d.finalChangeRect() : rect | d.finalChangeRect(); } diff --git a/libs/image/layerstyles/kis_ls_satin_filter.h b/libs/image/layerstyles/kis_ls_satin_filter.h index bb3c170e95..056429726a 100644 --- a/libs/image/layerstyles/kis_ls_satin_filter.h +++ b/libs/image/layerstyles/kis_ls_satin_filter.h @@ -1,49 +1,57 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_LS_SATIN_FILTER_H #define KIS_LS_SATIN_FILTER_H #include #include "kis_layer_style_filter.h" #include class KRITAIMAGE_EXPORT KisLsSatinFilter : public KisLayerStyleFilter { public: KisLsSatinFilter(); KisLayerStyleFilter* clone() const override; void processDirectly(KisPaintDeviceSP src, KisMultipleProjection *dst, const QRect &applyRect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const override; QRect neededRect(const QRect & rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const override; QRect changedRect(const QRect & rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const override; private: KisLsSatinFilter(const KisLsSatinFilter &rhs); + + + void applySatin(KisPaintDeviceSP srcDevice, + KisMultipleProjection *dst, + const QRect &applyRect, + const psd_layer_effects_context *context, + const psd_layer_effects_satin *config, + KisLayerStyleFilterEnvironment *env) const; }; #endif diff --git a/libs/image/layerstyles/kis_ls_stroke_filter.cpp b/libs/image/layerstyles/kis_ls_stroke_filter.cpp index 1546b850ff..84f6a3bfa8 100644 --- a/libs/image/layerstyles/kis_ls_stroke_filter.cpp +++ b/libs/image/layerstyles/kis_ls_stroke_filter.cpp @@ -1,161 +1,165 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_ls_stroke_filter.h" #include #include #include #include #include "psd.h" #include "kis_convolution_kernel.h" #include "kis_convolution_painter.h" #include "kis_gaussian_kernel.h" #include "kis_pixel_selection.h" #include "kis_fill_painter.h" #include "kis_gradient_painter.h" #include "kis_iterator_ng.h" #include "kis_random_accessor_ng.h" #include "kis_psd_layer_style.h" #include "kis_layer_style_filter_environment.h" #include "kis_ls_utils.h" #include "kis_multiple_projection.h" +#include "kis_cached_paint_device.h" namespace { int borderSize(psd_stroke_position position, int size) { int border = 0; switch (position) { case psd_stroke_outside: border = 2 * size + 1; break; case psd_stroke_center: border = size + 1; break; case psd_stroke_inside: border = 1; break; } return border; } } KisLsStrokeFilter::KisLsStrokeFilter() : KisLayerStyleFilter(KoID("lsstroke", i18n("Stroke (style)"))) { } KisLsStrokeFilter::KisLsStrokeFilter(const KisLsStrokeFilter &rhs) : KisLayerStyleFilter(rhs) { } KisLayerStyleFilter *KisLsStrokeFilter::clone() const { return new KisLsStrokeFilter(*this); } void KisLsStrokeFilter::applyStroke(KisPaintDeviceSP srcDevice, KisMultipleProjection *dst, const QRect &applyRect, const psd_layer_effects_stroke *config, KisLayerStyleFilterEnvironment *env) const { if (applyRect.isEmpty()) return; const QRect needRect = kisGrowRect(applyRect, borderSize(config->position(), config->size())); - KisSelectionSP baseSelection = KisLsUtils::selectionFromAlphaChannel(srcDevice, needRect); + KisCachedSelection::Guard s1(*env->cachedSelection()); + KisSelectionSP baseSelection = s1.selection(); + KisLsUtils::selectionFromAlphaChannel(srcDevice, baseSelection, needRect); KisPixelSelectionSP selection = baseSelection->pixelSelection(); { - KisPixelSelectionSP knockOutSelection = new KisPixelSelection(new KisSelectionEmptyBounds(0)); + KisCachedSelection::Guard s2(*env->cachedSelection()); + KisPixelSelectionSP knockOutSelection = s2.selection()->pixelSelection(); knockOutSelection->makeCloneFromRough(selection, needRect); if (config->position() == psd_stroke_outside) { KisGaussianKernel::applyDilate(selection, needRect, 2 * config->size(), QBitArray(), 0, true); } else if (config->position() == psd_stroke_inside) { KisGaussianKernel::applyErodeU8(knockOutSelection, needRect, 2 * config->size(), QBitArray(), 0, true); } else if (config->position() == psd_stroke_center) { KisGaussianKernel::applyDilate(selection, needRect, config->size(), QBitArray(), 0, true); KisGaussianKernel::applyErodeU8(knockOutSelection, needRect, config->size(), QBitArray(), 0, true); } KisPainter gc(selection); gc.setCompositeOp(COMPOSITE_ERASE); gc.bitBlt(needRect.topLeft(), knockOutSelection, needRect); gc.end(); } - KisPaintDeviceSP fillDevice = new KisPaintDevice(srcDevice->colorSpace()); + KisCachedPaintDevice::Guard d1(srcDevice, *env->cachedPaintDevice()); + KisPaintDeviceSP fillDevice = d1.device(); KisLsUtils::fillOverlayDevice(fillDevice, applyRect, config, env); - const QString compositeOp = config->blendMode(); const quint8 opacityU8 = quint8(qRound(255.0 / 100.0 * config->opacity())); KisPaintDeviceSP dstDevice = dst->getProjection(KisMultipleProjection::defaultProjectionId(), compositeOp, opacityU8, QBitArray(), srcDevice); KisPainter::copyAreaOptimized(applyRect.topLeft(), fillDevice, dstDevice, applyRect, baseSelection); } void KisLsStrokeFilter::processDirectly(KisPaintDeviceSP src, KisMultipleProjection *dst, const QRect &applyRect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const { Q_UNUSED(env); KIS_ASSERT_RECOVER_RETURN(style); const psd_layer_effects_stroke *config = style->stroke(); if (!KisLsUtils::checkEffectEnabled(config, dst)) return; KisLsUtils::LodWrapper w(env->currentLevelOfDetail(), config); applyStroke(src, dst, applyRect, w.config, env); } QRect KisLsStrokeFilter::neededRect(const QRect &rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const { const psd_layer_effects_stroke *config = style->stroke(); if (!config->effectEnabled()) return rect; KisLsUtils::LodWrapper w(env->currentLevelOfDetail(), config); return kisGrowRect(rect, borderSize(w.config->position(), w.config->size())); } QRect KisLsStrokeFilter::changedRect(const QRect &rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const { return neededRect(rect, style, env); } diff --git a/libs/image/layerstyles/kis_ls_utils.cpp b/libs/image/layerstyles/kis_ls_utils.cpp index c04e93fa28..93b606ef05 100644 --- a/libs/image/layerstyles/kis_ls_utils.cpp +++ b/libs/image/layerstyles/kis_ls_utils.cpp @@ -1,588 +1,591 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_ls_utils.h" #include #include #include #include "psd.h" #include "kis_default_bounds.h" #include "kis_pixel_selection.h" #include "kis_random_accessor_ng.h" #include "kis_iterator_ng.h" #include "kis_convolution_kernel.h" #include "kis_convolution_painter.h" #include "kis_gaussian_kernel.h" #include "kis_fill_painter.h" #include "kis_gradient_painter.h" #include "kis_layer_style_filter_environment.h" #include "kis_selection_filters.h" #include "kis_multiple_projection.h" #include "kis_default_bounds_base.h" - +#include "kis_cached_paint_device.h" namespace KisLsUtils { QRect growSelectionUniform(KisPixelSelectionSP selection, int growSize, const QRect &applyRect) { QRect changeRect = applyRect; if (growSize > 0) { KisGrowSelectionFilter filter(growSize, growSize); changeRect = filter.changeRect(applyRect, selection->defaultBounds()); filter.process(selection, applyRect); } else if (growSize < 0) { KisShrinkSelectionFilter filter(qAbs(growSize), qAbs(growSize), false); changeRect = filter.changeRect(applyRect, selection->defaultBounds()); filter.process(selection, applyRect); } return changeRect; } - KisSelectionSP selectionFromAlphaChannel(KisPaintDeviceSP device, - const QRect &srcRect) + void selectionFromAlphaChannel(KisPaintDeviceSP srcDevice, + KisSelectionSP dstSelection, + const QRect &srcRect) { - const KoColorSpace *cs = device->colorSpace(); + const KoColorSpace *cs = srcDevice->colorSpace(); + - KisSelectionSP baseSelection = new KisSelection(new KisSelectionEmptyBounds(0)); - KisPixelSelectionSP selection = baseSelection->pixelSelection(); + KisPixelSelectionSP selection = dstSelection->pixelSelection(); - KisSequentialConstIterator srcIt(device, srcRect); + KisSequentialConstIterator srcIt(srcDevice, srcRect); KisSequentialIterator dstIt(selection, srcRect); while (srcIt.nextPixel() && dstIt.nextPixel()) { quint8 *dstPtr = dstIt.rawData(); const quint8* srcPtr = srcIt.rawDataConst(); *dstPtr = cs->opacityU8(srcPtr); } - return baseSelection; } void findEdge(KisPixelSelectionSP selection, const QRect &applyRect, const bool edgeHidden) { KisSequentialIterator dstIt(selection, applyRect); if (edgeHidden) { while(dstIt.nextPixel()) { quint8 *pixelPtr = dstIt.rawData(); *pixelPtr = (*pixelPtr < 24) ? *pixelPtr * 10 : 0xFF; } } else { while(dstIt.nextPixel()) { quint8 *pixelPtr = dstIt.rawData(); *pixelPtr = 0xFF; } } } QRect growRectFromRadius(const QRect &rc, int radius) { int halfSize = KisGaussianKernel::kernelSizeFromRadius(radius) / 2; return rc.adjusted(-halfSize, -halfSize, halfSize, halfSize); } void applyGaussianWithTransaction(KisPixelSelectionSP selection, const QRect &applyRect, qreal radius) { KisGaussianKernel::applyGaussian(selection, applyRect, radius, radius, QBitArray(), 0, true); } namespace Private { void getGradientTable(const KoAbstractGradient *gradient, QVector *table, const KoColorSpace *colorSpace) { KIS_ASSERT_RECOVER_RETURN(table->size() == 256); for (int i = 0; i < 256; i++) { gradient->colorAt(((*table)[i]), qreal(i) / 255.0); (*table)[i].convertTo(colorSpace); } } struct LinearGradientIndex { int popOneIndex(int selectionAlpha) { return 255 - selectionAlpha; } bool nextPixel() { return true; } }; struct JitterGradientIndex { JitterGradientIndex(const QRect &applyRect, int jitter, const KisLayerStyleFilterEnvironment *env) : randomSelection(env->cachedRandomSelection(applyRect)), noiseIt(randomSelection, applyRect), m_jitterCoeff(jitter * 255 / 100) { } int popOneIndex(int selectionAlpha) { int gradientIndex = 255 - selectionAlpha; gradientIndex += m_jitterCoeff * *noiseIt.rawDataConst() >> 8; gradientIndex &= 0xFF; return gradientIndex; } bool nextPixel() { return noiseIt.nextPixel(); } private: KisPixelSelectionSP randomSelection; KisSequentialConstIterator noiseIt; int m_jitterCoeff; }; template void applyGradientImpl(KisPaintDeviceSP device, KisPixelSelectionSP selection, const QRect &applyRect, const QVector &table, bool edgeHidden, IndexFetcher &indexFetcher) { KIS_ASSERT_RECOVER_RETURN( *table.first().colorSpace() == *device->colorSpace()); const KoColorSpace *cs = device->colorSpace(); const int pixelSize = cs->pixelSize(); KisSequentialConstIterator selIt(selection, applyRect); KisSequentialIterator dstIt(device, applyRect); if (edgeHidden) { while (selIt.nextPixel() && dstIt.nextPixel() && indexFetcher.nextPixel()) { quint8 selAlpha = *selIt.rawDataConst(); int gradientIndex = indexFetcher.popOneIndex(selAlpha); const KoColor &color = table[gradientIndex]; quint8 tableAlpha = color.opacityU8(); memcpy(dstIt.rawData(), color.data(), pixelSize); if (selAlpha < 24 && tableAlpha == 255) { tableAlpha = int(selAlpha) * 10 * tableAlpha >> 8; cs->setOpacity(dstIt.rawData(), tableAlpha, 1); } } } else { while (selIt.nextPixel() && dstIt.nextPixel() && indexFetcher.nextPixel()) { int gradientIndex = indexFetcher.popOneIndex(*selIt.rawDataConst()); const KoColor &color = table[gradientIndex]; memcpy(dstIt.rawData(), color.data(), pixelSize); } } } void applyGradient(KisPaintDeviceSP device, KisPixelSelectionSP selection, const QRect &applyRect, const QVector &table, bool edgeHidden, int jitter, const KisLayerStyleFilterEnvironment *env) { if (!jitter) { LinearGradientIndex fetcher; applyGradientImpl(device, selection, applyRect, table, edgeHidden, fetcher); } else { JitterGradientIndex fetcher(applyRect, jitter, env); applyGradientImpl(device, selection, applyRect, table, edgeHidden, fetcher); } } } const int noiseNeedBorder = 8; void applyNoise(KisPixelSelectionSP selection, const QRect &applyRect, int noise, const psd_layer_effects_context *context, - const KisLayerStyleFilterEnvironment *env) + KisLayerStyleFilterEnvironment *env) { Q_UNUSED(context); const QRect overlayRect = kisGrowRect(applyRect, noiseNeedBorder); KisPixelSelectionSP randomSelection = env->cachedRandomSelection(overlayRect); - KisPixelSelectionSP randomOverlay = new KisPixelSelection(); + + + KisCachedSelection::Guard s1(*env->cachedSelection()); + KisPixelSelectionSP randomOverlay = s1.selection()->pixelSelection(); KisSequentialConstIterator noiseIt(randomSelection, overlayRect); KisSequentialConstIterator srcIt(selection, overlayRect); KisRandomAccessorSP dstIt = randomOverlay->createRandomAccessorNG(overlayRect.x(), overlayRect.y()); while (noiseIt.nextPixel() && srcIt.nextPixel()) { int itX = noiseIt.x(); int itY = noiseIt.y(); int x = itX + (*noiseIt.rawDataConst() >> 4) - 8; int y = itY + (*noiseIt.rawDataConst() & 0x0F) - 8; x = (x + itX) >> 1; y = (y + itY) >> 1; dstIt->moveTo(x, y); quint8 dstAlpha = *dstIt->rawData(); quint8 srcAlpha = *srcIt.rawDataConst(); int value = qMin(255, dstAlpha + srcAlpha); *dstIt->rawData() = value; } noise = noise * 255 / 100; KisPainter gc(selection); gc.setOpacity(noise); gc.setCompositeOp(COMPOSITE_COPY); gc.bitBlt(applyRect.topLeft(), randomOverlay, applyRect); } //const int FULL_PERCENT_RANGE = 100; void adjustRange(KisPixelSelectionSP selection, const QRect &applyRect, const int range) { KIS_ASSERT_RECOVER_RETURN(range >= 1 && range <= 100); quint8 rangeTable[256]; for(int i = 0; i < 256; i ++) { quint8 value = i * 100 / range; rangeTable[i] = qMin(value, quint8(255)); } KisSequentialIterator dstIt(selection, applyRect); while (dstIt.nextPixel()) { quint8 *pixelPtr = dstIt.rawData(); *pixelPtr = rangeTable[*pixelPtr]; } } void applyContourCorrection(KisPixelSelectionSP selection, const QRect &applyRect, const quint8 *lookup_table, bool antiAliased, bool edgeHidden) { quint8 contour[PSD_LOOKUP_TABLE_SIZE] = { 0x00, 0x0b, 0x16, 0x21, 0x2c, 0x37, 0x42, 0x4d, 0x58, 0x63, 0x6e, 0x79, 0x84, 0x8f, 0x9a, 0xa5, 0xb0, 0xbb, 0xc6, 0xd1, 0xdc, 0xf2, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; if (edgeHidden) { if (antiAliased) { for (int i = 0; i < PSD_LOOKUP_TABLE_SIZE; i++) { contour[i] = contour[i] * lookup_table[i] >> 8; } } else { for (int i = 0; i < PSD_LOOKUP_TABLE_SIZE; i++) { contour[i] = contour[i] * lookup_table[(int)((int)(i / 2.55) * 2.55 + 0.5)] >> 8; } } } else { if (antiAliased) { for (int i = 0; i < PSD_LOOKUP_TABLE_SIZE; i++) { contour[i] = lookup_table[i]; } } else { for (int i = 0; i < PSD_LOOKUP_TABLE_SIZE; i++) { contour[i] = lookup_table[(int)((int)(i / 2.55) * 2.55 + 0.5)]; } } } KisSequentialIterator dstIt(selection, applyRect); while (dstIt.nextPixel()) { quint8 *pixelPtr = dstIt.rawData(); *pixelPtr = contour[*pixelPtr]; } } void knockOutSelection(KisPixelSelectionSP selection, KisPixelSelectionSP knockOutSelection, const QRect &srcRect, const QRect &dstRect, const QRect &totalNeedRect, const bool knockOutInverted) { KIS_ASSERT_RECOVER_RETURN(knockOutSelection); QRect knockOutRect = !knockOutInverted ? srcRect : totalNeedRect; knockOutRect &= dstRect; KisPainter gc(selection); gc.setCompositeOp(COMPOSITE_ERASE); gc.bitBlt(knockOutRect.topLeft(), knockOutSelection, knockOutRect); } void fillPattern(KisPaintDeviceSP fillDevice, const QRect &applyRect, KisLayerStyleFilterEnvironment *env, int scale, KoPattern *pattern, int horizontalPhase, int verticalPhase, bool alignWithLayer) { if (scale != 100) { warnKrita << "KisLsOverlayFilter::applyOverlay(): Pattern scaling is NOT implemented!"; } KIS_SAFE_ASSERT_RECOVER_RETURN(pattern); QSize psize(pattern->width(), pattern->height()); QPoint patternOffset(qreal(psize.width()) * horizontalPhase / 100, qreal(psize.height()) * verticalPhase / 100); const QRect boundsRect = alignWithLayer ? env->layerBounds() : env->defaultBounds(); patternOffset += boundsRect.topLeft(); patternOffset.rx() %= psize.width(); patternOffset.ry() %= psize.height(); QRect fillRect = applyRect | applyRect.translated(patternOffset); KisFillPainter gc(fillDevice); gc.fillRect(fillRect.x(), fillRect.y(), fillRect.width(), fillRect.height(), pattern, -patternOffset); gc.end(); } void fillOverlayDevice(KisPaintDeviceSP fillDevice, const QRect &applyRect, const psd_layer_effects_overlay_base *config, KisLayerStyleFilterEnvironment *env) { if (config->fillType() == psd_fill_solid_color) { KoColor color(config->color(), fillDevice->colorSpace()); fillDevice->setDefaultPixel(color); } else if (config->fillType() == psd_fill_pattern) { fillPattern(fillDevice, applyRect, env, config->scale(), config->pattern(), config->horizontalPhase(), config->verticalPhase(), config->alignWithLayer()); } else if (config->fillType() == psd_fill_gradient) { const QRect boundsRect = config->alignWithLayer() ? env->layerBounds() : env->defaultBounds(); QPoint center = boundsRect.center(); center += QPoint(boundsRect.width() * config->gradientXOffset() / 100, boundsRect.height() * config->gradientYOffset() / 100); int width = (boundsRect.width() * config->scale() + 100) / 200; int height = (boundsRect.height() * config->scale() + 100) / 200; /* copy paste from libpsd */ int angle = config->angle(); int corner_angle = (int)(atan((qreal)boundsRect.height() / boundsRect.width()) * 180 / M_PI + 0.5); int sign_x = 1; int sign_y = 1; if(angle < 0) { angle += 360; } if (angle >= 90 && angle < 180) { angle = 180 - angle; sign_x = -1; } else if (angle >= 180 && angle < 270) { angle = angle - 180; sign_x = -1; sign_y = -1; } else if (angle >= 270 && angle <= 360) { angle = 360 - angle; sign_y = -1; } int radius_x = 0; int radius_y = 0; if (angle <= corner_angle) { radius_x = width; radius_y = (int)(radius_x * tan(kisDegreesToRadians(qreal(angle))) + 0.5); } else { radius_y = height; radius_x = (int)(radius_y / tan(kisDegreesToRadians(qreal(angle))) + 0.5); } int radius_corner = (int)(std::sqrt((qreal)(radius_x * radius_x + radius_y * radius_y)) + 0.5); /* end of copy paste from libpsd */ KisGradientPainter gc(fillDevice); gc.setGradient(config->gradient().data()); QPointF gradStart; QPointF gradEnd; KisGradientPainter::enumGradientRepeat repeat = KisGradientPainter::GradientRepeatNone; QPoint rectangularOffset(sign_x * radius_x, -sign_y * radius_y); switch(config->style()) { case psd_gradient_style_linear: gc.setGradientShape(KisGradientPainter::GradientShapeLinear); repeat = KisGradientPainter::GradientRepeatNone; gradStart = center - rectangularOffset; gradEnd = center + rectangularOffset; break; case psd_gradient_style_radial: gc.setGradientShape(KisGradientPainter::GradientShapeRadial); repeat = KisGradientPainter::GradientRepeatNone; gradStart = center; gradEnd = center + QPointF(radius_corner, 0); break; case psd_gradient_style_angle: gc.setGradientShape(KisGradientPainter::GradientShapeConical); repeat = KisGradientPainter::GradientRepeatNone; gradStart = center; gradEnd = center + rectangularOffset; break; case psd_gradient_style_reflected: gc.setGradientShape(KisGradientPainter::GradientShapeLinear); repeat = KisGradientPainter::GradientRepeatAlternate; gradStart = center - rectangularOffset; gradEnd = center; break; case psd_gradient_style_diamond: gc.setGradientShape(KisGradientPainter::GradientShapeBiLinear); repeat = KisGradientPainter::GradientRepeatNone; gradStart = center - rectangularOffset; gradEnd = center + rectangularOffset; break; default: qFatal("Gradient Overlay: unknown switch case!"); break; } gc.paintGradient(gradStart, gradEnd, repeat, 0.0, config->reverse(), applyRect); } } void applyFinalSelection(const QString &projectionId, KisSelectionSP baseSelection, KisPaintDeviceSP srcDevice, KisMultipleProjection *dst, const QRect &/*srcRect*/, const QRect &dstRect, const psd_layer_effects_context */*context*/, const psd_layer_effects_shadow_base *config, const KisLayerStyleFilterEnvironment *env) { const KoColor effectColor(config->color(), srcDevice->colorSpace()); const QRect effectRect(dstRect); const QString compositeOp = config->blendMode(); const quint8 opacityU8 = quint8(qRound(255.0 / 100.0 * config->opacity())); KisPaintDeviceSP dstDevice = dst->getProjection(projectionId, compositeOp, opacityU8, QBitArray(), srcDevice); if (config->fillType() == psd_fill_solid_color) { KisFillPainter gc(dstDevice); gc.setCompositeOp(COMPOSITE_COPY); gc.setSelection(baseSelection); gc.fillSelection(effectRect, effectColor); gc.end(); } else if (config->fillType() == psd_fill_gradient) { if (!config->gradient()) { warnKrita << "KisLsUtils::applyFinalSelection: Gradient object is null! Skipping..."; return; } QVector table(256); Private::getGradientTable(config->gradient().data(), &table, dstDevice->colorSpace()); Private::applyGradient(dstDevice, baseSelection->pixelSelection(), effectRect, table, true, config->jitter(), env); } //dstDevice->convertToQImage(0, QRect(0,0,300,300)).save("6_device_shadow.png"); } bool checkEffectEnabled(const psd_layer_effects_shadow_base *config, KisMultipleProjection *dst) { bool result = config->effectEnabled(); if (!result) { dst->freeAllProjections(); } return result; } } diff --git a/libs/image/layerstyles/kis_ls_utils.h b/libs/image/layerstyles/kis_ls_utils.h index 71d78019d4..bd6c7e5ac7 100644 --- a/libs/image/layerstyles/kis_ls_utils.h +++ b/libs/image/layerstyles/kis_ls_utils.h @@ -1,127 +1,129 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_LS_UTILS_H #define __KIS_LS_UTILS_H #include "kis_types.h" #include "kritaimage_export.h" #include "kis_lod_transform.h" struct psd_layer_effects_context; class psd_layer_effects_shadow_base; struct psd_layer_effects_overlay_base; class KisLayerStyleFilterEnvironment; class KoPattern; class KisMultipleProjection; +class KisCachedSelection; namespace KisLsUtils { QRect growSelectionUniform(KisPixelSelectionSP selection, int growSize, const QRect &applyRect); - KRITAIMAGE_EXPORT KisSelectionSP selectionFromAlphaChannel(KisPaintDeviceSP device, - const QRect &srcRect); + KRITAIMAGE_EXPORT void selectionFromAlphaChannel(KisPaintDeviceSP srcDevice, + KisSelectionSP dstSelection, + const QRect &srcRect); void findEdge(KisPixelSelectionSP selection, const QRect &applyRect, const bool edgeHidden); QRect growRectFromRadius(const QRect &rc, int radius); void applyGaussianWithTransaction(KisPixelSelectionSP selection, const QRect &applyRect, qreal radius); static const int FULL_PERCENT_RANGE = 100; void adjustRange(KisPixelSelectionSP selection, const QRect &applyRect, const int range); void applyContourCorrection(KisPixelSelectionSP selection, const QRect &applyRect, const quint8 *lookup_table, bool antiAliased, bool edgeHidden); extern const int noiseNeedBorder; void applyNoise(KisPixelSelectionSP selection, const QRect &applyRect, int noise, const psd_layer_effects_context *context, - const KisLayerStyleFilterEnvironment *env); + KisLayerStyleFilterEnvironment *env); void knockOutSelection(KisPixelSelectionSP selection, KisPixelSelectionSP knockOutSelection, const QRect &srcRect, const QRect &dstRect, const QRect &totalNeedRect, const bool knockOutInverted); void fillPattern(KisPaintDeviceSP fillDevice, const QRect &applyRect, KisLayerStyleFilterEnvironment *env, int scale, KoPattern *pattern, int horizontalPhase, int verticalPhase, bool alignWithLayer); void fillOverlayDevice(KisPaintDeviceSP fillDevice, const QRect &applyRect, const psd_layer_effects_overlay_base *config, KisLayerStyleFilterEnvironment *env); void applyFinalSelection(const QString &projectionId, KisSelectionSP baseSelection, KisPaintDeviceSP srcDevice, KisMultipleProjection *dst, const QRect &srcRect, const QRect &dstRect, const psd_layer_effects_context *context, const psd_layer_effects_shadow_base *config, const KisLayerStyleFilterEnvironment *env); bool checkEffectEnabled(const psd_layer_effects_shadow_base *config, KisMultipleProjection *dst); template struct LodWrapper { LodWrapper(int lod, const ConfigStruct *srcStruct) { if (lod > 0) { storage.reset(new ConfigStruct(*srcStruct)); const qreal lodScale = KisLodTransform::lodToScale(lod); storage->scaleLinearSizes(lodScale); config = storage.data(); } else { config = srcStruct; } } const ConfigStruct *config; private: QScopedPointer storage; }; } #endif /* __KIS_LS_UTILS_H */ diff --git a/libs/image/lazybrush/kis_colorize_mask.cpp b/libs/image/lazybrush/kis_colorize_mask.cpp index d5437bf9c4..f59a3eca84 100644 --- a/libs/image/lazybrush/kis_colorize_mask.cpp +++ b/libs/image/lazybrush/kis_colorize_mask.cpp @@ -1,1176 +1,1172 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_colorize_mask.h" #include #include #include #include "kis_pixel_selection.h" #include "kis_icon_utils.h" #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_painter.h" #include "kis_fill_painter.h" #include "kis_lazy_fill_tools.h" #include "kis_cached_paint_device.h" #include "kis_paint_device_debug_utils.h" #include "kis_layer_properties_icons.h" #include "kis_thread_safe_signal_compressor.h" #include "kis_colorize_stroke_strategy.h" #include "kis_multiway_cut.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_macro_based_undo_store.h" #include "kis_post_execution_undo_adapter.h" #include "kis_command_utils.h" #include "kis_processing_applicator.h" #include "krita_utils.h" using namespace KisLazyFillTools; struct KisColorizeMask::Private { Private(KisColorizeMask *_q) : q(_q), coloringProjection(new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8())), fakePaintDevice(new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8())), filteredSource(new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8())), needAddCurrentKeyStroke(false), showKeyStrokes(true), showColoring(true), needsUpdate(true), originalSequenceNumber(-1), updateCompressor(1000, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT), dirtyParentUpdateCompressor(200, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT), prefilterRecalculationCompressor(1000, KisSignalCompressor::POSTPONE), updateIsRunning(false), filteringOptions(false, 4.0, 15, 0.7), limitToDeviceBounds(false) { } Private(const Private &rhs, KisColorizeMask *_q) : q(_q), coloringProjection(new KisPaintDevice(*rhs.coloringProjection)), fakePaintDevice(new KisPaintDevice(*rhs.fakePaintDevice)), filteredSource(new KisPaintDevice(*rhs.filteredSource)), filteredDeviceBounds(rhs.filteredDeviceBounds), needAddCurrentKeyStroke(rhs.needAddCurrentKeyStroke), showKeyStrokes(rhs.showKeyStrokes), showColoring(rhs.showColoring), needsUpdate(false), originalSequenceNumber(-1), updateCompressor(1000, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT), dirtyParentUpdateCompressor(200, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT), prefilterRecalculationCompressor(1000, KisSignalCompressor::POSTPONE), offset(rhs.offset), updateIsRunning(false), filteringOptions(rhs.filteringOptions), limitToDeviceBounds(rhs.limitToDeviceBounds) { Q_FOREACH (const KeyStroke &stroke, rhs.keyStrokes) { keyStrokes << KeyStroke(KisPaintDeviceSP(new KisPaintDevice(*stroke.dev)), stroke.color, stroke.isTransparent); } } KisColorizeMask *q = 0; QList keyStrokes; KisPaintDeviceSP coloringProjection; KisPaintDeviceSP fakePaintDevice; KisPaintDeviceSP filteredSource; QRect filteredDeviceBounds; KoColor currentColor; KisPaintDeviceSP currentKeyStrokeDevice; bool needAddCurrentKeyStroke; bool showKeyStrokes; bool showColoring; KisCachedSelection cachedSelection; - KisCachedSelection cachedConversionSelection; bool needsUpdate; int originalSequenceNumber; KisThreadSafeSignalCompressor updateCompressor; KisThreadSafeSignalCompressor dirtyParentUpdateCompressor; KisThreadSafeSignalCompressor prefilterRecalculationCompressor; QPoint offset; bool updateIsRunning; QStack extentBeforeUpdateStart; FilteringOptions filteringOptions; bool filteringDirty = true; bool limitToDeviceBounds = false; bool filteredSourceValid(KisPaintDeviceSP parentDevice) { return !filteringDirty && originalSequenceNumber == parentDevice->sequenceNumber(); } void setNeedsUpdateImpl(bool value, bool requestedByUser); bool shouldShowFilteredSource() const; bool shouldShowColoring() const; }; KisColorizeMask::KisColorizeMask() : m_d(new Private(this)) { connect(&m_d->updateCompressor, SIGNAL(timeout()), SLOT(slotUpdateRegenerateFilling())); connect(this, SIGNAL(sigUpdateOnDirtyParent()), &m_d->dirtyParentUpdateCompressor, SLOT(start())); connect(&m_d->dirtyParentUpdateCompressor, SIGNAL(timeout()), SLOT(slotUpdateOnDirtyParent())); connect(&m_d->prefilterRecalculationCompressor, SIGNAL(timeout()), SLOT(slotRecalculatePrefilteredImage())); m_d->updateCompressor.moveToThread(qApp->thread()); } KisColorizeMask::~KisColorizeMask() { } KisColorizeMask::KisColorizeMask(const KisColorizeMask& rhs) : KisEffectMask(rhs), m_d(new Private(*rhs.m_d, this)) { connect(&m_d->updateCompressor, SIGNAL(timeout()), SLOT(slotUpdateRegenerateFilling())); connect(this, SIGNAL(sigUpdateOnDirtyParent()), &m_d->dirtyParentUpdateCompressor, SLOT(start())); connect(&m_d->dirtyParentUpdateCompressor, SIGNAL(timeout()), SLOT(slotUpdateOnDirtyParent())); m_d->updateCompressor.moveToThread(qApp->thread()); } void KisColorizeMask::initializeCompositeOp() { KisLayerSP parentLayer(qobject_cast(parent().data())); if (!parentLayer || !parentLayer->original()) return; KisImageSP image = parentLayer->image(); if (!image) return; const qreal samplePortion = 0.1; const qreal alphaPortion = KritaUtils::estimatePortionOfTransparentPixels(parentLayer->original(), image->bounds(), samplePortion); setCompositeOpId(alphaPortion > 0.3 ? COMPOSITE_BEHIND : COMPOSITE_MULT); } const KoColorSpace* KisColorizeMask::colorSpace() const { return m_d->fakePaintDevice->colorSpace(); } struct SetKeyStrokesColorSpaceCommand : public KUndo2Command { SetKeyStrokesColorSpaceCommand(const KoColorSpace *dstCS, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, QList *list, KisColorizeMaskSP node) : m_dstCS(dstCS), m_renderingIntent(renderingIntent), m_conversionFlags(conversionFlags), m_list(list), m_node(node) {} void undo() override { KIS_ASSERT_RECOVER_RETURN(m_list->size() == m_oldColors.size()); for (int i = 0; i < m_list->size(); i++) { (*m_list)[i].color = m_oldColors[i]; } m_node->setNeedsUpdate(true); } void redo() override { if (m_oldColors.isEmpty()) { Q_FOREACH(const KeyStroke &stroke, *m_list) { m_oldColors << stroke.color; m_newColors << stroke.color; m_newColors.last().convertTo(m_dstCS, m_renderingIntent, m_conversionFlags); } } KIS_ASSERT_RECOVER_RETURN(m_list->size() == m_newColors.size()); for (int i = 0; i < m_list->size(); i++) { (*m_list)[i].color = m_newColors[i]; } m_node->setNeedsUpdate(true); } private: QVector m_oldColors; QVector m_newColors; const KoColorSpace *m_dstCS; KoColorConversionTransformation::Intent m_renderingIntent; KoColorConversionTransformation::ConversionFlags m_conversionFlags; QList *m_list; KisColorizeMaskSP m_node; }; void KisColorizeMask::setProfile(const KoColorProfile *profile) { // WARNING: there is no undo information, used only while loading! m_d->fakePaintDevice->setProfile(profile); m_d->coloringProjection->setProfile(profile); for (auto stroke : m_d->keyStrokes) { stroke.color.setProfile(profile); } } KUndo2Command* KisColorizeMask::setColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { using namespace KisCommandUtils; CompositeCommand *composite = new CompositeCommand(); m_d->fakePaintDevice->convertTo(dstColorSpace, renderingIntent, conversionFlags, composite); m_d->coloringProjection->convertTo(dstColorSpace, renderingIntent, conversionFlags, composite); KUndo2Command *strokesConversionCommand = new SetKeyStrokesColorSpaceCommand( dstColorSpace, renderingIntent, conversionFlags, &m_d->keyStrokes, KisColorizeMaskSP(this)); strokesConversionCommand->redo(); composite->addCommand(new SkipFirstRedoWrapper(strokesConversionCommand)); return composite; } bool KisColorizeMask::needsUpdate() const { return m_d->needsUpdate; } void KisColorizeMask::setNeedsUpdate(bool value) { m_d->setNeedsUpdateImpl(value, true); } void KisColorizeMask::Private::setNeedsUpdateImpl(bool value, bool requestedByUser) { if (value != needsUpdate) { needsUpdate = value; q->baseNodeChangedCallback(); if (!value && requestedByUser) { updateCompressor.start(); } } } void KisColorizeMask::slotUpdateRegenerateFilling(bool prefilterOnly) { KisPaintDeviceSP src = parent()->original(); KIS_ASSERT_RECOVER_RETURN(src); const bool filteredSourceValid = m_d->filteredSourceValid(src); m_d->originalSequenceNumber = src->sequenceNumber(); m_d->filteringDirty = false; if (!prefilterOnly) { m_d->coloringProjection->clear(); } KisLayerSP parentLayer(qobject_cast(parent().data())); if (!parentLayer) return; KisImageSP image = parentLayer->image(); if (image) { m_d->updateIsRunning = true; QRect fillBounds; if (m_d->limitToDeviceBounds) { fillBounds |= src->exactBounds(); Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { fillBounds |= stroke.dev->exactBounds(); } fillBounds &= image->bounds(); } else { fillBounds = image->bounds(); } m_d->filteredDeviceBounds = fillBounds; KisColorizeStrokeStrategy *strategy = new KisColorizeStrokeStrategy(src, m_d->coloringProjection, m_d->filteredSource, filteredSourceValid, fillBounds, this, prefilterOnly); strategy->setFilteringOptions(m_d->filteringOptions); Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { const KoColor color = !stroke.isTransparent ? stroke.color : KoColor(Qt::transparent, stroke.color.colorSpace()); strategy->addKeyStroke(stroke.dev, color); } m_d->extentBeforeUpdateStart.push(extent()); connect(strategy, SIGNAL(sigFinished(bool)), SLOT(slotRegenerationFinished(bool))); connect(strategy, SIGNAL(sigCancelled()), SLOT(slotRegenerationCancelled())); KisStrokeId id = image->startStroke(strategy); image->endStroke(id); } } void KisColorizeMask::slotUpdateOnDirtyParent() { if (!parent()) { // When the colorize mask is being merged, // the update is performed for all the layers, // so the invisible areas around the canvas are included in the merged layer. // Colorize Mask gets the info that its parent is "dirty" (needs updating), // but when it arrives, the parent doesn't exists anymore and is set to null. // Colorize Mask doesn't work outside of the canvas anyway (at least in time of writing). return; } KisPaintDeviceSP src = parent()->original(); KIS_ASSERT_RECOVER_RETURN(src); if (!m_d->filteredSourceValid(src)) { const QRect &oldExtent = extent(); m_d->setNeedsUpdateImpl(true, false); m_d->filteringDirty = true; setDirty(oldExtent | extent()); } } void KisColorizeMask::slotRecalculatePrefilteredImage() { slotUpdateRegenerateFilling(true); } void KisColorizeMask::slotRegenerationFinished(bool prefilterOnly) { m_d->updateIsRunning = false; if (!prefilterOnly) { m_d->setNeedsUpdateImpl(false, false); } QRect oldExtent; if (!m_d->extentBeforeUpdateStart.isEmpty()) { oldExtent = m_d->extentBeforeUpdateStart.pop(); } else { KIS_SAFE_ASSERT_RECOVER_NOOP(!m_d->extentBeforeUpdateStart.isEmpty()); // always fail! } setDirty(oldExtent | extent()); } void KisColorizeMask::slotRegenerationCancelled() { slotRegenerationFinished(true); } KisBaseNode::PropertyList KisColorizeMask::sectionModelProperties() const { KisBaseNode::PropertyList l = KisMask::sectionModelProperties(); l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::colorizeNeedsUpdate, needsUpdate()); l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::colorizeEditKeyStrokes, showKeyStrokes()); l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::colorizeShowColoring, showColoring()); return l; } void KisColorizeMask::setSectionModelProperties(const KisBaseNode::PropertyList &properties) { KisMask::setSectionModelProperties(properties); Q_FOREACH (const KisBaseNode::Property &property, properties) { if (property.id == KisLayerPropertiesIcons::colorizeNeedsUpdate.id()) { if (m_d->needsUpdate && m_d->needsUpdate != property.state.toBool()) { setNeedsUpdate(property.state.toBool()); } } if (property.id == KisLayerPropertiesIcons::colorizeEditKeyStrokes.id()) { if (m_d->showKeyStrokes != property.state.toBool()) { setShowKeyStrokes(property.state.toBool()); } } if (property.id == KisLayerPropertiesIcons::colorizeShowColoring.id()) { if (m_d->showColoring != property.state.toBool()) { setShowColoring(property.state.toBool()); } } } } KisPaintDeviceSP KisColorizeMask::paintDevice() const { return m_d->showKeyStrokes && !m_d->updateIsRunning ? m_d->fakePaintDevice : KisPaintDeviceSP(); } KisPaintDeviceSP KisColorizeMask::coloringProjection() const { return m_d->coloringProjection; } KisPaintDeviceSP KisColorizeMask::colorPickSourceDevice() const { return m_d->shouldShowColoring() && !m_d->coloringProjection->extent().isEmpty() ? m_d->coloringProjection : projection(); } QIcon KisColorizeMask::icon() const { return KisIconUtils::loadIcon("colorizeMask"); } bool KisColorizeMask::accept(KisNodeVisitor &v) { return v.visit(this); } void KisColorizeMask::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } bool KisColorizeMask::Private::shouldShowFilteredSource() const { return !updateIsRunning && showKeyStrokes && !filteringDirty && filteredSource && !filteredSource->extent().isEmpty(); } bool KisColorizeMask::Private::shouldShowColoring() const { return !updateIsRunning && showColoring && coloringProjection; } QRect KisColorizeMask::decorateRect(KisPaintDeviceSP &src, KisPaintDeviceSP &dst, const QRect &rect, PositionToFilthy maskPos) const { Q_UNUSED(maskPos); if (maskPos == N_ABOVE_FILTHY) { // the source layer has changed, we should update the filtered cache! if (!m_d->filteringDirty) { emit sigUpdateOnDirtyParent(); } } KIS_ASSERT(dst != src); // Draw the filling and the original layer { KisPainter gc(dst); if (m_d->shouldShowFilteredSource()) { const QRect drawRect = m_d->limitToDeviceBounds ? rect & m_d->filteredDeviceBounds : rect; gc.setOpacity(128); gc.bitBlt(drawRect.topLeft(), m_d->filteredSource, drawRect); } else { gc.setOpacity(255); gc.bitBlt(rect.topLeft(), src, rect); } if (m_d->shouldShowColoring()) { gc.setOpacity(opacity()); gc.setCompositeOp(compositeOpId()); gc.bitBlt(rect.topLeft(), m_d->coloringProjection, rect); } } // Draw the key strokes if (m_d->showKeyStrokes) { KisIndirectPaintingSupport::ReadLocker locker(this); - KisSelectionSP selection = m_d->cachedSelection.getSelection(); - KisSelectionSP conversionSelection = m_d->cachedConversionSelection.getSelection(); - KisPixelSelectionSP tempSelection = conversionSelection->pixelSelection(); + KisCachedSelection::Guard s1(m_d->cachedSelection); + KisCachedSelection::Guard s2(m_d->cachedSelection); + + KisSelectionSP selection = s1.selection(); + KisPixelSelectionSP tempSelection = s2.selection()->pixelSelection(); KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); const bool isTemporaryTargetErasing = temporaryCompositeOp() == COMPOSITE_ERASE; const QRect temporaryExtent = temporaryTarget ? temporaryTarget->extent() : QRect(); KisFillPainter gc(dst); QList extendedStrokes = m_d->keyStrokes; if (m_d->currentKeyStrokeDevice && m_d->needAddCurrentKeyStroke && !isTemporaryTargetErasing) { extendedStrokes << KeyStroke(m_d->currentKeyStrokeDevice, m_d->currentColor); } Q_FOREACH (const KeyStroke &stroke, extendedStrokes) { selection->pixelSelection()->makeCloneFromRough(stroke.dev, rect); gc.setSelection(selection); if (stroke.color == m_d->currentColor || (isTemporaryTargetErasing && temporaryExtent.intersects(selection->pixelSelection()->selectedRect()))) { if (temporaryTarget) { tempSelection->copyAlphaFrom(temporaryTarget, rect); KisPainter selectionPainter(selection->pixelSelection()); setupTemporaryPainter(&selectionPainter); selectionPainter.bitBlt(rect.topLeft(), tempSelection, rect); } } gc.fillSelection(rect, stroke.color); } - - m_d->cachedSelection.putSelection(selection); - m_d->cachedSelection.putSelection(conversionSelection); } return rect; } struct DeviceExtentPolicy { inline QRect operator() (const KisPaintDevice *dev) { return dev->extent(); } }; struct DeviceExactBoundsPolicy { inline QRect operator() (const KisPaintDevice *dev) { return dev->exactBounds(); } }; template QRect KisColorizeMask::calculateMaskBounds(DeviceMetricPolicy boundsPolicy) const { QRect rc; if (m_d->shouldShowFilteredSource()) { rc |= boundsPolicy(m_d->filteredSource); } if (m_d->shouldShowColoring()) { rc |= boundsPolicy(m_d->coloringProjection); } if (m_d->showKeyStrokes) { Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { rc |= boundsPolicy(stroke.dev); } KisIndirectPaintingSupport::ReadLocker locker(this); KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); if (temporaryTarget) { rc |= boundsPolicy(temporaryTarget); } } return rc; } QRect KisColorizeMask::extent() const { return calculateMaskBounds(DeviceExtentPolicy()); } QRect KisColorizeMask::exactBounds() const { return calculateMaskBounds(DeviceExactBoundsPolicy()); } QRect KisColorizeMask::nonDependentExtent() const { return extent(); } KisImageSP KisColorizeMask::fetchImage() const { KisLayerSP parentLayer(qobject_cast(parent().data())); if (!parentLayer) return KisImageSP(); return parentLayer->image(); } void KisColorizeMask::setImage(KisImageWSP image) { KisDefaultBoundsSP bounds(new KisDefaultBounds(image)); auto it = m_d->keyStrokes.begin(); for(; it != m_d->keyStrokes.end(); ++it) { it->dev->setDefaultBounds(bounds); } m_d->coloringProjection->setDefaultBounds(bounds); m_d->fakePaintDevice->setDefaultBounds(bounds); m_d->filteredSource->setDefaultBounds(bounds); } void KisColorizeMask::setCurrentColor(const KoColor &_color) { KoColor color = _color; color.convertTo(colorSpace()); WriteLocker locker(this); m_d->setNeedsUpdateImpl(true, false); QList::const_iterator it = std::find_if(m_d->keyStrokes.constBegin(), m_d->keyStrokes.constEnd(), [color] (const KeyStroke &s) { return s.color == color; }); KisPaintDeviceSP activeDevice; bool newKeyStroke = false; if (it == m_d->keyStrokes.constEnd()) { activeDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); activeDevice->setParentNode(this); activeDevice->setDefaultBounds(KisDefaultBoundsBaseSP(new KisDefaultBounds(fetchImage()))); newKeyStroke = true; } else { activeDevice = it->dev; } m_d->currentColor = color; m_d->currentKeyStrokeDevice = activeDevice; m_d->needAddCurrentKeyStroke = newKeyStroke; } struct KeyStrokeAddRemoveCommand : public KisCommandUtils::FlipFlopCommand { KeyStrokeAddRemoveCommand(bool add, int index, KeyStroke stroke, QList *list, KisColorizeMaskSP node) : FlipFlopCommand(!add), m_index(index), m_stroke(stroke), m_list(list), m_node(node) {} void partA() override { m_list->insert(m_index, m_stroke); m_node->setNeedsUpdate(true); emit m_node->sigKeyStrokesListChanged(); } void partB() override { KIS_ASSERT_RECOVER_RETURN((*m_list)[m_index] == m_stroke); m_list->removeAt(m_index); m_node->setNeedsUpdate(true); emit m_node->sigKeyStrokesListChanged(); } private: int m_index; KeyStroke m_stroke; QList *m_list; KisColorizeMaskSP m_node; }; void KisColorizeMask::mergeToLayer(KisNodeSP layer, KisPostExecutionUndoAdapter *undoAdapter, const KUndo2MagicString &transactionText,int timedID) { Q_UNUSED(layer); WriteLocker locker(this); KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); const bool isTemporaryTargetErasing = temporaryCompositeOp() == COMPOSITE_ERASE; const QRect temporaryExtent = temporaryTarget ? temporaryTarget->extent() : QRect(); KisSavedMacroCommand *macro = undoAdapter->createMacro(transactionText); KisMacroBasedUndoStore store(macro); KisPostExecutionUndoAdapter fakeUndoAdapter(&store, undoAdapter->strokesFacade()); /** * Add a new key stroke plane */ if (m_d->needAddCurrentKeyStroke && !isTemporaryTargetErasing) { KeyStroke key(m_d->currentKeyStrokeDevice, m_d->currentColor); KUndo2Command *cmd = new KeyStrokeAddRemoveCommand( true, m_d->keyStrokes.size(), key, &m_d->keyStrokes, KisColorizeMaskSP(this)); cmd->redo(); fakeUndoAdapter.addCommand(toQShared(cmd)); } /** * When erasing, the brush affects all the key strokes, not only * the current one. */ if (!isTemporaryTargetErasing) { mergeToLayerImpl(m_d->currentKeyStrokeDevice, &fakeUndoAdapter, transactionText, timedID, false); } else { Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { if (temporaryExtent.intersects(stroke.dev->extent())) { mergeToLayerImpl(stroke.dev, &fakeUndoAdapter, transactionText, timedID, false); } } } mergeToLayerImpl(m_d->fakePaintDevice, &fakeUndoAdapter, transactionText, timedID, false); m_d->currentKeyStrokeDevice = 0; m_d->currentColor = KoColor(); releaseResources(); /** * Try removing the key strokes that has been completely erased */ if (isTemporaryTargetErasing) { for (int index = 0; index < m_d->keyStrokes.size(); /*noop*/) { const KeyStroke &stroke = m_d->keyStrokes[index]; if (stroke.dev->exactBounds().isEmpty()) { KUndo2Command *cmd = new KeyStrokeAddRemoveCommand( false, index, stroke, &m_d->keyStrokes, KisColorizeMaskSP(this)); cmd->redo(); fakeUndoAdapter.addCommand(toQShared(cmd)); } else { index++; } } } undoAdapter->addMacro(macro); } void KisColorizeMask::writeMergeData(KisPainter *painter, KisPaintDeviceSP src) { const KoColorSpace *alpha8 = KoColorSpaceRegistry::instance()->alpha8(); const bool nonAlphaDst = !(*painter->device()->colorSpace() == *alpha8); if (nonAlphaDst) { Q_FOREACH (const QRect &rc, src->region().rects()) { painter->bitBlt(rc.topLeft(), src, rc); } } else { - KisSelectionSP conversionSelection = m_d->cachedConversionSelection.getSelection(); - KisPixelSelectionSP tempSelection = conversionSelection->pixelSelection(); + KisCachedSelection::Guard s1(m_d->cachedSelection); + KisPixelSelectionSP tempSelection = s1.selection()->pixelSelection(); Q_FOREACH (const QRect &rc, src->region().rects()) { tempSelection->copyAlphaFrom(src, rc); painter->bitBlt(rc.topLeft(), tempSelection, rc); } - m_d->cachedSelection.putSelection(conversionSelection); } } bool KisColorizeMask::supportsNonIndirectPainting() const { return false; } bool KisColorizeMask::showColoring() const { return m_d->showColoring; } void KisColorizeMask::setShowColoring(bool value) { QRect savedExtent; if (m_d->showColoring && !value) { savedExtent = extent(); } m_d->showColoring = value; baseNodeChangedCallback(); if (!savedExtent.isEmpty()) { setDirty(savedExtent); } } bool KisColorizeMask::showKeyStrokes() const { return m_d->showKeyStrokes; } void KisColorizeMask::setShowKeyStrokes(bool value) { QRect savedExtent; if (m_d->showKeyStrokes && !value) { savedExtent = extent(); } m_d->showKeyStrokes = value; baseNodeChangedCallback(); if (!savedExtent.isEmpty()) { setDirty(savedExtent); } regeneratePrefilteredDeviceIfNeeded(); } KisColorizeMask::KeyStrokeColors KisColorizeMask::keyStrokesColors() const { KeyStrokeColors colors; // TODO: thread safety! for (int i = 0; i < m_d->keyStrokes.size(); i++) { colors.colors << m_d->keyStrokes[i].color; if (m_d->keyStrokes[i].isTransparent) { colors.transparentIndex = i; } } return colors; } struct SetKeyStrokeColorsCommand : public KUndo2Command { SetKeyStrokeColorsCommand(const QList newList, QList *list, KisColorizeMaskSP node) : m_newList(newList), m_oldList(*list), m_list(list), m_node(node) {} void redo() override { *m_list = m_newList; m_node->setNeedsUpdate(true); emit m_node->sigKeyStrokesListChanged(); m_node->setDirty(); } void undo() override { *m_list = m_oldList; m_node->setNeedsUpdate(true); emit m_node->sigKeyStrokesListChanged(); m_node->setDirty(); } private: QList m_newList; QList m_oldList; QList *m_list; KisColorizeMaskSP m_node; }; void KisColorizeMask::setKeyStrokesColors(KeyStrokeColors colors) { KIS_ASSERT_RECOVER_RETURN(colors.colors.size() == m_d->keyStrokes.size()); QList newList = m_d->keyStrokes; for (int i = 0; i < newList.size(); i++) { newList[i].color = colors.colors[i]; newList[i].color.convertTo(colorSpace()); newList[i].isTransparent = colors.transparentIndex == i; } KisProcessingApplicator applicator(fetchImage(), KisNodeSP(this), KisProcessingApplicator::NONE, KisImageSignalVector(), kundo2_i18n("Change Key Stroke Color")); applicator.applyCommand( new SetKeyStrokeColorsCommand( newList, &m_d->keyStrokes, KisColorizeMaskSP(this))); applicator.end(); } void KisColorizeMask::removeKeyStroke(const KoColor &_color) { KoColor color = _color; color.convertTo(colorSpace()); QList::iterator it = std::find_if(m_d->keyStrokes.begin(), m_d->keyStrokes.end(), [color] (const KeyStroke &s) { return s.color == color; }); KIS_SAFE_ASSERT_RECOVER_RETURN(it != m_d->keyStrokes.end()); const int index = it - m_d->keyStrokes.begin(); KisProcessingApplicator applicator(KisImageWSP(fetchImage()), KisNodeSP(this), KisProcessingApplicator::NONE, KisImageSignalVector(), kundo2_i18n("Remove Key Stroke")); applicator.applyCommand( new KeyStrokeAddRemoveCommand( false, index, *it, &m_d->keyStrokes, KisColorizeMaskSP(this))); applicator.end(); } QVector KisColorizeMask::allPaintDevices() const { QVector devices; Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { devices << stroke.dev; } devices << m_d->coloringProjection; devices << m_d->fakePaintDevice; return devices; } void KisColorizeMask::resetCache() { m_d->filteredSource->clear(); m_d->originalSequenceNumber = -1; m_d->filteringDirty = true; rerenderFakePaintDevice(); slotUpdateRegenerateFilling(true); } void KisColorizeMask::setUseEdgeDetection(bool value) { m_d->filteringOptions.useEdgeDetection = value; m_d->filteringDirty = true; setNeedsUpdate(true); } bool KisColorizeMask::useEdgeDetection() const { return m_d->filteringOptions.useEdgeDetection; } void KisColorizeMask::setEdgeDetectionSize(qreal value) { m_d->filteringOptions.edgeDetectionSize = value; m_d->filteringDirty = true; setNeedsUpdate(true); } qreal KisColorizeMask::edgeDetectionSize() const { return m_d->filteringOptions.edgeDetectionSize; } void KisColorizeMask::setFuzzyRadius(qreal value) { m_d->filteringOptions.fuzzyRadius = value; m_d->filteringDirty = true; setNeedsUpdate(true); } qreal KisColorizeMask::fuzzyRadius() const { return m_d->filteringOptions.fuzzyRadius; } void KisColorizeMask::setCleanUpAmount(qreal value) { m_d->filteringOptions.cleanUpAmount = value; setNeedsUpdate(true); } qreal KisColorizeMask::cleanUpAmount() const { return m_d->filteringOptions.cleanUpAmount; } void KisColorizeMask::setLimitToDeviceBounds(bool value) { m_d->limitToDeviceBounds = value; m_d->filteringDirty = true; setNeedsUpdate(true); } bool KisColorizeMask::limitToDeviceBounds() const { return m_d->limitToDeviceBounds; } void KisColorizeMask::rerenderFakePaintDevice() { m_d->fakePaintDevice->clear(); KisFillPainter gc(m_d->fakePaintDevice); - KisSelectionSP selection = m_d->cachedSelection.getSelection(); + KisCachedSelection::Guard s1(m_d->cachedSelection); + KisSelectionSP selection = s1.selection(); Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { const QRect rect = stroke.dev->extent(); selection->pixelSelection()->makeCloneFromRough(stroke.dev, rect); gc.setSelection(selection); gc.fillSelection(rect, stroke.color); } - - m_d->cachedSelection.putSelection(selection); } void KisColorizeMask::testingAddKeyStroke(KisPaintDeviceSP dev, const KoColor &color, bool isTransparent) { m_d->keyStrokes << KeyStroke(dev, color, isTransparent); } void KisColorizeMask::testingRegenerateMask() { slotUpdateRegenerateFilling(); m_d->updateIsRunning = false; } KisPaintDeviceSP KisColorizeMask::testingFilteredSource() const { return m_d->filteredSource; } QList KisColorizeMask::fetchKeyStrokesDirect() const { return m_d->keyStrokes; } void KisColorizeMask::setKeyStrokesDirect(const QList &strokes) { m_d->keyStrokes = strokes; for (auto it = m_d->keyStrokes.begin(); it != m_d->keyStrokes.end(); ++it) { it->dev->setParentNode(this); } KisImageSP image = fetchImage(); KIS_SAFE_ASSERT_RECOVER_RETURN(image); setImage(image); } qint32 KisColorizeMask::x() const { return m_d->offset.x(); } qint32 KisColorizeMask::y() const { return m_d->offset.y(); } void KisColorizeMask::setX(qint32 x) { const QPoint oldOffset = m_d->offset; m_d->offset.rx() = x; moveAllInternalDevices(m_d->offset - oldOffset); } void KisColorizeMask::setY(qint32 y) { const QPoint oldOffset = m_d->offset; m_d->offset.ry() = y; moveAllInternalDevices(m_d->offset - oldOffset); } KisPaintDeviceList KisColorizeMask::getLodCapableDevices() const { KisPaintDeviceList list; auto it = m_d->keyStrokes.begin(); for(; it != m_d->keyStrokes.end(); ++it) { list << it->dev; } list << m_d->coloringProjection; list << m_d->fakePaintDevice; list << m_d->filteredSource; return list; } void KisColorizeMask::regeneratePrefilteredDeviceIfNeeded() { if (!parent()) return; KisPaintDeviceSP src = parent()->original(); KIS_ASSERT_RECOVER_RETURN(src); if (!m_d->filteredSourceValid(src)) { // update the prefiltered source if needed slotUpdateRegenerateFilling(true); } } void KisColorizeMask::moveAllInternalDevices(const QPoint &diff) { QVector devices = allPaintDevices(); Q_FOREACH (KisPaintDeviceSP dev, devices) { dev->moveTo(dev->offset() + diff); } } diff --git a/libs/image/tests/kis_layer_style_projection_plane_test.cpp b/libs/image/tests/kis_layer_style_projection_plane_test.cpp index cea0e38ac3..095c1f56f7 100644 --- a/libs/image/tests/kis_layer_style_projection_plane_test.cpp +++ b/libs/image/tests/kis_layer_style_projection_plane_test.cpp @@ -1,616 +1,617 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_layer_style_projection_plane_test.h" #include #include "testutil.h" #include #include #include #include #include "kis_transparency_mask.h" #include "kis_paint_layer.h" #include "kis_image.h" #include "kis_painter.h" #include "kis_selection.h" #include "kis_pixel_selection.h" #include "layerstyles/kis_layer_style_projection_plane.h" #include "kis_psd_layer_style.h" #include "kis_paint_device_debug_utils.h" void KisLayerStyleProjectionPlaneTest::test(KisPSDLayerStyleSP style, const QString testName) { const QRect imageRect(0, 0, 200, 200); const QRect rFillRect(10, 10, 100, 100); const QRect tMaskRect(50, 50, 20, 20); const QRect partialSelectionRect(90, 50, 20, 20); const QRect updateRect1(10, 10, 50, 100); const QRect updateRect2(60, 10, 50, 100); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "styles test"); KisPaintLayerSP layer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8); image->addNode(layer); KisLayerStyleProjectionPlane plane(layer.data(), style); KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "00L_initial", testName); //layer->paintDevice()->fill(rFillRect, KoColor(Qt::red, cs)); { KisPainter gc(layer->paintDevice()); gc.setPaintColor(KoColor(Qt::red, cs)); gc.setFillStyle(KisPainter::FillStyleForegroundColor); gc.paintEllipse(rFillRect); } KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "01L_fill", testName); KisPaintDeviceSP projection = new KisPaintDevice(cs); { const QRect changeRect = plane.changeRect(rFillRect, KisLayer::N_FILTHY); dbgKrita << ppVar(rFillRect) << ppVar(changeRect); plane.recalculate(changeRect, layer); KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "02L_recalculate_fill", testName); KisPainter painter(projection); plane.apply(&painter, changeRect); KIS_DUMP_DEVICE_2(projection, imageRect, "03P_apply_on_fill", testName); } //return; KisTransparencyMaskSP transparencyMask = new KisTransparencyMask(); KisSelectionSP selection = new KisSelection(); selection->pixelSelection()->select(tMaskRect, OPACITY_OPAQUE_U8); transparencyMask->setSelection(selection); image->addNode(transparencyMask, layer); KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "04L_mask_added", testName); plane.recalculate(imageRect, layer); KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "05L_mask_added_recalculated", testName); { projection->clear(); KisPainter painter(projection); plane.apply(&painter, imageRect); KIS_DUMP_DEVICE_2(projection, imageRect, "06P_apply_on_mask", testName); } selection->pixelSelection()->select(partialSelectionRect, OPACITY_OPAQUE_U8); { const QRect changeRect = plane.changeRect(partialSelectionRect, KisLayer::N_FILTHY); projection->clear(changeRect); dbgKrita << ppVar(partialSelectionRect) << ppVar(changeRect); plane.recalculate(changeRect, layer); KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "07L_recalculate_partial", testName); KisPainter painter(projection); plane.apply(&painter, changeRect); KIS_DUMP_DEVICE_2(projection, imageRect, "08P_apply_partial", testName); } // half updates transparencyMask->setVisible(false); { const QRect changeRect = plane.changeRect(updateRect1, KisLayer::N_FILTHY); projection->clear(changeRect); dbgKrita << ppVar(updateRect1) << ppVar(changeRect); plane.recalculate(changeRect, layer); KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "09L_recalculate_half1", testName); KisPainter painter(projection); plane.apply(&painter, changeRect); KIS_DUMP_DEVICE_2(projection, imageRect, "10P_apply_half1", testName); } { const QRect changeRect = plane.changeRect(updateRect2, KisLayer::N_FILTHY); projection->clear(changeRect); dbgKrita << ppVar(updateRect2) << ppVar(changeRect); plane.recalculate(changeRect, layer); KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "09L_recalculate_half1", testName); KisPainter painter(projection); plane.apply(&painter, changeRect); KIS_DUMP_DEVICE_2(projection, imageRect, "10P_apply_half2", testName); } } void KisLayerStyleProjectionPlaneTest::testShadow() { KisPSDLayerStyleSP style(new KisPSDLayerStyle()); style->dropShadow()->setSize(15); style->dropShadow()->setDistance(15); style->dropShadow()->setOpacity(70); style->dropShadow()->setNoise(30); style->dropShadow()->setEffectEnabled(true); style->innerShadow()->setSize(10); style->innerShadow()->setSpread(10); style->innerShadow()->setDistance(5); style->innerShadow()->setOpacity(70); style->innerShadow()->setNoise(30); style->innerShadow()->setEffectEnabled(true); test(style, "shadow"); } void KisLayerStyleProjectionPlaneTest::testGlow() { KisPSDLayerStyleSP style(new KisPSDLayerStyle()); style->outerGlow()->setSize(15); style->outerGlow()->setSpread(10); style->outerGlow()->setOpacity(70); style->outerGlow()->setNoise(30); style->outerGlow()->setEffectEnabled(true); style->outerGlow()->setColor(Qt::green); test(style, "glow_outer"); } #include void KisLayerStyleProjectionPlaneTest::testGlowGradient() { KisPSDLayerStyleSP style(new KisPSDLayerStyle()); style->outerGlow()->setSize(15); style->outerGlow()->setSpread(10); style->outerGlow()->setOpacity(70); style->outerGlow()->setNoise(10); style->outerGlow()->setEffectEnabled(true); style->outerGlow()->setColor(Qt::green); QLinearGradient testGradient; testGradient.setColorAt(0.0, Qt::white); testGradient.setColorAt(0.5, Qt::green); testGradient.setColorAt(1.0, Qt::black); testGradient.setSpread(QGradient::ReflectSpread); QSharedPointer gradient( KoStopGradient::fromQGradient(&testGradient)); style->outerGlow()->setGradient(gradient); style->outerGlow()->setFillType(psd_fill_gradient); test(style, "glow_outer_grad"); } void KisLayerStyleProjectionPlaneTest::testGlowGradientJitter() { KisPSDLayerStyleSP style(new KisPSDLayerStyle()); style->outerGlow()->setSize(15); style->outerGlow()->setSpread(10); style->outerGlow()->setOpacity(70); style->outerGlow()->setNoise(0); style->outerGlow()->setEffectEnabled(true); style->outerGlow()->setColor(Qt::green); QLinearGradient testGradient; testGradient.setColorAt(0.0, Qt::white); testGradient.setColorAt(0.5, Qt::green); testGradient.setColorAt(1.0, Qt::black); testGradient.setSpread(QGradient::ReflectSpread); QSharedPointer gradient( KoStopGradient::fromQGradient(&testGradient)); style->outerGlow()->setGradient(gradient); style->outerGlow()->setFillType(psd_fill_gradient); style->outerGlow()->setJitter(20); test(style, "glow_outer_grad_jit"); } void KisLayerStyleProjectionPlaneTest::testGlowInnerGradient() { KisPSDLayerStyleSP style(new KisPSDLayerStyle()); style->innerGlow()->setSize(15); style->innerGlow()->setSpread(10); style->innerGlow()->setOpacity(80); style->innerGlow()->setNoise(10); style->innerGlow()->setEffectEnabled(true); style->innerGlow()->setColor(Qt::white); QLinearGradient testGradient; testGradient.setColorAt(0.0, Qt::white); testGradient.setColorAt(0.5, Qt::green); testGradient.setColorAt(1.0, Qt::black); testGradient.setSpread(QGradient::ReflectSpread); QSharedPointer gradient( KoStopGradient::fromQGradient(&testGradient)); style->innerGlow()->setGradient(gradient); style->innerGlow()->setFillType(psd_fill_gradient); test(style, "glow_inner_grad"); style->innerGlow()->setFillType(psd_fill_solid_color); style->innerGlow()->setSource(psd_glow_center); test(style, "glow_inner_grad_center"); } #include void KisLayerStyleProjectionPlaneTest::testSatin() { KisPSDLayerStyleSP style(new KisPSDLayerStyle()); style->satin()->setSize(15); style->satin()->setOpacity(80); style->satin()->setAngle(180); style->satin()->setEffectEnabled(true); style->satin()->setColor(Qt::white); style->satin()->setBlendMode(COMPOSITE_LINEAR_DODGE); test(style, "satin"); } void KisLayerStyleProjectionPlaneTest::testColorOverlay() { KisPSDLayerStyleSP style(new KisPSDLayerStyle()); style->colorOverlay()->setOpacity(80); style->colorOverlay()->setEffectEnabled(true); style->colorOverlay()->setColor(Qt::white); style->colorOverlay()->setBlendMode(COMPOSITE_LINEAR_DODGE); test(style, "color_overlay"); } void KisLayerStyleProjectionPlaneTest::testGradientOverlay() { KisPSDLayerStyleSP style(new KisPSDLayerStyle()); style->gradientOverlay()->setAngle(90); style->gradientOverlay()->setOpacity(80); style->gradientOverlay()->setEffectEnabled(true); style->gradientOverlay()->setBlendMode(COMPOSITE_LINEAR_DODGE); style->gradientOverlay()->setAlignWithLayer(true); style->gradientOverlay()->setScale(100); style->gradientOverlay()->setStyle(psd_gradient_style_diamond); QLinearGradient testGradient; testGradient.setColorAt(0.0, Qt::white); testGradient.setColorAt(0.5, Qt::green); testGradient.setColorAt(1.0, Qt::black); testGradient.setSpread(QGradient::ReflectSpread); QSharedPointer gradient( KoStopGradient::fromQGradient(&testGradient)); style->gradientOverlay()->setGradient(gradient); test(style, "grad_overlay"); } void KisLayerStyleProjectionPlaneTest::testPatternOverlay() { KisPSDLayerStyleSP style(new KisPSDLayerStyle()); style->patternOverlay()->setOpacity(80); style->patternOverlay()->setEffectEnabled(true); style->patternOverlay()->setBlendMode(COMPOSITE_LINEAR_DODGE); style->patternOverlay()->setScale(100); style->patternOverlay()->setAlignWithLayer(false); QString fileName(TestUtil::fetchDataFileLazy("pattern.pat")); KoPattern pattern(fileName); QVERIFY(pattern.load()); style->patternOverlay()->setPattern(&pattern); test(style, "pat_overlay"); } void KisLayerStyleProjectionPlaneTest::testStroke() { KisPSDLayerStyleSP style(new KisPSDLayerStyle()); style->stroke()->setColor(Qt::blue); style->stroke()->setOpacity(80); style->stroke()->setEffectEnabled(true); style->stroke()->setBlendMode(COMPOSITE_OVER); style->stroke()->setSize(3); style->stroke()->setPosition(psd_stroke_center); test(style, "stroke_col_ctr"); style->stroke()->setPosition(psd_stroke_outside); test(style, "stroke_col_out"); style->stroke()->setPosition(psd_stroke_inside); test(style, "stroke_col_in"); QString fileName(TestUtil::fetchDataFileLazy("pattern.pat")); KoPattern pattern(fileName); QVERIFY(pattern.load()); style->stroke()->setPattern(&pattern); style->stroke()->setFillType(psd_fill_pattern); test(style, "stroke_pat"); QLinearGradient testGradient; testGradient.setColorAt(0.0, Qt::white); testGradient.setColorAt(0.5, Qt::green); testGradient.setColorAt(1.0, Qt::black); testGradient.setSpread(QGradient::ReflectSpread); QSharedPointer gradient( KoStopGradient::fromQGradient(&testGradient)); style->stroke()->setGradient(gradient); style->stroke()->setFillType(psd_fill_gradient); test(style, "stroke_grad"); } #include "layerstyles/gimp_bump_map.h" void KisLayerStyleProjectionPlaneTest::testBumpmap() { KisPixelSelectionSP device = new KisPixelSelection(); const int numCycles = 30; const int step = 5; QRect applyRect(200, 100, 100, 100); QRect fillRect(210, 110, 80, 80); quint8 selectedness = 256 - numCycles * step; for (int i = 0; i < numCycles; i++) { device->select(fillRect, selectedness); fillRect = kisGrowRect(fillRect, -1); selectedness += step; } KIS_DUMP_DEVICE_2(device, applyRect, "00_initial", "bumpmap"); bumpmap_vals_t bmvals; bmvals.azimuth = 240; bmvals.elevation = 30; bmvals.depth = 50; bmvals.ambient = 128; bmvals.compensate = false; bmvals.invert = false; bmvals.type = 0; bumpmap(device, applyRect, bmvals); KIS_DUMP_DEVICE_2(device, applyRect, "01_bumpmapped", "bumpmap"); } void KisLayerStyleProjectionPlaneTest::testBevel() { KisPSDLayerStyleSP style(new KisPSDLayerStyle()); style->bevelAndEmboss()->setEffectEnabled(true); style->bevelAndEmboss()->setAngle(135); style->bevelAndEmboss()->setAltitude(45); style->bevelAndEmboss()->setDepth(100); style->bevelAndEmboss()->setHighlightColor(Qt::white); style->bevelAndEmboss()->setHighlightBlendMode(COMPOSITE_OVER); style->bevelAndEmboss()->setHighlightOpacity(100); style->bevelAndEmboss()->setShadowColor(Qt::black); style->bevelAndEmboss()->setShadowBlendMode(COMPOSITE_OVER); style->bevelAndEmboss()->setShadowOpacity(100); QString fileName(TestUtil::fetchDataFileLazy("pattern.pat")); KoPattern pattern(fileName); QVERIFY(pattern.load()); style->bevelAndEmboss()->setTexturePattern(&pattern); style->bevelAndEmboss()->setTextureEnabled(true); style->bevelAndEmboss()->setTextureDepth(-10); style->bevelAndEmboss()->setTextureInvert(false); style->bevelAndEmboss()->setStyle(psd_bevel_outer_bevel); style->bevelAndEmboss()->setDirection(psd_direction_up); style->bevelAndEmboss()->setSoften(0); test(style, "bevel_outer_up"); style->bevelAndEmboss()->setTextureInvert(true); style->bevelAndEmboss()->setStyle(psd_bevel_outer_bevel); style->bevelAndEmboss()->setDirection(psd_direction_up); style->bevelAndEmboss()->setSoften(0); test(style, "bevel_outer_up_invert_texture"); style->bevelAndEmboss()->setTextureInvert(false); style->bevelAndEmboss()->setStyle(psd_bevel_outer_bevel); style->bevelAndEmboss()->setDirection(psd_direction_down); style->bevelAndEmboss()->setSoften(0); test(style, "bevel_outer_down"); style->bevelAndEmboss()->setStyle(psd_bevel_emboss); style->bevelAndEmboss()->setDirection(psd_direction_up); style->bevelAndEmboss()->setSoften(0); test(style, "bevel_emboss_up"); style->bevelAndEmboss()->setStyle(psd_bevel_pillow_emboss); style->bevelAndEmboss()->setDirection(psd_direction_up); style->bevelAndEmboss()->setSoften(0); test(style, "bevel_pillow_up"); style->bevelAndEmboss()->setStyle(psd_bevel_pillow_emboss); style->bevelAndEmboss()->setDirection(psd_direction_down); style->bevelAndEmboss()->setSoften(0); test(style, "bevel_pillow_down"); style->bevelAndEmboss()->setStyle(psd_bevel_pillow_emboss); style->bevelAndEmboss()->setDirection(psd_direction_up); style->bevelAndEmboss()->setSoften(3); test(style, "bevel_pillow_up_soft"); } #include "kis_ls_utils.h" void KisLayerStyleProjectionPlaneTest::testBlending() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP layer = new KisPaintDevice(cs); KisPaintDeviceSP overlay = new KisPaintDevice(cs); KisPaintDeviceSP bg = new KisPaintDevice(cs); KisPaintDeviceSP result = new KisPaintDevice(cs); const int width = 20; KoColor color(Qt::transparent, cs); QVector layerColors; QVector overlayColors; QVector bgColors; layerColors << QColor(0, 255, 0); layerColors << QColor(128, 255, 64); overlayColors << QColor(255, 0, 0); overlayColors << QColor(255, 128, 64); bgColors << QColor(0, 0, 0, 0); bgColors << QColor(0, 0, 0, 255); bgColors << QColor(255, 255, 255, 255); bgColors << QColor(64, 128, 255, 255); bgColors << QColor(0, 0, 0, 128); bgColors << QColor(255, 255, 255, 128); bgColors << QColor(64, 128, 255, 128); const int overlayOpacity = 255; const int layerOpacity = 255; int y = 1; Q_FOREACH(const QColor &layerColor, layerColors) { Q_FOREACH(const QColor &overlayColor, overlayColors) { Q_FOREACH(const QColor &bgColor, bgColors) { bg->setPixel(0, y, layerColor); bg->setPixel(1, y, overlayColor); bg->setPixel(2, y, bgColor); bg->setPixel(3, y, QColor(layerOpacity, layerOpacity, layerOpacity, 255)); bg->setPixel(4, y, QColor(overlayOpacity, overlayOpacity, overlayOpacity, 255)); for (int i = 5; i < width; i++) { bg->setPixel(i, y, bgColor); } for (int i = 0; i <= 10; i++) { const quint8 alpha = i == 0 ? 71 : qRound(255 * qreal(i) / 10); { QColor c(layerColor); c.setAlpha(alpha); layer->setPixel(7 + i, y, c); } { QColor c(overlayColor); c.setAlpha(alpha); overlay->setPixel(7 + i, y, c); } } y++; } } } const QRect rc = bg->exactBounds() | layer->exactBounds(); KIS_DUMP_DEVICE_2(layer, rc, "00_layer", "dd"); KIS_DUMP_DEVICE_2(overlay, rc, "01_overlay", "dd"); KIS_DUMP_DEVICE_2(bg, rc, "02_bg", "dd"); KisPaintDeviceSP originalBg = new KisPaintDevice(*bg); - KisSelectionSP selection = KisLsUtils::selectionFromAlphaChannel(layer, rc); + KisSelectionSP selection = new KisSelection(); + KisLsUtils::selectionFromAlphaChannel(layer, selection, rc); { KisSequentialIterator it(layer, rc); while (it.nextPixel()) { cs->setOpacity(it.rawData(), quint8(255), 1); } } { KisSequentialIterator it(overlay, rc); while (it.nextPixel()) { cs->setOpacity(it.rawData(), quint8(255), 1); } } KisPainter painter(bg); painter.setOpacity(layerOpacity); painter.setCompositeOp(COMPOSITE_OVER); painter.bitBlt(rc.topLeft(), layer, rc); painter.setOpacity(overlayOpacity); painter.setCompositeOp(COMPOSITE_ADD); painter.bitBlt(rc.topLeft(), overlay, rc); KIS_DUMP_DEVICE_2(bg, rc, "03_result", "dd"); KisPainter bgPainter(originalBg); bgPainter.setCompositeOp(COMPOSITE_COPY); bgPainter.setSelection(selection); bgPainter.bitBlt(rc.topLeft(), bg, rc); KIS_DUMP_DEVICE_2(originalBg, rc, "04_knockout", "dd"); } QTEST_MAIN(KisLayerStyleProjectionPlaneTest) diff --git a/plugins/filters/gaussianhighpass/gaussianhighpass_filter.cpp b/plugins/filters/gaussianhighpass/gaussianhighpass_filter.cpp index d92d48fb18..07a004bd62 100644 --- a/plugins/filters/gaussianhighpass/gaussianhighpass_filter.cpp +++ b/plugins/filters/gaussianhighpass/gaussianhighpass_filter.cpp @@ -1,125 +1,124 @@ /* * This file is part of Krita * * Copyright (c) 2019 Miguel Lopez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include //MSVC requires that Vc come first #include "gaussianhighpass_filter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_lod_transform.h" #include #include "wdg_gaussianhighpass.h" #include "ui_wdggaussianhighpass.h" #include "KoColorSpaceTraits.h" #include KisGaussianHighPassFilter::KisGaussianHighPassFilter() : KisFilter(id(), FiltersCategoryEdgeDetectionId, i18n("&Gaussian High Pass...")) { setSupportsPainting(true); setSupportsAdjustmentLayers(true); setSupportsThreading(true); setSupportsLevelOfDetail(true); setColorSpaceIndependence(FULLY_INDEPENDENT); } KisConfigWidget * KisGaussianHighPassFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool /* useForMasks */) const { return new KisWdgGaussianHighPass(parent); } KisFilterConfigurationSP KisGaussianHighPassFilter::factoryConfiguration() const { KisFilterConfigurationSP config = new KisFilterConfiguration(id().id(), 1); config->setProperty("blurAmount", 1); return config; } void KisGaussianHighPassFilter::processImpl(KisPaintDeviceSP device, const QRect& applyRect, const KisFilterConfigurationSP _config, KoUpdater * ) const { QPointer convolutionUpdater = 0; KisFilterConfigurationSP config = _config ? _config : new KisFilterConfiguration(id().id(), 1); QVariant value; KisLodTransformScalar t(device); const qreal blurAmount = t.scale(config->getProperty("blurAmount", value) ? value.toDouble() : 1.0); QBitArray channelFlags = config->channelFlags(); const QRect gaussNeedRect = this->neededRect(applyRect, config, device->defaultBounds()->currentLevelOfDetail()); - KisPaintDeviceSP blur = m_cachedPaintDevice.getDevice(device); + KisCachedPaintDevice::Guard d1(device, m_cachedPaintDevice); + KisPaintDeviceSP blur = d1.device(); KisPainter::copyAreaOptimizedOldData(gaussNeedRect.topLeft(), device, blur, gaussNeedRect); KisGaussianKernel::applyGaussian(blur, applyRect, blurAmount, blurAmount, channelFlags, convolutionUpdater, true); // make sure we cerate an internal transaction on temp device KisPainter painter(device); painter.setCompositeOp(blur->colorSpace()->compositeOp(COMPOSITE_GRAIN_EXTRACT)); painter.bitBlt(applyRect.topLeft(), blur, applyRect); painter.end(); - - m_cachedPaintDevice.putDevice(blur); } QRect KisGaussianHighPassFilter::neededRect(const QRect & rect, const KisFilterConfigurationSP config, int lod) const { KisLodTransformScalar t(lod); QVariant value; const int halfSize = config->getProperty("blurAmount", value) ? KisGaussianKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; return rect.adjusted( -halfSize * 2, -halfSize * 2, halfSize * 2, halfSize * 2); } QRect KisGaussianHighPassFilter::changedRect(const QRect & rect, const KisFilterConfigurationSP config, int lod) const { KisLodTransformScalar t(lod); QVariant value; const int halfSize = config->getProperty("blurAmount", value) ? KisGaussianKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; return rect.adjusted( -halfSize, -halfSize, halfSize, halfSize); }