diff --git a/krita/image/kis_group_layer.cc b/krita/image/kis_group_layer.cc index a0ca4e1b38..10def21df0 100644 --- a/krita/image/kis_group_layer.cc +++ b/krita/image/kis_group_layer.cc @@ -1,341 +1,383 @@ /* * Copyright (c) 2005 C. Boemann * Copyright (c) 2007 Boudewijn Rempt * Copyright (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_group_layer.h" #include #include #include #include #include "kis_types.h" #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_debug.h" #include "kis_image.h" #include "kis_paint_device.h" #include "kis_default_bounds.h" #include "kis_clone_layer.h" #include "kis_selection_mask.h" #include "kis_psd_layer_style.h" struct KisGroupLayer::Private { public: Private() : paintDevice(0) , x(0) , y(0) , passThroughMode(false) { } KisPaintDeviceSP paintDevice; qint32 x; qint32 y; bool passThroughMode; }; KisGroupLayer::KisGroupLayer(KisImageWSP image, const QString &name, quint8 opacity) : KisLayer(image, name, opacity), m_d(new Private()) { resetCache(); } KisGroupLayer::KisGroupLayer(const KisGroupLayer &rhs) : KisLayer(rhs), m_d(new Private()) { m_d->paintDevice = new KisPaintDevice(*rhs.m_d->paintDevice.data()); m_d->x = rhs.m_d->x; m_d->y = rhs.m_d->y; m_d->paintDevice->setDefaultPixel(const_cast(&rhs)->m_d->paintDevice->defaultPixel()); } KisGroupLayer::~KisGroupLayer() { delete m_d; } bool KisGroupLayer::checkCloneLayer(KisCloneLayerSP clone) const { KisNodeSP source = clone->copyFrom(); if (source) { if(!allowAsChild(source)) return false; if (source->inherits("KisGroupLayer")) { KisNodeSP newParent = const_cast(this); while (newParent) { if (newParent == source) { return false; } newParent = newParent->parent(); } } } return true; } bool KisGroupLayer::checkNodeRecursively(KisNodeSP node) const { KisCloneLayerSP cloneLayer = dynamic_cast(node.data()); if(cloneLayer) { return checkCloneLayer(cloneLayer); } else if (node->inherits("KisGroupLayer")) { KisNodeSP child = node->firstChild(); while (child) { if (!checkNodeRecursively(child)) { return false; } child = child->nextSibling(); } } return true; } bool KisGroupLayer::allowAsChild(KisNodeSP node) const { return checkNodeRecursively(node) && (parent() || (node->inherits("KisSelectionMask") && !selectionMask()) || !node->inherits("KisMask")); } const KoColorSpace * KisGroupLayer::colorSpace() const { return m_d->paintDevice->colorSpace(); } QIcon KisGroupLayer::icon() const { return koIcon("folder"); } void KisGroupLayer::setImage(KisImageWSP image) { m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image)); KisLayer::setImage(image); } KisLayerSP KisGroupLayer::createMergedLayer(KisLayerSP prevLayer) { KisGroupLayer *prevGroup = dynamic_cast(prevLayer.data()); if (prevGroup) { KisSharedPtr merged(new KisGroupLayer(*prevGroup)); KisNodeSP child, cloned; for (child = firstChild(); child; child = child->nextSibling()) { cloned = child->clone(); image()->addNode(cloned, merged); } image()->refreshGraphAsync(merged); return merged; } else return KisLayer::createMergedLayer(prevLayer); } void KisGroupLayer::resetCache(const KoColorSpace *colorSpace) { if (!colorSpace) colorSpace = image()->colorSpace(); Q_ASSERT(colorSpace); if (!m_d->paintDevice) { m_d->paintDevice = new KisPaintDevice(this, colorSpace, new KisDefaultBounds(image())); m_d->paintDevice->setX(m_d->x); m_d->paintDevice->setY(m_d->y); } else if(!(*m_d->paintDevice->colorSpace() == *colorSpace)) { KisPaintDeviceSP dev = new KisPaintDevice(this, colorSpace, new KisDefaultBounds(image())); dev->setX(m_d->x); dev->setY(m_d->y); quint8* defaultPixel = new quint8[colorSpace->pixelSize()]; m_d->paintDevice->colorSpace()-> convertPixelsTo(m_d->paintDevice->defaultPixel(), defaultPixel, colorSpace, 1, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags); dev->setDefaultPixel(defaultPixel); delete[] defaultPixel; m_d->paintDevice = dev; } else { m_d->paintDevice->clear(); } } KisLayer* KisGroupLayer::onlyMeaningfulChild() const { KisNode *child = firstChild().data(); KisLayer *onlyLayer = 0; while (child) { KisLayer *layer = dynamic_cast(child); if (layer) { if (onlyLayer) return 0; onlyLayer = layer; } child = child->nextSibling().data(); } return onlyLayer; } KisPaintDeviceSP KisGroupLayer::tryObligeChild() const { const KisLayer *child = onlyMeaningfulChild(); if (child && child->channelFlags().isEmpty() && child->projection() && child->visible() && (child->compositeOpId() == COMPOSITE_OVER || child->compositeOpId() == COMPOSITE_ALPHA_DARKEN || child->compositeOpId() == COMPOSITE_COPY) && child->opacity() == OPACITY_OPAQUE_U8 && *child->projection()->colorSpace() == *colorSpace() && !child->layerStyle()) { quint8 defaultOpacity = m_d->paintDevice->colorSpace()->opacityU8( m_d->paintDevice->defaultPixel()); if(defaultOpacity == OPACITY_TRANSPARENT_U8) { return child->projection(); } } return 0; } KisPaintDeviceSP KisGroupLayer::original() const { /** * We are too lazy! Let's our children work for us. * Try to use children's paintDevice if it's the only * one in stack and meets some conditions */ KisPaintDeviceSP realOriginal = tryObligeChild(); if (!realOriginal) { if (!childCount() && !m_d->paintDevice->extent().isEmpty()) { m_d->paintDevice->clear(); } realOriginal = m_d->paintDevice; } return realOriginal; } void KisGroupLayer::setDefaultProjectionColor(KoColor color) { color.convertTo(m_d->paintDevice->colorSpace()); m_d->paintDevice->setDefaultPixel(color.data()); } KoColor KisGroupLayer::defaultProjectionColor() const { KoColor color(m_d->paintDevice->defaultPixel(), m_d->paintDevice->colorSpace()); return color; } bool KisGroupLayer::passThroughMode() const { return m_d->passThroughMode; } void KisGroupLayer::setPassThroughMode(bool value) { m_d->passThroughMode = value; } KisDocumentSectionModel::PropertyList KisGroupLayer::sectionModelProperties() const { KisDocumentSectionModel::PropertyList l = KisLayer::sectionModelProperties(); // XXX: get right icons l << KisDocumentSectionModel::Property(i18n("Pass Through"), koIcon("passthrough-enabled"), koIcon("passthrough-disabled"), passThroughMode()); return l; } void KisGroupLayer::setSectionModelProperties(const KisDocumentSectionModel::PropertyList &properties) { foreach (const KisDocumentSectionModel::Property &property, properties) { if (property.name == i18n("Pass Through")) { setPassThroughMode(property.state.toBool()); } } KisLayer::setSectionModelProperties(properties); } bool KisGroupLayer::accept(KisNodeVisitor &v) { return v.visit(this); } void KisGroupLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } qint32 KisGroupLayer::x() const { return m_d->x; } qint32 KisGroupLayer::y() const { return m_d->y; } void KisGroupLayer::setX(qint32 x) { qint32 delta = x - m_d->x; m_d->x = x; if(m_d->paintDevice) { m_d->paintDevice->setX(m_d->paintDevice->x() + delta); Q_ASSERT(m_d->paintDevice->x() == m_d->x); } } void KisGroupLayer::setY(qint32 y) { qint32 delta = y - m_d->y; m_d->y = y; if(m_d->paintDevice) { m_d->paintDevice->setY(m_d->paintDevice->y() + delta); Q_ASSERT(m_d->paintDevice->y() == m_d->y); } } +struct ExtentPolicy +{ + inline QRect operator() (const KisNode *node) { + return node->extent(); + } +}; + +struct ExactBoundsPolicy +{ + inline QRect operator() (const KisNode *node) { + return node->exactBounds(); + } +}; + +template +QRect collectRects(const KisNode *node, MetricPolicy policy, bool skipFirst = true) +{ + QRect accumulator; + + const KisNode *child = node->firstChild(); + while (child) { + accumulator |= policy(child); + child = child->nextSibling(); + } + + return accumulator; +} + +QRect KisGroupLayer::extent() const +{ + return m_d->passThroughMode ? + collectRects(this, ExtentPolicy()) : + KisLayer::extent(); +} + +QRect KisGroupLayer::exactBounds() const +{ + return m_d->passThroughMode ? + collectRects(this, ExactBoundsPolicy()) : + KisLayer::exactBounds(); +} + #include "kis_group_layer.moc" diff --git a/krita/image/kis_group_layer.h b/krita/image/kis_group_layer.h index e08c19829c..8005dc3f70 100644 --- a/krita/image/kis_group_layer.h +++ b/krita/image/kis_group_layer.h @@ -1,115 +1,118 @@ /* * Copyright (c) 2005 C. Boemann * Copyright (c) 2007 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. */ #ifndef KIS_GROUP_LAYER_H_ #define KIS_GROUP_LAYER_H_ #include "kis_layer.h" #include "kis_types.h" class KoColorSpace; /** * A KisLayer that bundles child layers into a single layer. * The top layer is firstChild(), with index 0; the bottommost lastChild() with index childCount() - 1. * KisLayer::nextSibling() moves towards higher indices, from the top to the bottom layer; prevSibling() the reverse. * (Implementation detail: internally, the indices are reversed, for speed.) **/ class KRITAIMAGE_EXPORT KisGroupLayer : public KisLayer { Q_OBJECT public: KisGroupLayer(KisImageWSP image, const QString &name, quint8 opacity); KisGroupLayer(const KisGroupLayer& rhs); virtual ~KisGroupLayer(); KisNodeSP clone() const { return KisNodeSP(new KisGroupLayer(*this)); } bool allowAsChild(KisNodeSP) const; QIcon icon() const; KisDocumentSectionModel::PropertyList sectionModelProperties() const; void setSectionModelProperties(const KisDocumentSectionModel::PropertyList &properties); virtual void setImage(KisImageWSP image); virtual KisLayerSP createMergedLayer(KisLayerSP prevLayer); /** * Clear the projection */ void resetCache(const KoColorSpace *colorSpace = 0); /** * XXX: make the colorspace of a layergroup user-settable: we want * to be able to have, for instance, a group of grayscale layers * resulting in a grayscale projection that is then merged with an * rgb image stack. */ const KoColorSpace * colorSpace() const; /// @return the projection of the layers in the group before the masks are applied. KisPaintDeviceSP original() const; qint32 x() const; qint32 y() const; void setX(qint32 x); void setY(qint32 y); /** Accect the specified visitor. @return true if the operation succeeded, false if it failed. */ bool accept(KisNodeVisitor &v); void accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter); /** * A special method that changes the default color of the * projection merged onto this group layer. Please note, that you * cannot use original()->setDefaultPixel(), because original() * device can be switched by tryOblidgeChild() mechanism randomly. */ void setDefaultProjectionColor(KoColor color); /** * \see setDefaultProjectionColor() */ KoColor defaultProjectionColor() const; bool passThroughMode() const; void setPassThroughMode(bool value); + QRect extent() const; + QRect exactBounds() const; + protected: KisLayer* onlyMeaningfulChild() const; KisPaintDeviceSP tryObligeChild() const; private: bool checkCloneLayer(KisCloneLayerSP clone) const; bool checkNodeRecursively(KisNodeSP node) const; private: struct Private; Private * const m_d; }; #endif // KIS_GROUP_LAYER_H_