soundeditor/qatsh/ColorMap.cpp

220 lines
5.5 KiB
C++

// ColorMap class implementation
//
// A ubiquitous color map with contrast and brightness management.
//
// QATSH Copyright 2009 Jean-Philippe MEURET <jpmeuret@free.fr>
#include "ColorMap.h"
#include "ATSMath.h"
#include <iostream>
ColorMap::ColorMap()
: _eStyle(eStyleNumber), _dMinValue(0.0), _dMaxValue(0.0), _dContrast(0.0), _dBrightness(0.0)
{
}
ColorMap::ColorMap(ColorMap::EStyle eStyle, int nColors,
double dMinValue, double dMaxValue, double dContrast, double dBrightness)
{
reset(eStyle, nColors, dMinValue, dMaxValue, dContrast, dBrightness);
}
void ColorMap::reset(ColorMap::EStyle eStyle, int nColors,
double dMinValue, double dMaxValue, double dContrast, double dBrightness)
{
// Check requested temperature, nb of colors, value interval and contrast :
// no reset if any issue.
eStyle = (eStyle >= 0 && eStyle < eStyleNumber) ? eStyle : eStyleNumber;
if (eStyle == eStyleNumber)
return;
nColors = (nColors > 1 && nColors <= 256) ? nColors : 0;
if (nColors == 0)
return;
if (dMinValue >= dMaxValue)
return;
if (dContrast < 0 || dContrast > 1)
return;
if (dBrightness < 0 || dBrightness > 1)
return;
// Save scale properties.
_eStyle = eStyle;
_dMinValue = dMinValue;
_dMaxValue = dMaxValue;
_dContrast = dContrast;
_dBrightness = dBrightness;
// Clear color list.
_qlColors.clear();
// std::cout << "ColorMap::reset(contrast=" << _dContrast
// << ", brightness=" << _dBrightness<< std::endl;
// Fill-in color list according to requested properties.
switch(_eStyle)
{
// Medium : Use partial hue scale from 239=blue=cold to 0=red=hot)
case eStyleStandard:
{
double dT, dH, dV;
const double dContPower = pow(2.0, 10.0 * (_dContrast - 0.5));
const double dBrightPower = pow(2.0, -4.0);
for (int nColorInd = 0; nColorInd < nColors; nColorInd++)
{
dT = pow(nColorInd / (double)(nColors - 1), dContPower);
dH = (1.0 - dT) * 240.0 / 360;
dV = (0.5 + 0.5 * _dBrightness) * pow(dT, dBrightPower);
_qlColors.append(QColor::fromHsvF(dH, 1.0, dV));
}
}
break;
// Cold : from ??? = cold to ??? = hot
case eStyleCold:
{
double dT, dR, dG, dB;
const double dBright = 0.25 + 0.75 * _dBrightness;
const double dContPower = pow(2.0, 10.0 * (_dContrast - 0.5));
for (int nColorInd = 0; nColorInd < nColors; nColorInd++)
{
dT = pow(nColorInd / (double)(nColors - 1), dContPower);
dR = dBright * dT;
dG = dBright * (1.0 - dT);
dB = dBright * 1.0;
_qlColors.append(QColor::fromRgbF(dR, dG, dB));
}
}
break;
// Hot : from red = cold to yellowish white = hot
case eStyleHot:
{
double dR, dG, dB;
const int nStop = (int)floor(3.0 * nColors / 8.0);
const double dBright = 0.25 + 0.75 * _dBrightness;
const double dContPower = pow(2.0, 10.0 * (_dContrast - 0.5));
for (int nColorInd = 0; nColorInd < nColors; nColorInd++)
{
if (nColorInd < nStop)
{
dR = dBright * pow(nColorInd / (double)nStop, dContPower);
dG = dBright * 0.0;
dB = dBright * 0.0;
}
else if (nColorInd >= 2*nStop)
{
dR = dBright * 1.0;
dG = dBright * 1.0;
dB = dBright * pow((nColorInd - 2*nStop + 1) / (double)nStop, dContPower);
}
else
{
dR = dBright * 1.0;
dG = dBright * pow((nColorInd - nStop + 1) / (double)nStop, dContPower);
dB = dBright * 0.0;
}
_qlColors.append(QColor::fromRgbF(dR, dG, dB));
}
}
break;
default: // Should never get there
break;
}
}
int ColorMap::nbColors() const
{
return _qlColors.size();
}
void ColorMap::setNbColors(int nColors)
{
reset(_eStyle, nColors, _dMinValue, _dMaxValue, _dContrast, _dBrightness);
}
ColorMap::EStyle ColorMap::style() const
{
return _eStyle;
}
void ColorMap::setStyle(EStyle eStyle)
{
reset(eStyle, _qlColors.size(), _dMinValue, _dMaxValue, _dContrast, _dBrightness);
}
double ColorMap::minValue() const
{
return _dMinValue;
}
void ColorMap::setMinValue(double dValue)
{
reset(_eStyle, _qlColors.size(), dValue, _dMaxValue, _dContrast, _dBrightness);
}
double ColorMap::maxValue() const
{
return _dMaxValue;
}
void ColorMap::setMaxValue(double dValue)
{
reset(_eStyle, _qlColors.size(), _dMinValue, dValue, _dContrast, _dBrightness);
}
double ColorMap::contrast() const
{
return _dContrast;
}
void ColorMap::setContrast(double dValue)
{
reset(_eStyle, _qlColors.size(), _dMinValue, _dMaxValue, dValue, _dBrightness);
}
double ColorMap::brightness() const
{
return _dBrightness;
}
void ColorMap::setBrightness(double dValue)
{
reset(_eStyle, _qlColors.size(), _dMinValue, _dMaxValue, _dContrast, dValue);
}
// TODO: Contrast feature, through a contrast function to apply to input values
// before querying for the color.
// Normal contrast => linear function : y = a + b.x^1
// High contrast => exponential / square function : y = a + b.x^n
// Low contrast => logarythmic / square root function : y = a + b.x^1/n
QColor ColorMap::color(double dValue) const
{
//if (_eStyle == eStyleNumber || _dMinValue >= _dMaxValue)
//return QColor(Qt::black);
if (dValue < _dMinValue)
return _qlColors[0];
else if (dValue >= _dMaxValue)
return _qlColors[_qlColors.size() - 1];
else
return _qlColors[(int)floor(_qlColors.size() * (dValue - _dMinValue)
/ (_dMaxValue - _dMinValue))];
}
const QColor& ColorMap::color(int nIndex) const
{
return _qlColors[nIndex];
}