QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskRgbValue.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskRgbValue.h"
7
8#include <qeasingcurve.h>
9#include <qimage.h>
10
11QSK_QT_PRIVATE_BEGIN
12#include <private/qdrawhelper_p.h>
13QSK_QT_PRIVATE_END
14
15namespace
16{
17 inline int value( int from, int to, qreal ratio )
18 {
19 return int( from + ( to - from ) * ratio );
20 }
21
22 inline qreal valueF( qreal from, qreal to, qreal ratio )
23 {
24 return int( from + ( to - from ) * ratio );
25 }
26}
27
28static inline QColor qskInterpolatedColor(
29 const QColor& c1, const QColor& c2, qreal ratio )
30{
31 switch ( c1.spec() )
32 {
33 case QColor::Rgb:
34 {
35 const int r = value( c1.red(), c2.red(), ratio );
36 const int g = value( c1.green(), c2.green(), ratio );
37 const int b = value( c1.blue(), c2.blue(), ratio );
38 const int a = value( c1.alpha(), c2.alpha(), ratio );
39
40 return QColor::fromRgb( r, g, b, a );
41 }
42 case QColor::Hsv:
43 {
44 const int h = value( c1.hue(), c2.hue(), ratio );
45 const int s = value( c1.saturation(), c2.saturation(), ratio );
46 const int v = value( c1.value(), c2.value(), ratio );
47 const int a = value( c1.alpha(), c2.alpha(), ratio );
48
49 return QColor::fromHsv( h, s, v, a );
50 }
51 case QColor::Cmyk:
52 {
53 const int c = value( c1.cyan(), c2.cyan(), ratio );
54 const int m = value( c1.magenta(), c2.magenta(), ratio );
55 const int y = value( c1.yellow(), c2.yellow(), ratio );
56 const int k = value( c1.black(), c2.black(), ratio );
57 const int a = value( c1.alpha(), c2.alpha(), ratio );
58
59 return QColor::fromCmykF( c, m, y, k, a );
60 }
61 case QColor::Hsl:
62 {
63 const int h = value( c1.hue(), c2.hue(), ratio );
64 const int s = value( c1.saturation(), c2.saturation(), ratio );
65 const int l = value( c1.lightness(), c2.lightness(), ratio );
66 const int a = value( c1.alpha(), c2.alpha(), ratio );
67
68 return QColor::fromHsl( h, s, l, a );
69 }
70 case QColor::ExtendedRgb:
71 {
72 const qreal r = valueF( c1.redF(), c2.redF(), ratio );
73 const qreal g = valueF( c1.greenF(), c2.greenF(), ratio );
74 const qreal b = valueF( c1.blueF(), c2.blueF(), ratio );
75 const qreal a = valueF( c1.alphaF(), c2.alphaF(), ratio );
76
77 return QColor::fromRgbF( r, g, b, a );
78 }
79 case QColor::Invalid:
80 break;
81 }
82
83 return c2;
84}
85
86QRgb QskRgb::interpolated( QRgb rgb1, QRgb rgb2, qreal ratio )
87{
88 // interpolating in HSV usually provides better results !!
89
90 if ( rgb1 == rgb2 )
91 return rgb1;
92
93 const int r = value( qRed( rgb1 ), qRed( rgb2 ), ratio );
94 const int g = value( qGreen( rgb1 ), qGreen( rgb2 ), ratio );
95 const int b = value( qBlue( rgb1 ), qBlue( rgb2 ), ratio );
96 const int a = value( qAlpha( rgb1 ), qAlpha( rgb2 ), ratio );
97
98 return qRgba( r, g, b, a );
99}
100
101QColor QskRgb::interpolated( const QColor& c1, const QColor& c2, qreal ratio )
102{
103 if ( c1 == c2 )
104 return c2;
105
106 /*
107 If one of the colors is invalid we treat it like
108 a transparent version of the other color
109 */
110
111 if ( !c1.isValid() )
112 {
113 QColor c = c2;
114 c.setAlpha( ratio * c2.alpha() );
115 return c;
116 }
117
118 if ( !c2.isValid() )
119 {
120 QColor c = c1;
121 c.setAlpha( ( 1.0 - ratio ) * c1.alpha() );
122 return c;
123 }
124
125 if ( c1.spec() == c2.spec() )
126 return qskInterpolatedColor( c1, c2, ratio );
127 else
128 return qskInterpolatedColor( c1.convertTo( c2.spec() ), c2, ratio );
129}
130
131QRgb QskRgb::rgb( Qt::GlobalColor color )
132{
133 using namespace QskRgb;
134
135 static constexpr QRgb rgbValues[] =
136 {
137 White, // Qt::color0
138 Black, // Qt::color1
139 Black, // Qt::black
140 White, // Qt::white
141 Grey, // Qt::darkGray
142 qRgb( 160, 160, 164 ), // Qt::gray
143 Silver, // Qt::lightGray
144 Red, // Qt::red
145 Lime, // Qt::green
146 Blue, // Qt::blue
147 Cyan, // Qt::cyan
148 Magenta, // Qt::magenta
149 Yellow, // Qt::yellow
150 Maroon, // Qt::darkRed
151 Green, // Qt::darkGreen
152 Navy, // Qt::darkBlue
153 Teal, // Qt::darkCyan
154 Purple, // Qt::darkMagenta
155 Olive, // Qt::darkYellow
156 Transparent // Qt::transparent
157 };
158
159 return rgbValues[ color ];
160}
161
162QRgb QskRgb::lighter( QRgb rgb, int factor ) noexcept
163{
164 if ( factor <= 0 )
165 return rgb;
166
167 // guess we can find a faster implementation without using QColor TODO ...
168 return QColor::fromRgba( rgb ).lighter( factor ).rgba();
169}
170
171QRgb QskRgb::darker( QRgb rgb, int factor ) noexcept
172{
173 if ( factor <= 0 )
174 return rgb;
175
176 // guess we can find a faster implementation without using QColor TODO ...
177 return QColor::fromRgba( rgb ).darker( factor ).rgba();
178}
179
180#ifndef QT_NO_DEBUG_STREAM
181
182#include <qdebug.h>
183
184void QskRgb::debugColor( QDebug debug, const QColor& color )
185{
186 debugColor( debug, color.rgba() );
187}
188
189void QskRgb::debugColor( QDebug debug, QRgb rgb )
190{
191 QDebugStateSaver saver( debug );
192 debug.nospace();
193
194 debug << '[';
195
196 debug << qRed( rgb ) << "r,"
197 << qGreen( rgb ) << "g," << qBlue( rgb ) << 'b';
198
199 if ( qAlpha( rgb ) != 255 )
200 debug << ',' << qAlpha( rgb ) << 'a';
201
202 debug << ']';
203}
204
205#endif
206
207QImage QskRgb::colorTable( int size, const QskGradientStops& stops )
208{
209 if ( size == 0 || stops.isEmpty() )
210 return QImage();
211
212 QImage image( size, 1, QImage::Format_RGBA8888_Premultiplied );
213
214 if ( stops.size() == 1 )
215 {
216 const auto rgb = ARGB2RGBA( qPremultiply( stops[0].rgb() ) );
217 image.fill( rgb );
218
219 return image;
220 }
221
222 auto values = reinterpret_cast< uint* >( image.bits() );
223
224 int index1, index2;
225 QRgb rgb1, rgb2;
226
227 index1 = index2 = qRound( stops[0].position() * size );
228 rgb1 = rgb2 = qPremultiply( stops[0].rgb() );
229
230 if ( index1 > 0 )
231 {
232 const auto v = ARGB2RGBA( rgb1 );
233
234 for ( int i = 0; i < index1; i++ )
235 values[i] = v;
236 }
237
238 for ( int i = 1; i < stops.count(); i++ )
239 {
240 const auto& stop = stops[i];
241
242 index2 = qRound( stop.position() * size );
243 rgb2 = qPremultiply( stop.rgb() );
244
245 const auto n = index2 - index1;
246
247 values[ index1 ] = ARGB2RGBA( rgb1 );
248 for ( int j = 1; j < n; j++ )
249 {
250 const auto rgb = QskRgb::interpolated( rgb1, rgb2, qreal( j ) / ( n - 1 ) );
251 values[ index1 + j] = ARGB2RGBA( rgb );
252 }
253
254 index1 = index2;
255 rgb1 = rgb2;
256 }
257
258 if ( index1 < size - 1 )
259 {
260 const auto v = ARGB2RGBA( rgb1 );
261
262 for ( int i = index1; i < size ; i++ )
263 values[i] = v;
264 }
265
266 return image;
267}
268
269QImage QskRgb::colorTable( const int size,
270 QRgb rgb1, QRgb rgb2, const QEasingCurve& curve )
271{
272 if ( size == 0 )
273 return QImage();
274
275 rgb1 = qPremultiply( rgb1 );
276 rgb2 = qPremultiply( rgb2 );
277
278 QImage image( size, 1, QImage::Format_RGBA8888_Premultiplied );
279
280 if ( rgb1 == rgb2 )
281 {
282 image.fill( ARGB2RGBA( rgb1 ) );
283 return image;
284 }
285
286 auto values = reinterpret_cast< uint* >( image.bits() );
287
288 for ( int i = 0; i < size; i++ )
289 {
290 qreal progress = curve.valueForProgress( qreal( i ) / ( size - 1 ) );
291 progress = qBound( 0.0, progress, 1.0 );
292
293 auto rgb = QskRgb::interpolated( rgb1, rgb2, progress );
294 values[i] = ARGB2RGBA( rgb );
295 }
296
297 return image;
298}