Chris@376: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@376: 
Chris@376: /*
Chris@376:     Sonic Visualiser
Chris@376:     An audio file viewer and annotation editor.
Chris@376:     Centre for Digital Music, Queen Mary, University of London.
Chris@376:     This file copyright 2006-2007 Chris Cannam and QMUL.
Chris@376:     
Chris@376:     This program is free software; you can redistribute it and/or
Chris@376:     modify it under the terms of the GNU General Public License as
Chris@376:     published by the Free Software Foundation; either version 2 of the
Chris@376:     License, or (at your option) any later version.  See the file
Chris@376:     COPYING included with this distribution for more information.
Chris@376: */
Chris@376: 
Chris@376: #include "ColourMapper.h"
Chris@376: 
Chris@376: #include <iostream>
Chris@376: 
Chris@376: #include <cmath>
Chris@376: 
Chris@682: #include "base/Debug.h"
Chris@682: 
Chris@376: ColourMapper::ColourMapper(int map, float min, float max) :
Chris@376:     QObject(),
Chris@376:     m_map(map),
Chris@376:     m_min(min),
Chris@376:     m_max(max)
Chris@376: {
Chris@376:     if (m_min == m_max) {
Chris@682:         cerr << "WARNING: ColourMapper: min == max (== " << m_min
Chris@682:                   << "), adjusting" << endl;
Chris@376:         m_max = m_min + 1;
Chris@376:     }
Chris@376: }
Chris@376: 
Chris@376: ColourMapper::~ColourMapper()
Chris@376: {
Chris@376: }
Chris@376: 
Chris@376: int
Chris@376: ColourMapper::getColourMapCount()
Chris@376: {
Chris@536:     return 12;
Chris@376: }
Chris@376: 
Chris@376: QString
Chris@376: ColourMapper::getColourMapName(int n)
Chris@376: {
Chris@376:     if (n >= getColourMapCount()) return tr("<unknown>");
Chris@376:     StandardMap map = (StandardMap)n;
Chris@376: 
Chris@376:     switch (map) {
Chris@376:     case DefaultColours:   return tr("Default");
Chris@376:     case WhiteOnBlack:     return tr("White on Black");
Chris@376:     case BlackOnWhite:     return tr("Black on White");
Chris@376:     case RedOnBlue:        return tr("Red on Blue");
Chris@376:     case YellowOnBlack:    return tr("Yellow on Black");
Chris@376:     case BlueOnBlack:      return tr("Blue on Black");
Chris@376:     case Sunset:           return tr("Sunset");
Chris@376:     case FruitSalad:       return tr("Fruit Salad");
Chris@376:     case Banded:           return tr("Banded");
Chris@376:     case Highlight:        return tr("Highlight");
Chris@376:     case Printer:          return tr("Printer");
Chris@536:     case HighGain:         return tr("High Gain");
Chris@376:     }
Chris@376: 
Chris@376:     return tr("<unknown>");
Chris@376: }
Chris@376: 
Chris@376: QColor
Chris@376: ColourMapper::map(float value) const
Chris@376: {
Chris@376:     float norm = (value - m_min) / (m_max - m_min);
Chris@376:     if (norm < 0.f) norm = 0.f;
Chris@376:     if (norm > 1.f) norm = 1.f;
Chris@376:     
Chris@376:     float h = 0.f, s = 0.f, v = 0.f, r = 0.f, g = 0.f, b = 0.f;
Chris@376:     bool hsv = true;
Chris@376: 
Chris@376: //    float red = 0.f, green = 0.3333f;
Chris@376:     float blue = 0.6666f, pieslice = 0.3333f;
Chris@376: 
Chris@376:     if (m_map >= getColourMapCount()) return Qt::black;
Chris@376:     StandardMap map = (StandardMap)m_map;
Chris@376: 
Chris@376:     switch (map) {
Chris@376: 
Chris@376:     case DefaultColours:
Chris@376:         h = blue - norm * 2.f * pieslice;
Chris@376:         s = 0.5f + norm/2.f;
Chris@376:         v = norm;
Chris@376:         break;
Chris@376: 
Chris@376:     case WhiteOnBlack:
Chris@376:         r = g = b = norm;
Chris@376:         hsv = false;
Chris@376:         break;
Chris@376: 
Chris@376:     case BlackOnWhite:
Chris@376:         r = g = b = 1.f - norm;
Chris@376:         hsv = false;
Chris@376:         break;
Chris@376: 
Chris@376:     case RedOnBlue:
Chris@376:         h = blue - pieslice/4.f + norm * (pieslice + pieslice/4.f);
Chris@376:         s = 1.f;
Chris@376:         v = norm;
Chris@376:         break;
Chris@376: 
Chris@376:     case YellowOnBlack:
Chris@376:         h = 0.15f;
Chris@376:         s = 1.f;
Chris@376:         v = norm;
Chris@376:         break;
Chris@376: 
Chris@376:     case BlueOnBlack:
Chris@376:         h = blue;
Chris@376:         s = 1.f;
Chris@376:         v = norm * 2.f;
Chris@376:         if (v > 1.f) {
Chris@376:             v = 1.f;
Chris@376:             s = 1.f - (sqrtf(norm) - 0.707f) * 3.413f;
Chris@376:             if (s < 0.f) s = 0.f;
Chris@376:             if (s > 1.f) s = 1.f;
Chris@376:         }
Chris@376:         break;
Chris@376: 
Chris@376:     case Sunset:
Chris@376:         r = (norm - 0.24f) * 2.38f;
Chris@376:         if (r > 1.f) r = 1.f;
Chris@376:         if (r < 0.f) r = 0.f;
Chris@376:         g = (norm - 0.64f) * 2.777f;
Chris@376:         if (g > 1.f) g = 1.f;
Chris@376:         if (g < 0.f) g = 0.f;
Chris@376:         b = (3.6f * norm);
Chris@376:         if (norm > 0.277f) b = 2.f - b;
Chris@376:         if (b > 1.f) b = 1.f;
Chris@376:         if (b < 0.f) b = 0.f;
Chris@376:         hsv = false;
Chris@376:         break;
Chris@376: 
Chris@376:     case FruitSalad:
Chris@376:         h = blue + (pieslice/6.f) - norm;
Chris@376:         if (h < 0.f) h += 1.f;
Chris@376:         s = 1.f;
Chris@376:         v = 1.f;
Chris@376:         break;
Chris@376: 
Chris@376:     case Banded:
Chris@376:         if      (norm < 0.125) return Qt::darkGreen;
Chris@376:         else if (norm < 0.25)  return Qt::green;
Chris@376:         else if (norm < 0.375) return Qt::darkBlue;
Chris@376:         else if (norm < 0.5)   return Qt::blue;
Chris@376:         else if (norm < 0.625) return Qt::darkYellow;
Chris@376:         else if (norm < 0.75)  return Qt::yellow;
Chris@376:         else if (norm < 0.875) return Qt::darkRed;
Chris@376:         else                   return Qt::red;
Chris@376:         break;
Chris@376: 
Chris@376:     case Highlight:
Chris@376:         if (norm > 0.99) return Qt::white;
Chris@376:         else return Qt::darkBlue;
Chris@376: 
Chris@376:     case Printer:
Chris@376:         if (norm > 0.8) {
Chris@376:             r = 1.f;
Chris@376:         } else if (norm > 0.7) {
Chris@376:             r = 0.9f;
Chris@376:         } else if (norm > 0.6) {
Chris@376:             r = 0.8f;
Chris@376:         } else if (norm > 0.5) {
Chris@376:             r = 0.7f;
Chris@376:         } else if (norm > 0.4) {
Chris@376:             r = 0.6f;
Chris@376:         } else if (norm > 0.3) {
Chris@376:             r = 0.5f;
Chris@376:         } else if (norm > 0.2) {
Chris@376:             r = 0.4f;
Chris@376:         } else {
Chris@376:             r = 0.f;
Chris@376:         }
Chris@376:         r = g = b = 1.f - r;
Chris@376:         hsv = false;
Chris@376:         break;
Chris@536: 
Chris@536:     case HighGain:
Chris@536:         if (norm <= 1.f / 256.f) {
Chris@536:             norm = 0.f;
Chris@536:         } else {
Chris@536:             norm = 0.1f + (powf(((norm - 0.5f) * 2.f), 3.f) + 1.f) / 2.081f;
Chris@536:         }
Chris@536:         // now as for Sunset
Chris@536:         r = (norm - 0.24f) * 2.38f;
Chris@536:         if (r > 1.f) r = 1.f;
Chris@536:         if (r < 0.f) r = 0.f;
Chris@536:         g = (norm - 0.64f) * 2.777f;
Chris@536:         if (g > 1.f) g = 1.f;
Chris@536:         if (g < 0.f) g = 0.f;
Chris@536:         b = (3.6f * norm);
Chris@536:         if (norm > 0.277f) b = 2.f - b;
Chris@536:         if (b > 1.f) b = 1.f;
Chris@536:         if (b < 0.f) b = 0.f;
Chris@536:         hsv = false;
Chris@536: /*
Chris@536:         if (r > 1.f) r = 1.f;
Chris@536:         r = g = b = 1.f - r;
Chris@536:         hsv = false;
Chris@536: */
Chris@536:         break;
Chris@376:     }
Chris@376: 
Chris@376:     if (hsv) {
Chris@376:         return QColor::fromHsvF(h, s, v);
Chris@376:     } else {
Chris@376:         return QColor::fromRgbF(r, g, b);
Chris@376:     }
Chris@376: }
Chris@376: 
Chris@376: QColor
Chris@376: ColourMapper::getContrastingColour() const
Chris@376: {
Chris@376:     if (m_map >= getColourMapCount()) return Qt::white;
Chris@376:     StandardMap map = (StandardMap)m_map;
Chris@376: 
Chris@376:     switch (map) {
Chris@376: 
Chris@376:     case DefaultColours:
Chris@376:         return QColor(255, 150, 50);
Chris@376: 
Chris@376:     case WhiteOnBlack:
Chris@376:         return Qt::red;
Chris@376: 
Chris@376:     case BlackOnWhite:
Chris@376:         return Qt::darkGreen;
Chris@376: 
Chris@376:     case RedOnBlue:
Chris@376:         return Qt::green;
Chris@376: 
Chris@376:     case YellowOnBlack:
Chris@376:         return QColor::fromHsv(240, 255, 255);
Chris@376: 
Chris@376:     case BlueOnBlack:
Chris@376:         return Qt::red;
Chris@376: 
Chris@376:     case Sunset:
Chris@376:         return Qt::white;
Chris@376: 
Chris@376:     case FruitSalad:
Chris@376:         return Qt::white;
Chris@376: 
Chris@376:     case Banded:
Chris@376:         return Qt::cyan;
Chris@376: 
Chris@376:     case Highlight:
Chris@376:         return Qt::red;
Chris@376: 
Chris@376:     case Printer:
Chris@376:         return Qt::red;
Chris@536: 
Chris@536:     case HighGain:
Chris@536:         return Qt::red;
Chris@376:     }
Chris@376: 
Chris@376:     return Qt::white;
Chris@376: }
Chris@376: 
Chris@376: bool
Chris@376: ColourMapper::hasLightBackground() const
Chris@376: {
Chris@376:     if (m_map >= getColourMapCount()) return false;
Chris@376:     StandardMap map = (StandardMap)m_map;
Chris@376: 
Chris@376:     switch (map) {
Chris@376: 
Chris@376:     case BlackOnWhite:
Chris@376:     case Printer:
Chris@536:     case HighGain:
Chris@376:         return true;
Chris@376: 
Chris@805:     case DefaultColours:
Chris@805:     case Sunset:
Chris@805:     case WhiteOnBlack:
Chris@805:     case RedOnBlue:
Chris@805:     case YellowOnBlack:
Chris@805:     case BlueOnBlack:
Chris@805:     case FruitSalad:
Chris@805:     case Banded:
Chris@805:     case Highlight:
Chris@805:         
Chris@376:     default:
Chris@376:         return false;
Chris@376:     }
Chris@376: }
Chris@376: 
Chris@376: