Chris@289: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@289: Chris@289: /* Chris@289: Sonic Visualiser Chris@289: An audio file viewer and annotation editor. Chris@289: Centre for Digital Music, Queen Mary, University of London. Chris@289: This file copyright 2007 QMUL. Chris@289: Chris@289: This program is free software; you can redistribute it and/or Chris@289: modify it under the terms of the GNU General Public License as Chris@289: published by the Free Software Foundation; either version 2 of the Chris@289: License, or (at your option) any later version. See the file Chris@289: COPYING included with this distribution for more information. Chris@289: */ Chris@289: Chris@289: #include "IconLoader.h" Chris@289: Chris@289: #include Chris@289: #include Chris@289: #include Chris@289: #include Chris@957: #include Chris@957: #include Chris@1452: #include Chris@1609: #include Chris@1609: #include Chris@289: Chris@957: #include Chris@957: #include Chris@1609: #include Chris@957: Chris@957: #include "base/Debug.h" Chris@1609: #include "base/Profiler.h" Chris@957: Chris@957: using namespace std; Chris@957: Chris@957: static set autoInvertExceptions { Chris@289: // These are the icons that look OK in their default colours, even Chris@289: // in a colour scheme with a black background. (They may also be Chris@289: // icons that would look worse if we tried to auto-invert them.) Chris@289: // If we have icons that look bad when auto-inverted but that are Chris@289: // not suitable for use without being inverted, we'll need to Chris@289: // supply inverted versions -- the loader will load xx_inverse.png Chris@289: // in preference to xx.png if a dark background is found.) Chris@289: "fileclose", Chris@289: "filenew", Chris@289: "fileopen", Chris@289: "fileopenaudio", Chris@289: "fileopensession", Chris@289: "filesave", Chris@289: "filesaveas", Chris@957: "filesaveas-sv", Chris@289: "help", Chris@289: "editcut", Chris@289: "editcopy", Chris@289: "editpaste", Chris@289: "editdelete", Chris@289: "exit", Chris@289: "zoom-fit", Chris@289: "zoom-in", Chris@289: "zoom-out", Chris@289: "zoom" Chris@289: }; Chris@289: Chris@289: QIcon Chris@289: IconLoader::load(QString name) Chris@289: { Chris@1609: Profiler profiler("IconLoader::load"); Chris@1609: Chris@1609: static QMutex mutex; Chris@1609: static map icons; Chris@1609: static const vector sizes { 0, 16, 22, 24, 32, 48, 64, 128 }; Chris@1609: Chris@1609: QMutexLocker locker(&mutex); Chris@1609: Chris@1609: if (icons.find(name) != icons.end()) { Chris@1609: return icons.at(name); Chris@1609: } Chris@1609: Chris@957: QIcon icon; Chris@957: for (int sz: sizes) { Chris@957: QPixmap pmap(loadPixmap(name, sz)); Chris@957: if (!pmap.isNull()) icon.addPixmap(pmap); Chris@957: } Chris@1609: Chris@1609: icons[name] = icon; Chris@1609: Chris@957: return icon; Chris@957: } Chris@957: Chris@957: bool Chris@957: IconLoader::shouldInvert() const Chris@957: { Chris@1452: QSettings settings; Chris@1452: settings.beginGroup("IconLoader"); Chris@1452: if (!settings.value("invert-icons-on-dark-background", true).toBool()) { Chris@1452: return false; Chris@1452: } Chris@957: QColor bg = QApplication::palette().window().color(); Chris@957: bool darkBackground = (bg.red() + bg.green() + bg.blue() <= 384); Chris@957: return darkBackground; Chris@957: } Chris@957: Chris@957: bool Chris@957: IconLoader::shouldAutoInvert(QString name) const Chris@957: { Chris@957: if (shouldInvert()) { Chris@957: return (autoInvertExceptions.find(name) == autoInvertExceptions.end()); Chris@957: } else { Chris@957: return false; Chris@957: } Chris@289: } Chris@289: Chris@289: QPixmap Chris@957: IconLoader::loadPixmap(QString name, int size) Chris@289: { Chris@957: bool invert = shouldInvert(); Chris@958: Chris@958: QString scalableName, nonScalableName; Chris@958: QPixmap pmap; Chris@958: Chris@1262: // attempt to load a pixmap with the right size and inversion Chris@958: nonScalableName = makeNonScalableFilename(name, size, invert); Chris@958: pmap = QPixmap(nonScalableName); Chris@958: Chris@1262: if (pmap.isNull() && size > 0) { Chris@1262: // if that failed, load a scalable vector with the right Chris@1262: // inversion and scale it Chris@958: scalableName = makeScalableFilename(name, invert); Chris@958: pmap = loadScalable(scalableName, size); Chris@958: } Chris@958: Chris@1262: if (pmap.isNull() && invert) { Chris@1262: // if that failed, and we were asking for an inverted pixmap, Chris@1262: // that may mean we don't have an inverted version of it. We Chris@1262: // could either auto-invert or use the uninverted version Chris@957: nonScalableName = makeNonScalableFilename(name, size, false); Chris@957: pmap = QPixmap(nonScalableName); Chris@957: Chris@1262: if (pmap.isNull() && size > 0) { Chris@958: scalableName = makeScalableFilename(name, false); Chris@958: pmap = loadScalable(scalableName, size); Chris@1262: } Chris@1262: Chris@1262: if (!pmap.isNull() && shouldAutoInvert(name)) { Chris@1262: pmap = invertPixmap(pmap); Chris@958: } Chris@957: } Chris@958: Chris@1262: return pmap; Chris@957: } Chris@957: Chris@957: QPixmap Chris@957: IconLoader::loadScalable(QString name, int size) Chris@957: { Chris@957: if (!QFile(name).exists()) { Chris@1003: // cerr << "loadScalable: no such file as: \"" << name << "\"" << endl; Chris@957: return QPixmap(); Chris@957: } Chris@957: QPixmap pmap(size, size); Chris@957: pmap.fill(Qt::transparent); Chris@957: QSvgRenderer renderer(name); Chris@958: QPainter painter; Chris@958: painter.begin(&pmap); Chris@1003: // cerr << "calling renderer for " << name << " at size " << size << "..." << endl; Chris@957: renderer.render(&painter); Chris@1003: // cerr << "renderer completed" << endl; Chris@958: painter.end(); Chris@957: return pmap; Chris@957: } Chris@957: Chris@957: QString Chris@957: IconLoader::makeNonScalableFilename(QString name, int size, bool invert) Chris@957: { Chris@957: if (invert) { Chris@957: if (size == 0) { Chris@957: return QString(":icons/%1_inverse.png").arg(name); Chris@957: } else { Chris@957: return QString(":icons/%1-%2_inverse.png").arg(name).arg(size); Chris@289: } Chris@957: } else { Chris@957: if (size == 0) { Chris@957: return QString(":icons/%1.png").arg(name); Chris@957: } else { Chris@957: return QString(":icons/%1-%2.png").arg(name).arg(size); Chris@289: } Chris@289: } Chris@957: } Chris@289: Chris@957: QString Chris@957: IconLoader::makeScalableFilename(QString name, bool invert) Chris@957: { Chris@957: if (invert) { Chris@957: return QString(":icons/scalable/%1_inverse.svg").arg(name); Chris@957: } else { Chris@957: return QString(":icons/scalable/%1.svg").arg(name); Chris@289: } Chris@957: } Chris@289: Chris@957: QPixmap Chris@957: IconLoader::invertPixmap(QPixmap pmap) Chris@957: { Chris@289: // No suitable inverted icon found for black background; try to Chris@289: // auto-invert the default one Chris@289: Chris@289: QImage img = pmap.toImage().convertToFormat(QImage::Format_ARGB32); Chris@289: Chris@289: for (int y = 0; y < img.height(); ++y) { Chris@289: for (int x = 0; x < img.width(); ++x) { Chris@289: Chris@289: QRgb rgba = img.pixel(x, y); Chris@289: QColor colour = QColor Chris@289: (qRed(rgba), qGreen(rgba), qBlue(rgba), qAlpha(rgba)); Chris@289: Chris@289: int alpha = colour.alpha(); Chris@289: if (colour.saturation() < 5 && colour.alpha() > 10) { Chris@289: colour.setHsv(colour.hue(), Chris@289: colour.saturation(), Chris@289: 255 - colour.value()); Chris@289: colour.setAlpha(alpha); Chris@289: img.setPixel(x, y, colour.rgba()); Chris@289: } Chris@289: } Chris@289: } Chris@289: Chris@289: pmap = QPixmap::fromImage(img); Chris@289: return pmap; Chris@289: } Chris@289: