File indexing completed on 2024-06-09 04:20:48

0001 /* This file is part of the KDE project
0002  *
0003  * SPDX-FileCopyrightText: 2007 Jan Hambrecht <jaham@gmx.net>
0004  * SPDX-FileCopyrightText: 2009 Thomas Zander <zander@kde.org>
0005  *
0006  * SPDX-License-Identifier: LGPL-2.0-or-later
0007  */
0008 
0009 #include "KoShapePainter.h"
0010 
0011 #include "KoCanvasBase.h"
0012 #include "KoSelectedShapesProxySimple.h"
0013 #include "KoShapeManager.h"
0014 #include "KoShape.h"
0015 #include "KoShapeStrokeModel.h"
0016 #include "KoShapeGroup.h"
0017 #include "KoShapeContainer.h"
0018 #include <KoViewConverter.h>
0019 #include <KoUnit.h>
0020 
0021 #include <QPainter>
0022 #include <QImage>
0023 
0024 class SimpleCanvas : public KoCanvasBase
0025 {
0026 public:
0027     SimpleCanvas()
0028         : KoCanvasBase(0),
0029           m_shapeManager(new KoShapeManager(this)),
0030           m_selectedShapesProxy(new KoSelectedShapesProxySimple(m_shapeManager.data()))
0031     {
0032     }
0033 
0034     ~SimpleCanvas() override
0035     {
0036     }
0037 
0038     void gridSize(QPointF *offset, QSizeF *spacing) const override
0039     {
0040         *offset = QPointF();
0041         *spacing = QSizeF();
0042     };
0043 
0044     bool snapToGrid() const override
0045     {
0046         return false;
0047     }
0048 
0049     void addCommand(KUndo2Command *) override
0050     {
0051     }
0052 
0053     KoShapeManager *shapeManager() const override
0054     {
0055         return m_shapeManager.data();
0056     }
0057 
0058     KoSelectedShapesProxy *selectedShapesProxy() const override
0059     {
0060         return m_selectedShapesProxy.data();
0061     }
0062 
0063     void updateCanvas(const QRectF&) override
0064     {
0065     }
0066 
0067     KoToolProxy *toolProxy() const override
0068     {
0069         return 0;
0070     }
0071 
0072     const KoViewConverter *viewConverter() const override
0073     {
0074         return 0;
0075     }
0076 
0077     KoViewConverter *viewConverter() override
0078     {
0079         return 0;
0080     }
0081 
0082     QWidget *canvasWidget() override
0083     {
0084         return 0;
0085     }
0086 
0087     const QWidget *canvasWidget() const override
0088     {
0089         return 0;
0090     }
0091 
0092     KoUnit unit() const override
0093     {
0094         return KoUnit(KoUnit::Point);
0095     }
0096 
0097     void setCursor(const QCursor &) override {}
0098 
0099 private:
0100     QScopedPointer<KoShapeManager> m_shapeManager;
0101     QScopedPointer<KoSelectedShapesProxySimple> m_selectedShapesProxy;
0102 };
0103 
0104 class Q_DECL_HIDDEN KoShapePainter::Private
0105 {
0106 public:
0107     Private()
0108         : canvas(new SimpleCanvas())
0109     {
0110     }
0111 
0112     ~Private() { delete canvas; }
0113     SimpleCanvas * canvas;
0114 };
0115 
0116 KoShapePainter::KoShapePainter()
0117     : d(new Private())
0118 {
0119 }
0120 
0121 KoShapePainter::~KoShapePainter()
0122 {
0123     delete d;
0124 }
0125 
0126 void KoShapePainter::setShapes(const QList<KoShape*> &shapes)
0127 {
0128     d->canvas->shapeManager()->setShapes(shapes, KoShapeManager::AddWithoutRepaint);
0129 }
0130 
0131 void KoShapePainter::paint(QPainter &painter)
0132 {
0133     foreach (KoShape *shape, d->canvas->shapeManager()->shapes()) {
0134         shape->waitUntilReady(false);
0135     }
0136 
0137     d->canvas->shapeManager()->paint(painter);
0138 }
0139 
0140 void KoShapePainter::paint(QPainter &painter, const QRect &painterRect, const QRectF &documentRect)
0141 {
0142     if (documentRect.width() == 0.0f || documentRect.height() == 0.0f)
0143         return;
0144 
0145     KoViewConverter converter;
0146     // calculate the painter destination rectangle size in document coordinates
0147     QRectF paintBox = converter.viewToDocument(QRectF(QPointF(), painterRect.size()));
0148 
0149     // compute the zoom factor based on the bounding rects in document coordinates
0150     // so that the content fits into the image
0151     qreal zoomW = paintBox.width() / documentRect.width();
0152     qreal zoomH = paintBox.height() / documentRect.height();
0153     qreal zoom = qMin(zoomW, zoomH);
0154 
0155     // now set the zoom into the zoom handler used for painting the shape
0156     converter.setZoom(zoom);
0157 
0158     painter.save();
0159 
0160     // initialize painter
0161     painter.setPen(QPen(Qt::NoPen));
0162     painter.setBrush(Qt::NoBrush);
0163     painter.setRenderHint(QPainter::Antialiasing);
0164     painter.setClipRect(painterRect.adjusted(-1,-1,1,1));
0165 
0166     // convert document rectangle to view coordinates
0167     QRectF zoomedBound = converter.documentToView(documentRect);
0168     // calculate offset between painter rectangle and converted document rectangle
0169     QPointF offset = QRectF(painterRect).center() - zoomedBound.center();
0170     // center content in painter rectangle
0171     painter.translate(offset.x(), offset.y());
0172     painter.setTransform(converter.documentToView(), true);
0173 
0174     // finally paint the shapes
0175     paint(painter);
0176 
0177     painter.restore();
0178 }
0179 
0180 void KoShapePainter::paint(QImage &image)
0181 {
0182     if (image.isNull())
0183         return;
0184 
0185     QPainter painter(&image);
0186 
0187     paint(painter, image.rect(), contentRect());
0188 }
0189 
0190 QRectF KoShapePainter::contentRect() const
0191 {
0192     QRectF bound;
0193     foreach (KoShape *shape, d->canvas->shapeManager()->shapes()) {
0194         if (!shape->isVisible())
0195             continue;
0196         if (dynamic_cast<KoShapeGroup*>(shape))
0197             continue;
0198 
0199         QRectF shapeRect = shape->boundingRect();
0200 
0201         if (bound.isEmpty())
0202             bound = shapeRect;
0203         else
0204             bound = bound.united(shapeRect);
0205     }
0206     return bound;
0207 }