diff --git a/src/img/img/gsiDeclImg.cc b/src/img/img/gsiDeclImg.cc index 27e13f72c..096375845 100644 --- a/src/img/img/gsiDeclImg.cc +++ b/src/img/img/gsiDeclImg.cc @@ -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) diff --git a/src/img/img/imgObject.cc b/src/img/img/imgObject.cc index f8758b9d3..7f265252c 100644 --- a/src/img/img/imgObject.cc +++ b/src/img/img/imgObject.cc @@ -31,6 +31,7 @@ #include "dbPolygonTools.h" #include "tlFileUtils.h" #include "tlUri.h" +#include "tlThreads.h" #include #include @@ -40,7 +41,6 @@ #include #include -#include 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 > &a, const std::pair > &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 > >::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 &clr = data_mapping ().false_color_nodes[i].second; + const std::pair &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 << ","; diff --git a/src/img/img/imgObject.h b/src/img/img/imgObject.h index c5dcdb467..1d3f734e1 100644 --- a/src/img/img/imgObject.h +++ b/src/img/img/imgObject.h @@ -33,13 +33,11 @@ #include "dbMatrix.h" #include "dbPolygon.h" #include "tlDataMapping.h" -#include "layViewOp.h" +#include "layColor.h" #include #include -#include - namespace img { class DataHeader; @@ -52,7 +50,7 @@ class DataHeader; struct IMG_PUBLIC DataMapping { public: - typedef std::vector< std::pair > > false_color_nodes_type; + typedef std::vector< std::pair > > 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 * diff --git a/src/img/img/imgPropertiesPage.cc b/src/img/img/imgPropertiesPage.cc index fef5a1fa8..aec31b1b0 100644 --- a/src/img/img/imgPropertiesPage.cc +++ b/src/img/img/imgPropertiesPage.cc @@ -735,9 +735,9 @@ PropertiesPage::blue_spinbox_changed (double value) void PropertiesPage::black_to_white () { - std::vector > > 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 > > 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 > > 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 > > 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 > > 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 > > 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 > > 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 > > 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 > > nodes (false_color_control->nodes ()); + std::vector > > 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); diff --git a/src/img/img/imgStream.cc b/src/img/img/imgStream.cc index 72dcb9c21..cb84b63b6 100644 --- a/src/img/img/imgStream.cc +++ b/src/img/img/imgStream.cc @@ -373,7 +373,7 @@ namespace { struct ColorMapConverter { - std::string to_string (const std::pair > &cm) const + std::string to_string (const std::pair > &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 > &cm) const + void from_string (const std::string &s, std::pair > &cm) const { tl::Extractor ex (s.c_str ()); @@ -427,7 +427,7 @@ tl::XMLStruct 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 >, 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::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") + diff --git a/src/img/img/imgWidgets.cc b/src/img/img/imgWidgets.cc index 587e77322..92952dac5 100644 --- a/src/img/img/imgWidgets.cc +++ b/src/img/img/imgWidgets.cc @@ -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 > &a, const std::pair > &b) const - { - return a.first < b.first; - } -}; - -QColor -interpolated_color (const std::vector > > &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 > >::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 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 > &a, const std::pair > &b) const + { + return a.first < b.first; + } +}; + +} + void -ColorBar::set_nodes (const std::vector > > &nodes) +ColorBar::set_nodes (const std::vector > > &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 > >::iterator w = m_nodes.begin (); - std::vector > >::const_iterator nn = m_nodes.begin (); - for (std::vector > >::const_iterator n = m_nodes.begin () + 1; n != m_nodes.end (); ++n) { + std::vector > >::iterator w = m_nodes.begin (); + std::vector > >::const_iterator nn = m_nodes.begin (); + for (std::vector > >::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 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 > >::const_iterator pmin = m_nodes.end (); - for (std::vector > >::const_iterator p = m_nodes.begin (); p != m_nodes.end (); ++p) { + std::vector > >::const_iterator pmin = m_nodes.end (); + for (std::vector > >::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 > >::const_iterator (m_nodes.begin ()), pmin)); + m_selected = int (std::distance (std::vector > >::const_iterator (m_nodes.begin ()), pmin)); emit selection_changed (); - emit selection_changed (m_nodes [m_selected].second); + std::pair 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 > >::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 > >::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 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 ()))); } diff --git a/src/img/img/imgWidgets.h b/src/img/img/imgWidgets.h index d0ce8ed63..06cef78ef 100644 --- a/src/img/img/imgWidgets.h +++ b/src/img/img/imgWidgets.h @@ -24,6 +24,8 @@ #define HDR_imgWidgets #include "layWidgets.h" +#include "layColor.h" +#include "imgObject.h" #include #include @@ -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 > > &nodes, double x); - /** * @brief A two-color widget * @@ -114,9 +109,9 @@ public: return m_selected >= 0; } - void set_nodes (const std::vector > > &nodes); + void set_nodes (const std::vector > > &nodes); - const std::vector > > &nodes () const + const std::vector > > &nodes () const { return m_nodes; } @@ -135,7 +130,7 @@ signals: private: bool m_dragging; int m_selected; - std::vector > > m_nodes; + std::vector > > m_nodes; std::vector m_histogram; }; diff --git a/src/img/unit_tests/imgFile.cc b/src/img/unit_tests/imgFile.cc index 49719aa02..5daf917f2 100644 --- a/src/img/unit_tests/imgFile.cc +++ b/src/img/unit_tests/imgFile.cc @@ -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); diff --git a/src/img/unit_tests/imgObject.cc b/src/img/unit_tests/imgObject.cc index 7eaa24198..1dfabda0c 100644 --- a/src/img/unit_tests/imgObject.cc +++ b/src/img/unit_tests/imgObject.cc @@ -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); diff --git a/src/laybasic/laybasic/layColor.cc b/src/laybasic/laybasic/layColor.cc index 67e0c700e..ac486cdbb 100644 --- a/src/laybasic/laybasic/layColor.cc +++ b/src/laybasic/laybasic/layColor.cc @@ -23,6 +23,7 @@ #include "layColor.h" #include "tlString.h" +#include "tlMath.h" #include @@ -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 (); + } + +} + } diff --git a/src/laybasic/laybasic/layColor.h b/src/laybasic/laybasic/layColor.h index 77cccebc8..79725ecfb 100644 --- a/src/laybasic/laybasic/layColor.h +++ b/src/laybasic/laybasic/layColor.h @@ -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; }; diff --git a/src/laybasic/unit_tests/layColorTests.cc b/src/laybasic/unit_tests/layColorTests.cc index c1d44c744..8da8e208b 100644 --- a/src/laybasic/unit_tests/layColorTests.cc +++ b/src/laybasic/unit_tests/layColorTests.cc @@ -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 +}