annotate widgets/IconLoader.cpp @ 1615:911330a28a7c

Where the "below" view represents only a subset of the "above" view, cut off the feature mappings at the outer edges of the "below" view - don't map everything outside this (it would all just map onto the same single points at beginning and end, which is excessive, confusing and not useful)
author Chris Cannam
date Thu, 02 Jul 2020 15:37:43 +0100
parents 705d1d979ae4
children
rev   line source
Chris@289 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@289 2
Chris@289 3 /*
Chris@289 4 Sonic Visualiser
Chris@289 5 An audio file viewer and annotation editor.
Chris@289 6 Centre for Digital Music, Queen Mary, University of London.
Chris@289 7 This file copyright 2007 QMUL.
Chris@289 8
Chris@289 9 This program is free software; you can redistribute it and/or
Chris@289 10 modify it under the terms of the GNU General Public License as
Chris@289 11 published by the Free Software Foundation; either version 2 of the
Chris@289 12 License, or (at your option) any later version. See the file
Chris@289 13 COPYING included with this distribution for more information.
Chris@289 14 */
Chris@289 15
Chris@289 16 #include "IconLoader.h"
Chris@289 17
Chris@289 18 #include <QPixmap>
Chris@289 19 #include <QApplication>
Chris@289 20 #include <QPainter>
Chris@289 21 #include <QPalette>
Chris@957 22 #include <QFile>
Chris@957 23 #include <QSvgRenderer>
Chris@1452 24 #include <QSettings>
Chris@1609 25 #include <QMutex>
Chris@1609 26 #include <QMutexLocker>
Chris@289 27
Chris@957 28 #include <vector>
Chris@957 29 #include <set>
Chris@1609 30 #include <map>
Chris@957 31
Chris@957 32 #include "base/Debug.h"
Chris@1609 33 #include "base/Profiler.h"
Chris@957 34
Chris@957 35 using namespace std;
Chris@957 36
Chris@957 37 static set<QString> autoInvertExceptions {
Chris@289 38 // These are the icons that look OK in their default colours, even
Chris@289 39 // in a colour scheme with a black background. (They may also be
Chris@289 40 // icons that would look worse if we tried to auto-invert them.)
Chris@289 41 // If we have icons that look bad when auto-inverted but that are
Chris@289 42 // not suitable for use without being inverted, we'll need to
Chris@289 43 // supply inverted versions -- the loader will load xx_inverse.png
Chris@289 44 // in preference to xx.png if a dark background is found.)
Chris@289 45 "fileclose",
Chris@289 46 "filenew",
Chris@289 47 "fileopen",
Chris@289 48 "fileopenaudio",
Chris@289 49 "fileopensession",
Chris@289 50 "filesave",
Chris@289 51 "filesaveas",
Chris@957 52 "filesaveas-sv",
Chris@289 53 "help",
Chris@289 54 "editcut",
Chris@289 55 "editcopy",
Chris@289 56 "editpaste",
Chris@289 57 "editdelete",
Chris@289 58 "exit",
Chris@289 59 "zoom-fit",
Chris@289 60 "zoom-in",
Chris@289 61 "zoom-out",
Chris@289 62 "zoom"
Chris@289 63 };
Chris@289 64
Chris@289 65 QIcon
Chris@289 66 IconLoader::load(QString name)
Chris@289 67 {
Chris@1609 68 Profiler profiler("IconLoader::load");
Chris@1609 69
Chris@1609 70 static QMutex mutex;
Chris@1609 71 static map<QString, QIcon> icons;
Chris@1609 72 static const vector<int> sizes { 0, 16, 22, 24, 32, 48, 64, 128 };
Chris@1609 73
Chris@1609 74 QMutexLocker locker(&mutex);
Chris@1609 75
Chris@1609 76 if (icons.find(name) != icons.end()) {
Chris@1609 77 return icons.at(name);
Chris@1609 78 }
Chris@1609 79
Chris@957 80 QIcon icon;
Chris@957 81 for (int sz: sizes) {
Chris@957 82 QPixmap pmap(loadPixmap(name, sz));
Chris@957 83 if (!pmap.isNull()) icon.addPixmap(pmap);
Chris@957 84 }
Chris@1609 85
Chris@1609 86 icons[name] = icon;
Chris@1609 87
Chris@957 88 return icon;
Chris@957 89 }
Chris@957 90
Chris@957 91 bool
Chris@957 92 IconLoader::shouldInvert() const
Chris@957 93 {
Chris@1452 94 QSettings settings;
Chris@1452 95 settings.beginGroup("IconLoader");
Chris@1452 96 if (!settings.value("invert-icons-on-dark-background", true).toBool()) {
Chris@1452 97 return false;
Chris@1452 98 }
Chris@957 99 QColor bg = QApplication::palette().window().color();
Chris@957 100 bool darkBackground = (bg.red() + bg.green() + bg.blue() <= 384);
Chris@957 101 return darkBackground;
Chris@957 102 }
Chris@957 103
Chris@957 104 bool
Chris@957 105 IconLoader::shouldAutoInvert(QString name) const
Chris@957 106 {
Chris@957 107 if (shouldInvert()) {
Chris@957 108 return (autoInvertExceptions.find(name) == autoInvertExceptions.end());
Chris@957 109 } else {
Chris@957 110 return false;
Chris@957 111 }
Chris@289 112 }
Chris@289 113
Chris@289 114 QPixmap
Chris@957 115 IconLoader::loadPixmap(QString name, int size)
Chris@289 116 {
Chris@957 117 bool invert = shouldInvert();
Chris@958 118
Chris@958 119 QString scalableName, nonScalableName;
Chris@958 120 QPixmap pmap;
Chris@958 121
Chris@1262 122 // attempt to load a pixmap with the right size and inversion
Chris@958 123 nonScalableName = makeNonScalableFilename(name, size, invert);
Chris@958 124 pmap = QPixmap(nonScalableName);
Chris@958 125
Chris@1262 126 if (pmap.isNull() && size > 0) {
Chris@1262 127 // if that failed, load a scalable vector with the right
Chris@1262 128 // inversion and scale it
Chris@958 129 scalableName = makeScalableFilename(name, invert);
Chris@958 130 pmap = loadScalable(scalableName, size);
Chris@958 131 }
Chris@958 132
Chris@1262 133 if (pmap.isNull() && invert) {
Chris@1262 134 // if that failed, and we were asking for an inverted pixmap,
Chris@1262 135 // that may mean we don't have an inverted version of it. We
Chris@1262 136 // could either auto-invert or use the uninverted version
Chris@957 137 nonScalableName = makeNonScalableFilename(name, size, false);
Chris@957 138 pmap = QPixmap(nonScalableName);
Chris@957 139
Chris@1262 140 if (pmap.isNull() && size > 0) {
Chris@958 141 scalableName = makeScalableFilename(name, false);
Chris@958 142 pmap = loadScalable(scalableName, size);
Chris@1262 143 }
Chris@1262 144
Chris@1262 145 if (!pmap.isNull() && shouldAutoInvert(name)) {
Chris@1262 146 pmap = invertPixmap(pmap);
Chris@958 147 }
Chris@957 148 }
Chris@958 149
Chris@1262 150 return pmap;
Chris@957 151 }
Chris@957 152
Chris@957 153 QPixmap
Chris@957 154 IconLoader::loadScalable(QString name, int size)
Chris@957 155 {
Chris@957 156 if (!QFile(name).exists()) {
Chris@1003 157 // cerr << "loadScalable: no such file as: \"" << name << "\"" << endl;
Chris@957 158 return QPixmap();
Chris@957 159 }
Chris@957 160 QPixmap pmap(size, size);
Chris@957 161 pmap.fill(Qt::transparent);
Chris@957 162 QSvgRenderer renderer(name);
Chris@958 163 QPainter painter;
Chris@958 164 painter.begin(&pmap);
Chris@1003 165 // cerr << "calling renderer for " << name << " at size " << size << "..." << endl;
Chris@957 166 renderer.render(&painter);
Chris@1003 167 // cerr << "renderer completed" << endl;
Chris@958 168 painter.end();
Chris@957 169 return pmap;
Chris@957 170 }
Chris@957 171
Chris@957 172 QString
Chris@957 173 IconLoader::makeNonScalableFilename(QString name, int size, bool invert)
Chris@957 174 {
Chris@957 175 if (invert) {
Chris@957 176 if (size == 0) {
Chris@957 177 return QString(":icons/%1_inverse.png").arg(name);
Chris@957 178 } else {
Chris@957 179 return QString(":icons/%1-%2_inverse.png").arg(name).arg(size);
Chris@289 180 }
Chris@957 181 } else {
Chris@957 182 if (size == 0) {
Chris@957 183 return QString(":icons/%1.png").arg(name);
Chris@957 184 } else {
Chris@957 185 return QString(":icons/%1-%2.png").arg(name).arg(size);
Chris@289 186 }
Chris@289 187 }
Chris@957 188 }
Chris@289 189
Chris@957 190 QString
Chris@957 191 IconLoader::makeScalableFilename(QString name, bool invert)
Chris@957 192 {
Chris@957 193 if (invert) {
Chris@957 194 return QString(":icons/scalable/%1_inverse.svg").arg(name);
Chris@957 195 } else {
Chris@957 196 return QString(":icons/scalable/%1.svg").arg(name);
Chris@289 197 }
Chris@957 198 }
Chris@289 199
Chris@957 200 QPixmap
Chris@957 201 IconLoader::invertPixmap(QPixmap pmap)
Chris@957 202 {
Chris@289 203 // No suitable inverted icon found for black background; try to
Chris@289 204 // auto-invert the default one
Chris@289 205
Chris@289 206 QImage img = pmap.toImage().convertToFormat(QImage::Format_ARGB32);
Chris@289 207
Chris@289 208 for (int y = 0; y < img.height(); ++y) {
Chris@289 209 for (int x = 0; x < img.width(); ++x) {
Chris@289 210
Chris@289 211 QRgb rgba = img.pixel(x, y);
Chris@289 212 QColor colour = QColor
Chris@289 213 (qRed(rgba), qGreen(rgba), qBlue(rgba), qAlpha(rgba));
Chris@289 214
Chris@289 215 int alpha = colour.alpha();
Chris@289 216 if (colour.saturation() < 5 && colour.alpha() > 10) {
Chris@289 217 colour.setHsv(colour.hue(),
Chris@289 218 colour.saturation(),
Chris@289 219 255 - colour.value());
Chris@289 220 colour.setAlpha(alpha);
Chris@289 221 img.setPixel(x, y, colour.rgba());
Chris@289 222 }
Chris@289 223 }
Chris@289 224 }
Chris@289 225
Chris@289 226 pmap = QPixmap::fromImage(img);
Chris@289 227 return pmap;
Chris@289 228 }
Chris@289 229