annotate layer/ColourMapper.cpp @ 1013:ac69043f82b6 colourschemes

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