QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgscolorutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscolorutils.cpp
3 ---------------------------
4 begin : July 2022
5 copyright : (C) 2022 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgscolorutils.h"
19
20#include <QColor>
21#include <QDomDocument>
22#include <QRegularExpression>
23#include <QRegularExpressionMatch>
24
25
26void QgsColorUtils::writeXml( const QColor &color, const QString &identifier, QDomDocument &document, QDomElement &element, const QgsReadWriteContext & )
27{
28 {
29 const QDomElement oldElement = element.firstChildElement( identifier );
30 if ( !oldElement.isNull() )
31 element.removeChild( oldElement );
32 }
33
34 QDomElement colorElement = document.createElement( identifier );
35 if ( !color.isValid() )
36 {
37 colorElement.setAttribute( QStringLiteral( "invalid" ), QStringLiteral( "1" ) );
38 }
39 else
40 {
41 QString spec;
42 switch ( color.spec() )
43 {
44 case QColor::Invalid:
45 break; // not possible
46
47 case QColor::Rgb:
48 case QColor::ExtendedRgb:
49 {
50 // QColor will automatically adapt between extended rgb/rgb based on value of red/green/blue components
51 spec = QStringLiteral( "rgb" );
52#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
53 float red = 1;
54 float green = 1;
55 float blue = 1;
56#else
57 qreal red = 1;
58 qreal green = 1;
59 qreal blue = 1;
60#endif
61 color.getRgbF( &red, &green, &blue );
62 colorElement.setAttribute( QStringLiteral( "red" ), qgsDoubleToString( red ) );
63 colorElement.setAttribute( QStringLiteral( "green" ), qgsDoubleToString( green ) );
64 colorElement.setAttribute( QStringLiteral( "blue" ), qgsDoubleToString( blue ) );
65 break;
66 }
67
68 case QColor::Hsv:
69 {
70 spec = QStringLiteral( "hsv" );
71
72#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
73 float h = 1;
74 float s = 1;
75 float v = 1;
76#else
77 qreal h = 1;
78 qreal s = 1;
79 qreal v = 1;
80#endif
81 color.getHsvF( &h, &s, &v );
82 colorElement.setAttribute( QStringLiteral( "hue" ), qgsDoubleToString( h ) );
83 colorElement.setAttribute( QStringLiteral( "saturation" ), qgsDoubleToString( s ) );
84 colorElement.setAttribute( QStringLiteral( "value" ), qgsDoubleToString( v ) );
85 break;
86 }
87
88 case QColor::Hsl:
89 {
90 spec = QStringLiteral( "hsl" );
91
92#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
93 float h = 1;
94 float s = 1;
95 float l = 1;
96#else
97 qreal h = 1;
98 qreal s = 1;
99 qreal l = 1;
100#endif
101 color.getHslF( &h, &s, &l );
102 colorElement.setAttribute( QStringLiteral( "hue" ), qgsDoubleToString( h ) );
103 colorElement.setAttribute( QStringLiteral( "saturation" ), qgsDoubleToString( s ) );
104 colorElement.setAttribute( QStringLiteral( "lightness" ), qgsDoubleToString( l ) );
105 break;
106 }
107
108 case QColor::Cmyk:
109 {
110 spec = QStringLiteral( "cmyk" );
111
112#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
113 float c = 1;
114 float m = 1;
115 float y = 1;
116 float k = 1;
117#else
118 qreal c = 1;
119 qreal m = 1;
120 qreal y = 1;
121 qreal k = 1;
122#endif
123
124 color.getCmykF( &c, &y, &m, &k );
125 colorElement.setAttribute( QStringLiteral( "c" ), qgsDoubleToString( c ) );
126 colorElement.setAttribute( QStringLiteral( "m" ), qgsDoubleToString( m ) );
127 colorElement.setAttribute( QStringLiteral( "y" ), qgsDoubleToString( y ) );
128 colorElement.setAttribute( QStringLiteral( "k" ), qgsDoubleToString( k ) );
129 break;
130 }
131 }
132 colorElement.setAttribute( QStringLiteral( "spec" ), spec );
133 if ( color.alphaF() < 1.0 )
134 {
135 colorElement.setAttribute( QStringLiteral( "alpha" ), qgsDoubleToString( color.alphaF() ) );
136 }
137 }
138 element.appendChild( colorElement );
139}
140
141QColor QgsColorUtils::readXml( const QDomElement &element, const QString &identifier, const QgsReadWriteContext & )
142{
143 const QDomElement colorElement = element.firstChildElement( identifier );
144 if ( colorElement.isNull() )
145 return QColor();
146
147 const bool invalid = colorElement.attribute( QStringLiteral( "invalid" ), QStringLiteral( "0" ) ).toInt();
148 if ( invalid )
149 return QColor();
150
151 QColor res;
152 const QString spec = colorElement.attribute( QStringLiteral( "spec" ) );
153 if ( spec == QLatin1String( "rgb" ) )
154 {
155 // QColor will automatically adapt between extended rgb/rgb based on value of red/green/blue components
156 const double red = colorElement.attribute( QStringLiteral( "red" ) ).toDouble();
157 const double green = colorElement.attribute( QStringLiteral( "green" ) ).toDouble();
158 const double blue = colorElement.attribute( QStringLiteral( "blue" ) ).toDouble();
159 res = QColor::fromRgbF( red, green, blue );
160 }
161 else if ( spec == QLatin1String( "hsv" ) )
162 {
163 const double hue = colorElement.attribute( QStringLiteral( "hue" ) ).toDouble();
164 const double saturation = colorElement.attribute( QStringLiteral( "saturation" ) ).toDouble();
165 const double value = colorElement.attribute( QStringLiteral( "value" ) ).toDouble();
166 res = QColor::fromHsvF( hue, saturation, value );
167 }
168 else if ( spec == QLatin1String( "hsl" ) )
169 {
170 const double hue = colorElement.attribute( QStringLiteral( "hue" ) ).toDouble();
171 const double saturation = colorElement.attribute( QStringLiteral( "saturation" ) ).toDouble();
172 const double value = colorElement.attribute( QStringLiteral( "lightness" ) ).toDouble();
173 res = QColor::fromHslF( hue, saturation, value );
174 }
175 else if ( spec == QLatin1String( "cmyk" ) )
176 {
177 const double cyan = colorElement.attribute( QStringLiteral( "c" ) ).toDouble();
178 const double magenta = colorElement.attribute( QStringLiteral( "m" ) ).toDouble();
179 const double yellow = colorElement.attribute( QStringLiteral( "y" ) ).toDouble();
180 const double black = colorElement.attribute( QStringLiteral( "k" ) ).toDouble();
181 res = QColor::fromCmykF( cyan, magenta, yellow, black );
182 }
183
184 {
185 const double alpha = colorElement.attribute( QStringLiteral( "alpha" ), QStringLiteral( "1" ) ).toDouble();
186 res.setAlphaF( alpha );
187 }
188
189 return res;
190}
191
192QString QgsColorUtils::colorToString( const QColor &color )
193{
194 if ( !color.isValid() )
195 return QString();
196
197 // this is the pre 3.28 deprecated string format -- we prefix the lossless encoded color with this so that older QGIS versions
198 // can still recover the lossy color via QgsSymbolLayerUtils::decodeColor
199 const QString compatString = QStringLiteral( "%1,%2,%3,%4," ).arg( color.red() ).arg( color.green() ).arg( color.blue() ).arg( color.alpha() );
200
201 switch ( color.spec() )
202 {
203 case QColor::Invalid:
204 break; // not possible
205
206 case QColor::Rgb:
207 case QColor::ExtendedRgb:
208 {
209 // QColor will automatically adapt between extended rgb/rgb based on value of red/green/blue components
210#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
211 float red = 1;
212 float green = 1;
213 float blue = 1;
214 float alpha = 1;
215#else
216 qreal red = 1;
217 qreal green = 1;
218 qreal blue = 1;
219 qreal alpha = 1;
220#endif
221 color.getRgbF( &red, &green, &blue, &alpha );
222 return compatString + QStringLiteral( "rgb:%1,%2,%3,%4" ).arg( qgsDoubleToString( red ),
223 qgsDoubleToString( green ),
224 qgsDoubleToString( blue ),
225 qgsDoubleToString( alpha ) );
226 }
227
228 case QColor::Hsv:
229 {
230#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
231 float h = 1;
232 float s = 1;
233 float v = 1;
234 float alpha = 1;
235#else
236 qreal h = 1;
237 qreal s = 1;
238 qreal v = 1;
239 qreal alpha = 1;
240#endif
241 color.getHsvF( &h, &s, &v, &alpha );
242 return compatString + QStringLiteral( "hsv:%1,%2,%3,%4" ).arg( qgsDoubleToString( h ),
245 qgsDoubleToString( alpha ) );
246 }
247
248 case QColor::Hsl:
249 {
250#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
251 float h = 1;
252 float s = 1;
253 float l = 1;
254 float alpha = 1;
255#else
256 qreal h = 1;
257 qreal s = 1;
258 qreal l = 1;
259 qreal alpha = 1;
260#endif
261 color.getHslF( &h, &s, &l, &alpha );
262 return compatString + QStringLiteral( "hsl:%1,%2,%3,%4" ).arg( qgsDoubleToString( h ),
265 qgsDoubleToString( alpha ) );
266 }
267
268 case QColor::Cmyk:
269 {
270#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
271 float c = 1;
272 float m = 1;
273 float y = 1;
274 float k = 1;
275 float alpha = 1;
276#else
277 qreal c = 1;
278 qreal m = 1;
279 qreal y = 1;
280 qreal k = 1;
281 qreal alpha = 1;
282#endif
283
284 color.getCmykF( &c, &m, &y, &k, &alpha );
285 return compatString + QStringLiteral( "cmyk:%1,%2,%3,%4,%5" ).arg( qgsDoubleToString( c ),
289 qgsDoubleToString( alpha ) );
290 }
291 }
292 return QString();
293}
294
295QColor QgsColorUtils::colorFromString( const QString &string )
296{
297 if ( string.isEmpty() )
298 return QColor();
299
300 const thread_local QRegularExpression rx( QStringLiteral( "^(.*),([a-z]+):([\\d\\.\\-]+),([\\d\\.\\-]+),([\\d\\.\\-]+),([\\d\\.\\-]+),?([\\d\\.\\-]*)$" ) );
301 const QRegularExpressionMatch match = rx.match( string );
302 if ( !match.hasMatch() )
303 {
304 // try reading older color format and hex strings
305 const QStringList lst = string.split( ',' );
306 if ( lst.count() < 3 )
307 {
308 return QColor( string );
309 }
310 int red, green, blue, alpha;
311 red = lst[0].toInt();
312 green = lst[1].toInt();
313 blue = lst[2].toInt();
314 alpha = lst.count() > 3 ? lst[3].toInt() : 255;
315 return QColor( red, green, blue, alpha );
316 }
317
318 const QString spec = match.captured( 2 );
319
320 if ( spec == QLatin1String( "rgb" ) )
321 {
322 // QColor will automatically adapt between extended rgb/rgb based on value of red/green/blue components
323 const double red = match.captured( 3 ).toDouble();
324 const double green = match.captured( 4 ).toDouble();
325 const double blue = match.captured( 5 ).toDouble();
326 const double alpha = match.captured( 6 ).toDouble();
327 return QColor::fromRgbF( red, green, blue, alpha );
328 }
329 else if ( spec == QLatin1String( "hsv" ) )
330 {
331 const double hue = match.captured( 3 ).toDouble();
332 const double saturation = match.captured( 4 ).toDouble();
333 const double value = match.captured( 5 ).toDouble();
334 const double alpha = match.captured( 6 ).toDouble();
335 return QColor::fromHsvF( hue, saturation, value, alpha );
336 }
337 else if ( spec == QLatin1String( "hsl" ) )
338 {
339 const double hue = match.captured( 3 ).toDouble();
340 const double saturation = match.captured( 4 ).toDouble();
341 const double lightness = match.captured( 5 ).toDouble();
342 const double alpha = match.captured( 6 ).toDouble();
343 return QColor::fromHslF( hue, saturation, lightness, alpha );
344 }
345 else if ( spec == QLatin1String( "cmyk" ) )
346 {
347 const double cyan = match.captured( 3 ).toDouble();
348 const double magenta = match.captured( 4 ).toDouble();
349 const double yellow = match.captured( 5 ).toDouble();
350 const double black = match.captured( 6 ).toDouble();
351 const double alpha = match.captured( 7 ).toDouble();
352 return QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
353 }
354 return QColor();
355}
static QColor readXml(const QDomElement &element, const QString &identifier, const QgsReadWriteContext &context)
Reads a color from an XML element, matching the specified identifier string.
static void writeXml(const QColor &color, const QString &identifier, QDomDocument &document, QDomElement &element, const QgsReadWriteContext &context)
Writes a color to an XML element, storing it under the specified identifier.
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
The class is used as a container of context for various read/write operations on other objects.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:5124