File indexing completed on 2024-06-23 04:28:10

0001 /*
0002  *  SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KoShapeGradientHandles.h"
0008 
0009 #include <QGradient>
0010 #include <KoShape.h>
0011 #include <KoGradientBackground.h>
0012 #include <KoShapeBackgroundCommand.h>
0013 #include <KoShapeFillWrapper.h>
0014 #include <kis_assert.h>
0015 #include "kis_algebra_2d.h"
0016 
0017 KoShapeGradientHandles::KoShapeGradientHandles(KoFlake::FillVariant fillVariant, KoShape *shape)
0018     : m_fillVariant(fillVariant),
0019       m_shape(shape)
0020 {
0021 }
0022 
0023 QVector<KoShapeGradientHandles::Handle> KoShapeGradientHandles::handles() const {
0024     QVector<Handle> result;
0025 
0026     const QGradient *g = gradient();
0027     if (!g) return result;
0028 
0029     switch (g->type()) {
0030     case QGradient::LinearGradient: {
0031         const QLinearGradient *lgradient = static_cast<const QLinearGradient*>(g);
0032         result << Handle(Handle::LinearStart, lgradient->start());
0033         result << Handle(Handle::LinearEnd, lgradient->finalStop());
0034         break;
0035     }
0036     case QGradient::RadialGradient: {
0037         const QRadialGradient *rgradient = static_cast<const QRadialGradient*>(g);
0038 
0039         result << Handle(Handle::RadialCenter, rgradient->center());
0040 
0041         if (rgradient->center() != rgradient->focalPoint()) {
0042             result << Handle(Handle::RadialFocalPoint, rgradient->focalPoint());
0043         }
0044 
0045         result << Handle(Handle::RadialRadius,
0046                          rgradient->center() + QPointF(rgradient->centerRadius(), 0));
0047         break;
0048     }
0049     case QGradient::ConicalGradient:
0050         // not supported
0051         break;
0052     case QGradient::NoGradient:
0053         // not supported
0054         break;
0055     }
0056 
0057     if (g->coordinateMode() == QGradient::ObjectBoundingMode) {
0058         const QRectF boundingRect = m_shape->outlineRect();
0059         const QTransform gradientToUser(boundingRect.width(), 0, 0, boundingRect.height(),
0060                                         boundingRect.x(), boundingRect.y());
0061         const QTransform t = gradientToUser * m_shape->absoluteTransformation();
0062 
0063         QVector<Handle>::iterator it = result.begin();
0064 
0065 
0066 
0067         for (; it != result.end(); ++it) {
0068             it->pos = t.map(it->pos);
0069         }
0070     }
0071 
0072     return result;
0073 }
0074 
0075 QGradient::Type KoShapeGradientHandles::type() const
0076 {
0077     const QGradient *g = gradient();
0078     return g ? g->type() : QGradient::NoGradient;
0079 }
0080 
0081 KUndo2Command *KoShapeGradientHandles::moveGradientHandle(KoShapeGradientHandles::Handle::Type handleType, const QPointF &absoluteOffset)
0082 {
0083     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(handleType != Handle::None, 0);
0084 
0085     KoShapeFillWrapper wrapper(m_shape, m_fillVariant);
0086     const QGradient *originalGradient = wrapper.gradient();
0087     QTransform originalTransform = wrapper.gradientTransform();
0088     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(originalGradient, 0);
0089 
0090     QScopedPointer<QGradient> newGradient;
0091 
0092     switch (originalGradient->type()) {
0093     case QGradient::LinearGradient: {
0094         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(handleType == Handle::LinearStart ||
0095                                              handleType == Handle::LinearEnd, 0);
0096 
0097         newGradient.reset(KoFlake::cloneGradient(originalGradient));
0098         QLinearGradient *lgradient = static_cast<QLinearGradient*>(newGradient.data());
0099 
0100         if (handleType == Handle::LinearStart) {
0101             lgradient->setStart(getNewHandlePos(lgradient->start(), absoluteOffset, newGradient->coordinateMode()));
0102         } else if (handleType == Handle::LinearEnd) {
0103             lgradient->setFinalStop(getNewHandlePos(lgradient->finalStop(), absoluteOffset, newGradient->coordinateMode()));
0104 
0105         }
0106         break;
0107     }
0108     case QGradient::RadialGradient: {
0109         newGradient.reset(KoFlake::cloneGradient(originalGradient));
0110         QRadialGradient *rgradient = static_cast<QRadialGradient*>(newGradient.data());
0111 
0112         if (handleType == Handle::RadialCenter) {
0113             rgradient->setCenter(getNewHandlePos(rgradient->center(), absoluteOffset, newGradient->coordinateMode()));
0114         } else if (handleType == Handle::RadialFocalPoint) {
0115             rgradient->setFocalPoint(getNewHandlePos(rgradient->focalPoint(), absoluteOffset, newGradient->coordinateMode()));
0116         } else if (handleType == Handle::RadialRadius) {
0117             QPointF radiusPos = rgradient->center() + QPointF(QPointF(rgradient->radius(), 0));
0118             radiusPos = getNewHandlePos(radiusPos, absoluteOffset, newGradient->coordinateMode());
0119             rgradient->setRadius(radiusPos.x() - rgradient->center().x());
0120         }
0121         break;
0122     }
0123     case QGradient::ConicalGradient:
0124         // not supported
0125         break;
0126     case QGradient::NoGradient:
0127         // not supported
0128         break;
0129     }
0130 
0131     return wrapper.setGradient(newGradient.data(), originalTransform);
0132 }
0133 
0134 KoShapeGradientHandles::Handle KoShapeGradientHandles::getHandle(KoShapeGradientHandles::Handle::Type handleType)
0135 {
0136     Handle result;
0137 
0138     Q_FOREACH (const Handle &h, handles()) {
0139         if (h.type == handleType) {
0140             result = h;
0141             break;
0142         }
0143     }
0144 
0145     return result;
0146 }
0147 
0148 const QGradient *KoShapeGradientHandles::gradient() const {
0149     KoShapeFillWrapper wrapper(m_shape, m_fillVariant);
0150     return wrapper.gradient();
0151 }
0152 
0153 QPointF KoShapeGradientHandles::getNewHandlePos(const QPointF &oldPos, const QPointF &absoluteOffset, QGradient::CoordinateMode mode)
0154 {
0155     const QTransform offset = QTransform::fromTranslate(absoluteOffset.x(), absoluteOffset.y());
0156     QTransform localToAbsolute = m_shape->absoluteTransformation();
0157     QTransform absoluteToLocal = localToAbsolute.inverted();
0158 
0159     if (mode == QGradient::ObjectBoundingMode) {
0160         const QRectF rect = m_shape->outlineRect();
0161         const QTransform gradientToUser = KisAlgebra2D::mapToRect(rect);
0162         localToAbsolute = gradientToUser * localToAbsolute;
0163 
0164         /// Some shapes may have zero-width/height, then inverted transform will not
0165         /// exist. Therefore we should use a special method for that.
0166         const QTransform userToGradient = KisAlgebra2D::mapToRectInverse(rect);
0167         absoluteToLocal = absoluteToLocal * userToGradient;
0168     }
0169 
0170     return (localToAbsolute * offset * absoluteToLocal).map(oldPos);
0171 }