Qwt User's Guide 6.3.0
Loading...
Searching...
No Matches
qwt_color_map.cpp
1/******************************************************************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10#include "qwt_color_map.h"
11#include "qwt_interval.h"
12
13#include <qvector.h>
14
15static inline QRgb qwtHsvToRgb( int h, int s, int v, int a )
16{
17#if 0
18 return QColor::fromHsv( h, s, v, a ).rgb();
19#else
20
21 const double vs = v * s / 255.0;
22 const int p = v - qRound( vs );
23
24 switch( h / 60 )
25 {
26 case 0:
27 {
28 const double r = ( 60 - h ) / 60.0;
29 return qRgba( v, v - qRound( r * vs ), p, a );
30 }
31 case 1:
32 {
33 const double r = ( h - 60 ) / 60.0;
34 return qRgba( v - qRound( r * vs ), v, p, a );
35 }
36 case 2:
37 {
38 const double r = ( 180 - h ) / 60.0;
39 return qRgba( p, v, v - qRound( r * vs ), a );
40 }
41 case 3:
42 {
43 const double r = ( h - 180 ) / 60.0;
44 return qRgba( p, v - qRound( r * vs ), v, a );
45 }
46 case 4:
47 {
48 const double r = ( 300 - h ) / 60.0;
49 return qRgba( v - qRound( r * vs ), p, v, a );
50 }
51 case 5:
52 default:
53 {
54 const double r = ( h - 300 ) / 60.0;
55 return qRgba( v, p, v - qRound( r * vs ), a );
56 }
57 }
58#endif
59}
60
61class QwtLinearColorMap::ColorStops
62{
63 public:
64 ColorStops():
65 m_doAlpha( false )
66 {
67 m_stops.reserve( 256 );
68 }
69
70 void insert( double pos, const QColor& color );
71 QRgb rgb( QwtLinearColorMap::Mode, double pos ) const;
72
73 QVector< double > stops() const;
74
75 private:
76
77 class ColorStop
78 {
79 public:
80 ColorStop():
81 pos( 0.0 ),
82 rgb( 0 )
83 {
84 };
85
86 ColorStop( double p, const QColor& c ):
87 pos( p ),
88 rgb( c.rgba() )
89 {
90 r = qRed( rgb );
91 g = qGreen( rgb );
92 b = qBlue( rgb );
93 a = qAlpha( rgb );
94
95 /*
96 when mapping a value to rgb we will have to calculate:
97 - const int v = int( ( s1.v0 + ratio * s1.vStep ) + 0.5 );
98
99 Thus adding 0.5 ( for rounding ) can be done in advance
100 */
101 r0 = r + 0.5;
102 g0 = g + 0.5;
103 b0 = b + 0.5;
104 a0 = a + 0.5;
105
106 rStep = gStep = bStep = aStep = 0.0;
107 posStep = 0.0;
108 }
109
110 void updateSteps( const ColorStop& nextStop )
111 {
112 rStep = nextStop.r - r;
113 gStep = nextStop.g - g;
114 bStep = nextStop.b - b;
115 aStep = nextStop.a - a;
116 posStep = nextStop.pos - pos;
117 }
118
119 double pos;
120 QRgb rgb;
121 int r, g, b, a;
122
123 // precalculated values
124 double rStep, gStep, bStep, aStep;
125 double r0, g0, b0, a0;
126 double posStep;
127 };
128
129 inline int findUpper( double pos ) const;
130 QVector< ColorStop > m_stops;
131 bool m_doAlpha;
132};
133
134void QwtLinearColorMap::ColorStops::insert( double pos, const QColor& color )
135{
136 // Lookups need to be very fast, insertions are not so important.
137 // Anyway, a balanced tree is what we need here. TODO ...
138
139 if ( pos < 0.0 || pos > 1.0 )
140 return;
141
142 int index;
143 if ( m_stops.size() == 0 )
144 {
145 index = 0;
146 m_stops.resize( 1 );
147 }
148 else
149 {
150 index = findUpper( pos );
151 if ( index == m_stops.size() ||
152 qAbs( m_stops[index].pos - pos ) >= 0.001 )
153 {
154 m_stops.resize( m_stops.size() + 1 );
155 for ( int i = m_stops.size() - 1; i > index; i-- )
156 m_stops[i] = m_stops[i - 1];
157 }
158 }
159
160 m_stops[index] = ColorStop( pos, color );
161 if ( color.alpha() != 255 )
162 m_doAlpha = true;
163
164 if ( index > 0 )
165 m_stops[index - 1].updateSteps( m_stops[index] );
166
167 if ( index < m_stops.size() - 1 )
168 m_stops[index].updateSteps( m_stops[index + 1] );
169}
170
171inline QVector< double > QwtLinearColorMap::ColorStops::stops() const
172{
173 QVector< double > positions( m_stops.size() );
174 for ( int i = 0; i < m_stops.size(); i++ )
175 positions[i] = m_stops[i].pos;
176 return positions;
177}
178
179inline int QwtLinearColorMap::ColorStops::findUpper( double pos ) const
180{
181 int index = 0;
182 int n = m_stops.size();
183
184 const ColorStop* stops = m_stops.data();
185
186 while ( n > 0 )
187 {
188 const int half = n >> 1;
189 const int middle = index + half;
190
191 if ( stops[middle].pos <= pos )
192 {
193 index = middle + 1;
194 n -= half + 1;
195 }
196 else
197 n = half;
198 }
199
200 return index;
201}
202
203inline QRgb QwtLinearColorMap::ColorStops::rgb(
204 QwtLinearColorMap::Mode mode, double pos ) const
205{
206 if ( pos <= 0.0 )
207 return m_stops[0].rgb;
208 if ( pos >= 1.0 )
209 return m_stops[ m_stops.size() - 1 ].rgb;
210
211 const int index = findUpper( pos );
212 if ( mode == FixedColors )
213 {
214 return m_stops[index - 1].rgb;
215 }
216 else
217 {
218 const ColorStop& s1 = m_stops[index - 1];
219
220 const double ratio = ( pos - s1.pos ) / ( s1.posStep );
221
222 const int r = int( s1.r0 + ratio * s1.rStep );
223 const int g = int( s1.g0 + ratio * s1.gStep );
224 const int b = int( s1.b0 + ratio * s1.bStep );
225
226 if ( m_doAlpha )
227 {
228 if ( s1.aStep )
229 {
230 const int a = int( s1.a0 + ratio * s1.aStep );
231 return qRgba( r, g, b, a );
232 }
233 else
234 {
235 return qRgba( r, g, b, s1.a );
236 }
237 }
238 else
239 {
240 return qRgb( r, g, b );
241 }
242 }
243}
244
250 : m_format( format )
251{
252}
253
258
265{
266 m_format = format;
267}
268
278uint QwtColorMap::colorIndex( int numColors,
279 const QwtInterval& interval, double value ) const
280{
281 const double width = interval.width();
282 if ( width <= 0.0 )
283 return 0;
284
285 if ( value <= interval.minValue() )
286 return 0;
287
288 const int maxIndex = numColors - 1;
289 if ( value >= interval.maxValue() )
290 return maxIndex;
291
292 const double v = maxIndex * ( ( value - interval.minValue() ) / width );
293 return static_cast< unsigned int >( v + 0.5 );
294}
295
305{
306 QVector< QRgb > table( 256 );
307
308 const QwtInterval interval( 0, 256 );
309
310 for ( int i = 0; i < 256; i++ )
311 table[i] = rgb( interval, i );
312
313 return table;
314}
315
326{
327 QVector< QRgb > table( numColors );
328
329 const QwtInterval interval( 0.0, 1.0 );
330
331 const double step = 1.0 / ( numColors - 1 );
332 for ( int i = 0; i < numColors; i++ )
333 table[i] = rgb( interval, step * i );
334
335 return table;
336}
337
338class QwtLinearColorMap::PrivateData
339{
340 public:
341 ColorStops colorStops;
343};
344
352 QwtColorMap( format )
353{
354 m_data = new PrivateData;
355 m_data->mode = ScaledColors;
356
357 setColorInterval( Qt::blue, Qt::yellow );
358}
359
368 const QColor& color2, QwtColorMap::Format format )
369 : QwtColorMap( format )
370{
371 m_data = new PrivateData;
372 m_data->mode = ScaledColors;
374}
375
378{
379 delete m_data;
380}
381
392{
393 m_data->mode = mode;
394}
395
401{
402 return m_data->mode;
403}
404
416 const QColor& color1, const QColor& color2 )
417{
418 m_data->colorStops = ColorStops();
419 m_data->colorStops.insert( 0.0, color1 );
420 m_data->colorStops.insert( 1.0, color2 );
421}
422
433void QwtLinearColorMap::addColorStop( double value, const QColor& color )
434{
435 if ( value >= 0.0 && value <= 1.0 )
436 m_data->colorStops.insert( value, color );
437}
438
443{
444 return m_data->colorStops.stops();
445}
446
452{
453 return QColor::fromRgba( m_data->colorStops.rgb( m_data->mode, 0.0 ) );
454}
455
461{
462 return QColor::fromRgba( m_data->colorStops.rgb( m_data->mode, 1.0 ) );
463}
464
474 const QwtInterval& interval, double value ) const
475{
476 const double width = interval.width();
477 if ( width <= 0.0 )
478 return 0u;
479
480 const double ratio = ( value - interval.minValue() ) / width;
481 return m_data->colorStops.rgb( m_data->mode, ratio );
482}
483
495 const QwtInterval& interval, double value ) const
496{
497 const double width = interval.width();
498 if ( width <= 0.0 )
499 return 0;
500
501 if ( value <= interval.minValue() )
502 return 0;
503
504 if ( value >= interval.maxValue() )
505 return numColors - 1;
506
507 const double v = ( numColors - 1 ) * ( value - interval.minValue() ) / width;
508 return static_cast< unsigned int >( ( m_data->mode == FixedColors ) ? v : v + 0.5 );
509}
510
511class QwtAlphaColorMap::PrivateData
512{
513 public:
514 PrivateData()
515 : alpha1(0)
516 , alpha2(255)
517 {
518 }
519
520 int alpha1, alpha2;
521
522 QColor color;
523 QRgb rgb;
524
525 QRgb rgbMin;
526 QRgb rgbMax;
527};
528
529
540 : QwtColorMap( QwtColorMap::RGB )
541{
542 m_data = new PrivateData;
543 setColor( color );
544}
545
548{
549 delete m_data;
550}
551
558void QwtAlphaColorMap::setColor( const QColor& color )
559{
560 m_data->color = color;
561 m_data->rgb = color.rgb() & qRgba( 255, 255, 255, 0 );
562
563 m_data->rgbMin = m_data->rgb | ( m_data->alpha1 << 24 );
564 m_data->rgbMax = m_data->rgb | ( m_data->alpha2 << 24 );
565}
566
572{
573 return m_data->color;
574}
575
587void QwtAlphaColorMap::setAlphaInterval( int alpha1, int alpha2 )
588{
589 m_data->alpha1 = qBound( 0, alpha1, 255 );
590 m_data->alpha2 = qBound( 0, alpha2, 255 );
591
592 m_data->rgbMin = m_data->rgb | ( alpha1 << 24 );
593 m_data->rgbMax = m_data->rgb | ( alpha2 << 24 );
594}
595
601{
602 return m_data->alpha1;
603}
604
610{
611 return m_data->alpha2;
612}
613
622QRgb QwtAlphaColorMap::rgb( const QwtInterval& interval, double value ) const
623{
624 const double width = interval.width();
625 if ( width <= 0.0 )
626 return 0u;
627
628 if ( value <= interval.minValue() )
629 return m_data->rgb;
630
631 if ( value >= interval.maxValue() )
632 return m_data->rgbMax;
633
634 const double ratio = ( value - interval.minValue() ) / width;
635 const int alpha = m_data->alpha1 + qRound( ratio * ( m_data->alpha2 - m_data->alpha1 ) );
636
637 return m_data->rgb | ( alpha << 24 );
638}
639
640class QwtHueColorMap::PrivateData
641{
642 public:
643 PrivateData();
644
645 void updateTable();
646
647 int hue1, hue2;
648 int saturation;
649 int value;
650 int alpha;
651
652 QRgb rgbMin;
653 QRgb rgbMax;
654
655 QRgb rgbTable[360];
656};
657
658QwtHueColorMap::PrivateData::PrivateData()
659 : hue1(0)
660 , hue2(359)
661 , saturation(255)
662 , value(255)
663 , alpha(255)
664{
665 updateTable();
666}
667
668void QwtHueColorMap::PrivateData::updateTable()
669{
670 const int p = qRound( value * ( 255 - saturation ) / 255.0 );
671 const double vs = value * saturation / 255.0;
672
673 for ( int i = 0; i < 60; i++ )
674 {
675 const double r = ( 60 - i ) / 60.0;
676 rgbTable[i] = qRgba( value, qRound( value - r * vs ), p, alpha );
677 }
678
679 for ( int i = 60; i < 120; i++ )
680 {
681 const double r = ( i - 60 ) / 60.0;
682 rgbTable[i] = qRgba( qRound( value - r * vs ), value, p, alpha );
683 }
684
685 for ( int i = 120; i < 180; i++ )
686 {
687 const double r = ( 180 - i ) / 60.0;
688 rgbTable[i] = qRgba( p, value, qRound( value - r * vs ), alpha );
689 }
690
691 for ( int i = 180; i < 240; i++ )
692 {
693 const double r = ( i - 180 ) / 60.0;
694 rgbTable[i] = qRgba( p, qRound( value - r * vs ), value, alpha );
695 }
696
697 for ( int i = 240; i < 300; i++ )
698 {
699 const double r = ( 300 - i ) / 60.0;
700 rgbTable[i] = qRgba( qRound( value - r * vs ), p, value, alpha );
701 }
702
703 for ( int i = 300; i < 360; i++ )
704 {
705 const double r = ( i - 300 ) / 60.0;
706 rgbTable[i] = qRgba( value, p, qRound( value - r * vs ), alpha );
707 }
708
709 rgbMin = rgbTable[ hue1 % 360 ];
710 rgbMax = rgbTable[ hue2 % 360 ];
711}
712
724 : QwtColorMap( format )
725{
726 m_data = new PrivateData;
727}
728
731{
732 delete m_data;
733}
734
746void QwtHueColorMap::setHueInterval( int hue1, int hue2 )
747{
748 m_data->hue1 = qMax( hue1, 0 );
749 m_data->hue2 = qMax( hue2, 0 );
750
751 m_data->rgbMin = m_data->rgbTable[ hue1 % 360 ];
752 m_data->rgbMax = m_data->rgbTable[ hue2 % 360 ];
753}
754
764void QwtHueColorMap::setSaturation( int saturation )
765{
766 saturation = qBound( 0, saturation, 255 );
767
768 if ( saturation != m_data->saturation )
769 {
770 m_data->saturation = saturation;
771 m_data->updateTable();
772 }
773}
774
785{
786 value = qBound( 0, value, 255 );
787
788 if ( value != m_data->value )
789 {
790 m_data->value = value;
791 m_data->updateTable();
792 }
793}
794
806{
807 alpha = qBound( 0, alpha, 255 );
808
809 if ( alpha != m_data->alpha )
810 {
811 m_data->alpha = alpha;
812 m_data->updateTable();
813 }
814}
815
821{
822 return m_data->hue1;
823}
824
830{
831 return m_data->hue2;
832}
833
839{
840 return m_data->saturation;
841}
842
848{
849 return m_data->value;
850}
851
857{
858 return m_data->alpha;
859}
860
869QRgb QwtHueColorMap::rgb( const QwtInterval& interval, double value ) const
870{
871 const double width = interval.width();
872 if ( width <= 0 )
873 return 0u;
874
875 if ( value <= interval.minValue() )
876 return m_data->rgbMin;
877
878 if ( value >= interval.maxValue() )
879 return m_data->rgbMax;
880
881 const double ratio = ( value - interval.minValue() ) / width;
882
883 int hue = m_data->hue1 + qRound( ratio * ( m_data->hue2 - m_data->hue1 ) );
884 if ( hue >= 360 )
885 {
886 hue -= 360;
887
888 if ( hue >= 360 )
889 hue = hue % 360;
890 }
891
892 return m_data->rgbTable[hue];
893}
894
895class QwtSaturationValueColorMap::PrivateData
896{
897 public:
898 PrivateData()
899 : hue(0)
900 , sat1(255)
901 , sat2(255)
902 , value1(0)
903 , value2(255)
904 , alpha(255)
905 , tableType(Invalid)
906 {
907 updateTable();
908 }
909
910 void updateTable()
911 {
912 tableType = Invalid;
913
914 if ( ( value1 == value2 ) && ( sat1 != sat2 ) )
915 {
916 rgbTable.resize( 256 );
917
918 for ( int i = 0; i < 256; i++ )
919 rgbTable[i] = qwtHsvToRgb( hue, i, value1, alpha );
920
921 tableType = Saturation;
922 }
923 else if ( ( value1 != value2 ) && ( sat1 == sat2 ) )
924 {
925 rgbTable.resize( 256 );
926
927 for ( int i = 0; i < 256; i++ )
928 rgbTable[i] = qwtHsvToRgb( hue, sat1, i, alpha );
929
930 tableType = Value;
931 }
932 else
933 {
934 rgbTable.resize( 256 * 256 );
935
936 for ( int s = 0; s < 256; s++ )
937 {
938 const int v0 = s * 256;
939
940 for ( int v = 0; v < 256; v++ )
941 rgbTable[v0 + v] = qwtHsvToRgb( hue, s, v, alpha );
942 }
943 }
944 }
945
946 int hue;
947 int sat1, sat2;
948 int value1, value2;
949 int alpha;
950
951 enum
952 {
953 Invalid,
954 Value,
955 Saturation
956
957 } tableType;
958
959 QVector< QRgb > rgbTable;
960};
961
973{
974 m_data = new PrivateData;
975}
976
982
993{
994 hue = hue % 360;
995
996 if ( hue != m_data->hue )
997 {
998 m_data->hue = hue;
999 m_data->updateTable();
1000 }
1001}
1002
1017 int saturation1, int saturation2 )
1018{
1019 saturation1 = qBound( 0, saturation1, 255 );
1020 saturation2 = qBound( 0, saturation2, 255 );
1021
1022 if ( ( saturation1 != m_data->sat1 ) || ( saturation2 != m_data->sat2 ) )
1023 {
1024 m_data->sat1 = saturation1;
1025 m_data->sat2 = saturation2;
1026
1027 m_data->updateTable();
1028 }
1029}
1030
1044{
1045 value1 = qBound( 0, value1, 255 );
1046 value2 = qBound( 0, value2, 255 );
1047
1048 if ( ( value1 != m_data->value1 ) || ( value2 != m_data->value2 ) )
1049 {
1050 m_data->value1 = value1;
1051 m_data->value2 = value2;
1052
1053 m_data->updateTable();
1054 }
1055}
1056
1068{
1069 alpha = qBound( 0, alpha, 255 );
1070
1071 if ( alpha != m_data->alpha )
1072 {
1073 m_data->alpha = alpha;
1074 m_data->updateTable();
1075 }
1076}
1077
1083{
1084 return m_data->hue;
1085}
1086
1092{
1093 return m_data->sat1;
1094}
1095
1101{
1102 return m_data->sat2;
1103}
1104
1110{
1111 return m_data->value1;
1112}
1113
1119{
1120 return m_data->value2;
1121}
1122
1128{
1129 return m_data->alpha;
1130}
1131
1141 const QwtInterval& interval, double value ) const
1142{
1143 const double width = interval.width();
1144 if ( width <= 0 )
1145 return 0u;
1146
1147 const QRgb* rgbTable = m_data->rgbTable.constData();
1148
1149 switch( m_data->tableType )
1150 {
1151 case PrivateData::Saturation:
1152 {
1153 if ( value <= interval.minValue() )
1154 return m_data->rgbTable[m_data->sat1];
1155
1156 if ( value >= interval.maxValue() )
1157 return m_data->rgbTable[m_data->sat2];
1158
1159 const double ratio = ( value - interval.minValue() ) / width;
1160 const int sat = m_data->sat1
1161 + qRound( ratio * ( m_data->sat2 - m_data->sat1 ) );
1162
1163 return rgbTable[sat];
1164 }
1165 case PrivateData::Value:
1166 {
1167 if ( value <= interval.minValue() )
1168 return m_data->rgbTable[m_data->value1];
1169
1170 if ( value >= interval.maxValue() )
1171 return m_data->rgbTable[m_data->value2];
1172
1173 const double ratio = ( value - interval.minValue() ) / width;
1174 const int v = m_data->value1 +
1175 qRound( ratio * ( m_data->value2 - m_data->value1 ) );
1176
1177 return rgbTable[ v ];
1178 }
1179 default:
1180 {
1181 int s, v;
1182 if ( value <= interval.minValue() )
1183 {
1184 s = m_data->sat1;
1185 v = m_data->value1;
1186 }
1187 else if ( value >= interval.maxValue() )
1188 {
1189 s = m_data->sat2;
1190 v = m_data->value2;
1191 }
1192 else
1193 {
1194 const double ratio = ( value - interval.minValue() ) / width;
1195
1196 v = m_data->value1 + qRound( ratio * ( m_data->value2 - m_data->value1 ) );
1197 s = m_data->sat1 + qRound( ratio * ( m_data->sat2 - m_data->sat1 ) );
1198 }
1199
1200 return rgbTable[ 256 * s + v ];
1201 }
1202 }
1203}
virtual QRgb rgb(const QwtInterval &, double value) const override
Map a value of a given interval into a alpha value.
void setColor(const QColor &)
void setAlphaInterval(int alpha1, int alpha2)
QColor color() const
virtual ~QwtAlphaColorMap()
Destructor.
QwtAlphaColorMap(const QColor &=QColor(Qt::gray))
Constructor.
QwtColorMap is used to map values into colors.
virtual uint colorIndex(int numColors, const QwtInterval &interval, double value) const
Map a value of a given interval into a color index.
QwtColorMap(Format=QwtColorMap::RGB)
QColor color(const QwtInterval &, double value) const
virtual QVector< QRgb > colorTable(int numColors) const
virtual QVector< QRgb > colorTable256() const
virtual QRgb rgb(const QwtInterval &interval, double value) const =0
void setFormat(Format)
virtual ~QwtColorMap()
Destructor.
QwtHueColorMap(QwtColorMap::Format=QwtColorMap::RGB)
Constructor.
int alpha() const
void setSaturation(int saturation)
Set the the saturation coordinate.
int value() const
void setHueInterval(int hue1, int hue2)
void setAlpha(int alpha)
Set the the alpha coordinate.
virtual ~QwtHueColorMap()
Destructor.
virtual QRgb rgb(const QwtInterval &, double value) const override
int saturation() const
void setValue(int value)
Set the the value coordinate.
A class representing an interval.
double minValue() const
double width() const
Return the width of an interval.
double maxValue() const
QwtLinearColorMap(QwtColorMap::Format=QwtColorMap::RGB)
QVector< double > colorStops() const
virtual uint colorIndex(int numColors, const QwtInterval &, double value) const override
Map a value of a given interval into a color index.
virtual ~QwtLinearColorMap()
Destructor.
void addColorStop(double value, const QColor &)
virtual QRgb rgb(const QwtInterval &, double value) const override
void setColorInterval(const QColor &color1, const QColor &color2)
@ ScaledColors
Interpolating the colors of the adjacent stops.
@ FixedColors
Return the color from the next lower color stop.
QColor color2() const
QColor color1() const
void setMode(Mode)
Set the mode of the color map.
void setSaturationInterval(int sat1, int sat2)
Set the interval for the saturation coordinate.
void setAlpha(int alpha)
Set the the alpha coordinate.
virtual QRgb rgb(const QwtInterval &, double value) const override
QwtSaturationValueColorMap()
Constructor.
virtual ~QwtSaturationValueColorMap()
Destructor.
void setValueInterval(int value1, int value2)
Set the interval for the value coordinate.
void setHue(int hue)
Set the the hue coordinate.