annotate layer/ColourDatabase.cpp @ 1608:6616e1899daa

Make ImageLayer able to report whether an image format can be opened
author Chris Cannam
date Mon, 11 May 2020 17:28:12 +0100
parents 57a4ee52ad69
children
rev   line source
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