ColourDatabase.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2007 QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "ColourDatabase.h"
17 #include "base/XmlExportable.h"
18 
19 #include <QPainter>
20 
21 //#define DEBUG_COLOUR_DATABASE 1
22 
25 
28 {
29  return &m_instance;
30 }
31 
33 {
34 }
35 
36 int
38 {
39  return int(m_colours.size());
40 }
41 
42 QString
44 {
45  if (!in_range_for(m_colours, c)) return "";
46  return m_colours[c].name;
47 }
48 
49 QColor
51 {
52  if (!in_range_for(m_colours, c)) return Qt::black;
53  return m_colours[c].colour;
54 }
55 
56 QColor
57 ColourDatabase::getColour(QString name) const
58 {
59  for (auto &c: m_colours) {
60  if (c.name == name) return c.colour;
61  }
62 
63  return Qt::black;
64 }
65 
66 int
67 ColourDatabase::getColourIndex(QString name) const
68 {
69  int index = 0;
70  for (auto &c: m_colours) {
71  if (c.name == name) return index;
72  ++index;
73  }
74 
75  return -1;
76 }
77 
78 int
80 {
81  int index = 0;
82  for (auto &c: m_colours) {
83  if (c.colour == col) return index;
84  ++index;
85  }
86 
87  return -1;
88 }
89 
90 int
92 {
93  int index = -1;
94  int closestIndex = -1;
95  double closestDistance = 0;
96 
97  for (auto &c: m_colours) {
98 
99  ++index;
100 
101  if (mode == WithDarkBackground && !c.darkbg) {
102 #ifdef DEBUG_COLOUR_DATABASE
103  SVDEBUG << "getNearbyColourIndex: dark background requested, skipping " << c.colour.name() << endl;
104 #endif
105  continue;
106  }
107  if (mode == WithLightBackground && c.darkbg) {
108 #ifdef DEBUG_COLOUR_DATABASE
109  SVDEBUG << "getNearbyColourIndex: light background requested, skipping " << c.colour.name() << endl;
110 #endif
111  continue;
112  }
113 
114  // This distance formula is "one of the better low-cost
115  // approximations" according to
116  // https://en.wikipedia.org/w/index.php?title=Color_difference&oldid=936888327
117 
118  double r1 = col.red(), r2 = c.colour.red();
119  double g1 = col.green(), g2 = c.colour.green();
120  double b1 = col.blue(), b2 = c.colour.blue();
121 
122  double rav = (r1 + r2) / 2.0;
123  double rterm = (2.0 + rav / 256.0) * (r1 - r2) * (r1 - r2);
124  double gterm = 4.0 * (g1 - g2) * (g1 - g2);
125  double bterm = (2.0 + (255 - rav) / 256.0) * (b1 - b2) * (b1 - b2);
126 
127  double distance = sqrt(rterm + gterm + bterm);
128 
129 #ifdef DEBUG_COLOUR_DATABASE
130  SVDEBUG << "getNearbyColourIndex: comparing " << c.colour.name()
131  << " to " << col.name() << ": distance = " << distance << endl;
132 #endif
133  if (closestIndex < 0 || distance < closestDistance) {
134  closestIndex = index;
135  closestDistance = distance;
136 #ifdef DEBUG_COLOUR_DATABASE
137  SVDEBUG << "(this is the best so far)" << endl;
138 #endif
139  }
140  }
141 
142 #ifdef DEBUG_COLOUR_DATABASE
143  SVDEBUG << "returning " << closestIndex << endl;
144 #endif
145  return closestIndex;
146 }
147 
148 QColor
150 {
151  QColor col = getColour(c);
152  QColor contrasting = Qt::red;
153  bool dark = (col.red() < 240 && col.green() < 240 && col.blue() < 240);
154  if (dark) {
155  if (col.red() > col.blue()) {
156  if (col.green() > col.blue()) {
157  contrasting = Qt::blue;
158  } else {
159  contrasting = Qt::yellow;
160  }
161  } else {
162  if (col.green() > col.blue()) {
163  contrasting = Qt::yellow;
164  } else {
165  contrasting = Qt::red;
166  }
167  }
168  } else {
169  if (col.red() > 230 && col.green() > 230 && col.blue() > 230) {
170  contrasting = QColor(30, 150, 255);
171  } else {
172  contrasting = QColor(255, 188, 80);
173  }
174  }
175 #ifdef DEBUG_COLOUR_DATABASE
176  SVDEBUG << "getContrastingColour(" << col.name() << "): dark = " << dark
177  << ", returning " << contrasting.name() << endl;
178 #endif
179  return contrasting;
180 }
181 
182 bool
184 {
185  if (!in_range_for(m_colours, c)) return false;
186  return m_colours[c].darkbg;
187 }
188 
189 void
191 {
192  if (!in_range_for(m_colours, c)) return;
193  if (m_colours[c].darkbg != dark) {
194  m_colours[c].darkbg = dark;
195  emit colourDatabaseChanged();
196  }
197 }
198 
199 int
200 ColourDatabase::addColour(QColor c, QString name)
201 {
202  int index = 0;
203 
204  for (ColourList::iterator i = m_colours.begin();
205  i != m_colours.end(); ++i) {
206  if (i->name == name) {
207  i->colour = c;
208  return index;
209  }
210  ++index;
211  }
212 
213  ColourRec rec;
214  rec.colour = c;
215  rec.name = name;
216  rec.darkbg = false;
217  m_colours.push_back(rec);
218  emit colourDatabaseChanged();
219  return index;
220 }
221 
222 void
224 {
225  for (ColourList::iterator i = m_colours.begin();
226  i != m_colours.end(); ++i) {
227  if (i->name == name) {
228  m_colours.erase(i);
229  return;
230  }
231  }
232 }
233 
234 void
236  QString &colourName,
237  QString &colourSpec,
238  QString &darkbg) const
239 {
240  colourName = "";
241  colourSpec = "";
242  if (!in_range_for(m_colours, index)) return;
243 
244  colourName = getColourName(index);
245  QColor c = getColour(index);
246  colourSpec = XmlExportable::encodeColour(c.red(), c.green(), c.blue());
247  darkbg = useDarkBackground(index) ? "true" : "false";
248 }
249 
250 int
252  QString colourSpec,
253  QString darkbg)
254 {
255  int index = -1;
256  if (colourSpec != "") {
257  QColor colour(colourSpec);
258  index = getColourIndex(colour);
259  if (index < 0) {
260  index = addColour(colour,
261  colourName == "" ? colourSpec : colourName);
262  }
263  } else if (colourName != "") {
264  index = getColourIndex(colourName);
265  }
266  if (index >= 0) {
267  setUseDarkBackground(index, darkbg == "true");
268  }
269  return index;
270 }
271 
272 void
273 ColourDatabase::getColourPropertyRange(int *min, int *max) const
274 {
275  ColourDatabase *db = getInstance();
276  if (min) *min = 0;
277  if (max) {
278  *max = 0;
279  if (db->getColourCount() > 0) *max = db->getColourCount()-1;
280  }
281 }
282 
283 QPixmap
284 ColourDatabase::getExamplePixmap(int index, QSize size) const
285 {
286  QPixmap pmap(size);
287  pmap.fill(useDarkBackground(index) ? Qt::black : Qt::white);
288  QPainter paint(&pmap);
289  QColor colour(getColour(index));
290  paint.setPen(colour);
291  paint.setBrush(colour);
292  int margin = 2;
293  if (size.width() < 4 || size.height() < 4) margin = 0;
294  else if (size.width() < 8 || size.height() < 8) margin = 1;
295  paint.drawRect(margin, margin,
296  size.width() - margin*2 - 1, size.height() - margin*2 - 1);
297  return pmap;
298 }
299 
QString getColourName(int c) const
Return the name of the colour at index c.
QColor getContrastingColour(int c) const
Return a colour that contrasts with the one at index c, according to some simple algorithm.
void colourDatabaseChanged()
int getNearbyColourIndex(QColor c, WithBackgroundMode mode=WithAnyBackground) const
Return the index of the colour in the database that is closest to the given one, by some simple measu...
void removeColour(QString)
Remove the colour with the given name from the database.
QColor getColour(int c) const
Return the colour at index c.
bool useDarkBackground(int c) const
Return true if the colour at index c is marked as using a dark background.
void getColourPropertyRange(int *min, int *max) const
static ColourDatabase m_instance
int putStringValues(QString colourName, QString colourSpec, QString darkbg)
void setUseDarkBackground(int c, bool dark)
Mark the colour at index c as using a dark background.
int getColourCount() const
Return the number of colours in the database.
int getColourIndex(QString name) const
Return the index of the colour with the given name, if found in the database.
void getStringValues(int index, QString &colourName, QString &colourSpec, QString &darkbg) const
int addColour(QColor c, QString name)
Add a colour to the database, with the associated name.
ColourList m_colours
static ColourDatabase * getInstance()
QPixmap getExamplePixmap(int c, QSize size) const
Generate a swatch pixmap illustrating the colour at index c.