annotate layer/ColourMapper.cpp @ 1024:3bce4c45b681 spectrogram-minor-refactor

Rearrange cache update calculations so as to use the actual painted width returned by paint functions (though they only ever return the same width as requested, at this point)
author Chris Cannam
date Mon, 25 Jan 2016 15:52:26 +0000
parents 9c890b7dfa83
children 65b183494331
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 2006-2007 Chris Cannam and 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 "ColourMapper.h"
Chris@376 17
Chris@376 18 #include <iostream>
Chris@376 19
Chris@376 20 #include <cmath>
Chris@376 21
Chris@682 22 #include "base/Debug.h"
Chris@682 23
Chris@1012 24 #include <vector>
Chris@1012 25
Chris@1012 26 using namespace std;
Chris@1012 27
Chris@1012 28 static vector<QColor> convertStrings(const vector<QString> &strs)
Chris@1012 29 {
Chris@1012 30 vector<QColor> converted;
Chris@1012 31 for (const auto &s: strs) converted.push_back(QColor(s));
Chris@1012 32 reverse(converted.begin(), converted.end());
Chris@1012 33 return converted;
Chris@1012 34 }
Chris@1012 35
Chris@1017 36 static vector<QColor> ice = convertStrings({
Chris@1015 37 // Based on ColorBrewer ylGnBu
Chris@1015 38 "#ffffff", "#ffff00", "#f7fcf0", "#e0f3db", "#ccebc5", "#a8ddb5",
Chris@1015 39 "#7bccc4", "#4eb3d3", "#2b8cbe", "#0868ac", "#084081", "#042040"
Chris@1013 40 });
Chris@1012 41
Chris@1017 42 static vector<QColor> cherry = convertStrings({
Chris@1016 43 "#f7f7f7", "#fddbc7", "#f4a582", "#d6604d", "#b2182b", "#dd3497",
Chris@1016 44 "#ae017e", "#7a0177", "#49006a"
Chris@1015 45 });
Chris@1015 46
Chris@1012 47 static void
Chris@1012 48 mapDiscrete(double norm, vector<QColor> &colours, double &r, double &g, double &b)
Chris@1012 49 {
Chris@1015 50 int n = int(colours.size());
Chris@1012 51 double m = norm * (n-1);
Chris@1012 52 if (m >= n-1) { colours[n-1].getRgbF(&r, &g, &b, 0); return; }
Chris@1012 53 if (m <= 0) { colours[0].getRgbF(&r, &g, &b, 0); return; }
Chris@1012 54 int base(int(floor(m)));
Chris@1012 55 double prop0 = (base + 1.0) - m, prop1 = m - base;
Chris@1012 56 QColor c0(colours[base]), c1(colours[base+1]);
Chris@1012 57 r = c0.redF() * prop0 + c1.redF() * prop1;
Chris@1012 58 g = c0.greenF() * prop0 + c1.greenF() * prop1;
Chris@1012 59 b = c0.blueF() * prop0 + c1.blueF() * prop1;
Chris@1012 60 }
Chris@1012 61
Chris@902 62 ColourMapper::ColourMapper(int map, double min, double max) :
Chris@376 63 QObject(),
Chris@376 64 m_map(map),
Chris@376 65 m_min(min),
Chris@376 66 m_max(max)
Chris@376 67 {
Chris@376 68 if (m_min == m_max) {
Chris@682 69 cerr << "WARNING: ColourMapper: min == max (== " << m_min
Chris@682 70 << "), adjusting" << endl;
Chris@376 71 m_max = m_min + 1;
Chris@376 72 }
Chris@376 73 }
Chris@376 74
Chris@376 75 ColourMapper::~ColourMapper()
Chris@376 76 {
Chris@376 77 }
Chris@376 78
Chris@376 79 int
Chris@376 80 ColourMapper::getColourMapCount()
Chris@376 81 {
Chris@1015 82 return 12;
Chris@376 83 }
Chris@376 84
Chris@376 85 QString
Chris@376 86 ColourMapper::getColourMapName(int n)
Chris@376 87 {
Chris@376 88 if (n >= getColourMapCount()) return tr("<unknown>");
Chris@376 89 StandardMap map = (StandardMap)n;
Chris@376 90
Chris@376 91 switch (map) {
Chris@1017 92 case Green: return tr("Green");
Chris@376 93 case WhiteOnBlack: return tr("White on Black");
Chris@376 94 case BlackOnWhite: return tr("Black on White");
Chris@1017 95 case Cherry: return tr("Cherry");
Chris@1017 96 case Wasp: return tr("Wasp");
Chris@1017 97 case Ice: return tr("Ice");
Chris@376 98 case Sunset: return tr("Sunset");
Chris@376 99 case FruitSalad: return tr("Fruit Salad");
Chris@376 100 case Banded: return tr("Banded");
Chris@376 101 case Highlight: return tr("Highlight");
Chris@376 102 case Printer: return tr("Printer");
Chris@536 103 case HighGain: return tr("High Gain");
Chris@376 104 }
Chris@376 105
Chris@376 106 return tr("<unknown>");
Chris@376 107 }
Chris@376 108
Chris@376 109 QColor
Chris@902 110 ColourMapper::map(double value) const
Chris@376 111 {
Chris@902 112 double norm = (value - m_min) / (m_max - m_min);
Chris@902 113 if (norm < 0.0) norm = 0.0;
Chris@902 114 if (norm > 1.0) norm = 1.0;
Chris@376 115
Chris@902 116 double h = 0.0, s = 0.0, v = 0.0, r = 0.0, g = 0.0, b = 0.0;
Chris@376 117 bool hsv = true;
Chris@376 118
Chris@911 119 double blue = 0.6666, pieslice = 0.3333;
Chris@376 120
Chris@376 121 if (m_map >= getColourMapCount()) return Qt::black;
Chris@376 122 StandardMap map = (StandardMap)m_map;
Chris@376 123
Chris@376 124 switch (map) {
Chris@376 125
Chris@1017 126 case Green:
Chris@902 127 h = blue - norm * 2.0 * pieslice;
Chris@902 128 s = 0.5f + norm/2.0;
Chris@376 129 v = norm;
Chris@376 130 break;
Chris@376 131
Chris@376 132 case WhiteOnBlack:
Chris@376 133 r = g = b = norm;
Chris@376 134 hsv = false;
Chris@376 135 break;
Chris@376 136
Chris@376 137 case BlackOnWhite:
Chris@902 138 r = g = b = 1.0 - norm;
Chris@376 139 hsv = false;
Chris@376 140 break;
Chris@376 141
Chris@1017 142 case Cherry:
Chris@1015 143 hsv = false;
Chris@1017 144 mapDiscrete(norm, cherry, r, g, b);
Chris@376 145 break;
Chris@376 146
Chris@1017 147 case Wasp:
Chris@902 148 h = 0.15;
Chris@902 149 s = 1.0;
Chris@376 150 v = norm;
Chris@376 151 break;
Chris@376 152
Chris@376 153 case Sunset:
Chris@902 154 r = (norm - 0.24) * 2.38;
Chris@902 155 if (r > 1.0) r = 1.0;
Chris@902 156 if (r < 0.0) r = 0.0;
Chris@902 157 g = (norm - 0.64) * 2.777;
Chris@902 158 if (g > 1.0) g = 1.0;
Chris@902 159 if (g < 0.0) g = 0.0;
Chris@376 160 b = (3.6f * norm);
Chris@902 161 if (norm > 0.277) b = 2.0 - b;
Chris@902 162 if (b > 1.0) b = 1.0;
Chris@902 163 if (b < 0.0) b = 0.0;
Chris@376 164 hsv = false;
Chris@376 165 break;
Chris@376 166
Chris@376 167 case FruitSalad:
Chris@902 168 h = blue + (pieslice/6.0) - norm;
Chris@902 169 if (h < 0.0) h += 1.0;
Chris@902 170 s = 1.0;
Chris@902 171 v = 1.0;
Chris@376 172 break;
Chris@376 173
Chris@376 174 case Banded:
Chris@376 175 if (norm < 0.125) return Qt::darkGreen;
Chris@376 176 else if (norm < 0.25) return Qt::green;
Chris@376 177 else if (norm < 0.375) return Qt::darkBlue;
Chris@376 178 else if (norm < 0.5) return Qt::blue;
Chris@376 179 else if (norm < 0.625) return Qt::darkYellow;
Chris@376 180 else if (norm < 0.75) return Qt::yellow;
Chris@376 181 else if (norm < 0.875) return Qt::darkRed;
Chris@376 182 else return Qt::red;
Chris@376 183 break;
Chris@376 184
Chris@376 185 case Highlight:
Chris@376 186 if (norm > 0.99) return Qt::white;
Chris@376 187 else return Qt::darkBlue;
Chris@376 188
Chris@376 189 case Printer:
Chris@376 190 if (norm > 0.8) {
Chris@902 191 r = 1.0;
Chris@376 192 } else if (norm > 0.7) {
Chris@902 193 r = 0.9;
Chris@376 194 } else if (norm > 0.6) {
Chris@902 195 r = 0.8;
Chris@376 196 } else if (norm > 0.5) {
Chris@902 197 r = 0.7;
Chris@376 198 } else if (norm > 0.4) {
Chris@902 199 r = 0.6;
Chris@376 200 } else if (norm > 0.3) {
Chris@902 201 r = 0.5;
Chris@376 202 } else if (norm > 0.2) {
Chris@902 203 r = 0.4;
Chris@376 204 } else {
Chris@902 205 r = 0.0;
Chris@376 206 }
Chris@902 207 r = g = b = 1.0 - r;
Chris@376 208 hsv = false;
Chris@376 209 break;
Chris@536 210
Chris@536 211 case HighGain:
Chris@902 212 if (norm <= 1.0 / 256.0) {
Chris@902 213 norm = 0.0;
Chris@536 214 } else {
Chris@904 215 norm = 0.1f + (pow(((norm - 0.5) * 2.0), 3.0) + 1.0) / 2.081;
Chris@536 216 }
Chris@536 217 // now as for Sunset
Chris@902 218 r = (norm - 0.24) * 2.38;
Chris@902 219 if (r > 1.0) r = 1.0;
Chris@902 220 if (r < 0.0) r = 0.0;
Chris@902 221 g = (norm - 0.64) * 2.777;
Chris@902 222 if (g > 1.0) g = 1.0;
Chris@902 223 if (g < 0.0) g = 0.0;
Chris@536 224 b = (3.6f * norm);
Chris@902 225 if (norm > 0.277) b = 2.0 - b;
Chris@902 226 if (b > 1.0) b = 1.0;
Chris@902 227 if (b < 0.0) b = 0.0;
Chris@536 228 hsv = false;
Chris@536 229 /*
Chris@902 230 if (r > 1.0) r = 1.0;
Chris@902 231 r = g = b = 1.0 - r;
Chris@536 232 hsv = false;
Chris@536 233 */
Chris@536 234 break;
Chris@1012 235
Chris@1017 236 case Ice:
Chris@1012 237 hsv = false;
Chris@1017 238 mapDiscrete(norm, ice, r, g, b);
Chris@376 239 }
Chris@376 240
Chris@376 241 if (hsv) {
Chris@376 242 return QColor::fromHsvF(h, s, v);
Chris@376 243 } else {
Chris@376 244 return QColor::fromRgbF(r, g, b);
Chris@376 245 }
Chris@376 246 }
Chris@376 247
Chris@376 248 QColor
Chris@376 249 ColourMapper::getContrastingColour() const
Chris@376 250 {
Chris@376 251 if (m_map >= getColourMapCount()) return Qt::white;
Chris@376 252 StandardMap map = (StandardMap)m_map;
Chris@376 253
Chris@376 254 switch (map) {
Chris@376 255
Chris@1017 256 case Green:
Chris@376 257 return QColor(255, 150, 50);
Chris@376 258
Chris@376 259 case WhiteOnBlack:
Chris@376 260 return Qt::red;
Chris@376 261
Chris@376 262 case BlackOnWhite:
Chris@376 263 return Qt::darkGreen;
Chris@376 264
Chris@1017 265 case Cherry:
Chris@376 266 return Qt::green;
Chris@376 267
Chris@1017 268 case Wasp:
Chris@376 269 return QColor::fromHsv(240, 255, 255);
Chris@376 270
Chris@1017 271 case Ice:
Chris@376 272 return Qt::red;
Chris@376 273
Chris@376 274 case Sunset:
Chris@376 275 return Qt::white;
Chris@376 276
Chris@376 277 case FruitSalad:
Chris@376 278 return Qt::white;
Chris@376 279
Chris@376 280 case Banded:
Chris@376 281 return Qt::cyan;
Chris@376 282
Chris@376 283 case Highlight:
Chris@376 284 return Qt::red;
Chris@376 285
Chris@376 286 case Printer:
Chris@376 287 return Qt::red;
Chris@536 288
Chris@536 289 case HighGain:
Chris@536 290 return Qt::red;
Chris@376 291 }
Chris@376 292
Chris@376 293 return Qt::white;
Chris@376 294 }
Chris@376 295
Chris@376 296 bool
Chris@376 297 ColourMapper::hasLightBackground() const
Chris@376 298 {
Chris@376 299 if (m_map >= getColourMapCount()) return false;
Chris@376 300 StandardMap map = (StandardMap)m_map;
Chris@376 301
Chris@376 302 switch (map) {
Chris@376 303
Chris@376 304 case BlackOnWhite:
Chris@376 305 case Printer:
Chris@536 306 case HighGain:
Chris@376 307 return true;
Chris@376 308
Chris@1017 309 case Green:
Chris@805 310 case Sunset:
Chris@805 311 case WhiteOnBlack:
Chris@1017 312 case Cherry:
Chris@1017 313 case Wasp:
Chris@1017 314 case Ice:
Chris@805 315 case FruitSalad:
Chris@805 316 case Banded:
Chris@805 317 case Highlight:
Chris@805 318
Chris@376 319 default:
Chris@376 320 return false;
Chris@376 321 }
Chris@376 322 }
Chris@376 323
Chris@376 324