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 }