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 2007 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 "ColourDatabase.h"
|
Chris@376
|
17 #include "base/XmlExportable.h"
|
Chris@376
|
18
|
Chris@376
|
19 #include <QPainter>
|
Chris@376
|
20
|
Chris@1445
|
21 //#define DEBUG_COLOUR_DATABASE 1
|
Chris@1445
|
22
|
Chris@376
|
23 ColourDatabase
|
Chris@376
|
24 ColourDatabase::m_instance;
|
Chris@376
|
25
|
Chris@376
|
26 ColourDatabase *
|
Chris@376
|
27 ColourDatabase::getInstance()
|
Chris@376
|
28 {
|
Chris@376
|
29 return &m_instance;
|
Chris@376
|
30 }
|
Chris@376
|
31
|
Chris@376
|
32 ColourDatabase::ColourDatabase()
|
Chris@376
|
33 {
|
Chris@376
|
34 }
|
Chris@376
|
35
|
Chris@376
|
36 int
|
Chris@376
|
37 ColourDatabase::getColourCount() const
|
Chris@376
|
38 {
|
Chris@904
|
39 return int(m_colours.size());
|
Chris@376
|
40 }
|
Chris@376
|
41
|
Chris@376
|
42 QString
|
Chris@376
|
43 ColourDatabase::getColourName(int c) const
|
Chris@376
|
44 {
|
Chris@904
|
45 if (!in_range_for(m_colours, c)) return "";
|
Chris@376
|
46 return m_colours[c].name;
|
Chris@376
|
47 }
|
Chris@376
|
48
|
Chris@376
|
49 QColor
|
Chris@376
|
50 ColourDatabase::getColour(int c) const
|
Chris@376
|
51 {
|
Chris@904
|
52 if (!in_range_for(m_colours, c)) return Qt::black;
|
Chris@376
|
53 return m_colours[c].colour;
|
Chris@376
|
54 }
|
Chris@376
|
55
|
Chris@376
|
56 QColor
|
Chris@376
|
57 ColourDatabase::getColour(QString name) const
|
Chris@376
|
58 {
|
Chris@904
|
59 for (auto &c: m_colours) {
|
Chris@904
|
60 if (c.name == name) return c.colour;
|
Chris@376
|
61 }
|
Chris@376
|
62
|
Chris@376
|
63 return Qt::black;
|
Chris@376
|
64 }
|
Chris@376
|
65
|
Chris@376
|
66 int
|
Chris@376
|
67 ColourDatabase::getColourIndex(QString name) const
|
Chris@376
|
68 {
|
Chris@376
|
69 int index = 0;
|
Chris@904
|
70 for (auto &c: m_colours) {
|
Chris@904
|
71 if (c.name == name) return index;
|
Chris@376
|
72 ++index;
|
Chris@376
|
73 }
|
Chris@376
|
74
|
Chris@376
|
75 return -1;
|
Chris@376
|
76 }
|
Chris@376
|
77
|
Chris@376
|
78 int
|
Chris@904
|
79 ColourDatabase::getColourIndex(QColor col) const
|
Chris@376
|
80 {
|
Chris@376
|
81 int index = 0;
|
Chris@904
|
82 for (auto &c: m_colours) {
|
Chris@904
|
83 if (c.colour == col) return index;
|
Chris@376
|
84 ++index;
|
Chris@376
|
85 }
|
Chris@376
|
86
|
Chris@376
|
87 return -1;
|
Chris@376
|
88 }
|
Chris@376
|
89
|
Chris@1445
|
90 int
|
Chris@1578
|
91 ColourDatabase::getNearbyColourIndex(QColor col, WithBackgroundMode mode) const
|
Chris@1445
|
92 {
|
Chris@1578
|
93 int index = -1;
|
Chris@1445
|
94 int closestIndex = -1;
|
Chris@1578
|
95 double closestDistance = 0;
|
Chris@1445
|
96
|
Chris@1445
|
97 for (auto &c: m_colours) {
|
Chris@1578
|
98
|
Chris@1578
|
99 ++index;
|
Chris@1578
|
100
|
Chris@1578
|
101 if (mode == WithDarkBackground && !c.darkbg) {
|
Chris@1578
|
102 #ifdef DEBUG_COLOUR_DATABASE
|
Chris@1578
|
103 SVDEBUG << "getNearbyColourIndex: dark background requested, skipping " << c.colour.name() << endl;
|
Chris@1578
|
104 #endif
|
Chris@1578
|
105 continue;
|
Chris@1578
|
106 }
|
Chris@1578
|
107 if (mode == WithLightBackground && c.darkbg) {
|
Chris@1578
|
108 #ifdef DEBUG_COLOUR_DATABASE
|
Chris@1578
|
109 SVDEBUG << "getNearbyColourIndex: light background requested, skipping " << c.colour.name() << endl;
|
Chris@1578
|
110 #endif
|
Chris@1578
|
111 continue;
|
Chris@1578
|
112 }
|
Chris@1578
|
113
|
Chris@1578
|
114 // This distance formula is "one of the better low-cost
|
Chris@1578
|
115 // approximations" according to
|
Chris@1578
|
116 // https://en.wikipedia.org/w/index.php?title=Color_difference&oldid=936888327
|
Chris@1578
|
117
|
Chris@1578
|
118 double r1 = col.red(), r2 = c.colour.red();
|
Chris@1578
|
119 double g1 = col.green(), g2 = c.colour.green();
|
Chris@1578
|
120 double b1 = col.blue(), b2 = c.colour.blue();
|
Chris@1578
|
121
|
Chris@1578
|
122 double rav = (r1 + r2) / 2.0;
|
Chris@1578
|
123 double rterm = (2.0 + rav / 256.0) * (r1 - r2) * (r1 - r2);
|
Chris@1578
|
124 double gterm = 4.0 * (g1 - g2) * (g1 - g2);
|
Chris@1578
|
125 double bterm = (2.0 + (255 - rav) / 256.0) * (b1 - b2) * (b1 - b2);
|
Chris@1578
|
126
|
Chris@1578
|
127 double distance = sqrt(rterm + gterm + bterm);
|
Chris@1578
|
128
|
Chris@1445
|
129 #ifdef DEBUG_COLOUR_DATABASE
|
Chris@1445
|
130 SVDEBUG << "getNearbyColourIndex: comparing " << c.colour.name()
|
Chris@1445
|
131 << " to " << col.name() << ": distance = " << distance << endl;
|
Chris@1445
|
132 #endif
|
Chris@1445
|
133 if (closestIndex < 0 || distance < closestDistance) {
|
Chris@1445
|
134 closestIndex = index;
|
Chris@1445
|
135 closestDistance = distance;
|
Chris@1445
|
136 #ifdef DEBUG_COLOUR_DATABASE
|
Chris@1445
|
137 SVDEBUG << "(this is the best so far)" << endl;
|
Chris@1445
|
138 #endif
|
Chris@1445
|
139 }
|
Chris@1445
|
140 }
|
Chris@1445
|
141
|
Chris@1445
|
142 #ifdef DEBUG_COLOUR_DATABASE
|
Chris@1445
|
143 SVDEBUG << "returning " << closestIndex << endl;
|
Chris@1445
|
144 #endif
|
Chris@1445
|
145 return closestIndex;
|
Chris@1445
|
146 }
|
Chris@1445
|
147
|
Chris@1367
|
148 QColor
|
Chris@1367
|
149 ColourDatabase::getContrastingColour(int c) const
|
Chris@1367
|
150 {
|
Chris@1367
|
151 QColor col = getColour(c);
|
Chris@1445
|
152 QColor contrasting = Qt::red;
|
Chris@1445
|
153 bool dark = (col.red() < 240 && col.green() < 240 && col.blue() < 240);
|
Chris@1445
|
154 if (dark) {
|
Chris@1445
|
155 if (col.red() > col.blue()) {
|
Chris@1445
|
156 if (col.green() > col.blue()) {
|
Chris@1445
|
157 contrasting = Qt::blue;
|
Chris@1445
|
158 } else {
|
Chris@1445
|
159 contrasting = Qt::yellow;
|
Chris@1445
|
160 }
|
Chris@1367
|
161 } else {
|
Chris@1445
|
162 if (col.green() > col.blue()) {
|
Chris@1445
|
163 contrasting = Qt::yellow;
|
Chris@1445
|
164 } else {
|
Chris@1445
|
165 contrasting = Qt::red;
|
Chris@1445
|
166 }
|
Chris@1367
|
167 }
|
Chris@1367
|
168 } else {
|
Chris@1445
|
169 if (col.red() > 230 && col.green() > 230 && col.blue() > 230) {
|
Chris@1445
|
170 contrasting = QColor(30, 150, 255);
|
Chris@1367
|
171 } else {
|
Chris@1445
|
172 contrasting = QColor(255, 188, 80);
|
Chris@1367
|
173 }
|
Chris@1367
|
174 }
|
Chris@1445
|
175 #ifdef DEBUG_COLOUR_DATABASE
|
Chris@1445
|
176 SVDEBUG << "getContrastingColour(" << col.name() << "): dark = " << dark
|
Chris@1445
|
177 << ", returning " << contrasting.name() << endl;
|
Chris@1445
|
178 #endif
|
Chris@1445
|
179 return contrasting;
|
Chris@1367
|
180 }
|
Chris@1367
|
181
|
Chris@376
|
182 bool
|
Chris@376
|
183 ColourDatabase::useDarkBackground(int c) const
|
Chris@376
|
184 {
|
Chris@904
|
185 if (!in_range_for(m_colours, c)) return false;
|
Chris@376
|
186 return m_colours[c].darkbg;
|
Chris@376
|
187 }
|
Chris@376
|
188
|
Chris@376
|
189 void
|
Chris@376
|
190 ColourDatabase::setUseDarkBackground(int c, bool dark)
|
Chris@376
|
191 {
|
Chris@904
|
192 if (!in_range_for(m_colours, c)) return;
|
Chris@376
|
193 if (m_colours[c].darkbg != dark) {
|
Chris@376
|
194 m_colours[c].darkbg = dark;
|
Chris@376
|
195 emit colourDatabaseChanged();
|
Chris@376
|
196 }
|
Chris@376
|
197 }
|
Chris@376
|
198
|
Chris@376
|
199 int
|
Chris@376
|
200 ColourDatabase::addColour(QColor c, QString name)
|
Chris@376
|
201 {
|
Chris@376
|
202 int index = 0;
|
Chris@904
|
203
|
Chris@376
|
204 for (ColourList::iterator i = m_colours.begin();
|
Chris@376
|
205 i != m_colours.end(); ++i) {
|
Chris@376
|
206 if (i->name == name) {
|
Chris@376
|
207 i->colour = c;
|
Chris@376
|
208 return index;
|
Chris@376
|
209 }
|
Chris@376
|
210 ++index;
|
Chris@376
|
211 }
|
Chris@376
|
212
|
Chris@376
|
213 ColourRec rec;
|
Chris@376
|
214 rec.colour = c;
|
Chris@376
|
215 rec.name = name;
|
Chris@376
|
216 rec.darkbg = false;
|
Chris@376
|
217 m_colours.push_back(rec);
|
Chris@376
|
218 emit colourDatabaseChanged();
|
Chris@376
|
219 return index;
|
Chris@376
|
220 }
|
Chris@376
|
221
|
Chris@376
|
222 void
|
Chris@376
|
223 ColourDatabase::removeColour(QString name)
|
Chris@376
|
224 {
|
Chris@376
|
225 for (ColourList::iterator i = m_colours.begin();
|
Chris@376
|
226 i != m_colours.end(); ++i) {
|
Chris@376
|
227 if (i->name == name) {
|
Chris@376
|
228 m_colours.erase(i);
|
Chris@376
|
229 return;
|
Chris@376
|
230 }
|
Chris@376
|
231 }
|
Chris@376
|
232 }
|
Chris@376
|
233
|
Chris@376
|
234 void
|
Chris@376
|
235 ColourDatabase::getStringValues(int index,
|
Chris@376
|
236 QString &colourName,
|
Chris@376
|
237 QString &colourSpec,
|
Chris@376
|
238 QString &darkbg) const
|
Chris@376
|
239 {
|
Chris@376
|
240 colourName = "";
|
Chris@376
|
241 colourSpec = "";
|
Chris@904
|
242 if (!in_range_for(m_colours, index)) return;
|
Chris@376
|
243
|
Chris@376
|
244 colourName = getColourName(index);
|
Chris@376
|
245 QColor c = getColour(index);
|
Chris@376
|
246 colourSpec = XmlExportable::encodeColour(c.red(), c.green(), c.blue());
|
Chris@376
|
247 darkbg = useDarkBackground(index) ? "true" : "false";
|
Chris@376
|
248 }
|
Chris@376
|
249
|
Chris@376
|
250 int
|
Chris@376
|
251 ColourDatabase::putStringValues(QString colourName,
|
Chris@376
|
252 QString colourSpec,
|
Chris@376
|
253 QString darkbg)
|
Chris@376
|
254 {
|
Chris@376
|
255 int index = -1;
|
Chris@376
|
256 if (colourSpec != "") {
|
Chris@1266
|
257 QColor colour(colourSpec);
|
Chris@376
|
258 index = getColourIndex(colour);
|
Chris@376
|
259 if (index < 0) {
|
Chris@376
|
260 index = addColour(colour,
|
Chris@376
|
261 colourName == "" ? colourSpec : colourName);
|
Chris@376
|
262 }
|
Chris@376
|
263 } else if (colourName != "") {
|
Chris@376
|
264 index = getColourIndex(colourName);
|
Chris@376
|
265 }
|
Chris@376
|
266 if (index >= 0) {
|
Chris@376
|
267 setUseDarkBackground(index, darkbg == "true");
|
Chris@376
|
268 }
|
Chris@376
|
269 return index;
|
Chris@376
|
270 }
|
Chris@376
|
271
|
Chris@376
|
272 void
|
Chris@376
|
273 ColourDatabase::getColourPropertyRange(int *min, int *max) const
|
Chris@376
|
274 {
|
Chris@376
|
275 ColourDatabase *db = getInstance();
|
Chris@376
|
276 if (min) *min = 0;
|
Chris@376
|
277 if (max) {
|
Chris@376
|
278 *max = 0;
|
Chris@376
|
279 if (db->getColourCount() > 0) *max = db->getColourCount()-1;
|
Chris@376
|
280 }
|
Chris@376
|
281 }
|
Chris@376
|
282
|
Chris@376
|
283 QPixmap
|
Chris@376
|
284 ColourDatabase::getExamplePixmap(int index, QSize size) const
|
Chris@376
|
285 {
|
Chris@376
|
286 QPixmap pmap(size);
|
Chris@376
|
287 pmap.fill(useDarkBackground(index) ? Qt::black : Qt::white);
|
Chris@376
|
288 QPainter paint(&pmap);
|
Chris@376
|
289 QColor colour(getColour(index));
|
Chris@376
|
290 paint.setPen(colour);
|
Chris@376
|
291 paint.setBrush(colour);
|
Chris@376
|
292 int margin = 2;
|
Chris@376
|
293 if (size.width() < 4 || size.height() < 4) margin = 0;
|
Chris@376
|
294 else if (size.width() < 8 || size.height() < 8) margin = 1;
|
Chris@376
|
295 paint.drawRect(margin, margin,
|
Chris@376
|
296 size.width() - margin*2 - 1, size.height() - margin*2 - 1);
|
Chris@376
|
297 return pmap;
|
Chris@376
|
298 }
|
Chris@376
|
299
|