WIP: HSV support in lay::Color

This commit is contained in:
Matthias Koefferlein 2022-04-27 23:03:17 +02:00
parent a6f2528aff
commit 7048dde7b3
12 changed files with 324 additions and 113 deletions

View File

@ -45,12 +45,12 @@ static void clear_colormap (img::DataMapping *dm)
static void add_colormap (img::DataMapping *dm, double value, lay::color_t color)
{
dm->false_color_nodes.push_back (std::make_pair (value, std::make_pair (QColor (color), QColor (color))));
dm->false_color_nodes.push_back (std::make_pair (value, std::make_pair (lay::Color (color), lay::Color (color))));
}
static void add_colormap2 (img::DataMapping *dm, double value, lay::color_t lcolor, lay::color_t rcolor)
{
dm->false_color_nodes.push_back (std::make_pair (value, std::make_pair (QColor (lcolor), QColor (rcolor))));
dm->false_color_nodes.push_back (std::make_pair (value, std::make_pair (lay::Color (lcolor), lay::Color (rcolor))));
}
static size_t num_colormap_entries (const img::DataMapping *dm)

View File

@ -31,6 +31,7 @@
#include "dbPolygonTools.h"
#include "tlFileUtils.h"
#include "tlUri.h"
#include "tlThreads.h"
#include <cmath>
#include <cstring>
@ -40,7 +41,6 @@
#include <memory.h>
#include <QImage>
#include <QMutex>
namespace img
{
@ -51,8 +51,8 @@ namespace img
DataMapping::DataMapping ()
: brightness (0.0), contrast (0.0), gamma (1.0), red_gain (1.0), green_gain (1.0), blue_gain (1.0)
{
false_color_nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (0, 0, 0), QColor (0, 0, 0))));
false_color_nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (255, 255, 255), QColor (255, 255, 255))));
false_color_nodes.push_back (std::make_pair (0.0, std::make_pair (lay::Color (0, 0, 0), lay::Color (0, 0, 0))));
false_color_nodes.push_back (std::make_pair (1.0, std::make_pair (lay::Color (255, 255, 255), lay::Color (255, 255, 255))));
}
bool
@ -192,11 +192,11 @@ DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, uns
for (unsigned int i = 1; i < false_color_nodes.size (); ++i) {
int h1, s1, v1;
false_color_nodes [i - 1].second.second.getHsv (&h1, &s1, &v1);
unsigned int h1, s1, v1;
false_color_nodes [i - 1].second.second.get_hsv (h1, s1, v1);
int h2, s2, v2;
false_color_nodes [i].second.first.getHsv (&h2, &s2, &v2);
unsigned int h2, s2, v2;
false_color_nodes [i].second.first.get_hsv (h2, s2, v2);
// The number of steps is chosen such that the full HSV band divides into approximately 200 steps
double nsteps = 0.5 * sqrt (double (h1 - h2) * double (h1 - h2) + double (s1 - s2) * double (s1 - s2) + double (v1 - v2) * double (v1 - v2));
@ -206,7 +206,7 @@ DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, uns
for (int j = 0; j < n; ++j) {
QColor c = interpolated_color (false_color_nodes, x);
lay::Color c = interpolated_color (false_color_nodes, x);
double y = 0.0;
if (channel == 0) {
@ -260,6 +260,57 @@ DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, uns
return dm;
}
// --------------------------------------------------------------------------------------
namespace
{
struct compare_first_of_node
{
bool operator() (const std::pair <double, std::pair<lay::Color, lay::Color> > &a, const std::pair <double, std::pair<lay::Color, lay::Color> > &b) const
{
return a.first < b.first;
}
};
}
lay::Color
interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x)
{
if (nodes.size () < 1) {
return lay::Color ();
} else if (nodes.size () < 2) {
return x < nodes[0].first ? nodes[0].second.first : nodes[0].second.second;
} else {
std::vector<std::pair<double, std::pair<lay::Color, lay::Color> > >::const_iterator p = std::lower_bound (nodes.begin (), nodes.end (), std::make_pair (x, std::make_pair (lay::Color (), lay::Color ())), compare_first_of_node ());
if (p == nodes.end ()) {
return nodes.back ().second.second;
} else if (p == nodes.begin ()) {
return nodes.front ().second.first;
} else {
double x1 = p[-1].first;
double x2 = p->first;
unsigned int h1 = 0, s1 = 0, v1 = 0;
p[-1].second.second.get_hsv (h1, s1, v1);
unsigned int h2 = 0, s2 = 0, v2 = 0;
p->second.first.get_hsv (h2, s2, v2);
int h = int (0.5 + h1 + double(x - x1) * double (h2 - h1) / double(x2 - x1));
int s = int (0.5 + s1 + double(x - x1) * double (s2 - s1) / double(x2 - x1));
int v = int (0.5 + v1 + double(x - x1) * double (v2 - v1) / double(x2 - x1));
return lay::Color::from_hsv ((unsigned int) h, (unsigned int) s, (unsigned int) v);
}
}
}
// --------------------------------------------------------------------------------------
// img::DataHeader definition and implementation
@ -690,7 +741,7 @@ private:
static size_t make_id ()
{
static QMutex id_lock;
static tl::Mutex id_lock;
static size_t s_id_counter = 1;
// Get a new Id for the object. Id == 0 is reserved.
@ -1283,7 +1334,7 @@ Object::from_string (const char *str, const char *base_dir)
double x = 0.0;
lay::ColorConverter cc;
QColor cl, cr;
lay::Color cl, cr;
std::string s;
m_data_mapping.false_color_nodes.clear ();
@ -1654,7 +1705,7 @@ Object::to_string () const
for (unsigned int i = 0; i < data_mapping ().false_color_nodes.size (); ++i) {
os << data_mapping ().false_color_nodes[i].first;
os << ",";
const std::pair<QColor, QColor> &clr = data_mapping ().false_color_nodes[i].second;
const std::pair<lay::Color, lay::Color> &clr = data_mapping ().false_color_nodes[i].second;
os << tl::to_word_or_quoted_string (cc.to_string (clr.first));
if (clr.first != clr.second) {
os << ",";

View File

@ -33,13 +33,11 @@
#include "dbMatrix.h"
#include "dbPolygon.h"
#include "tlDataMapping.h"
#include "layViewOp.h"
#include "layColor.h"
#include <string>
#include <vector>
#include <QColor>
namespace img {
class DataHeader;
@ -52,7 +50,7 @@ class DataHeader;
struct IMG_PUBLIC DataMapping
{
public:
typedef std::vector< std::pair<double, std::pair<QColor, QColor> > > false_color_nodes_type;
typedef std::vector< std::pair<double, std::pair<lay::Color, lay::Color> > > false_color_nodes_type;
/**
* @brief The constructor
@ -139,6 +137,11 @@ public:
tl::DataMappingBase *create_data_mapping (bool monochrome, double xmin, double xmax, unsigned int channel) const;
};
/**
* @brief A helper function to interpolate a color in the color bar at a given x
*/
lay::Color interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x);
/**
* @brief A image object
*

View File

@ -735,9 +735,9 @@ PropertiesPage::blue_spinbox_changed (double value)
void
PropertiesPage::black_to_white ()
{
std::vector <std::pair <double, std::pair<QColor, QColor> > > nodes;
nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (0, 0, 0), QColor (0, 0, 0))));
nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (255, 255, 255), QColor (255, 255, 255))));
std::vector <std::pair <double, std::pair<lay::Color, lay::Color> > > nodes;
nodes.push_back (std::make_pair (0.0, std::make_pair (lay::Color (0, 0, 0), lay::Color (0, 0, 0))));
nodes.push_back (std::make_pair (1.0, std::make_pair (lay::Color (255, 255, 255), lay::Color (255, 255, 255))));
false_color_control->set_nodes (nodes);
emit edited ();
}
@ -745,9 +745,9 @@ PropertiesPage::black_to_white ()
void
PropertiesPage::white_to_black ()
{
std::vector <std::pair <double, std::pair<QColor, QColor> > > nodes;
nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (255, 255, 255), QColor (255, 255, 255))));
nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (0, 0, 0), QColor (0, 0, 0))));
std::vector <std::pair <double, std::pair<lay::Color, lay::Color> > > nodes;
nodes.push_back (std::make_pair (0.0, std::make_pair (lay::Color (255, 255, 255), lay::Color (255, 255, 255))));
nodes.push_back (std::make_pair (1.0, std::make_pair (lay::Color (0, 0, 0), lay::Color (0, 0, 0))));
false_color_control->set_nodes (nodes);
emit edited ();
}
@ -755,9 +755,9 @@ PropertiesPage::white_to_black ()
void
PropertiesPage::red_to_blue ()
{
std::vector <std::pair <double, std::pair<QColor, QColor> > > nodes;
nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (255, 0, 0), QColor (255, 0, 0))));
nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (0, 0, 255), QColor (0, 0, 255))));
std::vector <std::pair <double, std::pair<lay::Color, lay::Color> > > nodes;
nodes.push_back (std::make_pair (0.0, std::make_pair (lay::Color (255, 0, 0), lay::Color (255, 0, 0))));
nodes.push_back (std::make_pair (1.0, std::make_pair (lay::Color (0, 0, 255), lay::Color (0, 0, 255))));
false_color_control->set_nodes (nodes);
emit edited ();
}
@ -765,9 +765,9 @@ PropertiesPage::red_to_blue ()
void
PropertiesPage::blue_to_red ()
{
std::vector <std::pair <double, std::pair<QColor, QColor> > > nodes;
nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (0, 0, 255), QColor (0, 0, 255))));
nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (255, 0, 0), QColor (255, 0, 0))));
std::vector <std::pair <double, std::pair<lay::Color, lay::Color> > > nodes;
nodes.push_back (std::make_pair (0.0, std::make_pair (lay::Color (0, 0, 255), lay::Color (0, 0, 255))));
nodes.push_back (std::make_pair (1.0, std::make_pair (lay::Color (255, 0, 0), lay::Color (255, 0, 0))));
false_color_control->set_nodes (nodes);
emit edited ();
}
@ -775,7 +775,7 @@ PropertiesPage::blue_to_red ()
void
PropertiesPage::reverse_color_order ()
{
std::vector <std::pair <double, std::pair<QColor, QColor> > > nodes (false_color_control->nodes ());
std::vector <std::pair <double, std::pair<lay::Color, lay::Color> > > nodes (false_color_control->nodes ());
for (size_t i = 0; i < nodes.size () / 2; ++i) {
std::swap (nodes [i].second.second, nodes [nodes.size () - 1 - i].second.first);
std::swap (nodes [i].second.first, nodes [nodes.size () - 1 - i].second.second);

View File

@ -373,7 +373,7 @@ namespace {
struct ColorMapConverter
{
std::string to_string (const std::pair<double, std::pair<QColor, QColor> > &cm) const
std::string to_string (const std::pair<double, std::pair<lay::Color, lay::Color> > &cm) const
{
std::string s;
s = tl::to_string (cm.first);
@ -389,7 +389,7 @@ namespace {
return s;
}
void from_string (const std::string &s, std::pair<double, std::pair<QColor, QColor> > &cm) const
void from_string (const std::string &s, std::pair<double, std::pair<lay::Color, lay::Color> > &cm) const
{
tl::Extractor ex (s.c_str ());
@ -427,7 +427,7 @@ tl::XMLStruct<ImageProxy> s_img_structure ("image-data",
tl::make_member (&ImageProxy::max_value, &ImageProxy::set_max_value, "max-value") +
tl::make_element (&ImageProxy::data_mapping, &ImageProxy::set_data_mapping, "data-mapping",
tl::make_element (&img::DataMapping::false_color_nodes, "color-map",
tl::make_member<std::pair<double, std::pair<QColor, QColor> >, img::DataMapping::false_color_nodes_type::const_iterator, img::DataMapping::false_color_nodes_type, ColorMapConverter> (&img::DataMapping::false_color_nodes_type::begin, &img::DataMapping::false_color_nodes_type::end, &img::DataMapping::false_color_nodes_type::push_back, "color-map-entry", ColorMapConverter ())
tl::make_member<std::pair<double, std::pair<lay::Color, lay::Color> >, img::DataMapping::false_color_nodes_type::const_iterator, img::DataMapping::false_color_nodes_type, ColorMapConverter> (&img::DataMapping::false_color_nodes_type::begin, &img::DataMapping::false_color_nodes_type::end, &img::DataMapping::false_color_nodes_type::push_back, "color-map-entry", ColorMapConverter ())
) +
tl::make_member (&img::DataMapping::brightness, "brightness") +
tl::make_member (&img::DataMapping::contrast, "contrast") +

View File

@ -44,52 +44,6 @@ const int min_bar_height = 4;
const double min_value_interval = 1e-3;
const double epsilon = 1e-6;
struct compare_first_of_node
{
bool operator() (const std::pair <double, std::pair<QColor, QColor> > &a, const std::pair <double, std::pair<QColor, QColor> > &b) const
{
return a.first < b.first;
}
};
QColor
interpolated_color (const std::vector<std::pair <double, std::pair<QColor, QColor> > > &nodes, double x)
{
if (nodes.size () < 1) {
return QColor ();
} else if (nodes.size () < 2) {
return x < nodes[0].first ? nodes[0].second.first : nodes[0].second.second;
} else {
std::vector<std::pair<double, std::pair<QColor, QColor> > >::const_iterator p = std::lower_bound (nodes.begin (), nodes.end (), std::make_pair (x, std::make_pair (QColor (), QColor ())), compare_first_of_node ());
if (p == nodes.end ()) {
return nodes.back ().second.second;
} else if (p == nodes.begin ()) {
return nodes.front ().second.first;
} else {
double x1 = p[-1].first;
double x2 = p->first;
int h1 = 0, s1 = 0, v1 = 0;
p[-1].second.second.getHsv (&h1, &s1, &v1);
int h2 = 0, s2 = 0, v2 = 0;
p->second.first.getHsv (&h2, &s2, &v2);
int h = int (0.5 + h1 + double(x - x1) * double (h2 - h1) / double(x2 - x1));
int s = int (0.5 + s1 + double(x - x1) * double (s2 - s1) / double(x2 - x1));
int v = int (0.5 + v1 + double(x - x1) * double (v2 - v1) / double(x2 - x1));
QColor r;
r.setHsv (h, s, v);
return r;
}
}
}
// --------------------------------------------------------------------------------------------------------------------
TwoColorWidget::TwoColorWidget (QWidget *parent)
@ -173,8 +127,8 @@ TwoColorWidget::lock_changed (bool checked)
ColorBar::ColorBar (QWidget *parent)
: QWidget (parent), m_dragging (false), m_selected (-1)
{
m_nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (0, 0, 0), QColor (0, 0, 0))));
m_nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (255, 255, 255), QColor (255, 255, 255))));
m_nodes.push_back (std::make_pair (0.0, std::make_pair (lay::Color (0, 0, 0), lay::Color (0, 0, 0))));
m_nodes.push_back (std::make_pair (1.0, std::make_pair (lay::Color (255, 255, 255), lay::Color (255, 255, 255))));
}
ColorBar::~ColorBar ()
@ -206,7 +160,7 @@ void
ColorBar::set_current_color (std::pair<QColor, QColor> c)
{
if (has_selection ()) {
m_nodes [m_selected].second = c;
m_nodes [m_selected].second = std::make_pair (lay::Color (c.first.rgb ()), lay::Color (c.second.rgb ()));
emit color_mapping_changed ();
update ();
}
@ -260,22 +214,35 @@ ColorBar::keyPressEvent (QKeyEvent *event)
}
}
namespace
{
struct compare_first_of_node
{
bool operator() (const std::pair <double, std::pair<lay::Color, lay::Color> > &a, const std::pair <double, std::pair<lay::Color, lay::Color> > &b) const
{
return a.first < b.first;
}
};
}
void
ColorBar::set_nodes (const std::vector<std::pair<double, std::pair<QColor, QColor> > > &nodes)
ColorBar::set_nodes (const std::vector<std::pair<double, std::pair<lay::Color, lay::Color> > > &nodes)
{
m_nodes = nodes;
std::sort (m_nodes.begin (), m_nodes.end (), compare_first_of_node ());
if (m_nodes.size () == 0 || fabs (m_nodes[0].first) > epsilon) {
m_nodes.insert (m_nodes.begin (), std::make_pair (0.0, std::make_pair (QColor (0, 0, 0), QColor (0, 0, 0))));
m_nodes.insert (m_nodes.begin (), std::make_pair (0.0, std::make_pair (lay::Color (0, 0, 0), lay::Color (0, 0, 0))));
} else {
m_nodes[0].first = 0.0;
}
std::vector <std::pair <double, std::pair<QColor, QColor> > >::iterator w = m_nodes.begin ();
std::vector <std::pair <double, std::pair<QColor, QColor> > >::const_iterator nn = m_nodes.begin ();
for (std::vector <std::pair <double, std::pair<QColor, QColor> > >::const_iterator n = m_nodes.begin () + 1; n != m_nodes.end (); ++n) {
std::vector <std::pair <double, std::pair<lay::Color, lay::Color> > >::iterator w = m_nodes.begin ();
std::vector <std::pair <double, std::pair<lay::Color, lay::Color> > >::const_iterator nn = m_nodes.begin ();
for (std::vector <std::pair <double, std::pair<lay::Color, lay::Color> > >::const_iterator n = m_nodes.begin () + 1; n != m_nodes.end (); ++n) {
if (fabs (nn->first - n->first) > min_value_interval) {
*w++ = *nn;
nn = n;
@ -288,7 +255,7 @@ ColorBar::set_nodes (const std::vector<std::pair<double, std::pair<QColor, QColo
if (m_nodes.back ().first > 1.0 - min_value_interval) {
m_nodes.back ().first = 1.0;
} else {
m_nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (255, 255, 255), QColor (255, 255, 255))));
m_nodes.push_back (std::make_pair (1.0, std::make_pair (lay::Color (255, 255, 255), lay::Color (255, 255, 255))));
}
m_selected = -1;
@ -313,8 +280,8 @@ ColorBar::mousePressEvent (QMouseEvent *event)
double xx = double (event->x () - xl) / double (xr - xl);
double dmin = 100.0;
std::vector<std::pair<double, std::pair<QColor, QColor> > >::const_iterator pmin = m_nodes.end ();
for (std::vector<std::pair<double, std::pair<QColor, QColor> > >::const_iterator p = m_nodes.begin (); p != m_nodes.end (); ++p) {
std::vector<std::pair<double, std::pair<lay::Color, lay::Color> > >::const_iterator pmin = m_nodes.end ();
for (std::vector<std::pair<double, std::pair<lay::Color, lay::Color> > >::const_iterator p = m_nodes.begin (); p != m_nodes.end (); ++p) {
double d = fabs (p->first - xx);
if (d < 0.05 && d < dmin) {
dmin = d;
@ -323,9 +290,10 @@ ColorBar::mousePressEvent (QMouseEvent *event)
}
if (pmin != m_nodes.end ()) {
m_selected = int (std::distance (std::vector<std::pair<double, std::pair<QColor, QColor> > >::const_iterator (m_nodes.begin ()), pmin));
m_selected = int (std::distance (std::vector<std::pair<double, std::pair<lay::Color, lay::Color> > >::const_iterator (m_nodes.begin ()), pmin));
emit selection_changed ();
emit selection_changed (m_nodes [m_selected].second);
std::pair<lay::Color, lay::Color> cp = m_nodes [m_selected].second;
emit selection_changed (std::make_pair (QColor (cp.first.rgb ()), QColor (cp.second.rgb ())));
m_dragging = true;
update ();
} else {
@ -359,13 +327,14 @@ ColorBar::mouseDoubleClickEvent (QMouseEvent *event)
double xx = double (event->x () - xl) / double (xr - xl);
std::vector<std::pair<double, std::pair<QColor, QColor> > >::iterator p = std::lower_bound (m_nodes.begin (), m_nodes.end (), std::make_pair (xx, std::make_pair (QColor (), QColor ())), compare_first_of_node ());
std::vector<std::pair<double, std::pair<lay::Color, lay::Color> > >::iterator p = std::lower_bound (m_nodes.begin (), m_nodes.end (), std::make_pair (xx, std::make_pair (lay::Color (), lay::Color ())), compare_first_of_node ());
if (p != m_nodes.begin () && p != m_nodes.end ()) {
m_selected = int (std::distance (m_nodes.begin (), p));
QColor ci = interpolated_color (m_nodes, xx);
lay::Color ci = interpolated_color (m_nodes, xx);
m_nodes.insert (p, std::make_pair (xx, std::make_pair (ci, ci)));
emit selection_changed ();
emit selection_changed (m_nodes [m_selected].second);
std::pair<lay::Color, lay::Color> cp = m_nodes [m_selected].second;
emit selection_changed (std::make_pair (QColor (cp.first.rgb ()), QColor (cp.second.rgb ())));
emit color_mapping_changed ();
update ();
}
@ -440,9 +409,9 @@ ColorBar::paintEvent (QPaintEvent *)
if (xr != xl) {
xx = double (x - xl) / double (xr - xl);
}
QColor c = interpolated_color (m_nodes, xx);
lay::Color c = interpolated_color (m_nodes, xx);
painter.fillRect (x, yb - hbar, 1, hbar + 1, QBrush (c));
painter.fillRect (x, yb - hbar, 1, hbar + 1, QBrush (QColor (c.rgb ())));
}

View File

@ -24,6 +24,8 @@
#define HDR_imgWidgets
#include "layWidgets.h"
#include "layColor.h"
#include "imgObject.h"
#include <QObject>
#include <QWidget>
@ -38,13 +40,6 @@ class QPaintEvent;
namespace img
{
/**
* @brief A helper function to interpolate a color in the color bar at a given x
*
* TODO: move this somewhere else.
*/
QColor interpolated_color (const std::vector<std::pair <double, std::pair<QColor, QColor> > > &nodes, double x);
/**
* @brief A two-color widget
*
@ -114,9 +109,9 @@ public:
return m_selected >= 0;
}
void set_nodes (const std::vector <std::pair <double, std::pair<QColor, QColor> > > &nodes);
void set_nodes (const std::vector <std::pair <double, std::pair<lay::Color, lay::Color> > > &nodes);
const std::vector <std::pair <double, std::pair<QColor, QColor> > > &nodes () const
const std::vector <std::pair <double, std::pair<lay::Color, lay::Color> > > &nodes () const
{
return m_nodes;
}
@ -135,7 +130,7 @@ signals:
private:
bool m_dragging;
int m_selected;
std::vector <std::pair <double, std::pair<QColor, QColor> > > m_nodes;
std::vector <std::pair <double, std::pair<lay::Color, lay::Color> > > m_nodes;
std::vector <size_t> m_histogram;
};

View File

@ -45,9 +45,9 @@ TEST(1_FloatMono)
dm.gamma = 1.5;
dm.brightness = 1.25;
dm.false_color_nodes.clear ();
dm.false_color_nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (0, 0, 0), QColor (0, 0, 0))));
dm.false_color_nodes.push_back (std::make_pair (0.5, std::make_pair (QColor (255, 0, 0), QColor (0, 255, 0))));
dm.false_color_nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (255, 255, 255), QColor (255, 255, 255))));
dm.false_color_nodes.push_back (std::make_pair (0.0, std::make_pair (lay::Color (0, 0, 0), lay::Color (0, 0, 0))));
dm.false_color_nodes.push_back (std::make_pair (0.5, std::make_pair (lay::Color (255, 0, 0), lay::Color (0, 255, 0))));
dm.false_color_nodes.push_back (std::make_pair (1.0, std::make_pair (lay::Color (255, 255, 255), lay::Color (255, 255, 255))));
image.set_data_mapping (dm);
image.set_pixel (0, 0, 0.25);

View File

@ -101,8 +101,8 @@ TEST(1)
dm.red_gain = 1.25;
dm.green_gain = 0.75;
dm.blue_gain = 2.5;
QColor c (128, 255, 64);
QColor c2 (64, 32, 192);
lay::Color c (128, 255, 64);
lay::Color c2 (64, 32, 192);
dm.false_color_nodes.insert (dm.false_color_nodes.begin () + 1, std::make_pair (0.5, std::make_pair (c, c)));
image.set_data_mapping (dm);
EXPECT_EQ (copy1.equals (&image), false);
@ -226,7 +226,7 @@ TEST(2)
dm.red_gain = 1.25;
dm.green_gain = 0.75;
dm.blue_gain = 2.5;
QColor c (128, 255, 64);
lay::Color c (128, 255, 64);
dm.false_color_nodes.insert (dm.false_color_nodes.begin () + 1, std::make_pair (0.5, std::make_pair (c, c)));
image.set_data_mapping (dm);
EXPECT_EQ (copy1.equals (&image), false);

View File

@ -23,6 +23,7 @@
#include "layColor.h"
#include "tlString.h"
#include "tlMath.h"
#include <ctype.h>
@ -116,4 +117,82 @@ Color::is_valid () const
return (m_color & 0xff000000) != 0;
}
void
Color::get_hsv (unsigned int &hue, unsigned int &saturation, unsigned int &value) const
{
double r = double (red ()) / 255.0;
double g = double (green ()) / 255.0;
double b = double (blue ()) / 255.0;
double max = std::max (r, std::max (g, b));
double min = std::min (r, std::min (g, b));
double delta = max - min;
value = (unsigned int) tl::round (255.0 * max, 1);
hue = 0;
saturation = 0;
if (! tl::equal (delta, 0.0)) {
saturation = (unsigned int) tl::round (255.0 * delta / max, 1);
double h = 0.0;
if (tl::equal (r, max)) {
h = (g - b) / delta;
} else if (tl::equal (g, max)) {
h = 2.0f + (b - r) / delta;
} else if (tl::equal (b, max)) {
h = 4.0f + (r - g) / delta;
}
h *= 60.0;
if (tl::less (h, 0.0)) {
h += 360.0;
}
hue = (unsigned int) tl::round (h, 1);
}
}
static lay::Color color_d (double r, double g, double b)
{
return lay::Color (tl::round (r * 255.0, 1), tl::round (g * 255.0, 1), tl::round (b * 255.0, 1));
}
lay::Color
Color::from_hsv (unsigned int hue, unsigned int saturation, unsigned int value)
{
if (saturation == 0) {
return lay::Color (value, value, value);
}
hue = (hue + 360) % 360;
double h = double (hue) / 60.0;
double s = double (saturation) / 255.0;
double v = double (value) / 255.0;
int i = int (tl::round_down (h, 1));
double f = (i & 1) != 0 ? h - i : 1.0 - h + i;
double p = v * (1.0 - s);
double q = v * (1.0 - s * f);
switch (i) {
case 0:
return color_d (v, q, p);
case 1:
return color_d (q, v, p);
case 2:
return color_d (p, v, q);
case 3:
return color_d (p, q, v);
case 4:
return color_d (q, p, v);
case 5:
return color_d (v, p, q);
default:
return lay::Color ();
}
}
}

View File

@ -142,6 +142,19 @@ public:
return (m_color & 0xff);
}
/**
* @brief Gets the HSV color components
* hue: 0..359
* saturation: 0..255
* value: 0..255
*/
void get_hsv (unsigned int &hue, unsigned int &saturation, unsigned int &value) const;
/**
* @brief Creates the color from a HSV color
*/
static lay::Color from_hsv (unsigned int hue, unsigned int saturation, unsigned int value);
private:
color_t m_color;
};

View File

@ -116,3 +116,104 @@ TEST(7)
EXPECT_EQ (QColor (16, 32, 48, 128).rgb (), 0xff102030);
#endif
}
TEST(8)
{
unsigned int h, s, v;
int ih, is, iv;
lay::Color c = lay::Color (16, 32, 48);
c.get_hsv (h, s, v);
EXPECT_EQ (h, 210);
EXPECT_EQ (s, 170);
EXPECT_EQ (v, 48);
EXPECT_EQ (lay::Color::from_hsv (h, s, v).to_string (), "#102030");
#if defined(HAVE_QT)
QColor qc = QColor (16, 32, 48);
qc.getHsv (&ih, &is, &iv);
EXPECT_EQ (ih, 210);
EXPECT_EQ (is, 170);
EXPECT_EQ (iv, 48);
#endif
c = lay::Color (32, 16, 48);
c.get_hsv (h, s, v);
EXPECT_EQ (h, 270);
EXPECT_EQ (s, 170);
EXPECT_EQ (v, 48);
EXPECT_EQ (lay::Color::from_hsv (h, s, v).to_string (), "#201030");
#if defined(HAVE_QT)
qc = QColor (32, 16, 48);
qc.getHsv (&ih, &is, &iv);
EXPECT_EQ (ih, 270);
EXPECT_EQ (is, 170);
EXPECT_EQ (iv, 48);
#endif
c = lay::Color (32, 48, 16);
c.get_hsv (h, s, v);
EXPECT_EQ (h, 90);
EXPECT_EQ (s, 170);
EXPECT_EQ (v, 48);
EXPECT_EQ (lay::Color::from_hsv (h, s, v).to_string (), "#203010");
#if defined(HAVE_QT)
qc = QColor (32, 48, 16);
qc.getHsv (&ih, &is, &iv);
EXPECT_EQ (ih, 90);
EXPECT_EQ (is, 170);
EXPECT_EQ (iv, 48);
#endif
c = lay::Color (48, 32, 16);
c.get_hsv (h, s, v);
EXPECT_EQ (h, 30);
EXPECT_EQ (s, 170);
EXPECT_EQ (v, 48);
EXPECT_EQ (lay::Color::from_hsv (h, s, v).to_string (), "#302010");
#if defined(HAVE_QT)
qc = QColor (48, 32, 16);
qc.getHsv (&ih, &is, &iv);
EXPECT_EQ (ih, 30);
EXPECT_EQ (is, 170);
EXPECT_EQ (iv, 48);
#endif
c = lay::Color (48, 16, 32);
c.get_hsv (h, s, v);
EXPECT_EQ (h, 330);
EXPECT_EQ (s, 170);
EXPECT_EQ (v, 48);
EXPECT_EQ (lay::Color::from_hsv (h, s, v).to_string (), "#301020");
#if defined(HAVE_QT)
qc = QColor (48, 16, 32);
qc.getHsv (&ih, &is, &iv);
EXPECT_EQ (ih, 330);
EXPECT_EQ (is, 170);
EXPECT_EQ (iv, 48);
#endif
c = lay::Color (16, 48, 32);
c.get_hsv (h, s, v);
EXPECT_EQ (h, 150);
EXPECT_EQ (s, 170);
EXPECT_EQ (v, 48);
EXPECT_EQ (lay::Color::from_hsv (h, s, v).to_string (), "#103020");
#if defined(HAVE_QT)
qc = QColor (16, 48, 32);
qc.getHsv (&ih, &is, &iv);
EXPECT_EQ (ih, 150);
EXPECT_EQ (is, 170);
EXPECT_EQ (iv, 48);
#endif
}