1 /*
2 * Copyright (c) 2017 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "KoShapeGradientHandles.h"
20
21 #include <QGradient>
22 #include <KoShape.h>
23 #include <KoGradientBackground.h>
24 #include <KoShapeBackgroundCommand.h>
25 #include <KoShapeFillWrapper.h>
26 #include <kis_assert.h>
27 #include "kis_algebra_2d.h"
28
KoShapeGradientHandles(KoFlake::FillVariant fillVariant,KoShape * shape)29 KoShapeGradientHandles::KoShapeGradientHandles(KoFlake::FillVariant fillVariant, KoShape *shape)
30 : m_fillVariant(fillVariant),
31 m_shape(shape)
32 {
33 }
34
handles() const35 QVector<KoShapeGradientHandles::Handle> KoShapeGradientHandles::handles() const {
36 QVector<Handle> result;
37
38 const QGradient *g = gradient();
39 if (!g) return result;
40
41 switch (g->type()) {
42 case QGradient::LinearGradient: {
43 const QLinearGradient *lgradient = static_cast<const QLinearGradient*>(g);
44 result << Handle(Handle::LinearStart, lgradient->start());
45 result << Handle(Handle::LinearEnd, lgradient->finalStop());
46 break;
47 }
48 case QGradient::RadialGradient: {
49 const QRadialGradient *rgradient = static_cast<const QRadialGradient*>(g);
50
51 result << Handle(Handle::RadialCenter, rgradient->center());
52
53 if (rgradient->center() != rgradient->focalPoint()) {
54 result << Handle(Handle::RadialFocalPoint, rgradient->focalPoint());
55 }
56
57 result << Handle(Handle::RadialRadius,
58 rgradient->center() + QPointF(rgradient->centerRadius(), 0));
59 break;
60 }
61 case QGradient::ConicalGradient:
62 // not supported
63 break;
64 case QGradient::NoGradient:
65 // not supported
66 break;
67 }
68
69 if (g->coordinateMode() == QGradient::ObjectBoundingMode) {
70 const QRectF boundingRect = m_shape->outlineRect();
71 const QTransform gradientToUser(boundingRect.width(), 0, 0, boundingRect.height(),
72 boundingRect.x(), boundingRect.y());
73 const QTransform t = gradientToUser * m_shape->absoluteTransformation();
74
75 QVector<Handle>::iterator it = result.begin();
76
77
78
79 for (; it != result.end(); ++it) {
80 it->pos = t.map(it->pos);
81 }
82 }
83
84 return result;
85 }
86
type() const87 QGradient::Type KoShapeGradientHandles::type() const
88 {
89 const QGradient *g = gradient();
90 return g ? g->type() : QGradient::NoGradient;
91 }
92
moveGradientHandle(KoShapeGradientHandles::Handle::Type handleType,const QPointF & absoluteOffset)93 KUndo2Command *KoShapeGradientHandles::moveGradientHandle(KoShapeGradientHandles::Handle::Type handleType, const QPointF &absoluteOffset)
94 {
95 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(handleType != Handle::None, 0);
96
97 KoShapeFillWrapper wrapper(m_shape, m_fillVariant);
98 const QGradient *originalGradient = wrapper.gradient();
99 QTransform originalTransform = wrapper.gradientTransform();
100 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(originalGradient, 0);
101
102 QScopedPointer<QGradient> newGradient;
103
104 switch (originalGradient->type()) {
105 case QGradient::LinearGradient: {
106 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(handleType == Handle::LinearStart ||
107 handleType == Handle::LinearEnd, 0);
108
109 newGradient.reset(KoFlake::cloneGradient(originalGradient));
110 QLinearGradient *lgradient = static_cast<QLinearGradient*>(newGradient.data());
111
112 if (handleType == Handle::LinearStart) {
113 lgradient->setStart(getNewHandlePos(lgradient->start(), absoluteOffset, newGradient->coordinateMode()));
114 } else if (handleType == Handle::LinearEnd) {
115 lgradient->setFinalStop(getNewHandlePos(lgradient->finalStop(), absoluteOffset, newGradient->coordinateMode()));
116
117 }
118 break;
119 }
120 case QGradient::RadialGradient: {
121 newGradient.reset(KoFlake::cloneGradient(originalGradient));
122 QRadialGradient *rgradient = static_cast<QRadialGradient*>(newGradient.data());
123
124 if (handleType == Handle::RadialCenter) {
125 rgradient->setCenter(getNewHandlePos(rgradient->center(), absoluteOffset, newGradient->coordinateMode()));
126 } else if (handleType == Handle::RadialFocalPoint) {
127 rgradient->setFocalPoint(getNewHandlePos(rgradient->focalPoint(), absoluteOffset, newGradient->coordinateMode()));
128 } else if (handleType == Handle::RadialRadius) {
129 QPointF radiusPos = rgradient->center() + QPointF(QPointF(rgradient->radius(), 0));
130 radiusPos = getNewHandlePos(radiusPos, absoluteOffset, newGradient->coordinateMode());
131 rgradient->setRadius(radiusPos.x() - rgradient->center().x());
132 }
133 break;
134 }
135 case QGradient::ConicalGradient:
136 // not supported
137 break;
138 case QGradient::NoGradient:
139 // not supported
140 break;
141 }
142
143 return wrapper.setGradient(newGradient.data(), originalTransform);
144 }
145
getHandle(KoShapeGradientHandles::Handle::Type handleType)146 KoShapeGradientHandles::Handle KoShapeGradientHandles::getHandle(KoShapeGradientHandles::Handle::Type handleType)
147 {
148 Handle result;
149
150 Q_FOREACH (const Handle &h, handles()) {
151 if (h.type == handleType) {
152 result = h;
153 break;
154 }
155 }
156
157 return result;
158 }
159
gradient() const160 const QGradient *KoShapeGradientHandles::gradient() const {
161 KoShapeFillWrapper wrapper(m_shape, m_fillVariant);
162 return wrapper.gradient();
163 }
164
getNewHandlePos(const QPointF & oldPos,const QPointF & absoluteOffset,QGradient::CoordinateMode mode)165 QPointF KoShapeGradientHandles::getNewHandlePos(const QPointF &oldPos, const QPointF &absoluteOffset, QGradient::CoordinateMode mode)
166 {
167 const QTransform offset = QTransform::fromTranslate(absoluteOffset.x(), absoluteOffset.y());
168 QTransform localToAbsolute = m_shape->absoluteTransformation();
169 QTransform absoluteToLocal = localToAbsolute.inverted();
170
171 if (mode == QGradient::ObjectBoundingMode) {
172 const QRectF rect = m_shape->outlineRect();
173 const QTransform gradientToUser = KisAlgebra2D::mapToRect(rect);
174 localToAbsolute = gradientToUser * localToAbsolute;
175
176 /// Some shapes may have zero-width/height, then inverted transform will not
177 /// exist. Therefore we should use a special method for that.
178 const QTransform userToGradient = KisAlgebra2D::mapToRectInverse(rect);
179 absoluteToLocal = absoluteToLocal * userToGradient;
180 }
181
182 return (localToAbsolute * offset * absoluteToLocal).map(oldPos);
183 }
184