changeset 196:22c99c8aa1e0

* Add separate colour mapping unit; use it in spectrogram (colour 3d plot to follow) * Add another colour scheme resembling that of a noted commercial application
author Chris Cannam
date Wed, 31 Jan 2007 12:13:47 +0000
parents 4a3bdde1ef13
children 6b023411087b
files layer/ColourMapper.cpp layer/ColourMapper.h layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h layer/layer.pro widgets/SubdividingMenu.cpp
diffstat 6 files changed, 263 insertions(+), 92 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/layer/ColourMapper.cpp	Wed Jan 31 12:13:47 2007 +0000
@@ -0,0 +1,176 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006-2007 Chris Cannam and QMUL.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "ColourMapper.h"
+
+ColourMapper::ColourMapper(int map, float min, float max) :
+    QObject(),
+    m_map(map),
+    m_min(min),
+    m_max(max)
+{
+}
+
+ColourMapper::~ColourMapper()
+{
+}
+
+int
+ColourMapper::getColourMapCount()
+{
+    return 8;
+}
+
+QString
+ColourMapper::getColourMapName(int n)
+{
+    if (n >= 8) return tr("<unknown>");
+    StandardMap map = (StandardMap)n;
+
+    switch (map) {
+    case DefaultColours:   return tr("Default");
+    case WhiteOnBlack:     return tr("White on Black");
+    case BlackOnWhite:     return tr("Black on White");
+    case RedOnBlue:        return tr("Red on Blue");
+    case YellowOnBlack:    return tr("Yellow on Black");
+    case BlueOnBlack:      return tr("Blue on Black");
+    case Sunset:           return tr("Sunset");
+    case FruitSalad:       return tr("Fruit Salad");
+    }
+
+    return tr("<unknown>");
+}
+
+QColor
+ColourMapper::map(float value) const
+{
+    float norm = (value - m_min) / (m_max - m_min);
+    if (norm < 0.f) norm = 0.f;
+    if (norm > 1.f) norm = 1.f;
+    
+    float h = 0.f, s = 0.f, v = 0.f, r = 0.f, g = 0.f, b = 0.f;
+    bool hsv = true;
+
+    float red = 0.f, green = 0.3333f, blue = 0.6666f, pieslice = 0.3333f;
+
+    if (m_map >= 8) return Qt::black;
+    StandardMap map = (StandardMap)m_map;
+
+    switch (map) {
+
+    case DefaultColours:
+        h = blue - norm * 2.f * pieslice;
+        s = 0.5f + norm/2.f;
+        v = norm;
+        break;
+
+    case WhiteOnBlack:
+        r = g = b = norm;
+        hsv = false;
+        break;
+
+    case BlackOnWhite:
+        r = g = b = 1.f - norm;
+        hsv = false;
+        break;
+
+    case RedOnBlue:
+        h = blue - pieslice/4.f + norm * (pieslice + pieslice/4.f);
+        s = 1.f;
+        v = norm;
+        break;
+
+    case YellowOnBlack:
+        h = 0.15f;
+        s = 1.f;
+        v = norm;
+        break;
+
+    case BlueOnBlack:
+        h = blue / 1.f;
+        s = 1.f;
+        v = norm * 2.f;
+        if (v > 1) {
+            v = 1.f;
+            s = 1.f - (sqrtf(norm) - 0.707f) * 3.414f;
+        }
+        break;
+
+    case Sunset:
+        r = (norm - 0.24f) * 2.38f;
+        if (r > 1.f) r = 1.f;
+        if (r < 0.f) r = 0.f;
+        g = (norm - 0.64f) * 2.777f;
+        if (g > 1.f) g = 1.f;
+        if (g < 0.f) g = 0.f;
+        b = (3.6f * norm);
+        if (norm > 0.277f) b = 2.f - b;
+        if (b > 1.f) b = 1.f;
+        if (b < 0.f) b = 0.f;
+        hsv = false;
+        break;
+
+    case FruitSalad:
+        h = blue + (pieslice/2.f) - norm;
+        if (h < 0.f) h += 1.f;
+        s = 1.f;
+        v = 1.f;
+        break;
+    }
+
+    if (hsv) {
+        return QColor::fromHsvF(h, s, v);
+    } else {
+        return QColor::fromRgbF(r, g, b);
+    }
+}
+
+QColor
+ColourMapper::getContrastingColour() const
+{
+    if (m_map >= 8) return Qt::white;
+    StandardMap map = (StandardMap)m_map;
+
+    switch (map) {
+
+    case DefaultColours:
+        return QColor(255, 150, 50);
+
+    case WhiteOnBlack:
+        return Qt::red;
+
+    case BlackOnWhite:
+        return Qt::darkGreen;
+
+    case RedOnBlue:
+        return Qt::green;
+
+    case YellowOnBlack:
+        return QColor::fromHsv(240, 255, 255);
+
+    case BlueOnBlack:
+        return Qt::red;
+
+    case Sunset:
+        return Qt::white;
+
+    case FruitSalad:
+        return Qt::white;
+    }
+
+    return Qt::white;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/layer/ColourMapper.h	Wed Jan 31 12:13:47 2007 +0000
@@ -0,0 +1,62 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006-2007 Chris Cannam and QMUL.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef _COLOUR_MAPPER_H_
+#define _COLOUR_MAPPER_H_
+
+#include <QObject>
+#include <QColor>
+#include <QString>
+
+/**
+ * A class for mapping intensity values onto various colour maps.
+ */
+
+class ColourMapper : public QObject
+{
+public:
+    ColourMapper(int map, float minValue, float maxValue);
+    virtual ~ColourMapper();
+
+    enum StandardMap {
+        DefaultColours,
+        Sunset,
+        WhiteOnBlack,
+        BlackOnWhite,
+        RedOnBlue,
+        YellowOnBlack,
+        BlueOnBlack,
+        FruitSalad
+    };
+
+    int getMap() const { return m_map; }
+    float getMinValue() const { return m_min; }
+    float getMaxValue() const { return m_max; }
+
+    static int getColourMapCount();
+    static QString getColourMapName(int n);
+
+    QColor map(float value) const;
+
+    QColor getContrastingColour() const; // for cursors etc
+
+protected:
+    int m_map;
+    float m_min;
+    float m_max;
+};
+
+#endif
+
--- a/layer/SpectrogramLayer.cpp	Mon Jan 29 18:11:20 2007 +0000
+++ b/layer/SpectrogramLayer.cpp	Wed Jan 31 12:13:47 2007 +0000
@@ -22,6 +22,7 @@
 #include "base/Pitch.h"
 #include "base/Preferences.h"
 #include "base/RangeMapper.h"
+#include "ColourMapper.h"
 
 #include <QPainter>
 #include <QImage>
@@ -53,7 +54,7 @@
     m_maxFrequency(8000),
     m_initialMaxFrequency(8000),
     m_colourScale(dBColourScale),
-    m_colourScheme(DefaultColours),
+    m_colourScheme(0),
     m_frequencyScale(LinearFrequencyScale),
     m_binDisplay(AllBins),
     m_normalizeColumns(false),
@@ -248,9 +249,9 @@
     } else if (name == "Colour") {
 
 	*min = 0;
-	*max = 6;
-
-	deft = (int)m_colourScheme;
+	*max = ColourMapper::getColourMapCount() - 1;
+
+	deft = m_colourScheme;
 
     } else if (name == "Window Size") {
 
@@ -343,16 +344,7 @@
 					int value) const
 {
     if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0: return tr("Default");
-	case 1: return tr("White on Black");
-	case 2: return tr("Black on White");
-	case 3: return tr("Red on Blue");
-	case 4: return tr("Yellow on Black");
-	case 5: return tr("Blue on Black");
-	case 6: return tr("Fruit Salad");
-	}
+        return ColourMapper::getColourMapName(value);
     }
     if (name == "Colour Scale") {
 	switch (value) {
@@ -453,16 +445,7 @@
     } else if (name == "Colour Rotation") {
 	setColourRotation(value);
     } else if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0:	setColourScheme(DefaultColours); break;
-	case 1: setColourScheme(WhiteOnBlack); break;
-	case 2: setColourScheme(BlackOnWhite); break;
-	case 3: setColourScheme(RedOnBlue); break;
-	case 4: setColourScheme(YellowOnBlack); break;
-	case 5: setColourScheme(BlueOnBlack); break;
-	case 6: setColourScheme(Rainbow); break;
-	}
+        setColourScheme(value);
     } else if (name == "Window Size") {
 	setWindowSize(32 << value);
     } else if (name == "Window Increment") {
@@ -798,7 +781,7 @@
 }
 
 void
-SpectrogramLayer::setColourScheme(ColourScheme scheme)
+SpectrogramLayer::setColourScheme(int scheme)
 {
     if (m_colourScheme == scheme) return;
 
@@ -810,7 +793,7 @@
     emit layerParametersChanged();
 }
 
-SpectrogramLayer::ColourScheme
+int
 SpectrogramLayer::getColourScheme() const
 {
     return m_colourScheme;
@@ -1013,72 +996,21 @@
 {
     int formerRotation = m_colourRotation;
 
-    if (m_colourScheme == BlackOnWhite) {
+    if (m_colourScheme == (int)ColourMapper::BlackOnWhite) {
 	m_colourMap.setColour(NO_VALUE, Qt::white);
     } else {
 	m_colourMap.setColour(NO_VALUE, Qt::black);
     }
 
+    ColourMapper mapper(m_colourScheme, 1.f, 256.f);
+    
     for (int pixel = 1; pixel < 256; ++pixel) {
 
-	QColor colour;
-	int hue, px;
-
-	switch (m_colourScheme) {
-
-	default:
-	case DefaultColours:
-	    hue = 256 - pixel;
-	    colour = QColor::fromHsv(hue, pixel/2 + 128, pixel);
-            m_crosshairColour = QColor(255, 150, 50);
-//            m_crosshairColour = QColor::fromHsv(240, 160, 255);
-	    break;
-
-	case WhiteOnBlack:
-	    colour = QColor(pixel, pixel, pixel);
-            m_crosshairColour = Qt::red;
-	    break;
-
-	case BlackOnWhite:
-	    colour = QColor(256-pixel, 256-pixel, 256-pixel);
-            m_crosshairColour = Qt::darkGreen;
-	    break;
-
-	case RedOnBlue:
-	    colour = QColor(pixel > 128 ? (pixel - 128) * 2 : 0, 0,
-			    pixel < 128 ? pixel : (256 - pixel));
-            m_crosshairColour = Qt::green;
-	    break;
-
-	case YellowOnBlack:
-	    px = 256 - pixel;
-	    colour = QColor(px < 64 ? 255 - px/2 :
-			    px < 128 ? 224 - (px - 64) :
-			    px < 192 ? 160 - (px - 128) * 3 / 2 :
-			    256 - px,
-			    pixel,
-			    pixel / 4);
-            m_crosshairColour = QColor::fromHsv(240, 255, 255);
-	    break;
-
-        case BlueOnBlack:
-            colour = QColor::fromHsv
-                (240, pixel > 226 ? 256 - (pixel - 226) * 8 : 255,
-                 (pixel * pixel) / 255);
-            m_crosshairColour = Qt::red;
-            break;
-
-	case Rainbow:
-	    hue = 250 - pixel;
-	    if (hue < 0) hue += 256;
-	    colour = QColor::fromHsv(pixel, 255, 255);
-            m_crosshairColour = Qt::white;
-	    break;
-	}
-
-	m_colourMap.setColour(pixel, colour);
+        m_colourMap.setColour(pixel, mapper.map(pixel));
     }
 
+    m_crosshairColour = mapper.getContrastingColour();
+
     m_colourRotation = 0;
     rotateColourmap(m_colourRotation - formerRotation);
     m_colourRotation = formerRotation;
@@ -1734,7 +1666,7 @@
 void
 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const
 {
-    if (m_colourScheme == BlackOnWhite) {
+    if (m_colourScheme == (int)ColourMapper::BlackOnWhite) {
 	v->setLightBackground(true);
     } else {
 	v->setLightBackground(false);
@@ -3064,8 +2996,7 @@
 	attributes.value("colourScale").toInt(&ok);
     if (ok) setColourScale(colourScale);
 
-    ColourScheme colourScheme = (ColourScheme)
-	attributes.value("colourScheme").toInt(&ok);
+    int colourScheme = attributes.value("colourScheme").toInt(&ok);
     if (ok) setColourScheme(colourScheme);
 
     int colourRotation = attributes.value("colourRotation").toInt(&ok);
--- a/layer/SpectrogramLayer.h	Mon Jan 29 18:11:20 2007 +0000
+++ b/layer/SpectrogramLayer.h	Wed Jan 31 12:13:47 2007 +0000
@@ -170,11 +170,8 @@
     void setNormalizeVisibleArea(bool n);
     bool getNormalizeVisibleArea() const;
 
-    enum ColourScheme { DefaultColours, WhiteOnBlack, BlackOnWhite,
-			RedOnBlue, YellowOnBlack, BlueOnBlack, Rainbow };
-
-    void setColourScheme(ColourScheme scheme);
-    ColourScheme getColourScheme() const;
+    void setColourScheme(int scheme);
+    int getColourScheme() const;
 
     /**
      * Specify the colourmap rotation for the colour scale.
@@ -241,7 +238,7 @@
     size_t              m_maxFrequency;
     size_t              m_initialMaxFrequency;
     ColourScale         m_colourScale;
-    ColourScheme        m_colourScheme;
+    int                 m_colourScheme;
     QColor              m_crosshairColour;
     FrequencyScale      m_frequencyScale;
     BinDisplay          m_binDisplay;
--- a/layer/layer.pro	Mon Jan 29 18:11:20 2007 +0000
+++ b/layer/layer.pro	Wed Jan 31 12:13:47 2007 +0000
@@ -15,6 +15,7 @@
 
 # Input
 HEADERS += Colour3DPlotLayer.h \
+           ColourMapper.h \
            Layer.h \
            LayerFactory.h \
            NoteLayer.h \
@@ -29,6 +30,7 @@
            TimeValueLayer.h \
            WaveformLayer.h
 SOURCES += Colour3DPlotLayer.cpp \
+           ColourMapper.cpp \
            Layer.cpp \
            LayerFactory.cpp \
            NoteLayer.cpp \
--- a/widgets/SubdividingMenu.cpp	Mon Jan 29 18:11:20 2007 +0000
+++ b/widgets/SubdividingMenu.cpp	Wed Jan 31 12:13:47 2007 +0000
@@ -57,6 +57,7 @@
 
     size_t count = 0;
     QMenu *chunkMenu = new QMenu();
+    chunkMenu->setTearOffEnabled(isTearOffEnabled());
 
     QString firstNameInChunk;
     QChar firstInitialInChunk;
@@ -120,6 +121,7 @@
             QMenu::addMenu(chunkMenu);
             
             chunkMenu = new QMenu();
+            chunkMenu->setTearOffEnabled(isTearOffEnabled());
             
             count = 0;
         }
@@ -244,6 +246,7 @@
 {
     if (!m_entriesSet) {
         QMenu *menu = new QMenu(name, this);
+        menu->setTearOffEnabled(isTearOffEnabled());
         m_pendingEntries[name] = menu;
         return menu;
     }