File indexing completed on 2024-05-12 15:56:39

0001 /*
0002  *  SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KoClipMask.h"
0008 
0009 #include <QRectF>
0010 #include <QTransform>
0011 #include <QPainter>
0012 #include <QSharedData>
0013 #include <QPainterPath>
0014 #include <KoShape.h>
0015 #include "kis_algebra_2d.h"
0016 
0017 #include <KoShapePainter.h>
0018 
0019 struct Q_DECL_HIDDEN KoClipMask::Private : public QSharedData
0020 {
0021     Private()
0022         : QSharedData()
0023     {}
0024 
0025     Private(const Private &rhs)
0026         : QSharedData()
0027         , coordinates(rhs.coordinates)
0028         , contentCoordinates(rhs.contentCoordinates)
0029         , maskRect(rhs.maskRect)
0030         , extraShapeTransform(rhs.extraShapeTransform)
0031     {
0032         Q_FOREACH (KoShape *shape, rhs.shapes) {
0033             KoShape *clonedShape = shape->cloneShape();
0034             KIS_ASSERT_RECOVER(clonedShape) { continue; }
0035 
0036             shapes << clonedShape;
0037         }
0038     }
0039 
0040     ~Private() {
0041         qDeleteAll(shapes);
0042         shapes.clear();
0043     }
0044 
0045 
0046     KoFlake::CoordinateSystem coordinates = KoFlake::ObjectBoundingBox;
0047     KoFlake::CoordinateSystem contentCoordinates = KoFlake::UserSpaceOnUse;
0048 
0049     QRectF maskRect = QRectF(-0.1, -0.1, 1.2, 1.2);
0050 
0051     QList<KoShape*> shapes;
0052     QTransform extraShapeTransform; // TODO: not used anymore, use direct shape transform instead
0053 
0054 };
0055 
0056 KoClipMask::KoClipMask()
0057     : m_d(new Private)
0058 {
0059 }
0060 
0061 KoClipMask::~KoClipMask()
0062 {
0063 }
0064 
0065 KoClipMask::KoClipMask(const KoClipMask &rhs)
0066     : m_d(new Private(*rhs.m_d))
0067 {
0068 }
0069 
0070 KoClipMask &KoClipMask::operator=(const KoClipMask &rhs)
0071 {
0072     m_d = rhs.m_d;
0073     return *this;
0074 }
0075 
0076 KoClipMask *KoClipMask::clone() const
0077 {
0078     return new KoClipMask(*this);
0079 }
0080 
0081 KoFlake::CoordinateSystem KoClipMask::coordinates() const
0082 {
0083     return m_d->coordinates;
0084 }
0085 
0086 void KoClipMask::setCoordinates(KoFlake::CoordinateSystem value)
0087 {
0088     m_d->coordinates = value;
0089 }
0090 
0091 KoFlake::CoordinateSystem KoClipMask::contentCoordinates() const
0092 {
0093     return m_d->contentCoordinates;
0094 }
0095 
0096 void KoClipMask::setContentCoordinates(KoFlake::CoordinateSystem value)
0097 {
0098     m_d->contentCoordinates = value;
0099 }
0100 
0101 QRectF KoClipMask::maskRect() const
0102 {
0103     return m_d->maskRect;
0104 }
0105 
0106 void KoClipMask::setMaskRect(const QRectF &value)
0107 {
0108     m_d->maskRect = value;
0109 }
0110 
0111 QList<KoShape *> KoClipMask::shapes() const
0112 {
0113     return m_d->shapes;
0114 }
0115 
0116 void KoClipMask::setShapes(const QList<KoShape *> &value)
0117 {
0118     m_d->shapes = value;
0119 }
0120 
0121 bool KoClipMask::isEmpty() const
0122 {
0123     return m_d->shapes.isEmpty();
0124 }
0125 
0126 void KoClipMask::setExtraShapeOffset(const QPointF &value)
0127 {
0128     /**
0129      * TODO: when we implement source shapes sharing, please wrap the shapes
0130      *       into a group and apply this transform to the group instead
0131      */
0132 
0133     if (m_d->contentCoordinates == KoFlake::UserSpaceOnUse) {
0134         const QTransform t = QTransform::fromTranslate(value.x(), value.y());
0135 
0136         Q_FOREACH (KoShape *shape, m_d->shapes) {
0137             shape->applyAbsoluteTransformation(t);
0138         }
0139     }
0140 
0141     if (m_d->coordinates == KoFlake::UserSpaceOnUse) {
0142         m_d->maskRect.translate(value);
0143     }
0144 }
0145 
0146 void KoClipMask::drawMask(QPainter *painter, KoShape *shape)
0147 {
0148     painter->save();
0149 
0150     QPainterPath clipPathInShapeSpace;
0151 
0152     if (m_d->coordinates == KoFlake::ObjectBoundingBox) {
0153         QTransform relativeToShape = KisAlgebra2D::mapToRect(shape->outlineRect());
0154         clipPathInShapeSpace.addPolygon(relativeToShape.map(m_d->maskRect));
0155     } else {
0156         clipPathInShapeSpace.addRect(m_d->maskRect);
0157         clipPathInShapeSpace = m_d->extraShapeTransform.map(clipPathInShapeSpace);
0158     }
0159 
0160     painter->setClipPath(clipPathInShapeSpace, Qt::IntersectClip);
0161 
0162     if (m_d->contentCoordinates == KoFlake::ObjectBoundingBox) {
0163         QTransform relativeToShape = KisAlgebra2D::mapToRect(shape->outlineRect());
0164 
0165         painter->setTransform(relativeToShape, true);
0166     } else {
0167         painter->setTransform(m_d->extraShapeTransform, true);
0168     }
0169 
0170     KoShapePainter p;
0171     p.setShapes(m_d->shapes);
0172     p.paint(*painter);
0173 
0174     painter->restore();
0175 }