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