Chris@376
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@376
|
2
|
Chris@376
|
3 /*
|
Chris@376
|
4 Sonic Visualiser
|
Chris@376
|
5 An audio file viewer and annotation editor.
|
Chris@376
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@376
|
7 This file copyright 2006-2007 Chris Cannam and QMUL.
|
Chris@376
|
8
|
Chris@376
|
9 This program is free software; you can redistribute it and/or
|
Chris@376
|
10 modify it under the terms of the GNU General Public License as
|
Chris@376
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@376
|
12 License, or (at your option) any later version. See the file
|
Chris@376
|
13 COPYING included with this distribution for more information.
|
Chris@376
|
14 */
|
Chris@376
|
15
|
Chris@376
|
16 #include "ColourMapper.h"
|
Chris@376
|
17
|
Chris@376
|
18 #include <iostream>
|
Chris@376
|
19
|
Chris@376
|
20 #include <cmath>
|
Chris@376
|
21
|
Chris@682
|
22 #include "base/Debug.h"
|
Chris@682
|
23
|
Chris@1012
|
24 #include <vector>
|
Chris@1012
|
25
|
Chris@1012
|
26 using namespace std;
|
Chris@1012
|
27
|
Chris@1012
|
28 static vector<QString> ylGnBuS {
|
Chris@1012
|
29 // this is ylGnBu 9
|
Chris@1012
|
30 // "#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4","#4eb3d3","#2b8cbe","#0868ac","#084081"
|
Chris@1012
|
31
|
Chris@1012
|
32 "#ffffff", "#ffff00", "#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4","#4eb3d3","#2b8cbe","#0868ac","#084081","#042040"
|
Chris@1012
|
33
|
Chris@1012
|
34 // this is PuOr 11
|
Chris@1012
|
35 // "#7f3b08","#b35806","#e08214","#fdb863","#fee0b6","#f7f7f7","#d8daeb","#b2abd2","#8073ac","#542788","#2d004b"
|
Chris@1012
|
36 };
|
Chris@1012
|
37
|
Chris@1012
|
38
|
Chris@1012
|
39 static vector<QColor> convertStrings(const vector<QString> &strs)
|
Chris@1012
|
40 {
|
Chris@1012
|
41 vector<QColor> converted;
|
Chris@1012
|
42 for (const auto &s: strs) converted.push_back(QColor(s));
|
Chris@1012
|
43 reverse(converted.begin(), converted.end());
|
Chris@1012
|
44 return converted;
|
Chris@1012
|
45 }
|
Chris@1012
|
46
|
Chris@1012
|
47 static vector<QColor> ylGnBu = convertStrings(ylGnBuS);
|
Chris@1012
|
48
|
Chris@1012
|
49 static void
|
Chris@1012
|
50 mapDiscrete(double norm, vector<QColor> &colours, double &r, double &g, double &b)
|
Chris@1012
|
51 {
|
Chris@1012
|
52 int n = colours.size();
|
Chris@1012
|
53 double m = norm * (n-1);
|
Chris@1012
|
54 if (m >= n-1) { colours[n-1].getRgbF(&r, &g, &b, 0); return; }
|
Chris@1012
|
55 if (m <= 0) { colours[0].getRgbF(&r, &g, &b, 0); return; }
|
Chris@1012
|
56 int base(int(floor(m)));
|
Chris@1012
|
57 double prop0 = (base + 1.0) - m, prop1 = m - base;
|
Chris@1012
|
58 QColor c0(colours[base]), c1(colours[base+1]);
|
Chris@1012
|
59 r = c0.redF() * prop0 + c1.redF() * prop1;
|
Chris@1012
|
60 g = c0.greenF() * prop0 + c1.greenF() * prop1;
|
Chris@1012
|
61 b = c0.blueF() * prop0 + c1.blueF() * prop1;
|
Chris@1012
|
62 cerr << "mapDiscrete: norm " << norm << " -> " << r << "," << g << "," << b << endl;
|
Chris@1012
|
63 }
|
Chris@1012
|
64
|
Chris@902
|
65 ColourMapper::ColourMapper(int map, double min, double max) :
|
Chris@376
|
66 QObject(),
|
Chris@376
|
67 m_map(map),
|
Chris@376
|
68 m_min(min),
|
Chris@376
|
69 m_max(max)
|
Chris@376
|
70 {
|
Chris@376
|
71 if (m_min == m_max) {
|
Chris@682
|
72 cerr << "WARNING: ColourMapper: min == max (== " << m_min
|
Chris@682
|
73 << "), adjusting" << endl;
|
Chris@376
|
74 m_max = m_min + 1;
|
Chris@376
|
75 }
|
Chris@376
|
76 }
|
Chris@376
|
77
|
Chris@376
|
78 ColourMapper::~ColourMapper()
|
Chris@376
|
79 {
|
Chris@376
|
80 }
|
Chris@376
|
81
|
Chris@376
|
82 int
|
Chris@376
|
83 ColourMapper::getColourMapCount()
|
Chris@376
|
84 {
|
Chris@1012
|
85 return 13;
|
Chris@376
|
86 }
|
Chris@376
|
87
|
Chris@376
|
88 QString
|
Chris@376
|
89 ColourMapper::getColourMapName(int n)
|
Chris@376
|
90 {
|
Chris@376
|
91 if (n >= getColourMapCount()) return tr("<unknown>");
|
Chris@376
|
92 StandardMap map = (StandardMap)n;
|
Chris@376
|
93
|
Chris@376
|
94 switch (map) {
|
Chris@376
|
95 case DefaultColours: return tr("Default");
|
Chris@376
|
96 case WhiteOnBlack: return tr("White on Black");
|
Chris@376
|
97 case BlackOnWhite: return tr("Black on White");
|
Chris@376
|
98 case RedOnBlue: return tr("Red on Blue");
|
Chris@376
|
99 case YellowOnBlack: return tr("Yellow on Black");
|
Chris@376
|
100 case BlueOnBlack: return tr("Blue on Black");
|
Chris@376
|
101 case Sunset: return tr("Sunset");
|
Chris@376
|
102 case FruitSalad: return tr("Fruit Salad");
|
Chris@376
|
103 case Banded: return tr("Banded");
|
Chris@376
|
104 case Highlight: return tr("Highlight");
|
Chris@376
|
105 case Printer: return tr("Printer");
|
Chris@536
|
106 case HighGain: return tr("High Gain");
|
Chris@1012
|
107 case YlGnBu: return tr("YlGnBu");
|
Chris@376
|
108 }
|
Chris@376
|
109
|
Chris@376
|
110 return tr("<unknown>");
|
Chris@376
|
111 }
|
Chris@376
|
112
|
Chris@376
|
113 QColor
|
Chris@902
|
114 ColourMapper::map(double value) const
|
Chris@376
|
115 {
|
Chris@902
|
116 double norm = (value - m_min) / (m_max - m_min);
|
Chris@902
|
117 if (norm < 0.0) norm = 0.0;
|
Chris@902
|
118 if (norm > 1.0) norm = 1.0;
|
Chris@376
|
119
|
Chris@902
|
120 double h = 0.0, s = 0.0, v = 0.0, r = 0.0, g = 0.0, b = 0.0;
|
Chris@376
|
121 bool hsv = true;
|
Chris@376
|
122
|
Chris@911
|
123 double blue = 0.6666, pieslice = 0.3333;
|
Chris@376
|
124
|
Chris@376
|
125 if (m_map >= getColourMapCount()) return Qt::black;
|
Chris@376
|
126 StandardMap map = (StandardMap)m_map;
|
Chris@376
|
127
|
Chris@376
|
128 switch (map) {
|
Chris@376
|
129
|
Chris@376
|
130 case DefaultColours:
|
Chris@902
|
131 h = blue - norm * 2.0 * pieslice;
|
Chris@902
|
132 s = 0.5f + norm/2.0;
|
Chris@376
|
133 v = norm;
|
Chris@376
|
134 break;
|
Chris@376
|
135
|
Chris@376
|
136 case WhiteOnBlack:
|
Chris@376
|
137 r = g = b = norm;
|
Chris@376
|
138 hsv = false;
|
Chris@376
|
139 break;
|
Chris@376
|
140
|
Chris@376
|
141 case BlackOnWhite:
|
Chris@902
|
142 r = g = b = 1.0 - norm;
|
Chris@376
|
143 hsv = false;
|
Chris@376
|
144 break;
|
Chris@376
|
145
|
Chris@376
|
146 case RedOnBlue:
|
Chris@902
|
147 h = blue - pieslice/4.0 + norm * (pieslice + pieslice/4.0);
|
Chris@902
|
148 s = 1.0;
|
Chris@376
|
149 v = norm;
|
Chris@376
|
150 break;
|
Chris@376
|
151
|
Chris@376
|
152 case YellowOnBlack:
|
Chris@902
|
153 h = 0.15;
|
Chris@902
|
154 s = 1.0;
|
Chris@376
|
155 v = norm;
|
Chris@376
|
156 break;
|
Chris@376
|
157
|
Chris@376
|
158 case BlueOnBlack:
|
Chris@376
|
159 h = blue;
|
Chris@902
|
160 s = 1.0;
|
Chris@902
|
161 v = norm * 2.0;
|
Chris@902
|
162 if (v > 1.0) {
|
Chris@902
|
163 v = 1.0;
|
Chris@904
|
164 s = 1.0 - (sqrt(norm) - 0.707) * 3.413;
|
Chris@902
|
165 if (s < 0.0) s = 0.0;
|
Chris@902
|
166 if (s > 1.0) s = 1.0;
|
Chris@376
|
167 }
|
Chris@376
|
168 break;
|
Chris@376
|
169
|
Chris@376
|
170 case Sunset:
|
Chris@902
|
171 r = (norm - 0.24) * 2.38;
|
Chris@902
|
172 if (r > 1.0) r = 1.0;
|
Chris@902
|
173 if (r < 0.0) r = 0.0;
|
Chris@902
|
174 g = (norm - 0.64) * 2.777;
|
Chris@902
|
175 if (g > 1.0) g = 1.0;
|
Chris@902
|
176 if (g < 0.0) g = 0.0;
|
Chris@376
|
177 b = (3.6f * norm);
|
Chris@902
|
178 if (norm > 0.277) b = 2.0 - b;
|
Chris@902
|
179 if (b > 1.0) b = 1.0;
|
Chris@902
|
180 if (b < 0.0) b = 0.0;
|
Chris@376
|
181 hsv = false;
|
Chris@376
|
182 break;
|
Chris@376
|
183
|
Chris@376
|
184 case FruitSalad:
|
Chris@902
|
185 h = blue + (pieslice/6.0) - norm;
|
Chris@902
|
186 if (h < 0.0) h += 1.0;
|
Chris@902
|
187 s = 1.0;
|
Chris@902
|
188 v = 1.0;
|
Chris@376
|
189 break;
|
Chris@376
|
190
|
Chris@376
|
191 case Banded:
|
Chris@376
|
192 if (norm < 0.125) return Qt::darkGreen;
|
Chris@376
|
193 else if (norm < 0.25) return Qt::green;
|
Chris@376
|
194 else if (norm < 0.375) return Qt::darkBlue;
|
Chris@376
|
195 else if (norm < 0.5) return Qt::blue;
|
Chris@376
|
196 else if (norm < 0.625) return Qt::darkYellow;
|
Chris@376
|
197 else if (norm < 0.75) return Qt::yellow;
|
Chris@376
|
198 else if (norm < 0.875) return Qt::darkRed;
|
Chris@376
|
199 else return Qt::red;
|
Chris@376
|
200 break;
|
Chris@376
|
201
|
Chris@376
|
202 case Highlight:
|
Chris@376
|
203 if (norm > 0.99) return Qt::white;
|
Chris@376
|
204 else return Qt::darkBlue;
|
Chris@376
|
205
|
Chris@376
|
206 case Printer:
|
Chris@376
|
207 if (norm > 0.8) {
|
Chris@902
|
208 r = 1.0;
|
Chris@376
|
209 } else if (norm > 0.7) {
|
Chris@902
|
210 r = 0.9;
|
Chris@376
|
211 } else if (norm > 0.6) {
|
Chris@902
|
212 r = 0.8;
|
Chris@376
|
213 } else if (norm > 0.5) {
|
Chris@902
|
214 r = 0.7;
|
Chris@376
|
215 } else if (norm > 0.4) {
|
Chris@902
|
216 r = 0.6;
|
Chris@376
|
217 } else if (norm > 0.3) {
|
Chris@902
|
218 r = 0.5;
|
Chris@376
|
219 } else if (norm > 0.2) {
|
Chris@902
|
220 r = 0.4;
|
Chris@376
|
221 } else {
|
Chris@902
|
222 r = 0.0;
|
Chris@376
|
223 }
|
Chris@902
|
224 r = g = b = 1.0 - r;
|
Chris@376
|
225 hsv = false;
|
Chris@376
|
226 break;
|
Chris@536
|
227
|
Chris@536
|
228 case HighGain:
|
Chris@902
|
229 if (norm <= 1.0 / 256.0) {
|
Chris@902
|
230 norm = 0.0;
|
Chris@536
|
231 } else {
|
Chris@904
|
232 norm = 0.1f + (pow(((norm - 0.5) * 2.0), 3.0) + 1.0) / 2.081;
|
Chris@536
|
233 }
|
Chris@536
|
234 // now as for Sunset
|
Chris@902
|
235 r = (norm - 0.24) * 2.38;
|
Chris@902
|
236 if (r > 1.0) r = 1.0;
|
Chris@902
|
237 if (r < 0.0) r = 0.0;
|
Chris@902
|
238 g = (norm - 0.64) * 2.777;
|
Chris@902
|
239 if (g > 1.0) g = 1.0;
|
Chris@902
|
240 if (g < 0.0) g = 0.0;
|
Chris@536
|
241 b = (3.6f * norm);
|
Chris@902
|
242 if (norm > 0.277) b = 2.0 - b;
|
Chris@902
|
243 if (b > 1.0) b = 1.0;
|
Chris@902
|
244 if (b < 0.0) b = 0.0;
|
Chris@536
|
245 hsv = false;
|
Chris@536
|
246 /*
|
Chris@902
|
247 if (r > 1.0) r = 1.0;
|
Chris@902
|
248 r = g = b = 1.0 - r;
|
Chris@536
|
249 hsv = false;
|
Chris@536
|
250 */
|
Chris@536
|
251 break;
|
Chris@1012
|
252
|
Chris@1012
|
253 case YlGnBu:
|
Chris@1012
|
254 hsv = false;
|
Chris@1012
|
255 mapDiscrete(norm, ylGnBu, r, g, b);
|
Chris@376
|
256 }
|
Chris@376
|
257
|
Chris@376
|
258 if (hsv) {
|
Chris@376
|
259 return QColor::fromHsvF(h, s, v);
|
Chris@376
|
260 } else {
|
Chris@376
|
261 return QColor::fromRgbF(r, g, b);
|
Chris@376
|
262 }
|
Chris@376
|
263 }
|
Chris@376
|
264
|
Chris@376
|
265 QColor
|
Chris@376
|
266 ColourMapper::getContrastingColour() const
|
Chris@376
|
267 {
|
Chris@376
|
268 if (m_map >= getColourMapCount()) return Qt::white;
|
Chris@376
|
269 StandardMap map = (StandardMap)m_map;
|
Chris@376
|
270
|
Chris@376
|
271 switch (map) {
|
Chris@376
|
272
|
Chris@376
|
273 case DefaultColours:
|
Chris@376
|
274 return QColor(255, 150, 50);
|
Chris@376
|
275
|
Chris@376
|
276 case WhiteOnBlack:
|
Chris@376
|
277 return Qt::red;
|
Chris@376
|
278
|
Chris@376
|
279 case BlackOnWhite:
|
Chris@376
|
280 return Qt::darkGreen;
|
Chris@376
|
281
|
Chris@376
|
282 case RedOnBlue:
|
Chris@376
|
283 return Qt::green;
|
Chris@376
|
284
|
Chris@376
|
285 case YellowOnBlack:
|
Chris@376
|
286 return QColor::fromHsv(240, 255, 255);
|
Chris@376
|
287
|
Chris@376
|
288 case BlueOnBlack:
|
Chris@376
|
289 return Qt::red;
|
Chris@376
|
290
|
Chris@376
|
291 case Sunset:
|
Chris@376
|
292 return Qt::white;
|
Chris@376
|
293
|
Chris@376
|
294 case FruitSalad:
|
Chris@376
|
295 return Qt::white;
|
Chris@376
|
296
|
Chris@376
|
297 case Banded:
|
Chris@376
|
298 return Qt::cyan;
|
Chris@376
|
299
|
Chris@376
|
300 case Highlight:
|
Chris@376
|
301 return Qt::red;
|
Chris@376
|
302
|
Chris@376
|
303 case Printer:
|
Chris@376
|
304 return Qt::red;
|
Chris@536
|
305
|
Chris@536
|
306 case HighGain:
|
Chris@536
|
307 return Qt::red;
|
Chris@376
|
308 }
|
Chris@376
|
309
|
Chris@376
|
310 return Qt::white;
|
Chris@376
|
311 }
|
Chris@376
|
312
|
Chris@376
|
313 bool
|
Chris@376
|
314 ColourMapper::hasLightBackground() const
|
Chris@376
|
315 {
|
Chris@376
|
316 if (m_map >= getColourMapCount()) return false;
|
Chris@376
|
317 StandardMap map = (StandardMap)m_map;
|
Chris@376
|
318
|
Chris@376
|
319 switch (map) {
|
Chris@376
|
320
|
Chris@376
|
321 case BlackOnWhite:
|
Chris@376
|
322 case Printer:
|
Chris@536
|
323 case HighGain:
|
Chris@376
|
324 return true;
|
Chris@376
|
325
|
Chris@805
|
326 case DefaultColours:
|
Chris@805
|
327 case Sunset:
|
Chris@805
|
328 case WhiteOnBlack:
|
Chris@805
|
329 case RedOnBlue:
|
Chris@805
|
330 case YellowOnBlack:
|
Chris@805
|
331 case BlueOnBlack:
|
Chris@805
|
332 case FruitSalad:
|
Chris@805
|
333 case Banded:
|
Chris@805
|
334 case Highlight:
|
Chris@805
|
335
|
Chris@376
|
336 default:
|
Chris@376
|
337 return false;
|
Chris@376
|
338 }
|
Chris@376
|
339 }
|
Chris@376
|
340
|
Chris@376
|
341
|