From 37299a8edfd800220d78eeef10725923dc202127 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 26 Apr 2020 23:52:45 +0200 Subject: [PATCH] Images: asymmetric color nodes. --- src/img/img/ImagePropertiesPage.ui | 905 ++++++++++++++++------------- src/img/img/gsiDeclImg.cc | 61 +- src/img/img/imgObject.cc | 54 +- src/img/img/imgObject.h | 2 +- src/img/img/imgPropertiesPage.cc | 50 +- src/img/img/imgWidgets.cc | 130 ++++- src/img/img/imgWidgets.h | 48 +- src/img/unit_tests/imgObject.cc | 24 +- src/lay/lay/images/locked_16.png | Bin 0 -> 318 bytes src/lay/lay/images/unlocked_16.png | Bin 0 -> 368 bytes src/lay/lay/layResources.qrc | 2 + 11 files changed, 795 insertions(+), 481 deletions(-) create mode 100644 src/lay/lay/images/locked_16.png create mode 100644 src/lay/lay/images/unlocked_16.png diff --git a/src/img/img/ImagePropertiesPage.ui b/src/img/img/ImagePropertiesPage.ui index 1c6ee4d0a..306dd99c7 100644 --- a/src/img/img/ImagePropertiesPage.ui +++ b/src/img/img/ImagePropertiesPage.ui @@ -1,7 +1,8 @@ - + + ImagePropertiesPage - - + + 0 0 @@ -9,42 +10,58 @@ 642 - + Form - - - 9 - - + + 6 + + 9 + + + 9 + + + 9 + + + 9 + - - + + QFrame::NoFrame - + QFrame::Raised - - + + 0 - + + 0 + + + 0 + + + 0 + + 6 - - - - - 5 - 0 + + + + 0 0 - + Sans Serif 12 @@ -55,43 +72,50 @@ false - + Image Properties - - - - - 5 - 5 + + + + 1 0 - + QFrame::Box - + QFrame::Plain - - + + 0 - + + 0 + + + 0 + + + 0 + + 0 - + Qt::Horizontal - + QSizePolicy::Fixed - + 10 20 @@ -100,29 +124,27 @@ - - - - 5 - 5 + + + 1 0 - - <Filename> + + <Filename> - + Qt::Horizontal - + QSizePolicy::Fixed - + 10 20 @@ -133,197 +155,206 @@ - - - + + + Browse - - - - <File info> + + + + <File info> - - - + + + QFrame::Plain - + File - - - + + + QFrame::NoFrame - + QFrame::Plain - - + + 0 - + + 0 + + + 0 + + + 0 + + 6 - - - + + + h = - - + + - - - + + + x = - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + - - - + + + Perspective - - + + - - + + - - - + + + degree - - + + - - + + - - - + + + Shear angle - - - + + + Pixel Size - - - + + + degree - - - + + + micron - - - + + + Rotation angle - - - + + + micron - - - + + + w = - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + micron - - - + + + Center - - - + + + x = - - - + + + Mirrored (at X-axis) - - - + + + micron - - + + - - - + + + degree - + - + Qt::Vertical - + 20 5 @@ -331,29 +362,29 @@ - - - + + + y = - - - + + + degree - + - + Qt::Vertical - + QSizePolicy::Fixed - + 507 16 @@ -361,18 +392,18 @@ - - + + - + - + Qt::Vertical - + QSizePolicy::Fixed - + 507 5 @@ -380,30 +411,30 @@ - - - + + + y = - - - + + + Landmarks - - - + + + Define - - - + + + (use them to align the image with the layout) @@ -415,61 +446,79 @@ - - + + Data To Display - - - 9 - - + + 6 + + 9 + + + 9 + + + 9 + + + 9 + - - + + QFrame::NoFrame - + QFrame::Raised - - - 0 - - + + 6 + + 0 + + + 0 + + + 0 + + + 0 + - - + + Pixel value range - + - - + + ... to - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + - + Qt::Horizontal - + 40 20 @@ -481,64 +530,97 @@ - - - 2 + + + 0 - - + + Color Mapping - - - 9 - - + + 6 + + 9 + + + 9 + + + 9 + + + 9 + - - - - 13 - 7 + + + 0 0 - + Qt::ActionsContextMenu - - <html><head><meta name="qrichtext" content="1" /><style type="text/css"> + + <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal; text-decoration:none;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Double-click to create a new node.</p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Press "Delete" to delete the selected node.</p></body></html> +</style></head><body style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal; text-decoration:none;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Double-click to create a new node.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Press "Delete" to delete the selected node.</p></body></html> - - + + QFrame::NoFrame - + QFrame::Raised - - - 0 - - + + 6 + + 0 + + + 0 + + + 0 + + + 0 + + + + + Value + + + + + + + + 0 + 0 + + + + - + Qt::Horizontal - + 21 20 @@ -547,35 +629,19 @@ p, li { white-space: pre-wrap; } - - - Value - - - - - - - - 5 - 0 - 0 - 0 - - - - - - - + + Color - - - + + + QFrame::NoFrame + + + QFrame::Raised @@ -584,53 +650,62 @@ p, li { white-space: pre-wrap; } - - + + Brightness / Contrast - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - - 100 - - + + + -100 - + + 100 + + 1 - - - + + + 100% - - - + + + 3.0 - + - + Qt::Horizontal - + QSizePolicy::Fixed - + 40 20 @@ -638,119 +713,119 @@ p, li { white-space: pre-wrap; } - - - + + + 100 - + Qt::Horizontal - - - + + + 100% - - - + + + 0.3 - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + -100 - + 100 - + Qt::Horizontal - - - + + + -100 - + 100 - + Qt::Horizontal - - - + + + Contrast - - - - 100 - - + + + -100 + + 100 + - - - + + + -100% - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + Gamma - - - + + + -100% - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + Brightness - + - + Qt::Horizontal - + QSizePolicy::Fixed - + 40 20 @@ -758,65 +833,74 @@ p, li { white-space: pre-wrap; } - - - - 3.000000000000000 - - + + + 0.000000000000000 - + + 3.000000000000000 + + 0.010000000000000 - - + + RGB Channels - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - + + + 2.000000000000000 - + 0.010000000000000 - - - + + + 0 - - - + + + x 2.0 - + - + Qt::Horizontal - + QSizePolicy::Fixed - + 40 20 @@ -824,114 +908,114 @@ p, li { white-space: pre-wrap; } - - - + + + 100 - + Qt::Horizontal - - - + + + Green - - - + + + 0 - - - + + + 0 - - - + + + 100 - + Qt::Horizontal - - - + + + 100 - + Qt::Horizontal - - - + + + Blue - - - + + + Red - - - + + + x 2.0 - - - + + + 2.000000000000000 - + 0.010000000000000 - - - + + + 2.000000000000000 - + 0.010000000000000 - - - + + + x 2.0 - + - + Qt::Horizontal - + QSizePolicy::Fixed - + 40 20 @@ -944,36 +1028,45 @@ p, li { white-space: pre-wrap; } - - + + QFrame::NoFrame - + QFrame::Raised - - - 0 - - + + 6 + + 0 + + + 0 + + + 0 + + + 0 + - - + + Preview (Auto apply) - + true - + Qt::Horizontal - + 40 20 @@ -982,8 +1075,8 @@ p, li { white-space: pre-wrap; } - - + + Reset @@ -996,10 +1089,10 @@ p, li { white-space: pre-wrap; } - + Qt::Vertical - + 524 16 @@ -1010,16 +1103,17 @@ p, li { white-space: pre-wrap; } - - lay::SimpleColorButton - QPushButton -
layWidgets.h
-
img::ColorBar QWidget
imgWidgets.h
+ + img::TwoColorWidget + QFrame +
imgWidgets.h
+ 1 +
width_le @@ -1048,7 +1142,6 @@ p, li { white-space: pre-wrap; } brightness_sb value_le browse_pb - color_pb gamma_slider brightness_slider diff --git a/src/img/img/gsiDeclImg.cc b/src/img/img/gsiDeclImg.cc index e72ee4a0c..836cb8a77 100644 --- a/src/img/img/gsiDeclImg.cc +++ b/src/img/img/gsiDeclImg.cc @@ -44,7 +44,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, QColor (color))); + dm->false_color_nodes.push_back (std::make_pair (value, std::make_pair (QColor (color), QColor (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)))); } static size_t num_colormap_entries (const img::DataMapping *dm) @@ -55,7 +60,25 @@ static size_t num_colormap_entries (const img::DataMapping *dm) static lay::color_t colormap_color (const img::DataMapping *dm, size_t i) { if (i < dm->false_color_nodes.size ()) { - return dm->false_color_nodes [i].second.rgb (); + return dm->false_color_nodes [i].second.first.rgb (); + } else { + return 0; + } +} + +static lay::color_t colormap_lcolor (const img::DataMapping *dm, size_t i) +{ + if (i < dm->false_color_nodes.size ()) { + return dm->false_color_nodes [i].second.first.rgb (); + } else { + return 0; + } +} + +static lay::color_t colormap_rcolor (const img::DataMapping *dm, size_t i) +{ + if (i < dm->false_color_nodes.size ()) { + return dm->false_color_nodes [i].second.second.rgb (); } else { return 0; } @@ -147,6 +170,22 @@ gsi::Class decl_ImageDataMapping ("lay", "ImageDataMapping", "blue component (0 to 255), the second byte the green component and the third byte the " "red component, i.e. 0xff0000 is red and 0x0000ff is blue. " ) + + gsi::method_ext ("add_colormap_entry", &gsi::add_colormap2, gsi::arg ("value"), gsi::arg ("lcolor"), gsi::arg ("rcolor"), + "@brief Add a colormap entry for this data mapping object.\n" + "@param value The value at which the given color should be applied.\n" + "@param lcolor The color to apply left of the value (a 32 bit RGB value).\n" + "@param rcolor The color to apply right of the value (a 32 bit RGB value).\n" + "\n" + "This settings establishes a color mapping for a given value in the monochrome channel. " + "The colors must be given as a 32 bit integer, where the lowest order byte describes the " + "blue component (0 to 255), the second byte the green component and the third byte the " + "red component, i.e. 0xff0000 is red and 0x0000ff is blue.\n" + "\n" + "In contrast to the version with one color, this version allows specifying a color left and right " + "of the value - i.e. a discontinuous step.\n" + "\n" + "This variant has been introduced in version 0.27.\n" + ) + gsi::method_ext ("num_colormap_entries", &gsi::num_colormap_entries, "@brief Returns the current number of color map entries.\n" "@return The number of entries.\n" @@ -155,11 +194,23 @@ gsi::Class decl_ImageDataMapping ("lay", "ImageDataMapping", "@brief Returns the color for a given color map entry.\n" "@param n The index of the entry (0..\\num_colormap_entries-1)\n" "@return The color (see \\add_colormap_entry for a description).\n" + "\n" + "NOTE: this version is deprecated and provided for backward compatibility. For discontinuous nodes " + "this method delivers the left-sided color." ) + - gsi::method_ext ("colormap_value", &gsi::colormap_value, gsi::arg ("n"), - "@brief Returns the vlue for a given color map entry.\n" + gsi::method_ext ("colormap_lcolor", &gsi::colormap_lcolor, gsi::arg ("n"), + "@brief Returns the left-side color for a given color map entry.\n" "@param n The index of the entry (0..\\num_colormap_entries-1)\n" - "@return The value (see \\add_colormap_entry for a description).\n" + "@return The color (see \\add_colormap_entry for a description).\n" + "\n" + "This method has been introduced in version 0.27." + ) + + gsi::method_ext ("colormap_rcolor", &gsi::colormap_rcolor, gsi::arg ("n"), + "@brief Returns the right-side color for a given color map entry.\n" + "@param n The index of the entry (0..\\num_colormap_entries-1)\n" + "@return The color (see \\add_colormap_entry for a description).\n" + "\n" + "This method has been introduced in version 0.27." ) + gsi::method_ext ("brightness=", &gsi::set_brightness, gsi::arg ("brightness"), "@brief Set the brightness\n" diff --git a/src/img/img/imgObject.cc b/src/img/img/imgObject.cc index 1f929ce91..1d3bb0386 100644 --- a/src/img/img/imgObject.cc +++ b/src/img/img/imgObject.cc @@ -50,8 +50,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, QColor (0, 0, 0))); - false_color_nodes.push_back (std::make_pair (1.0, QColor (255, 255, 255))); + 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)))); } bool @@ -91,7 +91,10 @@ DataMapping::operator== (const DataMapping &d) const if (fabs (false_color_nodes[i].first - d.false_color_nodes[i].first) > epsilon) { return false; } - if (false_color_nodes[i].second != d.false_color_nodes[i].second) { + if (false_color_nodes[i].second.first != d.false_color_nodes[i].second.first) { + return false; + } + if (false_color_nodes[i].second.second != d.false_color_nodes[i].second.second) { return false; } } @@ -136,8 +139,11 @@ DataMapping::operator< (const DataMapping &d) const if (fabs (false_color_nodes[i].first - d.false_color_nodes[i].first) > epsilon) { return false_color_nodes[i].first < d.false_color_nodes[i].first; } - if (false_color_nodes[i].second != d.false_color_nodes[i].second) { - return false_color_nodes[i].second.rgb () < d.false_color_nodes[i].second.rgb (); + if (false_color_nodes[i].second.first != d.false_color_nodes[i].second.first) { + return false_color_nodes[i].second.first.rgb () < d.false_color_nodes[i].second.first.rgb (); + } + if (false_color_nodes[i].second.second != d.false_color_nodes[i].second.second) { + return false_color_nodes[i].second.second.rgb () < d.false_color_nodes[i].second.second.rgb (); } } @@ -186,10 +192,10 @@ 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.getHsv (&h1, &s1, &v1); + false_color_nodes [i - 1].second.second.getHsv (&h1, &s1, &v1); int h2, s2, v2; - false_color_nodes [i].second.getHsv (&h2, &s2, &v2); + false_color_nodes [i].second.first.getHsv (&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)); @@ -220,11 +226,11 @@ DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, uns double ylast = 0.0; if (channel == 0) { - ylast = false_color_nodes.back ().second.red (); + ylast = false_color_nodes.back ().second.second.red (); } else if (channel == 1) { - ylast = false_color_nodes.back ().second.green (); + ylast = false_color_nodes.back ().second.second.green (); } else if (channel == 2) { - ylast = false_color_nodes.back ().second.blue (); + ylast = false_color_nodes.back ().second.second.blue (); } gray_to_color->push_back (false_color_nodes.back ().first, ylast / 255.0); @@ -1266,18 +1272,33 @@ Object::from_string (const char *str, const char *base_dir) double x = 0.0; lay::ColorConverter cc; - QColor c; + QColor cl, cr; std::string s; m_data_mapping.false_color_nodes.clear (); while (! ex.at_end () && ! ex.test ("]")) { + ex.read (x); + ex.test (","); + + s.clear (); ex.read_word_or_quoted (s); - cc.from_string (s, c); - m_data_mapping.false_color_nodes.push_back (std::make_pair (x, c)); + cc.from_string (s, cl); + + if (ex.test (",")) { + s.clear (); + ex.read_word_or_quoted (s); + cc.from_string (s, cr); + } else { + cr = cl; + } + + m_data_mapping.false_color_nodes.push_back (std::make_pair (x, std::make_pair (cl, cr))); + ex.test (";"); + } } else if (ex.test ("width=")) { @@ -1604,7 +1625,12 @@ 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 << ","; - os << tl::to_word_or_quoted_string (cc.to_string (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 << ","; + os << tl::to_word_or_quoted_string (cc.to_string (clr.second)); + } os << ";"; } diff --git a/src/img/img/imgObject.h b/src/img/img/imgObject.h index 82c9702ff..edbe85ac6 100644 --- a/src/img/img/imgObject.h +++ b/src/img/img/imgObject.h @@ -73,7 +73,7 @@ public: * Each node is a pair or x-value (normalized to a range of 0..1) and a corresponding color. * The list should have an element with x value of 0.0 and one with an x value of 1.0. */ - std::vector< std::pair > false_color_nodes; + std::vector< std::pair > > false_color_nodes; /** * @brief The brightness value diff --git a/src/img/img/imgPropertiesPage.cc b/src/img/img/imgPropertiesPage.cc index b18293bb2..9a90cc34d 100644 --- a/src/img/img/imgPropertiesPage.cc +++ b/src/img/img/imgPropertiesPage.cc @@ -100,11 +100,12 @@ PropertiesPage::init () connect (action, SIGNAL (triggered ()), this, SLOT (reverse_color_order ())); false_color_control->addAction (action); - color_pb->set_color (QColor ()); + colors->set_color (std::make_pair (QColor (), QColor ())); + colors->setEnabled (false); connect (browse_pb, SIGNAL (clicked ()), this, SLOT (browse ())); - connect (color_pb, SIGNAL (color_changed (QColor)), false_color_control, SLOT (set_current_color (QColor))); - connect (false_color_control, SIGNAL (selection_changed (QColor)), color_pb, SLOT (set_color (QColor))); + connect (colors, SIGNAL (color_changed (std::pair)), false_color_control, SLOT (set_current_color (std::pair))); + connect (false_color_control, SIGNAL (selection_changed (std::pair)), colors, SLOT (set_color (std::pair))); connect (brightness_slider, SIGNAL (valueChanged (int)), this, SLOT (brightness_slider_changed (int))); connect (brightness_sb, SIGNAL (valueChanged (int)), this, SLOT (brightness_spinbox_changed (int))); @@ -206,7 +207,8 @@ BEGIN_PROTECTED value_le->setText (QString ()); value_le->setEnabled (false); - color_pb->setEnabled (false_color_control->has_selection ()); + colors->setEnabled (false_color_control->has_selection ()); + colors->set_single_mode (false); double xmin, xmax; tl::from_string (tl::to_string (from_le->text ()), xmin); @@ -223,6 +225,10 @@ BEGIN_PROTECTED value_le->setText (tl::to_qstring (tl::sprintf ("%.4g", xx))); value_le->setEnabled (true); + } else if (false_color_control->has_selection ()) { + + colors->set_single_mode (true); + } recompute_histogram (); @@ -240,7 +246,8 @@ PropertiesPage::color_mapping_changed () value_le->setText (QString ()); value_le->setEnabled (false); - color_pb->setEnabled (false_color_control->has_selection ()); + colors->setEnabled (false_color_control->has_selection ()); + colors->set_single_mode (false); try { @@ -259,6 +266,10 @@ PropertiesPage::color_mapping_changed () value_le->setText (tl::to_qstring (tl::sprintf ("%.4g", xx))); value_le->setEnabled (true); + } else if (false_color_control->has_selection ()) { + + colors->set_single_mode (true); + } } catch (...) { } @@ -668,27 +679,27 @@ PropertiesPage::blue_spinbox_changed (double value) void PropertiesPage::black_to_white () { - std::vector > nodes; - nodes.push_back (std::make_pair (0.0, QColor (0, 0, 0))); - nodes.push_back (std::make_pair (1.0, QColor (255, 255, 255))); + 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)))); false_color_control->set_nodes (nodes); } void PropertiesPage::white_to_black () { - std::vector > nodes; - nodes.push_back (std::make_pair (0.0, QColor (255, 255, 255))); - nodes.push_back (std::make_pair (1.0, QColor (0, 0, 0))); + 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)))); false_color_control->set_nodes (nodes); } void PropertiesPage::red_to_blue () { - std::vector > nodes; - nodes.push_back (std::make_pair (0.0, QColor (255, 0, 0))); - nodes.push_back (std::make_pair (1.0, QColor (0, 0, 255))); + 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)))); false_color_control->set_nodes (nodes); } @@ -696,18 +707,19 @@ PropertiesPage::red_to_blue () void PropertiesPage::blue_to_red () { - std::vector > nodes; - nodes.push_back (std::make_pair (0.0, QColor (0, 0, 255))); - nodes.push_back (std::make_pair (1.0, QColor (255, 0, 0))); + 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)))); false_color_control->set_nodes (nodes); } 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, nodes [nodes.size () - 1 - i].second); + 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); } false_color_control->set_nodes (nodes); } diff --git a/src/img/img/imgWidgets.cc b/src/img/img/imgWidgets.cc index 297db30a9..b50dd5496 100644 --- a/src/img/img/imgWidgets.cc +++ b/src/img/img/imgWidgets.cc @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -45,36 +46,36 @@ const double epsilon = 1e-6; struct compare_first_of_node { - bool operator() (const std::pair &a, const std::pair &b) const + 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) +interpolated_color (const std::vector > > &nodes, double x) { if (nodes.size () < 1) { return QColor (); } else if (nodes.size () < 2) { - return nodes[0].second; + 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, QColor ()), compare_first_of_node ()); + 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; + return nodes.back ().second.second; } else if (p == nodes.begin ()) { - return nodes.front ().second; + 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.getHsv (&h1, &s1, &v1); + p[-1].second.second.getHsv (&h1, &s1, &v1); int h2 = 0, s2 = 0, v2 = 0; - p->second.getHsv (&h2, &s2, &v2); + 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)); @@ -89,11 +90,91 @@ interpolated_color (const std::vector > &nodes, doubl } } +// -------------------------------------------------------------------------------------------------------------------- + +TwoColorWidget::TwoColorWidget (QWidget *parent) + : QFrame (parent) +{ + setLayout (new QHBoxLayout (this)); + + mp_left = new lay::SimpleColorButton (this); + layout ()->addWidget (mp_left); + mp_right = new lay::SimpleColorButton (this); + layout ()->addWidget (mp_right); + mp_lock = new QToolButton (this); + layout ()->addWidget (mp_lock); + mp_lock->setCheckable (true); + mp_lock->setAutoRaise (true); + mp_lock->setIconSize (QSize (16, 16)); + + QIcon icon; + icon.addFile (":/locked_16.png", QSize (), QIcon::Normal, QIcon::On); + icon.addFile (":/unlocked_16.png", QSize (), QIcon::Normal, QIcon::Off); + mp_lock->setIcon (icon); + + connect (mp_left, SIGNAL (color_changed (QColor)), this, SLOT (lcolor_changed (QColor))); + connect (mp_right, SIGNAL (color_changed (QColor)), this, SLOT (rcolor_changed (QColor))); + connect (mp_lock, SIGNAL (clicked (bool)), this, SLOT (lock_changed (bool))); +} + +void +TwoColorWidget::set_color (std::pair c) +{ + mp_left->set_color (c.first); + mp_right->set_color (c.second); + mp_lock->setChecked (c.first == c.second); + mp_right->setVisible (! mp_lock->isChecked ()); +} + +void +TwoColorWidget::set_single_mode (bool f) +{ + mp_lock->setEnabled (! f); +} + +void +TwoColorWidget::lcolor_changed (QColor) +{ + if (mp_lock->isChecked ()) { + mp_right->set_color (mp_left->get_color ()); + } + emit color_changed (std::make_pair (mp_left->get_color (), mp_right->get_color ())); +} + +void +TwoColorWidget::rcolor_changed (QColor) +{ + if (mp_lock->isChecked ()) { + mp_left->set_color (mp_right->get_color ()); + } + emit color_changed (std::make_pair (mp_left->get_color (), mp_right->get_color ())); +} + +void +TwoColorWidget::lock_changed (bool checked) +{ + if (checked) { + + QColor cl = mp_left->get_color (); + QColor cr = mp_right->get_color (); + + QColor ca ((cl.red () + cr.red ()) / 2, (cl.green () + cr.green ()) / 2, (cl.blue () + cr.blue ()) / 2); + set_color (std::make_pair (ca, ca)); + + emit color_changed (std::make_pair (mp_left->get_color (), mp_right->get_color ())); + + } + + mp_right->setVisible (! mp_lock->isChecked ()); +} + +// -------------------------------------------------------------------------------------------------------------------- + ColorBar::ColorBar (QWidget *parent) : QWidget (parent), m_dragging (false), m_selected (-1) { - m_nodes.push_back (std::make_pair (0.0, QColor (0, 0, 0))); - m_nodes.push_back (std::make_pair (1.0, QColor (255, 255, 255))); + 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)))); } ColorBar::~ColorBar () @@ -122,7 +203,7 @@ ColorBar::mouseMoveEvent (QMouseEvent *event) } void -ColorBar::set_current_color (QColor c) +ColorBar::set_current_color (std::pair c) { if (has_selection ()) { m_nodes [m_selected].second = c; @@ -174,27 +255,27 @@ ColorBar::keyPressEvent (QKeyEvent *event) m_nodes.erase (m_nodes.begin () + m_selected); m_selected = -1; emit selection_changed (); - emit selection_changed (QColor ()); + emit selection_changed (std::make_pair (QColor (), QColor ())); update (); } } 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, QColor (0, 0, 0))); + m_nodes.insert (m_nodes.begin (), std::make_pair (0.0, std::make_pair (QColor (0, 0, 0), QColor (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; @@ -207,7 +288,7 @@ ColorBar::set_nodes (const std::vector > &nodes) 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, QColor (255, 255, 255))); + m_nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (255, 255, 255), QColor (255, 255, 255)))); } m_selected = -1; @@ -232,8 +313,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; @@ -242,7 +323,7 @@ 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); m_dragging = true; @@ -250,7 +331,7 @@ ColorBar::mousePressEvent (QMouseEvent *event) } else { m_selected = -1; emit selection_changed (); - emit selection_changed (QColor ()); + emit selection_changed (std::make_pair (QColor (), QColor ())); update (); } @@ -278,10 +359,11 @@ 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, 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 (QColor (), QColor ())), compare_first_of_node ()); if (p != m_nodes.begin () && p != m_nodes.end ()) { m_selected = int (std::distance (m_nodes.begin (), p)); - m_nodes.insert (p, std::make_pair (xx, interpolated_color (m_nodes, xx))); + QColor 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); emit color_mapping_changed (); diff --git a/src/img/img/imgWidgets.h b/src/img/img/imgWidgets.h index cbcd6ba6f..1870fcb06 100644 --- a/src/img/img/imgWidgets.h +++ b/src/img/img/imgWidgets.h @@ -20,13 +20,15 @@ */ - - #ifndef HDR_imgWidgets #define HDR_imgWidgets +#include "layWidgets.h" + #include #include +#include +#include #include class QMouseEvent; @@ -41,7 +43,37 @@ namespace img * * TODO: move this somewhere else. */ -QColor interpolated_color (const std::vector > &nodes, double x); +QColor interpolated_color (const std::vector > > &nodes, double x); + +/** + * @brief A two-color widget + * + * This widget has two color buttons and a "lock" checkbox which makes both colors identical + */ +class TwoColorWidget + : public QFrame +{ +Q_OBJECT + +public: + TwoColorWidget (QWidget *parent); + +signals: + void color_changed (std::pair c); + +public slots: + void set_color (std::pair c); + void set_single_mode (bool f); + +private slots: + void lcolor_changed (QColor c); + void rcolor_changed (QColor c); + void lock_changed (bool checked); + +private: + lay::SimpleColorButton *mp_left, *mp_right; + QToolButton *mp_lock; +}; /** * @brief A color bar widget @@ -82,9 +114,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; } @@ -92,18 +124,18 @@ public: void set_histogram (const std::vector &histogram); public slots: - void set_current_color (QColor c); + void set_current_color (std::pair c); void set_current_position (double x); signals: void color_mapping_changed (); void selection_changed (); - void selection_changed (QColor c); + void selection_changed (std::pair c); 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/imgObject.cc b/src/img/unit_tests/imgObject.cc index bc8200d64..1786d2006 100644 --- a/src/img/unit_tests/imgObject.cc +++ b/src/img/unit_tests/imgObject.cc @@ -25,6 +25,13 @@ #include "imgObject.h" #include "tlUnitTest.h" +static img::Object from_s (const std::string &s) +{ + img::Object img; + img.from_string (s.c_str ()); + return img; +} + TEST(1) { img::Object image (12, 8, db::DCplxTrans (), false); @@ -95,23 +102,32 @@ TEST(1) dm.green_gain = 0.75; dm.blue_gain = 2.5; QColor c (128, 255, 64); - dm.false_color_nodes.insert (dm.false_color_nodes.begin () + 1, std::make_pair (0.5, c)); + QColor 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); + EXPECT_EQ (from_s (image.to_string ()).equals (&image), true); + copy1 = image; + EXPECT_EQ (copy1.equals (&image), true); + dm.false_color_nodes.insert (dm.false_color_nodes.begin () + 1, std::make_pair (0.75, std::make_pair (c, c2))); + image.set_data_mapping (dm); + EXPECT_EQ (copy1.equals (&image), false); + EXPECT_EQ (from_s (image.to_string ()).equals (&image), true); copy1 = image; EXPECT_EQ (copy1.equals (&image), true); EXPECT_EQ (copy1.data_mapping ().brightness, 0.5); EXPECT_EQ (copy1.data_mapping ().red_gain, 1.25); - EXPECT_EQ (copy1.data_mapping ().false_color_nodes.size (), size_t (3)); + EXPECT_EQ (copy1.data_mapping ().false_color_nodes.size (), size_t (4)); img::Object copy2; copy2.from_string (image.to_string ().c_str ()); + EXPECT_EQ (copy2.equals (&image), true); EXPECT_EQ (copy2.data_mapping ().brightness, 0.5); EXPECT_EQ (tl::to_string (copy2.data_mapping ().red_gain), "1.25"); - EXPECT_EQ (copy2.data_mapping ().false_color_nodes.size (), size_t (3)); + EXPECT_EQ (copy2.data_mapping ().false_color_nodes.size (), size_t (4)); EXPECT_EQ (copy2.equals (&image), true); EXPECT_EQ (image.to_string (), copy2.to_string ()); @@ -203,7 +219,7 @@ TEST(2) dm.green_gain = 0.75; dm.blue_gain = 2.5; QColor c (128, 255, 64); - dm.false_color_nodes.insert (dm.false_color_nodes.begin () + 1, std::make_pair (0.5, c)); + 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/lay/lay/images/locked_16.png b/src/lay/lay/images/locked_16.png new file mode 100644 index 0000000000000000000000000000000000000000..7d29dc559d678cdb8ffa7f2d0a4e584a11527986 GIT binary patch literal 318 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2>u zwLzGX$zsxXprB-lYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt;PfT^vI! zdd~(K@-;aK%-I>U?g96)X{{~p0$g$_Ni}UEe;l~E?H{mnLMwtnQ(HyAP~)l%JV;^H$Vu_5iyT-Pfuvm)klch@UB|r6nou~ zSyoCaIict0oduavE`cH@9APIu%eV$Me6iDMsNZqu`VQtrO-4MezaKjTy}{t=>gTe~ HDWM4fqNjC5 literal 0 HcmV?d00001 diff --git a/src/lay/lay/images/unlocked_16.png b/src/lay/lay/images/unlocked_16.png new file mode 100644 index 0000000000000000000000000000000000000000..6b5b0d538525e35c0457db2555abc77a7c856500 GIT binary patch literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2>u zwLzGX$zsxXprB-lYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt*poT^vI! zdXG*t%sb>D;QF6wlc;9i1n=;K=*>I49N+5-PJh6%alw;CC+YmEV`AVqd_i?lu*~tz@9wJpd3(^>HE_w+ zYt6_4m#U<images/folder_12.png images/file_12.png images/empty_12.png + images/unlocked_16.png + images/locked_16.png syntax/ruby.xml