diff --git a/libs/ui/flake/kis_shape_selection.cpp b/libs/ui/flake/kis_shape_selection.cpp index f53c802529..d675959ca9 100644 --- a/libs/ui/flake/kis_shape_selection.cpp +++ b/libs/ui/flake/kis_shape_selection.cpp @@ -1,390 +1,400 @@ /* * Copyright (c) 2010 Sven Langkamp * Copyright (c) 2011 Jan Hambrecht * * 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_shape_selection.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 "kis_shape_selection_model.h" #include "kis_shape_selection_canvas.h" #include "kis_take_all_shapes_command.h" #include "kis_image_view_converter.h" #include "kis_shape_layer.h" #include KisShapeSelection::KisShapeSelection(KoShapeControllerBase *shapeControllerBase, KisImageWSP image, KisSelectionWSP selection) : KoShapeLayer(m_model = new KisShapeSelectionModel(image, selection, this)) , m_image(image) , m_shapeControllerBase(shapeControllerBase) { Q_ASSERT(m_image); setShapeId("KisShapeSelection"); setSelectable(false); m_converter = new KisImageViewConverter(image); m_canvas = new KisShapeSelectionCanvas(shapeControllerBase); m_canvas->shapeManager()->addShape(this); m_model->setObjectName("KisShapeSelectionModel"); m_model->moveToThread(image->thread()); m_canvas->setObjectName("KisShapeSelectionCanvas"); m_canvas->moveToThread(image->thread()); connect(this, SIGNAL(sigMoveShapes(QPointF)), SLOT(slotMoveShapes(QPointF))); } KisShapeSelection::~KisShapeSelection() { m_model->setShapeSelection(0); delete m_canvas; delete m_converter; } KisShapeSelection::KisShapeSelection(const KisShapeSelection& rhs, KisSelection* selection) : KoShapeLayer(m_model = new KisShapeSelectionModel(rhs.m_image, selection, this)) { m_image = rhs.m_image; m_shapeControllerBase = rhs.m_shapeControllerBase; m_converter = new KisImageViewConverter(m_image); m_canvas = new KisShapeSelectionCanvas(m_shapeControllerBase); m_canvas->shapeManager()->addShape(this); Q_FOREACH (KoShape *shape, rhs.shapes()) { KoShape *clonedShape = shape->cloneShape(); KIS_SAFE_ASSERT_RECOVER(clonedShape) { continue; } this->addShape(clonedShape); } } KisSelectionComponent* KisShapeSelection::clone(KisSelection* selection) { return new KisShapeSelection(*this, selection); } bool KisShapeSelection::saveSelection(KoStore * store) const { const QSizeF sizeInPx = m_image->bounds().size(); const QSizeF sizeInPt(sizeInPx.width() / m_image->xRes(), sizeInPx.height() / m_image->yRes()); return KisShapeLayer::saveShapesToStore(store, this->shapes(), sizeInPt); } bool KisShapeSelection::loadSelection(KoStore* store) { QSizeF fragmentSize; // unused! // FIXME: we handle xRes() only! KIS_SAFE_ASSERT_RECOVER_NOOP(qFuzzyCompare(m_image->xRes(), m_image->yRes())); const qreal resolutionPPI = 72.0 * m_image->xRes(); QList shapes; if (store->open("content.svg")) { KoStoreDevice storeDev(store); storeDev.open(QIODevice::ReadOnly); shapes = KisShapeLayer::createShapesFromSvg(&storeDev, "", m_image->bounds(), resolutionPPI, m_canvas->shapeController()->resourceManager(), &fragmentSize); store->close(); Q_FOREACH (KoShape *shape, shapes) { addShape(shape); } return true; } KoOdfReadStore odfStore(store); QString errorMessage; odfStore.loadAndParse(errorMessage); if (!errorMessage.isEmpty()) { dbgKrita << errorMessage; return false; } KoXmlElement contents = odfStore.contentDoc().documentElement(); // dbgKrita <<"Start loading OASIS document..." << contents.text(); // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().localName(); // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().namespaceURI(); // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().isElement(); KoXmlElement body(KoXml::namedItemNS(contents, KoXmlNS::office, "body")); if (body.isNull()) { dbgKrita << "No office:body found!"; //setErrorMessage( i18n( "Invalid OASIS document. No office:body tag found." ) ); return false; } body = KoXml::namedItemNS(body, KoXmlNS::office, "drawing"); if (body.isNull()) { dbgKrita << "No office:drawing found!"; //setErrorMessage( i18n( "Invalid OASIS document. No office:drawing tag found." ) ); return false; } KoXmlElement page(KoXml::namedItemNS(body, KoXmlNS::draw, "page")); if (page.isNull()) { dbgKrita << "No office:drawing found!"; //setErrorMessage( i18n( "Invalid OASIS document. No draw:page tag found." ) ); return false; } KoXmlElement * master = 0; if (odfStore.styles().masterPages().contains("Standard")) master = odfStore.styles().masterPages().value("Standard"); else if (odfStore.styles().masterPages().contains("Default")) master = odfStore.styles().masterPages().value("Default"); else if (! odfStore.styles().masterPages().empty()) master = odfStore.styles().masterPages().begin().value(); if (master) { const KoXmlElement *style = odfStore.styles().findStyle( master->attributeNS(KoXmlNS::style, "page-layout-name", QString())); KoPageLayout pageLayout; pageLayout.loadOdf(*style); setSize(QSizeF(pageLayout.width, pageLayout.height)); } else { dbgKrita << "No master page found!"; return false; } KoOdfLoadingContext context(odfStore.styles(), odfStore.store()); KoShapeLoadingContext shapeContext(context, 0); KoXmlElement layerElement; forEachElement(layerElement, context.stylesReader().layerSet()) { if (!loadOdf(layerElement, shapeContext)) { dbgKrita << "Could not load vector layer!"; return false; } } KoXmlElement child; forEachElement(child, page) { KoShape * shape = KoShapeRegistry::instance()->createShapeFromOdf(child, shapeContext); if (shape) { addShape(shape); } } return true; } void KisShapeSelection::setUpdatesEnabled(bool enabled) { m_model->setUpdatesEnabled(enabled); } bool KisShapeSelection::updatesEnabled() const { return m_model->updatesEnabled(); } KUndo2Command* KisShapeSelection::resetToEmpty() { return new KisTakeAllShapesCommand(this, true); } bool KisShapeSelection::isEmpty() const { return !m_model->count(); } QPainterPath KisShapeSelection::outlineCache() const { return m_outline; } bool KisShapeSelection::outlineCacheValid() const { return true; } void KisShapeSelection::recalculateOutlineCache() { QList shapesList = shapes(); QPainterPath outline; Q_FOREACH (KoShape * shape, shapesList) { QTransform shapeMatrix = shape->absoluteTransformation(0); outline = outline.united(shapeMatrix.map(shape->outline())); } QTransform resolutionMatrix; resolutionMatrix.scale(m_image->xRes(), m_image->yRes()); m_outline = resolutionMatrix.map(outline); } void KisShapeSelection::paintComponent(QPainter& painter, const KoViewConverter& converter, KoShapePaintingContext &) { Q_UNUSED(painter); Q_UNUSED(converter); } void KisShapeSelection::renderToProjection(KisPaintDeviceSP projection) { Q_ASSERT(projection); Q_ASSERT(m_image); QRectF boundingRect = outlineCache().boundingRect(); renderSelection(projection, boundingRect.toAlignedRect()); } void KisShapeSelection::renderToProjection(KisPaintDeviceSP projection, const QRect& r) { Q_ASSERT(projection); renderSelection(projection, r); } -void KisShapeSelection::renderSelection(KisPaintDeviceSP projection, const QRect& r) +void KisShapeSelection::renderSelection(KisPaintDeviceSP projection, const QRect& requestedRect) { KIS_SAFE_ASSERT_RECOVER_RETURN(projection); KIS_SAFE_ASSERT_RECOVER_RETURN(m_image); const qint32 MASK_IMAGE_WIDTH = 256; const qint32 MASK_IMAGE_HEIGHT = 256; + const QPainterPath selectionOutline = outlineCache(); + + if (*projection->defaultPixel().data() == OPACITY_TRANSPARENT_U8) { + projection->clear(requestedRect); + } else { + KoColor transparentColor(Qt::transparent, projection->colorSpace()); + projection->fill(requestedRect, transparentColor); + } + const QRect r = requestedRect & selectionOutline.boundingRect().toAlignedRect(); + QImage polygonMaskImage(MASK_IMAGE_WIDTH, MASK_IMAGE_HEIGHT, QImage::Format_ARGB32); QPainter maskPainter(&polygonMaskImage); maskPainter.setRenderHint(QPainter::Antialiasing, true); // Break the mask up into chunks so we don't have to allocate a potentially very large QImage. for (qint32 x = r.x(); x < r.x() + r.width(); x += MASK_IMAGE_WIDTH) { for (qint32 y = r.y(); y < r.y() + r.height(); y += MASK_IMAGE_HEIGHT) { maskPainter.fillRect(polygonMaskImage.rect(), Qt::black); maskPainter.translate(-x, -y); - maskPainter.fillPath(outlineCache(), Qt::white); + maskPainter.fillPath(selectionOutline, Qt::white); maskPainter.translate(x, y); qint32 rectWidth = qMin(r.x() + r.width() - x, MASK_IMAGE_WIDTH); qint32 rectHeight = qMin(r.y() + r.height() - y, MASK_IMAGE_HEIGHT); KisSequentialIterator it(projection, QRect(x, y, rectWidth, rectHeight)); while (it.nextPixel()) { (*it.rawData()) = qRed(polygonMaskImage.pixel(it.x() - x, it.y() - y)); } } } } KoShapeManager* KisShapeSelection::shapeManager() const { return m_canvas->shapeManager(); } KisShapeSelectionFactory::KisShapeSelectionFactory() : KoShapeFactoryBase("KisShapeSelection", "selection shape container") { setHidden(true); } void KisShapeSelection::moveX(qint32 x) { const QPointF diff(x / m_image->xRes(), 0); emit sigMoveShapes(diff); } void KisShapeSelection::moveY(qint32 y) { const QPointF diff(0, y / m_image->yRes()); emit sigMoveShapes(diff); } void KisShapeSelection::slotMoveShapes(const QPointF &diff) { Q_FOREACH (KoShape* shape, shapeManager()->shapes()) { if (shape != this) { QPointF pos = shape->position(); shape->setPosition(pos + diff); } } } // TODO same code as in vector layer, refactor! KUndo2Command* KisShapeSelection::transform(const QTransform &transform) { QList shapes = m_canvas->shapeManager()->shapes(); if(shapes.isEmpty()) return 0; QTransform realTransform = m_converter->documentToView() * transform * m_converter->viewToDocument(); QList oldTransformations; QList newTransformations; // this code won't work if there are shapes, that inherit the transformation from the parent container. // the chart and tree shapes are examples for that, but they aren't used in krita and there are no other shapes like that. Q_FOREACH (const KoShape* shape, shapes) { QTransform oldTransform = shape->transformation(); oldTransformations.append(oldTransform); if (dynamic_cast(shape)) { newTransformations.append(oldTransform); } else { QTransform globalTransform = shape->absoluteTransformation(0); QTransform localTransform = globalTransform * realTransform * globalTransform.inverted(); newTransformations.append(localTransform*oldTransform); } } return new KoShapeTransformCommand(shapes, oldTransformations, newTransformations); } diff --git a/libs/ui/flake/kis_shape_selection.h b/libs/ui/flake/kis_shape_selection.h index 6e4a696db8..3b2e8530e1 100644 --- a/libs/ui/flake/kis_shape_selection.h +++ b/libs/ui/flake/kis_shape_selection.h @@ -1,140 +1,140 @@ /* * Copyright (c) 2007 Sven Langkamp * * 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_SHAPE_SELECTION_H #define KIS_SHAPE_SELECTION_H #include #include #include #include #include #include #include class KoStore; class KoShapeManager; class KisShapeSelectionCanvas; class KisShapeSelectionModel; class KisImageViewConverter; class KUndo2Command; /** * The marker class. * It is added to the shape's user data to show this shape * is a part of a shape selection */ class KisShapeSelectionMarker : public KoShapeUserData { KoShapeUserData* clone() const override { return new KisShapeSelectionMarker(*this); } }; class KRITAUI_EXPORT KisShapeSelection : public QObject, public KoShapeLayer, public KisSelectionComponent { Q_OBJECT KisShapeSelection(const KisShapeSelection& rhs); public: KisShapeSelection(KoShapeControllerBase *shapeControllerBase, KisImageWSP image, KisSelectionWSP selection); ~KisShapeSelection() override; KisShapeSelection(const KisShapeSelection& rhs, KisSelection* selection); KisSelectionComponent* clone(KisSelection* selection) override; bool saveSelection(KoStore * store) const; bool loadSelection(KoStore * store); /** * Renders the shapes to a selection. This method should only be called * by KisSelection to update it's projection. * * @param projection the target selection */ void renderToProjection(KisPaintDeviceSP projection) override; void renderToProjection(KisPaintDeviceSP projection, const QRect& r) override; KUndo2Command* resetToEmpty() override; bool isEmpty() const override; QPainterPath outlineCache() const override; bool outlineCacheValid() const override; void recalculateOutlineCache() override; KoShapeManager *shapeManager() const; void moveX(qint32 x) override; void moveY(qint32 y) override; KUndo2Command* transform(const QTransform &transform) override; Q_SIGNALS: void sigMoveShapes(const QPointF &diff); private Q_SLOTS: void slotMoveShapes(const QPointF &diff); protected: void paintComponent(QPainter& painter, const KoViewConverter& converter, KoShapePaintingContext &paintcontext) override; private: friend class KisTakeAllShapesCommand; void setUpdatesEnabled(bool enabled); bool updatesEnabled() const; private: - void renderSelection(KisPaintDeviceSP projection, const QRect& r); + void renderSelection(KisPaintDeviceSP projection, const QRect& requestedRect); KisImageWSP m_image; QPainterPath m_outline; KisImageViewConverter *m_converter; KisShapeSelectionCanvas *m_canvas; KisShapeSelectionModel *m_model; KoShapeControllerBase *m_shapeControllerBase; friend class KisShapeSelectionModel; }; class KRITAUI_EXPORT KisShapeSelectionFactory : public KoShapeFactoryBase { public: KisShapeSelectionFactory(); ~KisShapeSelectionFactory() override {} KoShape *createDefaultShape(KoDocumentResourceManager *documentResources = 0) const override { Q_UNUSED(documentResources); return 0; } bool supports(const KoXmlElement & e, KoShapeLoadingContext &context) const override { Q_UNUSED(e); Q_UNUSED(context); return false; } }; #endif