1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qtgradientutils.h"
5#include "qtgradientmanager.h"
6#include <QtGui/QLinearGradient>
7#include <QtGui/QRadialGradient>
8#include <QtGui/QConicalGradient>
9#include <QtXml/QDomDocument>
10#include <QtCore/QDebug>
11
12QT_BEGIN_NAMESPACE
13
14using namespace Qt::StringLiterals;
15
16static QString gradientTypeToString(QGradient::Type type)
17{
18 if (type == QGradient::LinearGradient)
19 return "LinearGradient"_L1;
20 if (type == QGradient::RadialGradient)
21 return "RadialGradient"_L1;
22 if (type == QGradient::ConicalGradient)
23 return "ConicalGradient"_L1;
24 return "NoGradient"_L1;
25}
26
27static QGradient::Type stringToGradientType(const QString &name)
28{
29 if (name == "LinearGradient"_L1)
30 return QGradient::LinearGradient;
31 if (name == "RadialGradient"_L1)
32 return QGradient::RadialGradient;
33 if (name == "ConicalGradient"_L1)
34 return QGradient::ConicalGradient;
35 return QGradient::NoGradient;
36}
37
38static QString gradientSpreadToString(QGradient::Spread spread)
39{
40 if (spread == QGradient::PadSpread)
41 return "PadSpread"_L1;
42 if (spread == QGradient::RepeatSpread)
43 return "RepeatSpread"_L1;
44 if (spread == QGradient::ReflectSpread)
45 return "ReflectSpread"_L1;
46 return "PadSpread"_L1;
47}
48
49static QGradient::Spread stringToGradientSpread(const QString &name)
50{
51 if (name == "PadSpread"_L1)
52 return QGradient::PadSpread;
53 if (name == "RepeatSpread"_L1)
54 return QGradient::RepeatSpread;
55 if (name == "ReflectSpread"_L1)
56 return QGradient::ReflectSpread;
57 return QGradient::PadSpread;
58}
59
60static QString gradientCoordinateModeToString(QGradient::CoordinateMode mode)
61{
62 if (mode == QGradient::LogicalMode)
63 return "LogicalMode"_L1;
64 if (mode == QGradient::StretchToDeviceMode)
65 return "StretchToDeviceMode"_L1;
66 if (mode == QGradient::ObjectBoundingMode)
67 return "ObjectBoundingMode"_L1;
68 return "StretchToDeviceMode"_L1;
69}
70
71static QGradient::CoordinateMode stringToGradientCoordinateMode(const QString &name)
72{
73 if (name == "LogicalMode"_L1)
74 return QGradient::LogicalMode;
75 if (name == "StretchToDeviceMode"_L1)
76 return QGradient::StretchToDeviceMode;
77 if (name == "ObjectBoundingMode"_L1)
78 return QGradient::ObjectBoundingMode;
79 return QGradient::StretchToDeviceMode;
80}
81
82static QDomElement saveColor(QDomDocument &doc, const QColor &color)
83{
84 QDomElement colorElem = doc.createElement(tagName: "colorData"_L1);
85
86 colorElem.setAttribute(name: "r"_L1, value: QString::number(color.red()));
87 colorElem.setAttribute(name: "g"_L1, value: QString::number(color.green()));
88 colorElem.setAttribute(name: "b"_L1, value: QString::number(color.blue()));
89 colorElem.setAttribute(name: "a"_L1, value: QString::number(color.alpha()));
90
91 return colorElem;
92}
93
94static QDomElement saveGradientStop(QDomDocument &doc, const QGradientStop &stop)
95{
96 QDomElement stopElem = doc.createElement(tagName: "stopData"_L1);
97
98 stopElem.setAttribute(name: "position"_L1, value: QString::number(stop.first));
99
100 const QDomElement colorElem = saveColor(doc, color: stop.second);
101 stopElem.appendChild(newChild: colorElem);
102
103 return stopElem;
104}
105
106static QDomElement saveGradient(QDomDocument &doc, const QGradient &gradient)
107{
108 QDomElement gradElem = doc.createElement(tagName: "gradientData"_L1);
109
110 const QGradient::Type type = gradient.type();
111 gradElem.setAttribute(name: "type"_L1, value: gradientTypeToString(type));
112 gradElem.setAttribute(name: "spread"_L1, value: gradientSpreadToString(spread: gradient.spread()));
113 gradElem.setAttribute(name: "coordinateMode"_L1, value: gradientCoordinateModeToString(mode: gradient.coordinateMode()));
114
115 const QGradientStops stops = gradient.stops();
116 for (const QGradientStop &stop : stops)
117 gradElem.appendChild(newChild: saveGradientStop(doc, stop));
118
119 if (type == QGradient::LinearGradient) {
120 const QLinearGradient &g = *static_cast<const QLinearGradient *>(&gradient);
121 gradElem.setAttribute(name: "startX"_L1, value: QString::number(g.start().x()));
122 gradElem.setAttribute(name: "startY"_L1, value: QString::number(g.start().y()));
123 gradElem.setAttribute(name: "endX"_L1, value: QString::number(g.finalStop().x()));
124 gradElem.setAttribute(name: "endY"_L1, value: QString::number(g.finalStop().y()));
125 } else if (type == QGradient::RadialGradient) {
126 const QRadialGradient &g = *static_cast<const QRadialGradient *>(&gradient);
127 gradElem.setAttribute(name: "centerX"_L1, value: QString::number(g.center().x()));
128 gradElem.setAttribute(name: "centerY"_L1, value: QString::number(g.center().y()));
129 gradElem.setAttribute(name: "focalX"_L1, value: QString::number(g.focalPoint().x()));
130 gradElem.setAttribute(name: "focalY"_L1, value: QString::number(g.focalPoint().y()));
131 gradElem.setAttribute(name: "radius"_L1, value: QString::number(g.radius()));
132 } else if (type == QGradient::ConicalGradient) {
133 const QConicalGradient &g = *static_cast<const QConicalGradient*>(&gradient);
134 gradElem.setAttribute(name: "centerX"_L1, value: QString::number(g.center().x()));
135 gradElem.setAttribute(name: "centerY"_L1, value: QString::number(g.center().y()));
136 gradElem.setAttribute(name: "angle"_L1, value: QString::number(g.angle()));
137 }
138
139 return gradElem;
140}
141
142static QColor loadColor(const QDomElement &elem)
143{
144 if (elem.tagName() != "colorData"_L1)
145 return QColor();
146
147 return QColor(elem.attribute(name: "r"_L1).toInt(),
148 elem.attribute(name: "g"_L1).toInt(),
149 elem.attribute(name: "b"_L1).toInt(),
150 elem.attribute(name: "a"_L1).toInt());
151}
152
153static QGradientStop loadGradientStop(const QDomElement &elem)
154{
155 if (elem.tagName() != "stopData"_L1)
156 return QGradientStop();
157
158 const qreal pos = static_cast<qreal>(elem.attribute(name: "position"_L1).toDouble());
159 return qMakePair(value1: pos, value2: loadColor(elem: elem.firstChild().toElement()));
160}
161
162static QGradient loadGradient(const QDomElement &elem)
163{
164 if (elem.tagName() != "gradientData"_L1)
165 return QLinearGradient();
166
167 const QGradient::Type type = stringToGradientType(name: elem.attribute(name: "type"_L1));
168 const QGradient::Spread spread = stringToGradientSpread(name: elem.attribute(name: "spread"_L1));
169 const QGradient::CoordinateMode mode = stringToGradientCoordinateMode(name: elem.attribute(name: "coordinateMode"_L1));
170
171 QGradient gradient = QLinearGradient();
172
173 if (type == QGradient::LinearGradient) {
174 QLinearGradient g;
175 g.setStart(x: elem.attribute(name: "startX"_L1).toDouble(), y: elem.attribute(name: "startY"_L1).toDouble());
176 g.setFinalStop(x: elem.attribute(name: "endX"_L1).toDouble(), y: elem.attribute(name: "endY"_L1).toDouble());
177 gradient = g;
178 } else if (type == QGradient::RadialGradient) {
179 QRadialGradient g;
180 g.setCenter(x: elem.attribute(name: "centerX"_L1).toDouble(), y: elem.attribute(name: "centerY"_L1).toDouble());
181 g.setFocalPoint(x: elem.attribute(name: "focalX"_L1).toDouble(), y: elem.attribute(name: "focalY"_L1).toDouble());
182 g.setRadius(elem.attribute(name: "radius"_L1).toDouble());
183 gradient = g;
184 } else if (type == QGradient::ConicalGradient) {
185 QConicalGradient g;
186 g.setCenter(x: elem.attribute(name: "centerX"_L1).toDouble(), y: elem.attribute(name: "centerY"_L1).toDouble());
187 g.setAngle(elem.attribute(name: "angle"_L1).toDouble());
188 gradient = g;
189 }
190
191 QDomElement stopElem = elem.firstChildElement();
192 while (!stopElem.isNull()) {
193 QGradientStop stop = loadGradientStop(elem: stopElem);
194
195 gradient.setColorAt(pos: stop.first, color: stop.second);
196
197 stopElem = stopElem.nextSiblingElement();
198 }
199
200 gradient.setSpread(spread);
201 gradient.setCoordinateMode(mode);
202
203 return gradient;
204}
205
206QString QtGradientUtils::saveState(const QtGradientManager *manager)
207{
208 QDomDocument doc;
209
210 QDomElement rootElem = doc.createElement(tagName: "gradients"_L1);
211
212 QMap<QString, QGradient> grads = manager->gradients();
213 for (auto itGrad = grads.cbegin(), end = grads.cend(); itGrad != end; ++itGrad) {
214 QDomElement idElem = doc.createElement(tagName: "gradient"_L1);
215 idElem.setAttribute(name: "name"_L1, value: itGrad.key());
216 QDomElement gradElem = saveGradient(doc, gradient: itGrad.value());
217 idElem.appendChild(newChild: gradElem);
218
219 rootElem.appendChild(newChild: idElem);
220 }
221
222 doc.appendChild(newChild: rootElem);
223
224 return doc.toString();
225}
226
227void QtGradientUtils::restoreState(QtGradientManager *manager, const QString &state)
228{
229 manager->clear();
230
231 QDomDocument doc;
232 doc.setContent(data: state);
233
234 QDomElement rootElem = doc.documentElement();
235
236 QDomElement gradElem = rootElem.firstChildElement();
237 while (!gradElem.isNull()) {
238 const QString name = gradElem.attribute(name: "name"_L1);
239 const QGradient gradient = loadGradient(elem: gradElem.firstChildElement());
240
241 manager->addGradient(id: name, gradient);
242 gradElem = gradElem.nextSiblingElement();
243 }
244}
245
246QPixmap QtGradientUtils::gradientPixmap(const QGradient &gradient, const QSize &size, bool checkeredBackground)
247{
248 QImage image(size, QImage::Format_ARGB32);
249 QPainter p(&image);
250 p.setCompositionMode(QPainter::CompositionMode_Source);
251
252 if (checkeredBackground) {
253 int pixSize = 20;
254 QPixmap pm(2 * pixSize, 2 * pixSize);
255
256 QPainter pmp(&pm);
257 pmp.fillRect(x: 0, y: 0, w: pixSize, h: pixSize, c: Qt::lightGray);
258 pmp.fillRect(x: pixSize, y: pixSize, w: pixSize, h: pixSize, c: Qt::lightGray);
259 pmp.fillRect(x: 0, y: pixSize, w: pixSize, h: pixSize, c: Qt::darkGray);
260 pmp.fillRect(x: pixSize, y: 0, w: pixSize, h: pixSize, c: Qt::darkGray);
261
262 p.setBrushOrigin(x: (size.width() % pixSize + pixSize) / 2, y: (size.height() % pixSize + pixSize) / 2);
263 p.fillRect(x: 0, y: 0, w: size.width(), h: size.height(), b: pm);
264 p.setBrushOrigin(x: 0, y: 0);
265 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
266 }
267
268 const qreal scaleFactor = 0.999999;
269 p.scale(sx: scaleFactor, sy: scaleFactor);
270 QGradient grad = gradient;
271 grad.setCoordinateMode(QGradient::StretchToDeviceMode);
272 p.fillRect(QRect(0, 0, size.width(), size.height()), grad);
273 p.drawRect(r: QRect(0, 0, size.width() - 1, size.height() - 1));
274
275 return QPixmap::fromImage(image);
276}
277
278static QString styleSheetFillName(const QGradient &gradient)
279{
280 QString result;
281
282 switch (gradient.type()) {
283 case QGradient::LinearGradient:
284 result += "qlineargradient"_L1;
285 break;
286 case QGradient::RadialGradient:
287 result += "qradialgradient"_L1;
288 break;
289 case QGradient::ConicalGradient:
290 result += "qconicalgradient"_L1;
291 break;
292 default:
293 qWarning() << "QtGradientUtils::styleSheetFillName(): gradient type" << gradient.type() << "not supported!";
294 break;
295 }
296
297 return result;
298}
299
300static QStringList styleSheetParameters(const QGradient &gradient)
301{
302 QStringList result;
303
304 if (gradient.type() != QGradient::ConicalGradient) {
305 QString spread;
306 switch (gradient.spread()) {
307 case QGradient::PadSpread:
308 spread = "pad"_L1;
309 break;
310 case QGradient::ReflectSpread:
311 spread = "reflect"_L1;
312 break;
313 case QGradient::RepeatSpread:
314 spread = "repeat"_L1;
315 break;
316 default:
317 qWarning() << "QtGradientUtils::styleSheetParameters(): gradient spread" << gradient.spread() << "not supported!";
318 break;
319 }
320 result << "spread:"_L1 + spread;
321 }
322
323 switch (gradient.type()) {
324 case QGradient::LinearGradient: {
325 const QLinearGradient *linearGradient = static_cast<const QLinearGradient*>(&gradient);
326 result << "x1:"_L1 + QString::number(linearGradient->start().x())
327 << "y1:"_L1 + QString::number(linearGradient->start().y())
328 << "x2:"_L1 + QString::number(linearGradient->finalStop().x())
329 << "y2:"_L1 + QString::number(linearGradient->finalStop().y());
330 break;
331 }
332 case QGradient::RadialGradient: {
333 const QRadialGradient *radialGradient = static_cast<const QRadialGradient*>(&gradient);
334 result << "cx:"_L1 + QString::number(radialGradient->center().x())
335 << "cy:"_L1 + QString::number(radialGradient->center().y())
336 << "radius:"_L1 + QString::number(radialGradient->radius())
337 << "fx:"_L1 + QString::number(radialGradient->focalPoint().x())
338 << "fy:"_L1 + QString::number(radialGradient->focalPoint().y());
339 break;
340 }
341 case QGradient::ConicalGradient: {
342 const QConicalGradient *conicalGradient = static_cast<const QConicalGradient*>(&gradient);
343 result << "cx:"_L1 + QString::number(conicalGradient->center().x())
344 << "cy:"_L1 + QString::number(conicalGradient->center().y())
345 << "angle:"_L1 + QString::number(conicalGradient->angle());
346 break;
347 }
348 default:
349 qWarning() << "QtGradientUtils::styleSheetParameters(): gradient type" << gradient.type() << "not supported!";
350 break;
351 }
352
353 return result;
354}
355
356static QStringList styleSheetStops(const QGradient &gradient)
357{
358 QStringList result;
359 const QGradientStops &stops = gradient.stops();
360 for (const QGradientStop &stop : stops) {
361 const QColor color = stop.second;
362
363 const QString stopDescription = "stop:"_L1 + QString::number(stop.first) + " rgba("_L1
364 + QString::number(color.red()) + ", "_L1
365 + QString::number(color.green()) + ", "_L1
366 + QString::number(color.blue()) + ", "_L1
367 + QString::number(color.alpha()) + QLatin1Char(')');
368 result << stopDescription;
369 }
370
371 return result;
372}
373
374QString QtGradientUtils::styleSheetCode(const QGradient &gradient)
375{
376 QStringList gradientParameters;
377 gradientParameters << styleSheetParameters(gradient) << styleSheetStops(gradient);
378
379 return styleSheetFillName(gradient) + QLatin1Char('(') + gradientParameters.join(sep: ", "_L1) + QLatin1Char(')');
380}
381
382QT_END_NAMESPACE
383

source code of qttools/src/shared/qtgradienteditor/qtgradientutils.cpp