Chris@376: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@376: Chris@376: /* Chris@376: Sonic Visualiser Chris@376: An audio file viewer and annotation editor. Chris@376: Centre for Digital Music, Queen Mary, University of London. Chris@376: This file copyright 2007 QMUL. Chris@376: Chris@376: This program is free software; you can redistribute it and/or Chris@376: modify it under the terms of the GNU General Public License as Chris@376: published by the Free Software Foundation; either version 2 of the Chris@376: License, or (at your option) any later version. See the file Chris@376: COPYING included with this distribution for more information. Chris@376: */ Chris@376: Chris@376: #include "ColourDatabase.h" Chris@376: #include "base/XmlExportable.h" Chris@376: Chris@376: #include Chris@376: Chris@1445: //#define DEBUG_COLOUR_DATABASE 1 Chris@1445: Chris@376: ColourDatabase Chris@376: ColourDatabase::m_instance; Chris@376: Chris@376: ColourDatabase * Chris@376: ColourDatabase::getInstance() Chris@376: { Chris@376: return &m_instance; Chris@376: } Chris@376: Chris@376: ColourDatabase::ColourDatabase() Chris@376: { Chris@376: } Chris@376: Chris@376: int Chris@376: ColourDatabase::getColourCount() const Chris@376: { Chris@904: return int(m_colours.size()); Chris@376: } Chris@376: Chris@376: QString Chris@376: ColourDatabase::getColourName(int c) const Chris@376: { Chris@904: if (!in_range_for(m_colours, c)) return ""; Chris@376: return m_colours[c].name; Chris@376: } Chris@376: Chris@376: QColor Chris@376: ColourDatabase::getColour(int c) const Chris@376: { Chris@904: if (!in_range_for(m_colours, c)) return Qt::black; Chris@376: return m_colours[c].colour; Chris@376: } Chris@376: Chris@376: QColor Chris@376: ColourDatabase::getColour(QString name) const Chris@376: { Chris@904: for (auto &c: m_colours) { Chris@904: if (c.name == name) return c.colour; Chris@376: } Chris@376: Chris@376: return Qt::black; Chris@376: } Chris@376: Chris@376: int Chris@376: ColourDatabase::getColourIndex(QString name) const Chris@376: { Chris@376: int index = 0; Chris@904: for (auto &c: m_colours) { Chris@904: if (c.name == name) return index; Chris@376: ++index; Chris@376: } Chris@376: Chris@376: return -1; Chris@376: } Chris@376: Chris@376: int Chris@904: ColourDatabase::getColourIndex(QColor col) const Chris@376: { Chris@376: int index = 0; Chris@904: for (auto &c: m_colours) { Chris@904: if (c.colour == col) return index; Chris@376: ++index; Chris@376: } Chris@376: Chris@376: return -1; Chris@376: } Chris@376: Chris@1445: int Chris@1578: ColourDatabase::getNearbyColourIndex(QColor col, WithBackgroundMode mode) const Chris@1445: { Chris@1578: int index = -1; Chris@1445: int closestIndex = -1; Chris@1578: double closestDistance = 0; Chris@1445: Chris@1445: for (auto &c: m_colours) { Chris@1578: Chris@1578: ++index; Chris@1578: Chris@1578: if (mode == WithDarkBackground && !c.darkbg) { Chris@1578: #ifdef DEBUG_COLOUR_DATABASE Chris@1578: SVDEBUG << "getNearbyColourIndex: dark background requested, skipping " << c.colour.name() << endl; Chris@1578: #endif Chris@1578: continue; Chris@1578: } Chris@1578: if (mode == WithLightBackground && c.darkbg) { Chris@1578: #ifdef DEBUG_COLOUR_DATABASE Chris@1578: SVDEBUG << "getNearbyColourIndex: light background requested, skipping " << c.colour.name() << endl; Chris@1578: #endif Chris@1578: continue; Chris@1578: } Chris@1578: Chris@1578: // This distance formula is "one of the better low-cost Chris@1578: // approximations" according to Chris@1578: // https://en.wikipedia.org/w/index.php?title=Color_difference&oldid=936888327 Chris@1578: Chris@1578: double r1 = col.red(), r2 = c.colour.red(); Chris@1578: double g1 = col.green(), g2 = c.colour.green(); Chris@1578: double b1 = col.blue(), b2 = c.colour.blue(); Chris@1578: Chris@1578: double rav = (r1 + r2) / 2.0; Chris@1578: double rterm = (2.0 + rav / 256.0) * (r1 - r2) * (r1 - r2); Chris@1578: double gterm = 4.0 * (g1 - g2) * (g1 - g2); Chris@1578: double bterm = (2.0 + (255 - rav) / 256.0) * (b1 - b2) * (b1 - b2); Chris@1578: Chris@1578: double distance = sqrt(rterm + gterm + bterm); Chris@1578: Chris@1445: #ifdef DEBUG_COLOUR_DATABASE Chris@1445: SVDEBUG << "getNearbyColourIndex: comparing " << c.colour.name() Chris@1445: << " to " << col.name() << ": distance = " << distance << endl; Chris@1445: #endif Chris@1445: if (closestIndex < 0 || distance < closestDistance) { Chris@1445: closestIndex = index; Chris@1445: closestDistance = distance; Chris@1445: #ifdef DEBUG_COLOUR_DATABASE Chris@1445: SVDEBUG << "(this is the best so far)" << endl; Chris@1445: #endif Chris@1445: } Chris@1445: } Chris@1445: Chris@1445: #ifdef DEBUG_COLOUR_DATABASE Chris@1445: SVDEBUG << "returning " << closestIndex << endl; Chris@1445: #endif Chris@1445: return closestIndex; Chris@1445: } Chris@1445: Chris@1367: QColor Chris@1367: ColourDatabase::getContrastingColour(int c) const Chris@1367: { Chris@1367: QColor col = getColour(c); Chris@1445: QColor contrasting = Qt::red; Chris@1445: bool dark = (col.red() < 240 && col.green() < 240 && col.blue() < 240); Chris@1445: if (dark) { Chris@1445: if (col.red() > col.blue()) { Chris@1445: if (col.green() > col.blue()) { Chris@1445: contrasting = Qt::blue; Chris@1445: } else { Chris@1445: contrasting = Qt::yellow; Chris@1445: } Chris@1367: } else { Chris@1445: if (col.green() > col.blue()) { Chris@1445: contrasting = Qt::yellow; Chris@1445: } else { Chris@1445: contrasting = Qt::red; Chris@1445: } Chris@1367: } Chris@1367: } else { Chris@1445: if (col.red() > 230 && col.green() > 230 && col.blue() > 230) { Chris@1445: contrasting = QColor(30, 150, 255); Chris@1367: } else { Chris@1445: contrasting = QColor(255, 188, 80); Chris@1367: } Chris@1367: } Chris@1445: #ifdef DEBUG_COLOUR_DATABASE Chris@1445: SVDEBUG << "getContrastingColour(" << col.name() << "): dark = " << dark Chris@1445: << ", returning " << contrasting.name() << endl; Chris@1445: #endif Chris@1445: return contrasting; Chris@1367: } Chris@1367: Chris@376: bool Chris@376: ColourDatabase::useDarkBackground(int c) const Chris@376: { Chris@904: if (!in_range_for(m_colours, c)) return false; Chris@376: return m_colours[c].darkbg; Chris@376: } Chris@376: Chris@376: void Chris@376: ColourDatabase::setUseDarkBackground(int c, bool dark) Chris@376: { Chris@904: if (!in_range_for(m_colours, c)) return; Chris@376: if (m_colours[c].darkbg != dark) { Chris@376: m_colours[c].darkbg = dark; Chris@376: emit colourDatabaseChanged(); Chris@376: } Chris@376: } Chris@376: Chris@376: int Chris@376: ColourDatabase::addColour(QColor c, QString name) Chris@376: { Chris@376: int index = 0; Chris@904: Chris@376: for (ColourList::iterator i = m_colours.begin(); Chris@376: i != m_colours.end(); ++i) { Chris@376: if (i->name == name) { Chris@376: i->colour = c; Chris@376: return index; Chris@376: } Chris@376: ++index; Chris@376: } Chris@376: Chris@376: ColourRec rec; Chris@376: rec.colour = c; Chris@376: rec.name = name; Chris@376: rec.darkbg = false; Chris@376: m_colours.push_back(rec); Chris@376: emit colourDatabaseChanged(); Chris@376: return index; Chris@376: } Chris@376: Chris@376: void Chris@376: ColourDatabase::removeColour(QString name) Chris@376: { Chris@376: for (ColourList::iterator i = m_colours.begin(); Chris@376: i != m_colours.end(); ++i) { Chris@376: if (i->name == name) { Chris@376: m_colours.erase(i); Chris@376: return; Chris@376: } Chris@376: } Chris@376: } Chris@376: Chris@376: void Chris@376: ColourDatabase::getStringValues(int index, Chris@376: QString &colourName, Chris@376: QString &colourSpec, Chris@376: QString &darkbg) const Chris@376: { Chris@376: colourName = ""; Chris@376: colourSpec = ""; Chris@904: if (!in_range_for(m_colours, index)) return; Chris@376: Chris@376: colourName = getColourName(index); Chris@376: QColor c = getColour(index); Chris@376: colourSpec = XmlExportable::encodeColour(c.red(), c.green(), c.blue()); Chris@376: darkbg = useDarkBackground(index) ? "true" : "false"; Chris@376: } Chris@376: Chris@376: int Chris@376: ColourDatabase::putStringValues(QString colourName, Chris@376: QString colourSpec, Chris@376: QString darkbg) Chris@376: { Chris@376: int index = -1; Chris@376: if (colourSpec != "") { Chris@1266: QColor colour(colourSpec); Chris@376: index = getColourIndex(colour); Chris@376: if (index < 0) { Chris@376: index = addColour(colour, Chris@376: colourName == "" ? colourSpec : colourName); Chris@376: } Chris@376: } else if (colourName != "") { Chris@376: index = getColourIndex(colourName); Chris@376: } Chris@376: if (index >= 0) { Chris@376: setUseDarkBackground(index, darkbg == "true"); Chris@376: } Chris@376: return index; Chris@376: } Chris@376: Chris@376: void Chris@376: ColourDatabase::getColourPropertyRange(int *min, int *max) const Chris@376: { Chris@376: ColourDatabase *db = getInstance(); Chris@376: if (min) *min = 0; Chris@376: if (max) { Chris@376: *max = 0; Chris@376: if (db->getColourCount() > 0) *max = db->getColourCount()-1; Chris@376: } Chris@376: } Chris@376: Chris@376: QPixmap Chris@376: ColourDatabase::getExamplePixmap(int index, QSize size) const Chris@376: { Chris@376: QPixmap pmap(size); Chris@376: pmap.fill(useDarkBackground(index) ? Qt::black : Qt::white); Chris@376: QPainter paint(&pmap); Chris@376: QColor colour(getColour(index)); Chris@376: paint.setPen(colour); Chris@376: paint.setBrush(colour); Chris@376: int margin = 2; Chris@376: if (size.width() < 4 || size.height() < 4) margin = 0; Chris@376: else if (size.width() < 8 || size.height() < 8) margin = 1; Chris@376: paint.drawRect(margin, margin, Chris@376: size.width() - margin*2 - 1, size.height() - margin*2 - 1); Chris@376: return pmap; Chris@376: } Chris@376: