IconLoader.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 "IconLoader.h"
17 
18 #include <QPixmap>
19 #include <QApplication>
20 #include <QPainter>
21 #include <QPalette>
22 #include <QFile>
23 #include <QSvgRenderer>
24 #include <QSettings>
25 #include <QMutex>
26 #include <QMutexLocker>
27 
28 #include <vector>
29 #include <set>
30 #include <map>
31 
32 #include "base/Debug.h"
33 #include "base/Profiler.h"
34 
35 using namespace std;
36 
37 static set<QString> autoInvertExceptions {
38  // These are the icons that look OK in their default colours, even
39  // in a colour scheme with a black background. (They may also be
40  // icons that would look worse if we tried to auto-invert them.)
41  // If we have icons that look bad when auto-inverted but that are
42  // not suitable for use without being inverted, we'll need to
43  // supply inverted versions -- the loader will load xx_inverse.png
44  // in preference to xx.png if a dark background is found.)
45  "fileclose",
46  "filenew",
47  "fileopen",
48  "fileopenaudio",
49  "fileopensession",
50  "filesave",
51  "filesaveas",
52  "filesaveas-sv",
53  "help",
54  "editcut",
55  "editcopy",
56  "editpaste",
57  "editdelete",
58  "exit",
59  "zoom-fit",
60  "zoom-in",
61  "zoom-out",
62  "zoom"
63 };
64 
65 QIcon
66 IconLoader::load(QString name)
67 {
68  Profiler profiler("IconLoader::load");
69 
70  static QMutex mutex;
71  static map<QString, QIcon> icons;
72  static const vector<int> sizes { 0, 16, 22, 24, 32, 48, 64, 128 };
73 
74  QMutexLocker locker(&mutex);
75 
76  if (icons.find(name) != icons.end()) {
77  return icons.at(name);
78  }
79 
80  QIcon icon;
81  for (int sz: sizes) {
82  QPixmap pmap(loadPixmap(name, sz));
83  if (!pmap.isNull()) icon.addPixmap(pmap);
84  }
85 
86  icons[name] = icon;
87 
88  return icon;
89 }
90 
91 bool
93 {
94  QSettings settings;
95  settings.beginGroup("IconLoader");
96  if (!settings.value("invert-icons-on-dark-background", true).toBool()) {
97  return false;
98  }
99  QColor bg = QApplication::palette().window().color();
100  bool darkBackground = (bg.red() + bg.green() + bg.blue() <= 384);
101  return darkBackground;
102 }
103 
104 bool
105 IconLoader::shouldAutoInvert(QString name) const
106 {
107  if (shouldInvert()) {
108  return (autoInvertExceptions.find(name) == autoInvertExceptions.end());
109  } else {
110  return false;
111  }
112 }
113 
114 QPixmap
115 IconLoader::loadPixmap(QString name, int size)
116 {
117  bool invert = shouldInvert();
118 
119  QString scalableName, nonScalableName;
120  QPixmap pmap;
121 
122  // attempt to load a pixmap with the right size and inversion
123  nonScalableName = makeNonScalableFilename(name, size, invert);
124  pmap = QPixmap(nonScalableName);
125 
126  if (pmap.isNull() && size > 0) {
127  // if that failed, load a scalable vector with the right
128  // inversion and scale it
129  scalableName = makeScalableFilename(name, invert);
130  pmap = loadScalable(scalableName, size);
131  }
132 
133  if (pmap.isNull() && invert) {
134  // if that failed, and we were asking for an inverted pixmap,
135  // that may mean we don't have an inverted version of it. We
136  // could either auto-invert or use the uninverted version
137  nonScalableName = makeNonScalableFilename(name, size, false);
138  pmap = QPixmap(nonScalableName);
139 
140  if (pmap.isNull() && size > 0) {
141  scalableName = makeScalableFilename(name, false);
142  pmap = loadScalable(scalableName, size);
143  }
144 
145  if (!pmap.isNull() && shouldAutoInvert(name)) {
146  pmap = invertPixmap(pmap);
147  }
148  }
149 
150  return pmap;
151 }
152 
153 QPixmap
154 IconLoader::loadScalable(QString name, int size)
155 {
156  if (!QFile(name).exists()) {
157 // cerr << "loadScalable: no such file as: \"" << name << "\"" << endl;
158  return QPixmap();
159  }
160  QPixmap pmap(size, size);
161  pmap.fill(Qt::transparent);
162  QSvgRenderer renderer(name);
163  QPainter painter;
164  painter.begin(&pmap);
165 // cerr << "calling renderer for " << name << " at size " << size << "..." << endl;
166  renderer.render(&painter);
167 // cerr << "renderer completed" << endl;
168  painter.end();
169  return pmap;
170 }
171 
172 QString
173 IconLoader::makeNonScalableFilename(QString name, int size, bool invert)
174 {
175  if (invert) {
176  if (size == 0) {
177  return QString(":icons/%1_inverse.png").arg(name);
178  } else {
179  return QString(":icons/%1-%2_inverse.png").arg(name).arg(size);
180  }
181  } else {
182  if (size == 0) {
183  return QString(":icons/%1.png").arg(name);
184  } else {
185  return QString(":icons/%1-%2.png").arg(name).arg(size);
186  }
187  }
188 }
189 
190 QString
191 IconLoader::makeScalableFilename(QString name, bool invert)
192 {
193  if (invert) {
194  return QString(":icons/scalable/%1_inverse.svg").arg(name);
195  } else {
196  return QString(":icons/scalable/%1.svg").arg(name);
197  }
198 }
199 
200 QPixmap
202 {
203  // No suitable inverted icon found for black background; try to
204  // auto-invert the default one
205 
206  QImage img = pmap.toImage().convertToFormat(QImage::Format_ARGB32);
207 
208  for (int y = 0; y < img.height(); ++y) {
209  for (int x = 0; x < img.width(); ++x) {
210 
211  QRgb rgba = img.pixel(x, y);
212  QColor colour = QColor
213  (qRed(rgba), qGreen(rgba), qBlue(rgba), qAlpha(rgba));
214 
215  int alpha = colour.alpha();
216  if (colour.saturation() < 5 && colour.alpha() > 10) {
217  colour.setHsv(colour.hue(),
218  colour.saturation(),
219  255 - colour.value());
220  colour.setAlpha(alpha);
221  img.setPixel(x, y, colour.rgba());
222  }
223  }
224  }
225 
226  pmap = QPixmap::fromImage(img);
227  return pmap;
228 }
229 
static set< QString > autoInvertExceptions
Definition: IconLoader.cpp:37
QString makeNonScalableFilename(QString, int, bool)
Definition: IconLoader.cpp:173
QString makeScalableFilename(QString, bool)
Definition: IconLoader.cpp:191
QPixmap invertPixmap(QPixmap)
Definition: IconLoader.cpp:201
bool shouldInvert() const
Definition: IconLoader.cpp:92
QPixmap loadScalable(QString, int)
Definition: IconLoader.cpp:154
QPixmap loadPixmap(QString, int)
Definition: IconLoader.cpp:115
bool shouldAutoInvert(QString) const
Definition: IconLoader.cpp:105
QIcon load(QString name)
Definition: IconLoader.cpp:66