Color gradient class






1.80/5 (5 votes)
A C++ class for defining a color gradient and interpolating a value to produce a color.
Introduction
A class that interpolates between a single value (an unsigned 16 bit integer) and an RGB color value on a color scale that can be defined.
Source code
Due to the simplicity of the class, I'm displaying it here so that you don't have to download a zip file and extract that. This code should go into a file called Gradient.h.
/*
* A C++ class for defining a color gradient and interpolating a bound
* value to produce a color value.
*
* Copyright (C) 2008 Stefán Freyr Stefánsson, Arnar Birgisson
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRADIENT_H_
#define GRADIENT_H_
#include <vector>
/**
* A representation of a color. Each channel is the standard 8-bit.
*/
typedef struct rgb {
uint8_t red;
uint8_t green;
uint8_t blue;
bool operator==(rgb lhr)
{
return this == &lhr;
}
}rgb;
/**
* A color instance that represents an invalid color
*/
static rgb INVALID_COLOR = {0, 0, 0};
/**
* A helper class to map a numerical value to a gradient color scale.
* The gradient appearance is controlled by specifying a list of colors
* (called stops) which will then be distributed across a specified
* range of values. Users can get a color value for a specific numerical
* value and this class will interpolate the correct color value from
* its stop list.
*/
class Gradient
{
private:
/**
* The minimum value that this gradient will represent.
*/
uint16_t m_min;
/**
* The color representing the minimum value.
*/
rgb m_minColor;
/**
* The outlier color for min outliers.
*/
rgb m_minOutlierColor;
/**
* The maximum value that this gradient will represent.
*/
uint16_t m_max;
/**
* The color representing the maximum value.
*/
rgb m_maxColor;
/**
* The outlier color for max outliers.
*/
rgb m_maxOutlierColor;
/**
* The color values of the gradient.
*/
std::vector<rgb> m_stops;
/**
* The workhorse of the class. This method calculates the color that
* represents the interpolation of the two specified color values using
* the specified normalized value.
* @param c1 the color representing the normalized value 0.0
* @param c2 the color representing the normalized value 1.0
* @param normalized_value a value between 0.0 and 1.0 representing
* where on the color scale between c1 and c2
* the returned color should be.
* @return the interpolated color at normalized_value between c1 and c2.
*/
rgb interpolate(rgb c1, rgb c2, float normalized_value){
if( normalized_value <= 0.0 ){ return c1; }
if( normalized_value >= 1.0 ){ return c2; }
uint8_t red = (uint8_t)((1.0-normalized_value)*c1.red +
normalized_value*c2.red);
uint8_t green = (uint8_t)((1.0-normalized_value)*c1.green +
normalized_value*c2.green);
uint8_t blue = (uint8_t)((1.0-normalized_value)*c1.blue +
normalized_value*c2.blue);
return (rgb){red, green, blue};
}
public:
/**
* Creates a new uninitialized instance of a Gradient. The caller must
* initialize the instance with values before use by calling the
* initialize method.
*/
Gradient(){}
/**
* Initializes this gradient by defining the range and intermediate stops as well as
* optional outlier colors (both for values less that the minimum as well as values
* more than the maximum. If outlier colors are not specified the first and last colors
* of the stops will be used for all values
* less than min and greater than max respectively.
*/
void initialize(uint16_t min, uint16_t max, std::vector<rgb> stops,
rgb minOutlierColor=INVALID_COLOR, rgb maxOutlierColor=INVALID_COLOR){
m_min = min;
m_max = max;
m_stops = stops;
m_minOutlierColor = minOutlierColor;
m_maxOutlierColor = maxOutlierColor;
}
/**
* Creates a new instance of a Gradient and initializes it with the specified values.
*/
Gradient(uint16_t min, uint16_t max, std::vector<rgb> stops,
rgb minOutlierColor=INVALID_COLOR, rgb maxOutlierColor=INVALID_COLOR){
initialize(min, max, stops, minOutlierColor, maxOutlierColor);
}
/**
* Destructor.
*/
virtual ~Gradient(){}
/**
* Retrieve an RGB color struct for a specified value. Caller must have invoked
* initialize before calling this method.
*/
rgb getRgb(uint16_t value){
// Handle outliers
if( value < m_min ){ return m_minOutlierColor ==
INVALID_COLOR ? m_stops.front() : m_minOutlierColor; }
if( value > m_max ){ return m_maxOutlierColor ==
INVALID_COLOR ? m_stops.back() : m_maxOutlierColor; }
// Find the "bin" that value falls in
uint16_t range = m_max - m_min;
uint16_t v = value - m_min;
float step = range / (float)(m_stops.size()-1);
int bin = (int)(v / step);
// Normalize value in the interval (0,1]
float normalized_v = (v - bin*step) / step;
return interpolate(m_stops[bin], m_stops[bin+1], normalized_v);
}
};
#endif /*GRADIENT_H_*/
Using the code
Here's an example of the code in action:
#include <Gradient.h>
[...]
std::vector<rgb> stops;
stops.push_back((rgb){255,255,255}); // white is the "closest"
stops.push_back((rgb){255,0,0}); // red
stops.push_back((rgb){255,255,0}); // yellow
stops.push_back((rgb){0,255,0}); // green
stops.push_back((rgb){0,0,255}); // blue
stops.push_back((rgb){75,25,150}); // purple is the "farthest"
Gradient grad;
// initialize with 0x00f as the min value, 0xfff0 as the max value,
// the above vector of rgb values as the color space definition and
// use black to represent any outliers (both <min and >max)
grad.initialize(0x000f, 0xfff0, stops, (rgb){0,0,0}, (rgb){0,0,0});
// Get the color value for 123456
rgb color = grad.getRgb(123456);
// use color.r, color.g, color.b to obtain the channel values
Known bugs
The Gradient
class currently only works for interpolating uint16_t
integral values to color. It's quite possible that this could be made more flexible (for example, by making the class a template class). There are some other potential bugs in it that have to do with an integer overflow, so any comments or improvements on the code are welcome.