diff --git a/krita/image/kis_default_bounds.cpp b/krita/image/kis_default_bounds.cpp index 6df3e98e53a..f00ac18bb41 100644 --- a/krita/image/kis_default_bounds.cpp +++ b/krita/image/kis_default_bounds.cpp @@ -1,107 +1,111 @@ /* * Copyright (c) 2010 Boudewijn Rempt * Copyright (c) 2010 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_global.h" #include "kis_default_bounds.h" #include "kis_paint_device.h" const QRect KisDefaultBounds::infiniteRect = QRect(qint32_MIN/2, qint32_MIN/2, qint32_MAX, qint32_MAX); /******************************************************************/ /* KisDefaultBounds */ /******************************************************************/ struct KisDefaultBounds::Private { KisImageWSP image; }; KisDefaultBounds::KisDefaultBounds(KisImageWSP image) : m_d(new Private()) { m_d->image = image; } KisDefaultBounds::~KisDefaultBounds() { delete m_d; } +void KisDefaultBounds::setImage(KisImageWSP image) +{ + m_d->image = image; +} + QRect KisDefaultBounds::bounds() const { /** * By default return infinite rect to cover everything */ return m_d->image ? m_d->image->bounds() : infiniteRect; } bool KisDefaultBounds::wrapAroundMode() const { return m_d->image ? m_d->image->wrapAroundModeActive() : false; } /******************************************************************/ /* KisSelectionDefaultBounds */ /******************************************************************/ struct KisSelectionDefaultBounds::Private { KisPaintDeviceSP parentDevice; }; KisSelectionDefaultBounds::KisSelectionDefaultBounds(KisPaintDeviceSP parentDevice, KisImageWSP image) : KisDefaultBounds(image), m_d(new Private()) { m_d->parentDevice = parentDevice; } KisSelectionDefaultBounds::~KisSelectionDefaultBounds() { delete m_d; } QRect KisSelectionDefaultBounds::bounds() const { QRect additionalRect = m_d->parentDevice ? m_d->parentDevice->extent() : QRect(); return additionalRect | KisDefaultBounds::bounds(); } - /******************************************************************/ /* KisSelectionEmptyBounds */ /******************************************************************/ KisSelectionEmptyBounds::KisSelectionEmptyBounds(KisImageWSP image) : KisDefaultBounds(image) { } KisSelectionEmptyBounds::~KisSelectionEmptyBounds() { } QRect KisSelectionEmptyBounds::bounds() const { return QRect(0, 0, 0, 0); } diff --git a/krita/image/kis_default_bounds.h b/krita/image/kis_default_bounds.h index 94c11625a49..ce5c8b802ab 100644 --- a/krita/image/kis_default_bounds.h +++ b/krita/image/kis_default_bounds.h @@ -1,77 +1,79 @@ /* * Copyright (c) 2010 Boudewijn Rempt * Copyright (c) 2010 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_DEFAULT_BOUNDS_H #define KIS_DEFAULT_BOUNDS_H #include #include "kis_types.h" #include "kis_image.h" #include "kis_default_bounds_base.h" class KisDefaultBounds; class KisSelectionDefaultBounds; class KisSelectionEmptyBounds; typedef KisSharedPtr KisDefaultBoundsSP; typedef KisSharedPtr KisSelectionDefaultBoundsSP; typedef KisSharedPtr KisSelectionEmptyBoundsSP; class KRITAIMAGE_EXPORT KisDefaultBounds : public KisDefaultBoundsBase { public: KisDefaultBounds(KisImageWSP image = 0); virtual ~KisDefaultBounds(); + void setImage(KisImageWSP image); + QRect bounds() const; bool wrapAroundMode() const; protected: friend class KisPaintDeviceTest; static const QRect infiniteRect; private: Q_DISABLE_COPY(KisDefaultBounds) struct Private; Private * const m_d; }; class KRITAIMAGE_EXPORT KisSelectionDefaultBounds : public KisDefaultBounds { public: KisSelectionDefaultBounds(KisPaintDeviceSP parentPaintDevice = 0, KisImageWSP image = 0); ~KisSelectionDefaultBounds(); QRect bounds() const; private: Q_DISABLE_COPY(KisSelectionDefaultBounds) struct Private; Private * const m_d; }; class KRITAIMAGE_EXPORT KisSelectionEmptyBounds : public KisDefaultBounds { public: KisSelectionEmptyBounds(KisImageWSP image); virtual ~KisSelectionEmptyBounds(); QRect bounds() const; }; #endif // KIS_DEFAULT_BOUNDS_H diff --git a/krita/image/kis_layer.cc b/krita/image/kis_layer.cc index a5e59f7efe4..8be7d653dd1 100644 --- a/krita/image/kis_layer.cc +++ b/krita/image/kis_layer.cc @@ -1,803 +1,809 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2005 C. Boemann * 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_layer.h" #include #include #include #include #include #include #include #include #include #include #include "kis_debug.h" #include "kis_image.h" #include "kis_painter.h" #include "kis_mask.h" #include "kis_effect_mask.h" #include "kis_selection_mask.h" #include "kis_meta_data_store.h" #include "kis_selection.h" #include "kis_paint_layer.h" #include "kis_clone_layer.h" #include "kis_psd_layer_style.h" #include "kis_layer_projection_plane.h" #include "layerstyles/kis_layer_style_projection_plane.h" class KisSafeProjection { public: KisPaintDeviceSP getDeviceLazy(KisPaintDeviceSP prototype) { QMutexLocker locker(&m_lock); if (!m_reusablePaintDevice) { m_reusablePaintDevice = new KisPaintDevice(*prototype); } if(!m_projection || !(*m_projection->colorSpace() == *prototype->colorSpace())) { m_projection = m_reusablePaintDevice; m_projection->makeCloneFromRough(prototype, prototype->extent()); } return m_projection; } void freeDevice() { QMutexLocker locker(&m_lock); m_projection = 0; if(m_reusablePaintDevice) { m_reusablePaintDevice->clear(); } } private: QMutex m_lock; KisPaintDeviceSP m_projection; KisPaintDeviceSP m_reusablePaintDevice; }; class KisCloneLayersList { public: void addClone(KisCloneLayerWSP cloneLayer) { m_clonesList.append(cloneLayer); } void removeClone(KisCloneLayerWSP cloneLayer) { m_clonesList.removeOne(cloneLayer); } void setDirty(const QRect &rect) { foreach(KisCloneLayerWSP clone, m_clonesList) { clone->setDirtyOriginal(rect); } } const QList registeredClones() const { return m_clonesList; } bool hasClones() const { return !m_clonesList.isEmpty(); } private: QList m_clonesList; }; struct KisLayer::Private { KisImageWSP image; QBitArray channelFlags; KisMetaData::Store* metaDataStore; KisSafeProjection safeProjection; KisCloneLayersList clonesList; KisPSDLayerStyleSP layerStyle; KisAbstractProjectionPlaneSP layerStyleProjectionPlane; KisAbstractProjectionPlaneSP projectionPlane; }; KisLayer::KisLayer(KisImageWSP image, const QString &name, quint8 opacity) : KisNode() , m_d(new Private) { setName(name); setOpacity(opacity); m_d->image = image; m_d->metaDataStore = new KisMetaData::Store(); m_d->projectionPlane = toQShared(new KisLayerProjectionPlane(this)); } KisLayer::KisLayer(const KisLayer& rhs) : KisNode(rhs) , m_d(new Private()) { if (this != &rhs) { m_d->image = rhs.m_d->image; m_d->metaDataStore = new KisMetaData::Store(*rhs.m_d->metaDataStore); setName(rhs.name()); m_d->projectionPlane = toQShared(new KisLayerProjectionPlane(this)); if (rhs.m_d->layerStyle) { setLayerStyle(rhs.m_d->layerStyle->clone()); } } } KisLayer::~KisLayer() { delete m_d->metaDataStore; delete m_d; } const KoColorSpace * KisLayer::colorSpace() const { if (m_d->image) return m_d->image->colorSpace(); return 0; } const KoCompositeOp * KisLayer::compositeOp() const { /** * FIXME: This function duplicates the same function from * KisMask. We can't move it to KisBaseNode as it doesn't * know anything about parent() method of KisNode * Please think it over... */ KisNodeSP parentNode = parent(); if (!parentNode) return 0; if (!parentNode->colorSpace()) return 0; const KoCompositeOp* op = parentNode->colorSpace()->compositeOp(compositeOpId()); return op ? op : parentNode->colorSpace()->compositeOp(COMPOSITE_OVER); } KisPSDLayerStyleSP KisLayer::layerStyle() const { return m_d->layerStyle; } void KisLayer::setLayerStyle(KisPSDLayerStyleSP layerStyle) { if (layerStyle) { m_d->layerStyle = layerStyle; KisAbstractProjectionPlaneSP plane = !layerStyle->isEmpty() ? KisAbstractProjectionPlaneSP(new KisLayerStyleProjectionPlane(this)) : KisAbstractProjectionPlaneSP(0); m_d->layerStyleProjectionPlane = plane; } else { m_d->layerStyleProjectionPlane.clear(); m_d->layerStyle.clear(); } } KisDocumentSectionModel::PropertyList KisLayer::sectionModelProperties() const { KisDocumentSectionModel::PropertyList l = KisBaseNode::sectionModelProperties(); l << KisDocumentSectionModel::Property(i18n("Opacity"), i18n("%1%", percentOpacity())); if (compositeOp()) { l << KisDocumentSectionModel::Property(i18n("Composite Mode"), compositeOp()->description()); } if (m_d->layerStyle && !m_d->layerStyle->isEmpty()) { l << KisDocumentSectionModel::Property(i18n("Layer Style"), koIcon("layer-style-enabled"), koIcon("layer-style-disabled"), m_d->layerStyle->isEnabled()); } l << KisDocumentSectionModel::Property(i18n("Inherit Alpha"), themedIcon("transparency-disabled"), themedIcon("transparency-enabled"), alphaChannelDisabled()); return l; } void KisLayer::setSectionModelProperties(const KisDocumentSectionModel::PropertyList &properties) { KisBaseNode::setSectionModelProperties(properties); foreach (const KisDocumentSectionModel::Property &property, properties) { if (property.name == i18n("Inherit Alpha")) { disableAlphaChannel(property.state.toBool()); } if (property.name == i18n("Layer Style")) { if (m_d->layerStyle) { m_d->layerStyle->setEnabled(property.state.toBool()); } } } } void KisLayer::disableAlphaChannel(bool disable) { QBitArray newChannelFlags = m_d->channelFlags; if(newChannelFlags.isEmpty()) newChannelFlags = colorSpace()->channelFlags(true, true); if(disable) newChannelFlags &= colorSpace()->channelFlags(true, false); else newChannelFlags |= colorSpace()->channelFlags(false, true); setChannelFlags(newChannelFlags); } bool KisLayer::alphaChannelDisabled() const { QBitArray flags = colorSpace()->channelFlags(false, true) & m_d->channelFlags; return flags.count(true) == 0 && !m_d->channelFlags.isEmpty(); } void KisLayer::setChannelFlags(const QBitArray & channelFlags) { Q_ASSERT(channelFlags.isEmpty() ||((quint32)channelFlags.count() == colorSpace()->channelCount())); if (!channelFlags.isEmpty() && channelFlags == QBitArray(channelFlags.size(), true)) { m_d->channelFlags.clear(); } else { m_d->channelFlags = channelFlags; } } QBitArray & KisLayer::channelFlags() const { return m_d->channelFlags; } bool KisLayer::temporary() const { return nodeProperties().boolProperty("temporary", false); } void KisLayer::setTemporary(bool t) { nodeProperties().setProperty("temporary", t); } KisImageWSP KisLayer::image() const { return m_d->image; } void KisLayer::setImage(KisImageWSP image) { m_d->image = image; for (uint i = 0; i < childCount(); ++i) { // Only layers know about the image - KisLayer * layer = dynamic_cast(at(i).data()); - if (layer) + KisLayer *layer = dynamic_cast(at(i).data()); + if (layer) { layer->setImage(image); + } + // We lied, through the defaultBounds, masks also know about the image + KisMask *mask = dynamic_cast(at(i).data()); + if (mask) { + mask->setImage(image); + } } } bool KisLayer::canMergeAndKeepBlendOptions(KisLayerSP otherLayer) { return this->compositeOpId() == otherLayer->compositeOpId() && this->opacity() == otherLayer->opacity() && this->channelFlags() == otherLayer->channelFlags() && !this->layerStyle() && !otherLayer->layerStyle() && (this->colorSpace() == otherLayer->colorSpace() || *this->colorSpace() == *otherLayer->colorSpace()); } KisLayerSP KisLayer::createMergedLayer(KisLayerSP prevLayer) { KisImageSP my_image = image(); QRect layerProjectionExtent = this->projection()->extent(); QRect prevLayerProjectionExtent = prevLayer->projection()->extent(); bool alphaDisabled = this->alphaChannelDisabled(); bool prevAlphaDisabled = prevLayer->alphaChannelDisabled(); KisPaintDeviceSP mergedDevice; bool keepBlendingOptions = canMergeAndKeepBlendOptions(prevLayer); if (!keepBlendingOptions) { KisNodeSP parentNode = parent(); const KoColorSpace *dstCs = parentNode && parentNode->colorSpace() ? parentNode->colorSpace() : my_image->colorSpace(); mergedDevice = new KisPaintDevice(dstCs, "merged"); KisPainter gc(mergedDevice); //Copy the pixels of previous layer with their actual alpha value prevLayer->disableAlphaChannel(false); prevLayer->projectionPlane()->apply(&gc, prevLayerProjectionExtent | my_image->bounds()); //Restore the previous prevLayer disableAlpha status for correct undo/redo prevLayer->disableAlphaChannel(prevAlphaDisabled); //Paint the pixels of the current layer, using their actual alpha value if (alphaDisabled == prevAlphaDisabled) { this->disableAlphaChannel(false); } this->projectionPlane()->apply(&gc, layerProjectionExtent | my_image->bounds()); //Restore the layer disableAlpha status for correct undo/redo this->disableAlphaChannel(alphaDisabled); } else { //Copy prevLayer my_image->lock(); mergedDevice = new KisPaintDevice(*prevLayer->projection()); my_image->unlock(); //Paint layer on the copy KisPainter gc(mergedDevice); gc.bitBlt(layerProjectionExtent.topLeft(), this->projection(), layerProjectionExtent); } KisLayerSP newLayer = new KisPaintLayer(my_image, prevLayer->name(), OPACITY_OPAQUE_U8, mergedDevice); if (keepBlendingOptions) { newLayer->setCompositeOp(compositeOpId()); newLayer->setOpacity(opacity()); newLayer->setChannelFlags(channelFlags()); } return newLayer; } void KisLayer::registerClone(KisCloneLayerWSP clone) { m_d->clonesList.addClone(clone); } void KisLayer::unregisterClone(KisCloneLayerWSP clone) { m_d->clonesList.removeClone(clone); } const QList KisLayer::registeredClones() const { return m_d->clonesList.registeredClones(); } bool KisLayer::hasClones() const { return m_d->clonesList.hasClones(); } void KisLayer::updateClones(const QRect &rect) { m_d->clonesList.setDirty(rect); } KisSelectionMaskSP KisLayer::selectionMask() const { KoProperties properties; properties.setProperty("active", true); QList masks = childNodes(QStringList("KisSelectionMask"), properties); // return the first visible mask foreach (KisNodeSP mask, masks) { if (mask->visible()) { return dynamic_cast(mask.data()); } } return 0; } KisSelectionSP KisLayer::selection() const { if (selectionMask()) { return selectionMask()->selection(); } else if (m_d->image) { return m_d->image->globalSelection(); } else { return 0; } } /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// QList KisLayer::effectMasks(KisNodeSP lastNode) const { QList masks; if (childCount() > 0) { KoProperties properties; properties.setProperty("visible", true); QList nodes = childNodes(QStringList("KisEffectMask"), properties); foreach(const KisNodeSP& node, nodes) { if (node == lastNode) break; KisEffectMaskSP mask = dynamic_cast(const_cast(node.data())); if (mask) masks.append(mask); } } return masks; } bool KisLayer::hasEffectMasks() const { if (childCount() == 0) return false; KisNodeSP node = firstChild(); while (node) { if (node->inherits("KisEffectMask") && node->visible()) { return true; } node = node->nextSibling(); } return false; } QRect KisLayer::masksChangeRect(const QList &masks, const QRect &requestedRect, bool &rectVariesFlag) const { rectVariesFlag = false; QRect prevChangeRect = requestedRect; /** * We set default value of the change rect for the case * when there is no mask at all */ QRect changeRect = requestedRect; foreach(const KisEffectMaskSP& mask, masks) { changeRect = mask->changeRect(prevChangeRect); if (changeRect != prevChangeRect) rectVariesFlag = true; prevChangeRect = changeRect; } return changeRect; } QRect KisLayer::masksNeedRect(const QList &masks, const QRect &changeRect, QStack &applyRects, bool &rectVariesFlag) const { rectVariesFlag = false; QRect prevNeedRect = changeRect; QRect needRect; for (qint32 i = masks.size() - 1; i >= 0; i--) { applyRects.push(prevNeedRect); needRect = masks[i]->needRect(prevNeedRect); if (prevNeedRect != needRect) rectVariesFlag = true; prevNeedRect = needRect; } return needRect; } KisNode::PositionToFilthy calculatePositionToFilthy(KisNodeSP nodeInQuestion, KisNodeSP filthy, KisNodeSP parent) { if (parent == filthy || parent != filthy->parent()) { return KisNode::N_ABOVE_FILTHY; } if (nodeInQuestion == filthy) { return KisNode::N_FILTHY; } KisNodeSP node = nodeInQuestion->prevSibling(); while (node) { if (node == filthy) { return KisNode::N_ABOVE_FILTHY; } node = node->prevSibling(); } return KisNode::N_BELOW_FILTHY; } QRect KisLayer::applyMasks(const KisPaintDeviceSP source, KisPaintDeviceSP destination, const QRect &requestedRect, KisNodeSP filthyNode, KisNodeSP lastNode) const { Q_ASSERT(source); Q_ASSERT(destination); QList masks = effectMasks(lastNode); QRect changeRect; QRect needRect; if (masks.isEmpty()) { changeRect = requestedRect; if (source != destination) { copyOriginalToProjection(source, destination, requestedRect); } } else { QStack applyRects; bool changeRectVaries; bool needRectVaries; /** * FIXME: Assume that varying of the changeRect has already * been taken into account while preparing walkers */ changeRectVaries = false; changeRect = requestedRect; //changeRect = masksChangeRect(masks, requestedRect, // changeRectVaries); needRect = masksNeedRect(masks, changeRect, applyRects, needRectVaries); if (!changeRectVaries && !needRectVaries) { /** * A bit of optimization: * All filters will read/write exactly from/to the requested * rect so we needn't create temporary paint device, * just apply it onto destination */ Q_ASSERT(needRect == requestedRect); if (source != destination) { copyOriginalToProjection(source, destination, needRect); } foreach(const KisEffectMaskSP& mask, masks) { const QRect maskApplyRect = applyRects.pop(); const QRect maskNeedRect = applyRects.isEmpty() ? needRect : applyRects.top(); PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast(this)); mask->apply(destination, maskApplyRect, maskNeedRect, maskPosition); } Q_ASSERT(applyRects.isEmpty()); } else { /** * We can't eliminate additional copy-op * as filters' behaviour may be quite insane here, * so let them work on their own paintDevice =) */ KisPaintDeviceSP tempDevice = new KisPaintDevice(colorSpace()); tempDevice->prepareClone(source); copyOriginalToProjection(source, tempDevice, needRect); QRect maskApplyRect = applyRects.pop(); QRect maskNeedRect = needRect; foreach(const KisEffectMaskSP& mask, masks) { PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast(this)); mask->apply(tempDevice, maskApplyRect, maskNeedRect, maskPosition); if (!applyRects.isEmpty()) { maskNeedRect = maskApplyRect; maskApplyRect = applyRects.pop(); } } Q_ASSERT(applyRects.isEmpty()); KisPainter::copyAreaOptimized(changeRect.topLeft(), tempDevice, destination, changeRect); } } return changeRect; } QRect KisLayer::updateProjection(const QRect& rect, KisNodeSP filthyNode) { QRect updatedRect = rect; KisPaintDeviceSP originalDevice = original(); if (!rect.isValid() || !visible() || !originalDevice) return QRect(); if (!needProjection() && !hasEffectMasks()) { m_d->safeProjection.freeDevice(); } else { if (!updatedRect.isEmpty()) { KisPaintDeviceSP projection = m_d->safeProjection.getDeviceLazy(originalDevice); updatedRect = applyMasks(originalDevice, projection, updatedRect, filthyNode, 0); } } return updatedRect; } QRect KisLayer::partialChangeRect(KisNodeSP lastNode, const QRect& rect) { bool changeRectVaries = false; QRect changeRect = outgoingChangeRect(rect); changeRect = masksChangeRect(effectMasks(lastNode), changeRect, changeRectVaries); return changeRect; } /** * \p rect is a dirty rect in layer's original() coordinates! */ void KisLayer::buildProjectionUpToNode(KisPaintDeviceSP projection, KisNodeSP lastNode, const QRect& rect) { QRect changeRect = partialChangeRect(lastNode, rect); KisPaintDeviceSP originalDevice = original(); KIS_ASSERT_RECOVER_RETURN(needProjection() || hasEffectMasks()); if (!changeRect.isEmpty()) { applyMasks(originalDevice, projection, changeRect, this, lastNode); } } bool KisLayer::needProjection() const { return false; } void KisLayer::copyOriginalToProjection(const KisPaintDeviceSP original, KisPaintDeviceSP projection, const QRect& rect) const { KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect); } KisAbstractProjectionPlaneSP KisLayer::projectionPlane() const { return m_d->layerStyleProjectionPlane ? m_d->layerStyleProjectionPlane : m_d->projectionPlane; } KisAbstractProjectionPlaneSP KisLayer::internalProjectionPlane() const { return m_d->projectionPlane; } KisPaintDeviceSP KisLayer::projection() const { KisPaintDeviceSP originalDevice = original(); return needProjection() || hasEffectMasks() ? m_d->safeProjection.getDeviceLazy(originalDevice) : originalDevice; } QRect KisLayer::changeRect(const QRect &rect, PositionToFilthy pos) const { QRect changeRect = rect; changeRect = incomingChangeRect(changeRect); if(pos == KisNode::N_FILTHY) { bool changeRectVaries; changeRect = outgoingChangeRect(changeRect); changeRect = masksChangeRect(effectMasks(), changeRect, changeRectVaries); } // TODO: string comparizon: optimize! if (pos != KisNode::N_FILTHY && pos != KisNode::N_FILTHY_PROJECTION && compositeOpId() != COMPOSITE_COPY) { changeRect |= rect; } return changeRect; } QRect KisLayer::incomingChangeRect(const QRect &rect) const { return rect; } QRect KisLayer::outgoingChangeRect(const QRect &rect) const { return rect; } QImage KisLayer::createThumbnail(qint32 w, qint32 h) { KisPaintDeviceSP originalDevice = original(); return originalDevice ? originalDevice->createThumbnail(w, h, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags) : QImage(); } qint32 KisLayer::x() const { KisPaintDeviceSP originalDevice = original(); return originalDevice ? originalDevice->x() : 0; } qint32 KisLayer::y() const { KisPaintDeviceSP originalDevice = original(); return originalDevice ? originalDevice->y() : 0; } void KisLayer::setX(qint32 x) { KisPaintDeviceSP originalDevice = original(); if (originalDevice) originalDevice->setX(x); } void KisLayer::setY(qint32 y) { KisPaintDeviceSP originalDevice = original(); if (originalDevice) originalDevice->setY(y); } QRect KisLayer::extent() const { KisPaintDeviceSP originalDevice = original(); return originalDevice ? originalDevice->extent() : QRect(); } QRect KisLayer::exactBounds() const { KisPaintDeviceSP originalDevice = original(); return originalDevice ? originalDevice->exactBounds() : QRect(); } KisLayerSP KisLayer::parentLayer() const { return dynamic_cast(parent().data()); } KisMetaData::Store* KisLayer::metaData() { return m_d->metaDataStore; } #include "kis_layer.moc" diff --git a/krita/image/kis_mask.cc b/krita/image/kis_mask.cc index c41584c7ec9..84a3258d962 100644 --- a/krita/image/kis_mask.cc +++ b/krita/image/kis_mask.cc @@ -1,350 +1,359 @@ /* * 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 "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" struct 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; void initSelectionImpl(KisSelectionSP copyFrom, KisLayerSP parentLayer, KisPaintDeviceSP copyFromDevice); }; KisMask::KisMask(const QString & name) : KisNode() , m_d(new Private(this)) { setName(name); } KisMask::KisMask(const KisMask& rhs) : KisNode(rhs) , KisIndirectPaintingSupport() , m_d(new Private(this)) { setName(rhs.name()); if (rhs.m_d->selection) { m_d->selection = new KisSelection(*rhs.m_d->selection.data()); m_d->selection->setParentNode(this); } } KisMask::~KisMask() { delete m_d; } +void KisMask::setImage(KisImageWSP image) +{ + KisDefaultBoundsBaseSP db = m_d->selection->pixelSelection()->defaultBounds(); + KisDefaultBounds *defaultBounds = dynamic_cast(db.data()); + if (defaultBounds) { + defaultBounds->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... */ KisNodeSP parentNode = parent(); if (!parentNode) return 0; if (!parentNode->colorSpace()) return 0; const KoCompositeOp* op = parentNode->colorSpace()->compositeOp(compositeOpId()); return op ? op : parentNode->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) { selection = new KisSelection(new KisSelectionDefaultBounds(parentPaintDevice, parentLayer->image())); QRect rc(copyFromDevice->extent()); KisPainter::copyAreaOptimized(rc.topLeft(), copyFromDevice, selection->pixelSelection(), rc); selection->pixelSelection()->invalidateOutlineCache(); } else { selection = new KisSelection(new KisSelectionDefaultBounds(parentPaintDevice, parentLayer->image())); quint8 newDefaultPixel = MAX_SELECTED; selection->pixelSelection()->setDefaultPixel(&newDefaultPixel); 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 { return selection()->pixelSelection(); } KisPaintDeviceSP KisMask::original() const { return paintDevice(); } KisPaintDeviceSP KisMask::projection() const { return paintDevice(); } 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; } void KisMask::apply(KisPaintDeviceSP projection, const QRect &applyRect, const QRect &needRect, PositionToFilthy maskPos) const { if (selection()) { m_d->selection->updateProjection(applyRect); if(!extent().intersects(applyRect)) return; KisPaintDeviceSP cacheDevice = m_d->paintDeviceCache.getDevice(projection); QRect updatedRect = decorateRect(projection, cacheDevice, applyRect, maskPos); // masks don't have any compositioning KisPainter::copyAreaOptimized(updatedRect.topLeft(), cacheDevice, projection, updatedRect, m_d->selection); m_d->paintDeviceCache.putDevice(cacheDevice); } else { KisPaintDeviceSP cacheDevice = m_d->paintDeviceCache.getDevice(projection); cacheDevice->makeCloneFromRough(projection, needRect); projection->clear(needRect); decorateRect(cacheDevice, projection, applyRect, maskPos); m_d->paintDeviceCache.putDevice(cacheDevice); } } QRect KisMask::needRect(const QRect &rect, PositionToFilthy pos) const { Q_UNUSED(pos); QRect resultRect = rect; if (m_d->selection) resultRect &= m_d->selection->selectedRect(); return resultRect; } QRect KisMask::changeRect(const QRect &rect, PositionToFilthy pos) const { Q_UNUSED(pos); QRect resultRect = rect; if (m_d->selection) resultRect &= m_d->selection->selectedRect(); return resultRect; } QRect KisMask::extent() const { return m_d->selection ? m_d->selection->selectedRect() : parent() ? parent()->extent() : QRect(); } QRect KisMask::exactBounds() const { return m_d->selection ? m_d->selection->selectedExactRect() : parent() ? parent()->exactBounds() : QRect(); } 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; } } QImage KisMask::createThumbnail(qint32 w, qint32 h) { KisPaintDeviceSP originalDevice = selection() ? selection()->projection() : 0; return originalDevice ? originalDevice->createThumbnail(w, h, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags) : QImage(); } void KisMask::testingInitSelection(const QRect &rect) { m_d->selection = new KisSelection(); m_d->selection->pixelSelection()->select(rect, OPACITY_OPAQUE_U8); m_d->selection->updateProjection(rect); m_d->selection->setParentNode(this); } #include "kis_mask.moc" diff --git a/krita/image/kis_mask.h b/krita/image/kis_mask.h index 4b6a43a469c..da4ff238e47 100644 --- a/krita/image/kis_mask.h +++ b/krita/image/kis_mask.h @@ -1,202 +1,204 @@ /* * 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. */ #ifndef _KIS_MASK_ #define _KIS_MASK_ #include #include "kis_types.h" #include "kis_node.h" #include "kis_global.h" #include "kis_indirect_painting_support.h" #include /** KisMask is the base class for all single channel mask-like paint devices in Krita. Masks can be rendered in different ways at different moments during the rendering stack. Masks are "owned" by layers (of any type), and cannot occur by themselves on themselves. The properties that masks implement are made available through the iterators created on their parent layer, or through iterators that can be created on the paint device that holds the mask data: masks are just paint devices, too. Masks should show up in the layerbox as sub-layers for the layer they are associated with and be ccp'able and draggable to other layers. Examples of masks are: - filter masks: like the alpha filter mask that is the most common type of mask and is simply known as "mask" in the gui. Other filter masks use any of krita's filters to filter the pixels of their parent. (In this they differ from adjustment layers, which filter all layers under them in their group stack). - selections: the selection mask is rendered after composition and zooming and determines the selectedness of the pixels of the parent layer. - painterly overlays: painterly overlays indicate a particular property of the pixel in the parent paint device they are associated with, like wetness, height or gravity. XXX: For now, all masks are 8 bit. Make the channel depth settable. */ class KRITAIMAGE_EXPORT KisMask : public KisNode, KisIndirectPaintingSupport { Q_OBJECT public: /** * Create a new KisMask. */ KisMask(const QString & name); /** * Copy the mask */ KisMask(const KisMask& rhs); virtual ~KisMask(); + void setImage(KisImageWSP image); + bool allowAsChild(KisNodeSP node) const; /** * @brief initSelection initializes the selection for the mask from * the given selection's projection. * @param copyFrom the selection we base the mask on * @param parentLayer the parent of this mask; it determines the default bounds of the mask. */ void initSelection(KisSelectionSP copyFrom, KisLayerSP parentLayer); /** * @brief initSelection initializes the selection for the mask from * the given paint device. * @param copyFromDevice the paint device we base the mask on * @param parentLayer the parent of this mask; it determines the default bounds of the mask. */ void initSelection(KisPaintDeviceSP copyFromDevice, KisLayerSP parentLayer); /** * @brief initSelection initializes an empty selection * @param parentLayer the parent of this mask; it determines the default bounds of the mask. */ void initSelection(KisLayerSP parentLayer); const KoColorSpace * colorSpace() const; const KoCompositeOp * compositeOp() const; /** * Return the selection associated with this mask. A selection can * contain both a paint device and shapes. */ KisSelectionSP selection() const; /** * @return the selection: if you paint on mask, you paint on the selections */ KisPaintDeviceSP paintDevice() const; /** * @return the same as paintDevice() */ KisPaintDeviceSP original() const; /** * @return the same as paintDevice() */ KisPaintDeviceSP projection() const; KisAbstractProjectionPlaneSP projectionPlane() const; /** * Change the selection to the specified selection object. The * selection is deep copied. */ void setSelection(KisSelectionSP selection); /** * Selected the specified rect with the specified amount of selectedness. */ void select(const QRect & rc, quint8 selectedness = MAX_SELECTED); /** * The extent and bounds of the mask are those of the selection inside */ QRect extent() const; QRect exactBounds() const; /** * overridden from KisBaseNode */ qint32 x() const; /** * overridden from KisBaseNode */ void setX(qint32 x); /** * overridden from KisBaseNode */ qint32 y() const; /** * overridden from KisBaseNode */ void setY(qint32 y); QRect needRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const; QRect changeRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const; QImage createThumbnail(qint32 w, qint32 h); void testingInitSelection(const QRect &rect); protected: /** * Apply the effect the projection using the mask as a selection. * Made public in KisEffectMask */ void apply(KisPaintDeviceSP projection, const QRect & applyRect, const QRect & needRect, PositionToFilthy maskPos) const; virtual QRect decorateRect(KisPaintDeviceSP &src, KisPaintDeviceSP &dst, const QRect & rc, PositionToFilthy maskPos) const; private: friend class KisMaskProjectionPlane; private: struct Private; Private * const m_d; }; #endif diff --git a/krita/ui/KisView.cpp b/krita/ui/KisView.cpp index 8837bc80323..2ec5858695c 100644 --- a/krita/ui/KisView.cpp +++ b/krita/ui/KisView.cpp @@ -1,1047 +1,1047 @@ /* * Copyright (C) 2014 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisView.h" #include "KisView_p.h" #include #include #include #include "KoDocumentInfo.h" #include "KoPageLayout.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_canvas2.h" #include "kis_canvas_controller.h" #include "kis_canvas_resource_provider.h" #include "kis_config.h" #include "KisDocument.h" #include "kis_image_manager.h" #include "KisMainWindow.h" #include "kis_mimedata.h" #include "kis_mirror_axis.h" #include "kis_node_commands_adapter.h" #include "kis_node_manager.h" #include "KisPart.h" #include "KisPrintJob.h" #include "kis_shape_controller.h" #include "kis_tool_freehand.h" #include "KisUndoStackAction.h" #include "KisViewManager.h" #include "kis_zoom_manager.h" #include "kis_composite_progress_proxy.h" #include "kis_statusbar.h" #include "kis_painting_assistants_decoration.h" #include "kis_progress_widget.h" #include "kis_signal_compressor.h" #include "kis_filter_manager.h" #include "krita/gemini/ViewModeSwitchEvent.h" //static QString KisView::newObjectName() { static int s_viewIFNumber = 0; QString name; name.setNum(s_viewIFNumber++); name.prepend("view_"); return name; } bool KisView::s_firstView = true; class KisView::Private { public: Private() : undo(0) , redo(0) , viewConverter(0) , canvasController(0) , canvas(0) , zoomManager(0) , viewManager(0) , actionCollection(0) , paintingAssistantsDecoration(0) , isCurrent(false) , showFloatingMessage(true) , floatingMessageCompressor(100, KisSignalCompressor::POSTPONE) { tempActiveWidget = 0; documentDeleted = false; } ~Private() { if (canvasController) { KoToolManager::instance()->removeCanvasController(canvasController); } delete zoomManager; delete canvasController; delete canvas; delete viewConverter; } KisUndoStackAction *undo; KisUndoStackAction *redo; class StatusBarItem; QList statusBarItems; // Our statusbar items bool inOperation; //in the middle of an operation (no screen refreshing)? QPointer document; // our KisDocument QWidget *tempActiveWidget; bool documentDeleted; // true when document gets deleted [can't use document==0 // since this only happens in ~QObject, and views // get deleted by ~KisDocument]. KisCoordinatesConverter *viewConverter; KisCanvasController *canvasController; KisCanvas2 *canvas; KisZoomManager *zoomManager; KisViewManager *viewManager; KisNodeSP currentNode; KActionCollection* actionCollection; KisPaintingAssistantsDecoration *paintingAssistantsDecoration; bool isCurrent; bool showFloatingMessage; QPointer savedFloatingMessage; KisSignalCompressor floatingMessageCompressor; // Hmm sorry for polluting the private class with such a big inner class. // At the beginning it was a little struct :) class StatusBarItem { public: StatusBarItem() // for QValueList : m_widget(0), m_connected(false), m_hidden(false) {} StatusBarItem(QWidget * widget, int stretch, bool permanent) : m_widget(widget), m_stretch(stretch), m_permanent(permanent), m_connected(false), m_hidden(false) {} bool operator==(const StatusBarItem& rhs) { return m_widget == rhs.m_widget; } bool operator!=(const StatusBarItem& rhs) { return m_widget != rhs.m_widget; } QWidget * widget() const { return m_widget; } void ensureItemShown(KStatusBar * sb) { Q_ASSERT(m_widget); if (!m_connected) { if (m_permanent) sb->addPermanentWidget(m_widget, m_stretch); else sb->addWidget(m_widget, m_stretch); if(!m_hidden) m_widget->show(); m_connected = true; } } void ensureItemHidden(KStatusBar * sb) { if (m_connected) { m_hidden = m_widget->isHidden(); sb->removeWidget(m_widget); m_widget->hide(); m_connected = false; } } private: QWidget * m_widget; int m_stretch; bool m_permanent; bool m_connected; bool m_hidden; }; }; #if defined HAVE_OPENGL && defined Q_OS_WIN #include #endif KisView::KisView(KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection, QWidget *parent) : QWidget(parent) , d(new Private) { Q_ASSERT(document); connect(document, SIGNAL(titleModified(QString,bool)), this, SIGNAL(titleModified(QString,bool))); setObjectName(newObjectName()); d->document = document; d->actionCollection = actionCollection; setFocusPolicy(Qt::StrongFocus); d->undo = new KisUndoStackAction(d->document->undoStack(), KisUndoStackAction::UNDO); d->redo = new KisUndoStackAction(d->document->undoStack(), KisUndoStackAction::RED0); KStatusBar * sb = statusBar(); if (sb) { // No statusbar in e.g. konqueror connect(d->document, SIGNAL(statusBarMessage(const QString&)), this, SLOT(slotActionStatusText(const QString&))); connect(d->document, SIGNAL(clearStatusBarMessage()), this, SLOT(slotClearStatusText())); } d->viewConverter = new KisCoordinatesConverter(); KisConfig cfg; d->canvasController = new KisCanvasController(this, d->actionCollection); d->canvasController->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->canvasController->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->canvasController->setDrawShadow(false); d->canvasController->setCanvasMode(KoCanvasController::Infinite); d->canvasController->setVastScrolling(cfg.vastScrolling()); KConfigGroup grp(KGlobal::config(), "krita/crashprevention"); if (grp.readEntry("CreatingCanvas", false)) { cfg.setUseOpenGL(false); } if (cfg.canvasState() == "OPENGL_FAILED") { cfg.setUseOpenGL(false); } grp.writeEntry("CreatingCanvas", true); grp.sync(); d->canvas = new KisCanvas2(d->viewConverter, resourceManager, this, document->shapeController()); /** * Warn about Intel's broken video drivers */ #if defined HAVE_OPENGL && defined Q_OS_WIN QString renderer((const char*)glGetString(GL_RENDERER)); if (cfg.useOpenGL() && renderer.startsWith("Intel") && !cfg.readEntry("WarnedAboutIntel", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You have an Intel(R) HD Graphics video adapter.\n" "If you experience problems like a black or blank screen," "please update your display driver to the latest version.\n\n" "You can also disable OpenGL rendering in Krita's Settings.\n")); cfg.writeEntry("WarnedAboutIntel", true); } #endif /* defined HAVE_OPENGL && defined Q_OS_WIN32 */ grp.writeEntry("CreatingCanvas", false); grp.sync(); d->canvasController->setCanvas(d->canvas); Q_ASSERT(d->canvasController); d->zoomManager = new KisZoomManager(this, d->viewConverter, d->canvasController); d->zoomManager->setup(d->actionCollection); connect(d->canvasController, SIGNAL(documentSizeChanged()), d->zoomManager, SLOT(slotScrollAreaSizeChanged())); setAcceptDrops(true); connect(d->document, SIGNAL(sigLoadingFinished()), this, SLOT(slotLoadingFinished())); connect(d->document, SIGNAL(sigSavingFinished()), this, SLOT(slotSavingFinished())); d->paintingAssistantsDecoration = new KisPaintingAssistantsDecoration(this); d->canvas->addDecoration(d->paintingAssistantsDecoration); d->showFloatingMessage = cfg.showCanvasMessages(); } KisView::~KisView() { if (d->viewManager->filterManager()->isStrokeRunning()) { d->viewManager->filterManager()->cancel(); } KisPart::instance()->removeView(this); delete d; } void KisView::notifyCurrentStateChanged(bool isCurrent) { d->isCurrent = isCurrent; if (!d->isCurrent && d->savedFloatingMessage) { d->savedFloatingMessage->removeMessage(); } } void KisView::setShowFloatingMessage(bool show) { d->showFloatingMessage = show; } void KisView::showFloatingMessageImpl(const QString message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment) { if (!d->viewManager) return; if(d->isCurrent && d->showFloatingMessage && d->viewManager->qtMainWindow()) { if (d->savedFloatingMessage) { d->savedFloatingMessage->tryOverrideMessage(message, icon, timeout, priority, alignment); } else { d->savedFloatingMessage = new KisFloatingMessage(message, this->canvasBase()->canvasWidget(), false, timeout, priority, alignment); d->savedFloatingMessage->setShowOverParent(true); d->savedFloatingMessage->setIcon(icon); connect(&d->floatingMessageCompressor, SIGNAL(timeout()), d->savedFloatingMessage, SLOT(showMessage())); d->floatingMessageCompressor.start(); } } } void KisView::setViewManager(KisViewManager *view) { d->viewManager = view; connect(canvasController(), SIGNAL(toolOptionWidgetsChanged(QList >)), d->viewManager->mainWindow(), SLOT(newOptionWidgets(QList >))); KoToolManager::instance()->addController(d->canvasController); KoToolManager::instance()->registerTools(d->actionCollection, d->canvasController); dynamic_cast(d->document->shapeController())->setInitialShapeForCanvas(d->canvas); if (resourceProvider()) { resourceProvider()->slotImageSizeChanged(); } if (d->viewManager && d->viewManager->nodeManager()) { d->viewManager->nodeManager()->nodesUpdated(); } connect(image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), this, SLOT(slotImageSizeChanged(const QPointF&, const QPointF&))); connect(image(), SIGNAL(sigResolutionChanged(double,double)), this, SLOT(slotImageResolutionChanged())); /* * WARNING: Currently we access the global progress bar in two ways: * connecting to composite progress proxy (strokes) and creating * progress updaters. The latter way should be deprecated in favour * of displaying the status of the global strokes queue */ image()->compositeProgressProxy()->addProxy(d->viewManager->statusBar()->progress()->progressProxy()); connect(d->viewManager->statusBar()->progress(), SIGNAL(sigCancellationRequested()), image(), SLOT(requestStrokeCancellation())); d->viewManager->updateGUI(); KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush"); } KisViewManager* KisView::viewManager() const { return d->viewManager; } KAction *KisView::undoAction() const { return d->undo; } KAction *KisView::redoAction() const { return d->redo; } KoZoomController *KisView::zoomController() const { return d->zoomManager->zoomController(); } KisZoomManager *KisView::zoomManager() const { return d->zoomManager; } KisCanvasController *KisView::canvasController() const { return d->canvasController; } KisCanvasResourceProvider *KisView::resourceProvider() const { if (d->viewManager) { return d->viewManager->resourceProvider(); } return 0; } KisInputManager* KisView::globalInputManager() const { return d->viewManager ? d->viewManager->inputManager() : 0; } KisCanvas2 *KisView::canvasBase() const { return d->canvas; } KisImageWSP KisView::image() const { if (d->document) { return d->document->image(); } return 0; } KisCoordinatesConverter *KisView::viewConverter() const { return d->viewConverter; } void KisView::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasImage() || event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/x-krita-node")) { event->accept(); // activate view if it should accept the drop this->setFocus(); } else { event->ignore(); } } void KisView::dropEvent(QDropEvent *event) { - KisImageSP kisimage = image(); + KisImageWSP kisimage = image(); Q_ASSERT(kisimage); QPoint cursorPos = canvasBase()->coordinatesConverter()->widgetToImage(event->pos()).toPoint(); QRect imageBounds = kisimage->bounds(); QPoint pasteCenter; bool forceRecenter; if (event->keyboardModifiers() & Qt::ShiftModifier && imageBounds.contains(cursorPos)) { pasteCenter = cursorPos; forceRecenter = true; } else { pasteCenter = imageBounds.center(); forceRecenter = false; } if (event->mimeData()->hasFormat("application/x-krita-node") || event->mimeData()->hasImage()) { KisShapeController *kritaShapeController = dynamic_cast(d->document->shapeController()); QList nodes = KisMimeData::loadNodes(event->mimeData(), imageBounds, pasteCenter, forceRecenter, kisimage, kritaShapeController); foreach(KisNodeSP node, nodes) { if (node) { KisNodeCommandsAdapter adapter(viewManager()); if (!viewManager()->nodeManager()->activeLayer()) { adapter.addNode(node, kisimage->rootLayer() , 0); } else { adapter.addNode(node, viewManager()->nodeManager()->activeLayer()->parent(), viewManager()->nodeManager()->activeLayer()); } } } } else if (event->mimeData()->hasUrls()) { QList urls = event->mimeData()->urls(); if (urls.length() > 0) { KMenu popup; popup.setObjectName("drop_popup"); QAction *insertAsNewLayer = new KAction(i18n("Insert as New Layer"), &popup); QAction *insertManyLayers = new KAction(i18n("Insert Many Layers"), &popup); QAction *openInNewDocument = new KAction(i18n("Open in New Document"), &popup); QAction *openManyDocuments = new KAction(i18n("Open Many Documents"), &popup); QAction *cancel = new KAction(i18n("Cancel"), &popup); popup.addAction(insertAsNewLayer); popup.addAction(openInNewDocument); popup.addAction(insertManyLayers); popup.addAction(openManyDocuments); insertAsNewLayer->setEnabled(image() && urls.count() == 1); openInNewDocument->setEnabled(urls.count() == 1); insertManyLayers->setEnabled(image() && urls.count() > 1); openManyDocuments->setEnabled(urls.count() > 1); popup.addSeparator(); popup.addAction(cancel); QAction *action = popup.exec(QCursor::pos()); if (action != 0 && action != cancel) { foreach(const QUrl &url, urls) { if (action == insertAsNewLayer || action == insertManyLayers) { d->viewManager->imageManager()->importImage(KUrl(url)); activateWindow(); } else { Q_ASSERT(action == openInNewDocument || action == openManyDocuments); if (mainWindow()) { mainWindow()->openDocument(url); } } } } } } } KisDocument *KisView::document() const { return d->document; } void KisView::setDocument(KisDocument *document) { d->document->disconnect(this); d->document = document; KStatusBar *sb = statusBar(); if (sb) { // No statusbar in e.g. konqueror connect(d->document, SIGNAL(statusBarMessage(const QString&)), this, SLOT(slotActionStatusText(const QString&))); connect(d->document, SIGNAL(clearStatusBarMessage()), this, SLOT(slotClearStatusText())); } } void KisView::setDocumentDeleted() { d->documentDeleted = true; } void KisView::addStatusBarItem(QWidget * widget, int stretch, bool permanent) { Private::StatusBarItem item(widget, stretch, permanent); KStatusBar * sb = statusBar(); if (sb) { item.ensureItemShown(sb); } d->statusBarItems.append(item); } void KisView::removeStatusBarItem(QWidget *widget) { KStatusBar *sb = statusBar(); int itemCount = d->statusBarItems.count(); for (int i = itemCount-1; i >= 0; --i) { Private::StatusBarItem &sbItem = d->statusBarItems[i]; if (sbItem.widget() == widget) { if (sb) { sbItem.ensureItemHidden(sb); } d->statusBarItems.removeOne(sbItem); break; } } } KoPageLayout KisView::pageLayout() const { return document()->pageLayout(); } QPrintDialog *KisView::createPrintDialog(KisPrintJob *printJob, QWidget *parent) { QPrintDialog *printDialog = KdePrint::createPrintDialog(&printJob->printer(), printJob->createOptionWidgets(), parent); printDialog->setMinMax(printJob->printer().fromPage(), printJob->printer().toPage()); printDialog->setEnabledOptions(printJob->printDialogOptions()); return printDialog; } KisMainWindow * KisView::mainWindow() const { return dynamic_cast(window()); } KStatusBar * KisView::statusBar() const { KisMainWindow *mw = mainWindow(); return mw ? mw->statusBar() : 0; } void KisView::slotActionStatusText(const QString &text) { KStatusBar *sb = statusBar(); if (sb) sb->showMessage(text); } void KisView::slotClearStatusText() { KStatusBar *sb = statusBar(); if (sb) sb->clearMessage(); } QList KisView::createChangeUnitActions(bool addPixelUnit) { UnitActionGroup* unitActions = new UnitActionGroup(d->document, addPixelUnit, this); return unitActions->actions(); } bool KisView::event(QEvent *event) { switch(static_cast(event->type())) { case ViewModeSwitchEvent::AboutToSwitchViewModeEvent: { ViewModeSynchronisationObject* syncObject = static_cast(event)->synchronisationObject(); d->canvasController->setFocus(); qApp->processEvents(); KisCanvasResourceProvider* provider = resourceProvider(); syncObject->backgroundColor = provider->bgColor(); syncObject->foregroundColor = provider->fgColor(); syncObject->exposure = provider->HDRExposure(); syncObject->gamma = provider->HDRGamma(); syncObject->compositeOp = provider->currentCompositeOp(); syncObject->pattern = provider->currentPattern(); syncObject->gradient = provider->currentGradient(); syncObject->node = provider->currentNode(); syncObject->paintOp = provider->currentPreset(); syncObject->opacity = provider->opacity(); syncObject->globalAlphaLock = provider->globalAlphaLock(); syncObject->documentOffset = d->canvasController->scrollBarValue() - pos(); syncObject->zoomLevel = zoomController()->zoomAction()->effectiveZoom(); syncObject->rotationAngle = canvasBase()->rotationAngle(); syncObject->activeToolId = KoToolManager::instance()->activeToolId(); syncObject->gridData = &document()->gridData(); syncObject->mirrorHorizontal = provider->mirrorHorizontal(); syncObject->mirrorVertical = provider->mirrorVertical(); syncObject->mirrorAxesCenter = provider->resourceManager()->resource(KisCanvasResourceProvider::MirrorAxesCenter).toPointF(); KisToolFreehand* tool = qobject_cast(KoToolManager::instance()->toolById(canvasBase(), syncObject->activeToolId)); if(tool) { syncObject->smoothingOptions = tool->smoothingOptions(); } syncObject->initialized = true; QMainWindow* mainWindow = qobject_cast(qApp->activeWindow()); if(mainWindow) { QList dockWidgets = mainWindow->findChildren(); foreach(QDockWidget* widget, dockWidgets) { if (widget->isFloating()) { widget->hide(); } } } return true; } case ViewModeSwitchEvent::SwitchedToDesktopModeEvent: { ViewModeSynchronisationObject* syncObject = static_cast(event)->synchronisationObject(); d->canvasController->setFocus(); qApp->processEvents(); if(syncObject->initialized) { KisCanvasResourceProvider* provider = resourceProvider(); provider->resourceManager()->setResource(KisCanvasResourceProvider::MirrorAxesCenter, syncObject->mirrorAxesCenter); if (provider->mirrorHorizontal() != syncObject->mirrorHorizontal) { QAction* mirrorAction = d->actionCollection->action("hmirror_action"); mirrorAction->setChecked(syncObject->mirrorHorizontal); provider->setMirrorHorizontal(syncObject->mirrorHorizontal); } if (provider->mirrorVertical() != syncObject->mirrorVertical) { QAction* mirrorAction = d->actionCollection->action("vmirror_action"); mirrorAction->setChecked(syncObject->mirrorVertical); provider->setMirrorVertical(syncObject->mirrorVertical); } provider->setPaintOpPreset(syncObject->paintOp); qApp->processEvents(); KoToolManager::instance()->switchToolRequested(syncObject->activeToolId); qApp->processEvents(); KisPaintOpPresetSP preset = canvasBase()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); preset->settings()->setProperty("CompositeOp", syncObject->compositeOp); if(preset->settings()->hasProperty("OpacityValue")) preset->settings()->setProperty("OpacityValue", syncObject->opacity); provider->setPaintOpPreset(preset); provider->setBGColor(syncObject->backgroundColor); provider->setFGColor(syncObject->foregroundColor); provider->setHDRExposure(syncObject->exposure); provider->setHDRGamma(syncObject->gamma); provider->slotPatternActivated(syncObject->pattern); provider->slotGradientActivated(syncObject->gradient); provider->slotNodeActivated(syncObject->node); provider->setOpacity(syncObject->opacity); provider->setGlobalAlphaLock(syncObject->globalAlphaLock); provider->setCurrentCompositeOp(syncObject->compositeOp); document()->gridData().setGrid(syncObject->gridData->gridX(), syncObject->gridData->gridY()); document()->gridData().setGridColor(syncObject->gridData->gridColor()); document()->gridData().setPaintGridInBackground(syncObject->gridData->paintGridInBackground()); document()->gridData().setShowGrid(syncObject->gridData->showGrid()); document()->gridData().setSnapToGrid(syncObject->gridData->snapToGrid()); d->actionCollection->action("zoom_in")->trigger(); qApp->processEvents(); QMainWindow* mainWindow = qobject_cast(qApp->activeWindow()); if(mainWindow) { QList dockWidgets = mainWindow->findChildren(); foreach(QDockWidget* widget, dockWidgets) { if (widget->isFloating()) { widget->show(); } } } zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, syncObject->zoomLevel); d->canvasController->rotateCanvas(syncObject->rotationAngle - canvasBase()->rotationAngle()); QPoint newOffset = syncObject->documentOffset + pos(); qApp->processEvents(); d->canvasController->setScrollBarValue(newOffset); KisToolFreehand* tool = qobject_cast(KoToolManager::instance()->toolById(canvasBase(), syncObject->activeToolId)); if(tool && syncObject->smoothingOptions) { tool->smoothingOptions()->setSmoothingType(syncObject->smoothingOptions->smoothingType()); tool->smoothingOptions()->setSmoothPressure(syncObject->smoothingOptions->smoothPressure()); tool->smoothingOptions()->setTailAggressiveness(syncObject->smoothingOptions->tailAggressiveness()); tool->smoothingOptions()->setUseScalableDistance(syncObject->smoothingOptions->useScalableDistance()); tool->smoothingOptions()->setSmoothnessDistance(syncObject->smoothingOptions->smoothnessDistance()); tool->smoothingOptions()->setUseDelayDistance(syncObject->smoothingOptions->useDelayDistance()); tool->smoothingOptions()->setDelayDistance(syncObject->smoothingOptions->delayDistance()); tool->smoothingOptions()->setFinishStabilizedCurve(syncObject->smoothingOptions->finishStabilizedCurve()); tool->smoothingOptions()->setStabilizeSensors(syncObject->smoothingOptions->stabilizeSensors()); tool->updateSettingsViews(); } } return true; } default: break; } return QWidget::event( event ); } void KisView::closeEvent(QCloseEvent *event) { // Check whether we're the last view int viewCount = KisPart::instance()->viewCount(document()); if (viewCount > 1) { // there are others still, so don't bother the user event->accept(); return; } if (queryClose()) { d->viewManager->removeStatusBarItem(zoomManager()->zoomActionWidget()); event->accept(); return; } event->ignore(); } bool KisView::queryClose() { if (!document()) return true; if (document()->isModified()) { QString name; if (document()->documentInfo()) { name = document()->documentInfo()->aboutInfo("title"); } if (name.isEmpty()) name = document()->url().fileName(); if (name.isEmpty()) name = i18n("Untitled"); int res = QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("

The document '%1' has been modified.

Do you want to save it?

", name), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); switch (res) { case QMessageBox::Yes : { bool isNative = (document()->outputMimeType() == document()->nativeFormatMimeType()); if (!viewManager()->mainWindow()->saveDocument(document(), !isNative)) return false; break; } case QMessageBox::No : document()->removeAutoSaveFiles(); document()->setModified(false); // Now when queryClose() is called by closeEvent it won't do anything. break; default : // case QMessageBox::Cancel : return false; } } return true; } void KisView::resetImageSizeAndScroll(bool changeCentering, const QPointF oldImageStillPoint, const QPointF newImageStillPoint) { const KisCoordinatesConverter *converter = d->canvas->coordinatesConverter(); QPointF oldPreferredCenter = d->canvasController->preferredCenter(); /** * Calculating the still point in old coordinates depending on the * parameters given */ QPointF oldStillPoint; if (changeCentering) { oldStillPoint = converter->imageToWidget(oldImageStillPoint) + converter->documentOffset(); } else { QSize oldDocumentSize = d->canvasController->documentSize(); oldStillPoint = QPointF(0.5 * oldDocumentSize.width(), 0.5 * oldDocumentSize.height()); } /** * Updating the document size */ QSizeF size(image()->width() / image()->xRes(), image()->height() / image()->yRes()); KoZoomController *zc = d->zoomManager->zoomController(); zc->setZoom(KoZoomMode::ZOOM_CONSTANT, zc->zoomAction()->effectiveZoom()); zc->setPageSize(size); zc->setDocumentSize(size, true); /** * Calculating the still point in new coordinates depending on the * parameters given */ QPointF newStillPoint; if (changeCentering) { newStillPoint = converter->imageToWidget(newImageStillPoint) + converter->documentOffset(); } else { QSize newDocumentSize = d->canvasController->documentSize(); newStillPoint = QPointF(0.5 * newDocumentSize.width(), 0.5 * newDocumentSize.height()); } d->canvasController->setPreferredCenter(oldPreferredCenter - oldStillPoint + newStillPoint); } void KisView::setCurrentNode(KisNodeSP node) { d->currentNode = node; } KisNodeSP KisView::currentNode() const { return d->currentNode; } KisLayerSP KisView::currentLayer() const { KisNodeSP node; KisMaskSP mask = currentMask(); if (mask) { node = mask->parent(); } else { node = d->currentNode; } return dynamic_cast(node.data()); } KisMaskSP KisView::currentMask() const { return dynamic_cast(d->currentNode.data()); } KisSelectionSP KisView::selection() { KisLayerSP layer = currentLayer(); if (layer) return layer->selection(); // falls through to the global // selection, or 0 in the end if (image()) { return image()->globalSelection(); } return 0; } void KisView::slotLoadingFinished() { if (!document()) return; /** * Cold-start of image size/resolution signals */ slotImageResolutionChanged(); if (image()->locked()) { // If this is the first view on the image, the image will have been locked // so unlock it. image()->blockSignals(false); image()->unlock(); } if (d->paintingAssistantsDecoration){ foreach(KisPaintingAssistant* assist, document()->preLoadedAssistants()){ d->paintingAssistantsDecoration->addAssistant(assist); } d->paintingAssistantsDecoration->setVisible(true); } canvasBase()->initializeImage(); /** * Dirty hack alert */ d->zoomManager->zoomController()->setAspectMode(true); if (viewConverter()) { viewConverter()->setZoomMode(KoZoomMode::ZOOM_PAGE); } connect(image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), this, SIGNAL(sigColorSpaceChanged(const KoColorSpace*))); connect(image(), SIGNAL(sigProfileChanged(const KoColorProfile*)), this, SIGNAL(sigProfileChanged(const KoColorProfile*))); connect(image(), SIGNAL(sigSizeChanged(QPointF,QPointF)), this, SIGNAL(sigSizeChanged(QPointF,QPointF))); KisNodeSP activeNode = document()->preActivatedNode(); document()->setPreActivatedNode(0); // to make sure that we don't keep a reference to a layer the user can later delete. if (!activeNode) { activeNode = image()->rootLayer()->lastChild(); } while (activeNode && !activeNode->inherits("KisLayer")) { activeNode = activeNode->prevSibling(); } setCurrentNode(activeNode); } void KisView::slotSavingFinished() { if (d->viewManager && d->viewManager->mainWindow()) { d->viewManager->mainWindow()->updateCaption(); } } KisPrintJob * KisView::createPrintJob() { return new KisPrintJob(image()); } void KisView::slotImageResolutionChanged() { resetImageSizeAndScroll(false); zoomManager()->updateGUI(); // update KoUnit value for the document if (resourceProvider()) { resourceProvider()->resourceManager()-> setResource(KoCanvasResourceManager::Unit, d->canvas->unit()); } } void KisView::slotImageSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint) { resetImageSizeAndScroll(true, oldStillPoint, newStillPoint); zoomManager()->updateGUI(); } #include #include