mirror of https://github.com/KLayout/klayout.git
* Menu item to hide or show all images * Images: asymmetric color nodes. * Added RBA::image::from_s for reading image back from string. Added tests. * Added image file reader test (RBA) * Added lyimg format for image file persistence. * Small fix of unit tests. * Added GSI binding for new image features and tests. * Save and load for .lyimg formats in image properties dialog.
This commit is contained in:
parent
86739794a0
commit
9f4ccaaedc
File diff suppressed because it is too large
Load Diff
|
|
@ -26,6 +26,7 @@
|
|||
#include "gsiSignals.h"
|
||||
#include "imgObject.h"
|
||||
#include "imgService.h"
|
||||
#include "imgStream.h"
|
||||
#include "dbTilingProcessor.h"
|
||||
#include "layLayoutView.h"
|
||||
|
||||
|
|
@ -44,7 +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, 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 +61,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,20 +171,53 @@ gsi::Class<img::DataMapping> 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"
|
||||
) +
|
||||
gsi::method_ext ("colormap_color", &gsi::colormap_color, gsi::arg ("n"),
|
||||
"@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"
|
||||
) +
|
||||
gsi::method_ext ("colormap_value", &gsi::colormap_value, gsi::arg ("n"),
|
||||
"@brief Returns the vlue 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"
|
||||
) +
|
||||
gsi::method_ext ("colormap_color", &gsi::colormap_color, gsi::arg ("n"),
|
||||
"@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_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 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"
|
||||
"See \\brightness for a description of this property.\n"
|
||||
|
|
@ -350,6 +407,31 @@ private:
|
|||
tl::DeferredMethod<ImageRef> dm_update_view;
|
||||
};
|
||||
|
||||
static ImageRef *img_from_s (const std::string &s)
|
||||
{
|
||||
std::auto_ptr<ImageRef> img (new ImageRef ());
|
||||
img->from_string (s.c_str ());
|
||||
return img.release ();
|
||||
}
|
||||
|
||||
static ImageRef *load_image (const std::string &path)
|
||||
{
|
||||
tl::InputFile file (path);
|
||||
tl::InputStream stream (file);
|
||||
|
||||
std::auto_ptr<img::Object> read;
|
||||
read.reset (img::ImageStreamer::read (stream));
|
||||
// need to create a copy for now ...
|
||||
return new ImageRef (*read);
|
||||
}
|
||||
|
||||
static void save_image (const ImageRef *image, const std::string &path)
|
||||
{
|
||||
tl::OutputFile file (path);
|
||||
tl::OutputStream stream (file);
|
||||
img::ImageStreamer::write (stream, *image);
|
||||
}
|
||||
|
||||
static ImageRef *new_image ()
|
||||
{
|
||||
return new ImageRef ();
|
||||
|
|
@ -474,6 +556,20 @@ static std::vector<bool> get_mask_data (ImageRef *obj)
|
|||
gsi::Class<img::Object> decl_BasicImage ("lay", "BasicImage", gsi::Methods (), "@hide");
|
||||
|
||||
gsi::Class<ImageRef> decl_Image (decl_BasicImage, "lay", "Image",
|
||||
gsi::constructor ("from_s", &gsi::img_from_s, gsi::arg ("s"),
|
||||
"@brief Creates an image from the string returned by \\to_s.\n"
|
||||
"This method has been introduced in version 0.27."
|
||||
) +
|
||||
gsi::constructor ("read", &load_image, gsi::arg ("path"),
|
||||
"@brief Loads the image from the given path.\n"
|
||||
"\n"
|
||||
"This method expects the image file as a KLayout image format file (.lyimg). "
|
||||
"This is a XML-based format containing the image data plus placement and transformation "
|
||||
"information for the image placement. In addition, image manipulation parameters for "
|
||||
"false color display and color channel enhancement are embedded.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27."
|
||||
) +
|
||||
gsi::constructor ("new", &gsi::new_image,
|
||||
"@brief Create a new image with the default attributes"
|
||||
"\n"
|
||||
|
|
@ -614,6 +710,10 @@ gsi::Class<ImageRef> decl_Image (decl_BasicImage, "lay", "Image",
|
|||
"@param t The magnifying transformation to apply\n"
|
||||
"@return The transformed object\n"
|
||||
) +
|
||||
gsi::method ("clear", &ImageRef::clear,
|
||||
"@brief Clears the image data (sets to 0 or black).\n"
|
||||
"This method has been introduced in version 0.27."
|
||||
) +
|
||||
gsi::method ("width", &ImageRef::width,
|
||||
"@brief Gets the width of the image in pixels\n"
|
||||
"@return The width in pixels\n"
|
||||
|
|
@ -922,7 +1022,12 @@ gsi::Class<ImageRef> decl_Image (decl_BasicImage, "lay", "Image",
|
|||
) +
|
||||
gsi::method ("to_s", &ImageRef::to_string,
|
||||
"@brief Converts the image to a string\n"
|
||||
"The string returned can be used to create an image object using \\from_s.\n"
|
||||
"@return The string\n"
|
||||
) +
|
||||
gsi::method_ext ("write", &save_image, gsi::arg ("path"),
|
||||
"@brief Saves the image to KLayout's image format (.lyimg)\n"
|
||||
"This method has been introduced in version 0.27."
|
||||
),
|
||||
"@brief An image to be stored as a layout annotation\n"
|
||||
"\n"
|
||||
|
|
@ -1276,7 +1381,7 @@ public:
|
|||
{
|
||||
if (mp_image) {
|
||||
db::Matrix3d m = db::Matrix3d::disp ((p0 - db::DPoint ()) + db::DVector (nx * dx * 0.5, ny * dy * 0.5)) * db::Matrix3d::mag (dx, dy);
|
||||
*mp_image = img::Object (nx, ny, m, false);
|
||||
*mp_image = img::Object (nx, ny, m, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ HEADERS = \
|
|||
imgService.h \
|
||||
imgWidgets.h \
|
||||
imgForceLink.h \
|
||||
imgCommon.h
|
||||
imgCommon.h \
|
||||
imgStream.h
|
||||
|
||||
FORMS = \
|
||||
AddNewImageDialog.ui \
|
||||
|
|
@ -31,7 +32,8 @@ SOURCES = \
|
|||
imgPropertiesPage.cc \
|
||||
imgService.cc \
|
||||
imgWidgets.cc \
|
||||
imgForceLink.cc
|
||||
imgForceLink.cc \
|
||||
imgStream.cc
|
||||
|
||||
INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC
|
||||
DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "imgObject.h"
|
||||
#include "imgWidgets.h" // for interpolate_color()
|
||||
#include "imgStream.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlTimer.h"
|
||||
#include "layPlugin.h"
|
||||
|
|
@ -50,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, 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 +92,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 +140,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 +193,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 +227,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);
|
||||
|
|
@ -704,31 +711,16 @@ Object::Object ()
|
|||
mp_pixel_data = 0;
|
||||
}
|
||||
|
||||
Object::Object (size_t w, size_t h, const db::DCplxTrans &trans, bool color)
|
||||
Object::Object (size_t w, size_t h, const db::DCplxTrans &trans, bool color, bool byte_data)
|
||||
: m_trans (trans), m_id (make_id ()), m_min_value (0.0), m_max_value (1.0), m_min_value_set (false), m_max_value_set (false), m_visible (true), m_z_position (0)
|
||||
{
|
||||
m_updates_enabled = false;
|
||||
mp_pixel_data = 0;
|
||||
|
||||
mp_data = new DataHeader (w, h, color, false);
|
||||
mp_data = new DataHeader (w, h, color, byte_data);
|
||||
mp_data->add_ref ();
|
||||
|
||||
// The default data type is float
|
||||
tl_assert (! is_byte_data ());
|
||||
|
||||
if (is_color ()) {
|
||||
for (unsigned int c = 0; c < 3; ++c) {
|
||||
float *d = mp_data->float_data (c);
|
||||
for (size_t i = data_length (); i > 0; --i) {
|
||||
*d++ = 0.0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
float *d = mp_data->float_data ();
|
||||
for (size_t i = data_length (); i > 0; --i) {
|
||||
*d++ = 0.0;
|
||||
}
|
||||
}
|
||||
clear ();
|
||||
m_updates_enabled = true;
|
||||
}
|
||||
|
||||
Object::Object (size_t w, size_t h, const db::DCplxTrans &trans, unsigned char *d)
|
||||
|
|
@ -802,32 +794,15 @@ Object::Object (const std::string &filename, const db::DCplxTrans &trans)
|
|||
m_updates_enabled = true;
|
||||
}
|
||||
|
||||
Object::Object (size_t w, size_t h, const db::Matrix3d &trans, bool color)
|
||||
Object::Object (size_t w, size_t h, const db::Matrix3d &trans, bool color, bool byte_data)
|
||||
: m_trans (trans), m_id (make_id ()), m_min_value (0.0), m_max_value (1.0), m_min_value_set (false), m_max_value_set (false), m_visible (true), m_z_position (0)
|
||||
{
|
||||
m_updates_enabled = false;
|
||||
mp_pixel_data = 0;
|
||||
|
||||
mp_data = new DataHeader (w, h, color, false);
|
||||
mp_data = new DataHeader (w, h, color, byte_data);
|
||||
mp_data->add_ref ();
|
||||
|
||||
// The default data type is float
|
||||
tl_assert (! is_byte_data ());
|
||||
|
||||
if (is_color ()) {
|
||||
for (unsigned int c = 0; c < 3; ++c) {
|
||||
float *d = mp_data->float_data (c);
|
||||
for (size_t i = data_length (); i > 0; --i) {
|
||||
*d++ = 0.0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
float *d = mp_data->float_data ();
|
||||
for (size_t i = data_length (); i > 0; --i) {
|
||||
*d++ = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
clear ();
|
||||
m_updates_enabled = true;
|
||||
}
|
||||
|
||||
|
|
@ -1078,6 +1053,48 @@ Object::clone () const
|
|||
return new img::Object (*this);
|
||||
}
|
||||
|
||||
void
|
||||
Object::clear ()
|
||||
{
|
||||
if (is_byte_data ()) {
|
||||
|
||||
if (is_color ()) {
|
||||
|
||||
for (unsigned int c = 0; c < 3; ++c) {
|
||||
unsigned char *d = mp_data->byte_data (c);
|
||||
for (size_t i = data_length (); i > 0; --i) {
|
||||
*d++ = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
unsigned char *d = mp_data->byte_data ();
|
||||
for (size_t i = data_length (); i > 0; --i) {
|
||||
*d++ = 0.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (is_color ()) {
|
||||
|
||||
for (unsigned int c = 0; c < 3; ++c) {
|
||||
float *d = mp_data->float_data (c);
|
||||
for (size_t i = data_length (); i > 0; --i) {
|
||||
*d++ = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
float *d = mp_data->float_data ();
|
||||
for (size_t i = data_length (); i > 0; --i) {
|
||||
*d++ = 0.0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
db::DPolygon
|
||||
Object::image_box_poly (const db::DBox vp, const db::DCplxTrans &vpt) const
|
||||
{
|
||||
|
|
@ -1266,18 +1283,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=")) {
|
||||
|
|
@ -1456,6 +1488,24 @@ Object::read_file ()
|
|||
tl::info << "Reading image file " << m_filename;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
tl::InputFile file (m_filename);
|
||||
tl::InputStream stream (file);
|
||||
std::auto_ptr<img::Object> read;
|
||||
read.reset (img::ImageStreamer::read (stream));
|
||||
read->m_filename = m_filename;
|
||||
|
||||
// for now we need to copy here ...
|
||||
*this = *read;
|
||||
|
||||
// exit on success
|
||||
return;
|
||||
|
||||
} catch (...) {
|
||||
// continue with other formats ...
|
||||
}
|
||||
|
||||
QImage qimage (tl::to_qstring (m_filename));
|
||||
|
||||
if (! qimage.isNull ()) {
|
||||
|
|
@ -1604,7 +1654,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<QColor, QColor> &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 << ";";
|
||||
}
|
||||
|
||||
|
|
@ -1681,6 +1736,25 @@ Object::to_string () const
|
|||
return os.str ();
|
||||
}
|
||||
|
||||
void
|
||||
Object::swap (Object &other)
|
||||
{
|
||||
m_filename.swap (other.m_filename);
|
||||
std::swap (m_trans, other.m_trans);
|
||||
std::swap (mp_data, other.mp_data);
|
||||
std::swap (m_id, other.m_id);
|
||||
std::swap (m_min_value, other.m_min_value);
|
||||
std::swap (m_max_value, other.m_max_value);
|
||||
std::swap (m_min_value_set, other.m_min_value_set);
|
||||
std::swap (m_max_value_set, other.m_max_value_set);
|
||||
std::swap (m_data_mapping, other.m_data_mapping);
|
||||
std::swap (m_visible, other.m_visible);
|
||||
std::swap (mp_pixel_data, other.mp_pixel_data);
|
||||
m_landmarks.swap (other.m_landmarks);
|
||||
std::swap (m_z_position, other.m_z_position);
|
||||
std::swap (m_updates_enabled, other.m_updates_enabled);
|
||||
}
|
||||
|
||||
size_t
|
||||
Object::width () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ class DataHeader;
|
|||
struct IMG_PUBLIC DataMapping
|
||||
{
|
||||
public:
|
||||
typedef std::vector< std::pair<double, std::pair<QColor, QColor> > > false_color_nodes_type;
|
||||
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
|
|
@ -73,7 +75,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<double, QColor> > false_color_nodes;
|
||||
false_color_nodes_type false_color_nodes;
|
||||
|
||||
/**
|
||||
* @brief The brightness value
|
||||
|
|
@ -177,8 +179,9 @@ public:
|
|||
* @param h The height of the image
|
||||
* @param trans The transformation from pixel space to micron space
|
||||
* @param color True to create a color image.
|
||||
* @param byte_data True to make the image store the data in bytes
|
||||
*/
|
||||
Object (size_t w, size_t h, const db::DCplxTrans &trans, bool color);
|
||||
Object (size_t w, size_t h, const db::DCplxTrans &trans, bool color, bool byte_data);
|
||||
|
||||
/**
|
||||
* @brief Constructor for a monochrome image with the given pixel values
|
||||
|
|
@ -301,8 +304,9 @@ public:
|
|||
* @param h The height of the image
|
||||
* @param matrix The 3d transformation matrix from pixel space to micron space
|
||||
* @param color True to create a color image.
|
||||
* @param byte_data True to create n image using bytes rather than floats
|
||||
*/
|
||||
Object (size_t w, size_t h, const db::Matrix3d &matrix, bool color);
|
||||
Object (size_t w, size_t h, const db::Matrix3d &matrix, bool color, bool byte_data);
|
||||
|
||||
/**
|
||||
* @brief Constructor for a monochrome image with the given pixel values
|
||||
|
|
@ -734,6 +738,11 @@ public:
|
|||
*/
|
||||
void set_data (size_t width, size_t height, const std::vector<double> &red, const std::vector<double> &green, const std::vector<double> &blue);
|
||||
|
||||
/**
|
||||
* @brief Clears the pixel data (sets the values to 0)
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Set the transformation matrix
|
||||
*
|
||||
|
|
@ -937,6 +946,11 @@ public:
|
|||
*/
|
||||
virtual std::string to_string () const;
|
||||
|
||||
/**
|
||||
* @brief Swap with another image object
|
||||
*/
|
||||
void swap (img::Object &other);
|
||||
|
||||
/**
|
||||
* @brief Return the memory used in bytes
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
namespace img
|
||||
{
|
||||
|
||||
const std::string cfg_images_visible ("images-visible");
|
||||
|
||||
void
|
||||
PluginDeclaration::get_menu_entries (std::vector<lay::MenuEntry> &menu_entries) const
|
||||
{
|
||||
|
|
@ -39,6 +41,7 @@ PluginDeclaration::get_menu_entries (std::vector<lay::MenuEntry> &menu_entries)
|
|||
menu_entries.push_back (lay::menu_item ("img::bring_to_front", "bring_to_front:edit", "edit_menu.image_menu.end", tl::to_string (QObject::tr ("Image Stack: Selected Images to Front"))));
|
||||
menu_entries.push_back (lay::menu_item ("img::bring_to_back", "bring_to_back:edit", "edit_menu.image_menu.end", tl::to_string (QObject::tr ("Image Stack: Selected Images to Back"))));
|
||||
menu_entries.push_back (lay::menu_item ("img::clear_all_images", "clear_all_images:edit", "edit_menu.image_menu.end", tl::to_string (QObject::tr ("Clear All Images"))));
|
||||
menu_entries.push_back (lay::config_menu_item ("show_images", "view_menu.layout_group+", tl::to_string (QObject::tr ("Show Images")), cfg_images_visible, "?"));
|
||||
}
|
||||
|
||||
lay::Plugin *
|
||||
|
|
@ -54,6 +57,12 @@ PluginDeclaration::implements_editable (std::string &title) const
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PluginDeclaration::get_options (std::vector < std::pair<std::string, std::string> > &options) const
|
||||
{
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_images_visible, "true"));
|
||||
}
|
||||
|
||||
static tl::RegisteredClass<lay::PluginDeclaration> config_decl (new img::PluginDeclaration (), 4000, "img::Plugin");
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,18 +29,16 @@
|
|||
namespace img
|
||||
{
|
||||
|
||||
extern const std::string cfg_images_visible;
|
||||
|
||||
class PluginDeclaration
|
||||
: public lay::PluginDeclaration
|
||||
{
|
||||
public:
|
||||
virtual void get_options (std::vector < std::pair<std::string, std::string> > & /*options*/) const
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual void get_menu_entries (std::vector<lay::MenuEntry> &menu_entries) const;
|
||||
virtual lay::Plugin *create_plugin (db::Manager *manager, lay::Dispatcher *, lay::LayoutView *view) const;
|
||||
virtual bool implements_editable (std::string &title) const;
|
||||
virtual void get_options (std::vector < std::pair<std::string, std::string> > &options) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,11 @@
|
|||
|
||||
#include "imgPropertiesPage.h"
|
||||
#include "imgLandmarksDialog.h"
|
||||
#include "imgStream.h"
|
||||
#include "layLayoutView.h"
|
||||
#include "layFileDialog.h"
|
||||
#include "tlExceptions.h"
|
||||
#include "tlFileUtils.h"
|
||||
|
||||
namespace img
|
||||
{
|
||||
|
|
@ -100,11 +102,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<QColor, QColor>)), false_color_control, SLOT (set_current_color (std::pair<QColor, QColor>)));
|
||||
connect (false_color_control, SIGNAL (selection_changed (std::pair<QColor, QColor>)), colors, SLOT (set_color (std::pair<QColor, QColor>)));
|
||||
|
||||
connect (brightness_slider, SIGNAL (valueChanged (int)), this, SLOT (brightness_slider_changed (int)));
|
||||
connect (brightness_sb, SIGNAL (valueChanged (int)), this, SLOT (brightness_spinbox_changed (int)));
|
||||
|
|
@ -126,6 +129,7 @@ PropertiesPage::init ()
|
|||
connect (value_le, SIGNAL (returnPressed ()), this, SLOT (value_return_pressed ()));
|
||||
|
||||
connect (reset_pb, SIGNAL (clicked ()), this, SLOT (reset_pressed ()));
|
||||
connect (save_pb, SIGNAL (clicked ()), this, SLOT (save_pressed ()));
|
||||
connect (preview_cbx, SIGNAL (clicked ()), this, SLOT (preview_checked ()));
|
||||
connect (define_landmarks_pb, SIGNAL (clicked ()), this, SLOT (define_landmarks_pressed ()));
|
||||
}
|
||||
|
|
@ -206,7 +210,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 +228,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 +249,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 +269,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 +682,27 @@ PropertiesPage::blue_spinbox_changed (double value)
|
|||
void
|
||||
PropertiesPage::black_to_white ()
|
||||
{
|
||||
std::vector <std::pair <double, QColor> > 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 <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))));
|
||||
false_color_control->set_nodes (nodes);
|
||||
}
|
||||
|
||||
void
|
||||
PropertiesPage::white_to_black ()
|
||||
{
|
||||
std::vector <std::pair <double, QColor> > 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 <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))));
|
||||
false_color_control->set_nodes (nodes);
|
||||
}
|
||||
|
||||
void
|
||||
PropertiesPage::red_to_blue ()
|
||||
{
|
||||
std::vector <std::pair <double, QColor> > 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 <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))));
|
||||
false_color_control->set_nodes (nodes);
|
||||
|
||||
}
|
||||
|
|
@ -696,18 +710,19 @@ PropertiesPage::red_to_blue ()
|
|||
void
|
||||
PropertiesPage::blue_to_red ()
|
||||
{
|
||||
std::vector <std::pair <double, QColor> > 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 <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))));
|
||||
false_color_control->set_nodes (nodes);
|
||||
}
|
||||
|
||||
void
|
||||
PropertiesPage::reverse_color_order ()
|
||||
{
|
||||
std::vector <std::pair <double, QColor> > nodes (false_color_control->nodes ());
|
||||
std::vector <std::pair <double, std::pair<QColor, QColor> > > 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);
|
||||
}
|
||||
|
|
@ -826,6 +841,31 @@ BEGIN_PROTECTED
|
|||
END_PROTECTED
|
||||
}
|
||||
|
||||
void
|
||||
PropertiesPage::save_pressed ()
|
||||
{
|
||||
BEGIN_PROTECTED
|
||||
|
||||
apply ();
|
||||
|
||||
lay::FileDialog file_dialog (this, tl::to_string (QObject::tr ("Save As KLayout Image File")), tl::to_string (QObject::tr ("KLayout image files (*.lyimg);;All files (*)")));
|
||||
|
||||
std::string filename = mp_direct_image->filename ();
|
||||
if (! filename.empty () && tl::extension (filename) != "lyimg") {
|
||||
filename = tl::basename (filename) + ".lyimg";
|
||||
}
|
||||
|
||||
if (file_dialog.get_save (filename)) {
|
||||
|
||||
tl::OutputFile file (filename);
|
||||
tl::OutputStream stream (file);
|
||||
img::ImageStreamer::write (stream, *mp_direct_image);
|
||||
|
||||
}
|
||||
|
||||
END_PROTECTED
|
||||
}
|
||||
|
||||
void
|
||||
PropertiesPage::reset_pressed ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ private slots:
|
|||
void min_max_return_pressed ();
|
||||
void preview_checked ();
|
||||
void reset_pressed ();
|
||||
void save_pressed ();
|
||||
void define_landmarks_pressed ();
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -34,8 +34,10 @@
|
|||
#include "laybasicConfig.h"
|
||||
#include "layLayoutCanvas.h"
|
||||
#include "layProperties.h"
|
||||
#include "layTipDialog.h"
|
||||
#include "tlExceptions.h"
|
||||
#include "imgService.h"
|
||||
#include "imgPlugin.h"
|
||||
#include "ui_AddNewImageDialog.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
|
@ -247,37 +249,6 @@ struct SortImagePtrByZOrder
|
|||
}
|
||||
};
|
||||
|
||||
static const db::DUserObject *find_image (lay::LayoutView *view, const db::DPoint &p, const db::DBox &search_box, double l, double &dmin, const std::map<img::Service::obj_iterator, unsigned int> *exclude = 0)
|
||||
{
|
||||
std::vector <const db::DUserObject *> images;
|
||||
|
||||
// get valid images and sort by reverse z order (top one first)
|
||||
lay::AnnotationShapes::touching_iterator r = view->annotation_shapes ().begin_touching (search_box);
|
||||
while (! r.at_end ()) {
|
||||
const img::Object *image = dynamic_cast<const img::Object *> ((*r).ptr ());
|
||||
if (image && image->is_visible () && (! exclude || exclude->find (view->annotation_shapes ().iterator_from_pointer (&*r)) == exclude->end ())) {
|
||||
images.push_back (&*r);
|
||||
}
|
||||
++r;
|
||||
}
|
||||
|
||||
std::stable_sort (images.begin (), images.end (), SortImagePtrByZOrder ());
|
||||
|
||||
// look for the "closest" image to the search box
|
||||
dmin = std::numeric_limits <double>::max ();
|
||||
const db::DUserObject *found = 0;
|
||||
|
||||
for (std::vector <const db::DUserObject *>::const_iterator robj = images.begin (); robj != images.end (); ++robj) {
|
||||
double d = std::numeric_limits <double>::max ();
|
||||
if (is_selected (*dynamic_cast<const img::Object *> ((*robj)->ptr ()), p, view->box (), l, d)) {
|
||||
found = *robj;
|
||||
dmin = d;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
|
||||
View::View (img::Service *service, obj_iterator image_ref, img::View::Mode mode)
|
||||
|
|
@ -440,7 +411,8 @@ Service::Service (db::Manager *manager, lay::LayoutView *view)
|
|||
mp_transient_view (0),
|
||||
m_move_mode (Service::move_none),
|
||||
m_moved_landmark (0),
|
||||
m_keep_selection_for_landmark (false)
|
||||
m_keep_selection_for_landmark (false),
|
||||
m_images_visible (true)
|
||||
{
|
||||
// place images behind the grid
|
||||
z_order (-1);
|
||||
|
|
@ -465,10 +437,29 @@ Service::annotations_changed ()
|
|||
images_changed_event ();
|
||||
}
|
||||
|
||||
bool
|
||||
Service::configure (const std::string & /*name*/, const std::string & /*value*/)
|
||||
void
|
||||
Service::show_images (bool f)
|
||||
{
|
||||
return false;
|
||||
if (m_images_visible != f) {
|
||||
m_images_visible = f;
|
||||
view ()->redraw ();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Service::configure (const std::string &name, const std::string &value)
|
||||
{
|
||||
if (name == cfg_images_visible) {
|
||||
|
||||
bool v = true;
|
||||
tl::from_string (value, v);
|
||||
show_images (v);
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -649,7 +640,7 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang
|
|||
m_p1 = p;
|
||||
double dmin = std::numeric_limits <double>::max ();
|
||||
|
||||
const db::DUserObject *robj = find_image (mp_view, p, search_dbox, l, dmin);
|
||||
const db::DUserObject *robj = find_image (p, search_dbox, l, dmin);
|
||||
if (robj) {
|
||||
|
||||
const img::Object *iobj = dynamic_cast<const img::Object *> (robj->ptr ());
|
||||
|
|
@ -919,6 +910,42 @@ Service::end_move (const db::DPoint &, lay::angle_constraint_type)
|
|||
m_move_mode = move_none;
|
||||
}
|
||||
|
||||
const db::DUserObject *
|
||||
Service::find_image (const db::DPoint &p, const db::DBox &search_box, double l, double &dmin, const std::map<img::Service::obj_iterator, unsigned int> *exclude)
|
||||
{
|
||||
if (! m_images_visible) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector <const db::DUserObject *> images;
|
||||
|
||||
// get valid images and sort by reverse z order (top one first)
|
||||
lay::AnnotationShapes::touching_iterator r = mp_view->annotation_shapes ().begin_touching (search_box);
|
||||
while (! r.at_end ()) {
|
||||
const img::Object *image = dynamic_cast<const img::Object *> ((*r).ptr ());
|
||||
if (image && image->is_visible () && (! exclude || exclude->find (mp_view->annotation_shapes ().iterator_from_pointer (&*r)) == exclude->end ())) {
|
||||
images.push_back (&*r);
|
||||
}
|
||||
++r;
|
||||
}
|
||||
|
||||
std::stable_sort (images.begin (), images.end (), SortImagePtrByZOrder ());
|
||||
|
||||
// look for the "closest" image to the search box
|
||||
dmin = std::numeric_limits <double>::max ();
|
||||
const db::DUserObject *found = 0;
|
||||
|
||||
for (std::vector <const db::DUserObject *>::const_iterator robj = images.begin (); robj != images.end (); ++robj) {
|
||||
double d = std::numeric_limits <double>::max ();
|
||||
if (is_selected (*dynamic_cast<const img::Object *> ((*robj)->ptr ()), p, mp_view->box (), l, d)) {
|
||||
found = *robj;
|
||||
dmin = d;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void
|
||||
Service::selection_to_view (img::View::Mode mode)
|
||||
{
|
||||
|
|
@ -1134,7 +1161,7 @@ Service::click_proximity (const db::DPoint &pos, lay::Editable::SelectionMode mo
|
|||
|
||||
// point selection: look for the "closest" images
|
||||
double dmin = std::numeric_limits <double>::max ();
|
||||
const db::DUserObject *robj = find_image (mp_view, pos, search_dbox, l, dmin, exclude);
|
||||
const db::DUserObject *robj = find_image (pos, search_dbox, l, dmin, exclude);
|
||||
|
||||
// return the proximity value
|
||||
if (robj) {
|
||||
|
|
@ -1157,7 +1184,7 @@ Service::transient_select (const db::DPoint &pos)
|
|||
|
||||
// point selection: look for the "closest" image
|
||||
double dmin = std::numeric_limits <double>::max ();
|
||||
const db::DUserObject *robj = find_image (mp_view, pos, search_dbox, l, dmin, &m_previous_selection);
|
||||
const db::DUserObject *robj = find_image (pos, search_dbox, l, dmin, &m_previous_selection);
|
||||
|
||||
// create the transient marker for the object found
|
||||
if (robj) {
|
||||
|
|
@ -1200,6 +1227,10 @@ Service::clear_transient_selection ()
|
|||
bool
|
||||
Service::select (const db::DBox &box, lay::Editable::SelectionMode mode)
|
||||
{
|
||||
if (! m_images_visible) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool needs_update = false;
|
||||
bool any_selected = false;
|
||||
|
||||
|
|
@ -1274,7 +1305,7 @@ Service::select (const db::DBox &box, lay::Editable::SelectionMode mode)
|
|||
|
||||
// point selection: look for the "closest" image
|
||||
double dmin = std::numeric_limits <double>::max ();
|
||||
const db::DUserObject *robj = find_image (mp_view, box.p1 (), search_dbox, l, dmin, exclude);
|
||||
const db::DUserObject *robj = find_image (box.p1 (), search_dbox, l, dmin, exclude);
|
||||
|
||||
// select the one that was found
|
||||
if (robj) {
|
||||
|
|
@ -1381,6 +1412,10 @@ Service::change_image_by_id (size_t id, const img::Object &to)
|
|||
void
|
||||
Service::render_bg (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas)
|
||||
{
|
||||
if (! m_images_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector <const img::Object *> images;
|
||||
|
||||
lay::AnnotationShapes::touching_iterator user_object = mp_view->annotation_shapes ().begin_touching (vp.box ());
|
||||
|
|
@ -1409,11 +1444,29 @@ void
|
|||
Service::menu_activated (const std::string &symbol)
|
||||
{
|
||||
if (symbol == "img::clear_all_images") {
|
||||
|
||||
manager ()->transaction (tl::to_string (QObject::tr ("Clear all images")));
|
||||
clear_images ();
|
||||
manager ()->commit ();
|
||||
|
||||
} else if (symbol == "img::add_image") {
|
||||
|
||||
if (! images_visible ()) {
|
||||
lay::TipDialog td (QApplication::activeWindow (),
|
||||
tl::to_string (QObject::tr ("Images are not visible. If you add an image you will not see it.\n\n"
|
||||
"Choose 'View/Show Images' to make images visible.")),
|
||||
"add-image-while-not-visible",
|
||||
lay::TipDialog::okcancel_buttons);
|
||||
lay::TipDialog::button_type button = lay::TipDialog::null_button;
|
||||
td.exec_dialog (button);
|
||||
if (button == lay::TipDialog::cancel_button) {
|
||||
// Don't bother the user with more dialogs.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
add_image ();
|
||||
|
||||
} else if (symbol == "img::bring_to_back") {
|
||||
bring_to_back ();
|
||||
} else if (symbol == "img::bring_to_front") {
|
||||
|
|
|
|||
|
|
@ -421,6 +421,19 @@ public:
|
|||
{
|
||||
return mp_view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shows or hides the images
|
||||
*/
|
||||
void show_images (bool f);
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating whether images are shown or hidden
|
||||
*/
|
||||
bool images_visible () const
|
||||
{
|
||||
return m_images_visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implement the menu response function
|
||||
|
|
@ -477,6 +490,8 @@ private:
|
|||
size_t m_moved_landmark;
|
||||
// Flag indicating that we want to keep the selection after the landmark was moved
|
||||
bool m_keep_selection_for_landmark;
|
||||
// Flag indicating whether images are visible
|
||||
bool m_images_visible;
|
||||
|
||||
void show_message ();
|
||||
|
||||
|
|
@ -506,6 +521,11 @@ private:
|
|||
*/
|
||||
void copy_selected ();
|
||||
|
||||
/**
|
||||
* @brief Finds an image object from the given point
|
||||
*/
|
||||
const db::DUserObject *find_image (const db::DPoint &p, const db::DBox &search_box, double l, double &dmin, const std::map<img::Service::obj_iterator, unsigned int> *exclude = 0);
|
||||
|
||||
/**
|
||||
* @brief Update m_selected_image_views to reflect the selection
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,470 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2020 Matthias Koefferlein
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "imgStream.h"
|
||||
#include "tlXMLParser.h"
|
||||
#include "tlXMLWriter.h"
|
||||
#include "tlTimer.h"
|
||||
#include "layConverters.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace img
|
||||
{
|
||||
|
||||
class ImageProxy
|
||||
{
|
||||
public:
|
||||
ImageProxy (const img::Object *img = 0)
|
||||
: mp_img (img),
|
||||
m_width (1), m_height (1),
|
||||
m_min_value (0.0), m_max_value (1.0),
|
||||
m_color (false)
|
||||
{
|
||||
init ();
|
||||
}
|
||||
|
||||
bool is_color () const
|
||||
{
|
||||
return mp_img->is_color ();
|
||||
}
|
||||
|
||||
void set_color (bool f)
|
||||
{
|
||||
m_color = f;
|
||||
}
|
||||
|
||||
size_t width () const
|
||||
{
|
||||
return mp_img->width ();
|
||||
}
|
||||
|
||||
void set_width (size_t w)
|
||||
{
|
||||
m_width = w;
|
||||
}
|
||||
|
||||
size_t height () const
|
||||
{
|
||||
return mp_img->height ();
|
||||
}
|
||||
|
||||
void set_height (size_t h)
|
||||
{
|
||||
m_height = h;
|
||||
}
|
||||
|
||||
std::list<std::string>::const_iterator begin_byte_data () const
|
||||
{
|
||||
return m_byte_data.begin ();
|
||||
}
|
||||
|
||||
std::list<std::string>::const_iterator end_byte_data () const
|
||||
{
|
||||
return m_byte_data.end ();
|
||||
}
|
||||
|
||||
void push_byte_data (const std::string &s)
|
||||
{
|
||||
m_byte_data.push_back (s);
|
||||
}
|
||||
|
||||
std::list<std::string>::const_iterator begin_data () const
|
||||
{
|
||||
return m_data.begin ();
|
||||
}
|
||||
|
||||
std::list<std::string>::const_iterator end_data () const
|
||||
{
|
||||
return m_data.end ();
|
||||
}
|
||||
|
||||
void push_data (const std::string &s)
|
||||
{
|
||||
m_data.push_back (s);
|
||||
}
|
||||
|
||||
const db::Matrix3d &matrix () const
|
||||
{
|
||||
return mp_img->matrix ();
|
||||
}
|
||||
|
||||
void set_matrix (const db::Matrix3d &m)
|
||||
{
|
||||
m_matrix = m;
|
||||
}
|
||||
|
||||
double min_value () const
|
||||
{
|
||||
return mp_img->min_value ();
|
||||
}
|
||||
|
||||
void set_min_value (double h)
|
||||
{
|
||||
m_min_value = h;
|
||||
}
|
||||
|
||||
double max_value () const
|
||||
{
|
||||
return mp_img->max_value ();
|
||||
}
|
||||
|
||||
void set_max_value (double h)
|
||||
{
|
||||
m_max_value = h;
|
||||
}
|
||||
|
||||
const img::DataMapping &data_mapping () const
|
||||
{
|
||||
return mp_img->data_mapping ();
|
||||
}
|
||||
|
||||
void set_data_mapping (const img::DataMapping &dm)
|
||||
{
|
||||
m_data_mapping = dm;
|
||||
}
|
||||
|
||||
const img::Object::landmarks_type &landmarks () const
|
||||
{
|
||||
return mp_img->landmarks ();
|
||||
}
|
||||
|
||||
void set_landmarks (const img::Object::landmarks_type &lm)
|
||||
{
|
||||
m_landmarks = lm;
|
||||
}
|
||||
|
||||
img::Object *get_image () const;
|
||||
|
||||
private:
|
||||
const img::Object *mp_img;
|
||||
|
||||
// reader mode
|
||||
size_t m_width, m_height;
|
||||
img::Object::landmarks_type m_landmarks;
|
||||
img::DataMapping m_data_mapping;
|
||||
double m_min_value, m_max_value;
|
||||
db::Matrix3d m_matrix;
|
||||
std::list<std::string> m_byte_data, m_data;
|
||||
bool m_color;
|
||||
|
||||
void init ();
|
||||
};
|
||||
|
||||
template <class T1, class T2>
|
||||
static void
|
||||
string_to_pixels (img::Object *img, const std::string &s, size_t row, size_t w, bool color)
|
||||
{
|
||||
tl::Extractor ex (s.c_str ());
|
||||
|
||||
size_t column = 0;
|
||||
while (! ex.at_end () && column < w) {
|
||||
|
||||
T1 r = 0;
|
||||
T1 g = 0;
|
||||
T1 b = 0;
|
||||
T2 m = 0;
|
||||
|
||||
unsigned int i = 0;
|
||||
bool has_mask = false;
|
||||
|
||||
while (! ex.at_end () && ! ex.test (";")) {
|
||||
|
||||
if (i == 0) {
|
||||
ex.read (r);
|
||||
} else if (color && i == 1) {
|
||||
ex.read (g);
|
||||
} else if (color && i == 2) {
|
||||
ex.read (b);
|
||||
} else {
|
||||
ex.read (m);
|
||||
has_mask = true;
|
||||
}
|
||||
++i;
|
||||
|
||||
ex.test (",");
|
||||
|
||||
}
|
||||
|
||||
if (color) {
|
||||
img->set_pixel (column, row, double (r), double (g), double (b));
|
||||
} else {
|
||||
img->set_pixel (column, row, double (r));
|
||||
}
|
||||
|
||||
if (has_mask) {
|
||||
img->set_mask (column, row, m);
|
||||
}
|
||||
|
||||
++column;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
img::Object *
|
||||
ImageProxy::get_image () const
|
||||
{
|
||||
std::auto_ptr<img::Object> img (new Object (std::max (size_t (1), m_width), std::max (size_t (1), m_height), m_matrix, m_color, ! m_byte_data.empty ()));
|
||||
img->set_min_value (m_min_value);
|
||||
img->set_max_value (m_max_value);
|
||||
img->set_data_mapping (m_data_mapping);
|
||||
img->set_landmarks (m_landmarks);
|
||||
|
||||
if (! m_byte_data.empty ()) {
|
||||
|
||||
std::list<std::string>::const_iterator s = m_byte_data.begin ();
|
||||
for (size_t i = 0; i < m_height; ++i) {
|
||||
string_to_pixels<unsigned char, unsigned char> (img.get (), *s++, i, m_width, m_color);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
std::list<std::string>::const_iterator s = m_data.begin ();
|
||||
for (size_t i = 0; i < m_height; ++i) {
|
||||
string_to_pixels<float, unsigned char> (img.get (), *s++, i, m_width, m_color);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return img.release ();
|
||||
}
|
||||
|
||||
static void add_entry (std::string &heap, const float *&b, bool &first)
|
||||
{
|
||||
if (b) {
|
||||
if (! first) {
|
||||
heap += ",";
|
||||
}
|
||||
heap += tl::to_string (*b++);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_entry (std::string &heap, const unsigned char *&b, bool &first)
|
||||
{
|
||||
if (b) {
|
||||
if (! first) {
|
||||
heap += ",";
|
||||
}
|
||||
heap += tl::to_string ((unsigned int) *b++);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T1, class T2, class T3, class T4>
|
||||
static const std::string &data_to_string (std::string &heap, size_t l, const T1 *r, const T2 *g, const T3 *b, const T4 *m)
|
||||
{
|
||||
heap.clear ();
|
||||
|
||||
while (l-- > 0) {
|
||||
bool first = true;
|
||||
add_entry (heap, r, first);
|
||||
add_entry (heap, g, first);
|
||||
add_entry (heap, b, first);
|
||||
add_entry (heap, m, first);
|
||||
if (l > 0) {
|
||||
heap += ";";
|
||||
}
|
||||
}
|
||||
|
||||
return heap;
|
||||
}
|
||||
|
||||
void
|
||||
ImageProxy::init ()
|
||||
{
|
||||
if (!mp_img) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t w = mp_img->width ();
|
||||
size_t h = mp_img->height ();
|
||||
|
||||
static std::string s;
|
||||
|
||||
if (mp_img->is_color ()) {
|
||||
|
||||
if (mp_img->is_byte_data ()) {
|
||||
|
||||
const unsigned char *r = mp_img->byte_data (0);
|
||||
const unsigned char *g = mp_img->byte_data (1);
|
||||
const unsigned char *b = mp_img->byte_data (2);
|
||||
const unsigned char *m = mp_img->mask ();
|
||||
|
||||
for (size_t i = 0; i < h; ++i) {
|
||||
m_byte_data.push_back (data_to_string (s, w, r + i * w, g + i * w, b + i * w, m ? (m + i * w) : 0));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const float *r = mp_img->float_data (0);
|
||||
const float *g = mp_img->float_data (1);
|
||||
const float *b = mp_img->float_data (2);
|
||||
const unsigned char *m = mp_img->mask ();
|
||||
|
||||
for (size_t i = 0; i < h; ++i) {
|
||||
m_data.push_back (data_to_string (s, w, r + i * w, g + i * w, b + i * w, m ? (m + i * w) : 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (mp_img->is_byte_data ()) {
|
||||
|
||||
const unsigned char *g = mp_img->byte_data ();
|
||||
const unsigned char *m = mp_img->mask ();
|
||||
|
||||
for (size_t i = 0; i < h; ++i) {
|
||||
m_byte_data.push_back (data_to_string (s, w, g + i * w, (const unsigned char *) 0, (const unsigned char *) 0, m ? (m + i * w) : 0));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const float *g = mp_img->float_data ();
|
||||
const unsigned char *m = mp_img->mask ();
|
||||
|
||||
for (size_t i = 0; i < h; ++i) {
|
||||
m_data.push_back (data_to_string (s, w, g + i * w, (const float *) 0, (const float *) 0, m ? (m + i * w) : 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
struct PointConverter
|
||||
{
|
||||
std::string to_string (const db::DPoint &p) const
|
||||
{
|
||||
return p.to_string ();
|
||||
}
|
||||
|
||||
void from_string (const std::string &s, db::DPoint &p) const
|
||||
{
|
||||
tl::Extractor ex (s.c_str ());
|
||||
ex.read (p);
|
||||
}
|
||||
};
|
||||
|
||||
struct ColorMapConverter
|
||||
{
|
||||
std::string to_string (const std::pair<double, std::pair<QColor, QColor> > &cm) const
|
||||
{
|
||||
std::string s;
|
||||
s = tl::to_string (cm.first);
|
||||
s += ":";
|
||||
|
||||
lay::ColorConverter cc;
|
||||
s += tl::to_word_or_quoted_string (cc.to_string (cm.second.first));
|
||||
if (cm.second.first != cm.second.second) {
|
||||
s += ",";
|
||||
s += tl::to_word_or_quoted_string (cc.to_string (cm.second.second));
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void from_string (const std::string &s, std::pair<double, std::pair<QColor, QColor> > &cm) const
|
||||
{
|
||||
tl::Extractor ex (s.c_str ());
|
||||
|
||||
ex.read (cm.first);
|
||||
ex.test (":");
|
||||
|
||||
lay::ColorConverter cc;
|
||||
|
||||
std::string w;
|
||||
ex.read_word_or_quoted (w);
|
||||
|
||||
cc.from_string (w, cm.second.first);
|
||||
|
||||
if (ex.test (",")) {
|
||||
|
||||
w.clear ();
|
||||
ex.read_word_or_quoted (w);
|
||||
|
||||
cc.from_string (w, cm.second.second);
|
||||
|
||||
} else {
|
||||
cm.second.second = cm.second.first;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
tl::XMLStruct<ImageProxy> s_img_structure ("image-data",
|
||||
tl::make_member (&ImageProxy::is_color, &ImageProxy::set_color, "color") +
|
||||
tl::make_member (&ImageProxy::width, &ImageProxy::set_width, "width") +
|
||||
tl::make_member (&ImageProxy::height, &ImageProxy::set_height, "height") +
|
||||
tl::make_member (&ImageProxy::matrix, &ImageProxy::set_matrix, "matrix") +
|
||||
tl::make_member (&ImageProxy::min_value, &ImageProxy::set_min_value, "min-value") +
|
||||
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::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") +
|
||||
tl::make_member (&img::DataMapping::gamma, "gamma") +
|
||||
tl::make_member (&img::DataMapping::red_gain, "red-gain") +
|
||||
tl::make_member (&img::DataMapping::green_gain, "green-gain") +
|
||||
tl::make_member (&img::DataMapping::blue_gain, "blue-gain")
|
||||
) +
|
||||
tl::make_element (&ImageProxy::landmarks, &ImageProxy::set_landmarks, "landmarks",
|
||||
tl::make_member (&img::Object::landmarks_type::begin, &img::Object::landmarks_type::end, &img::Object::landmarks_type::push_back, "landmark", PointConverter ())
|
||||
) +
|
||||
tl::make_member (&ImageProxy::begin_byte_data, &ImageProxy::end_byte_data, &ImageProxy::push_byte_data, "byte-data") +
|
||||
tl::make_member (&ImageProxy::begin_data, &ImageProxy::end_data, &ImageProxy::push_data, "data")
|
||||
);
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
img::Object *
|
||||
ImageStreamer::read (tl::InputStream &stream)
|
||||
{
|
||||
ImageProxy proxy;
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading image file: ")) + stream.source ());
|
||||
tl::XMLStreamSource in (stream, tl::to_string (tr ("Image file")));
|
||||
s_img_structure.parse (in, proxy);
|
||||
|
||||
return proxy.get_image ();
|
||||
}
|
||||
|
||||
void
|
||||
ImageStreamer::write (tl::OutputStream &stream, const img::Object &img)
|
||||
{
|
||||
ImageProxy proxy (&img);
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Writing image file: ")) + stream.path ());
|
||||
s_img_structure.write (stream, proxy);
|
||||
}
|
||||
|
||||
} // namespace img
|
||||
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2020 Matthias Koefferlein
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_imgStream
|
||||
#define HDR_imgStream
|
||||
|
||||
#include "imgCommon.h"
|
||||
|
||||
#include "imgObject.h"
|
||||
#include "tlStream.h"
|
||||
|
||||
namespace img {
|
||||
|
||||
/**
|
||||
* @brief An object streaming image data from or to files
|
||||
*/
|
||||
struct IMG_PUBLIC ImageStreamer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
ImageStreamer () { }
|
||||
|
||||
/**
|
||||
* @brief Reads an image Object from a stream
|
||||
*
|
||||
* This method returns a new'd object. It's the responsibility of the caller to delete the object.
|
||||
*/
|
||||
static Object *read(tl::InputStream &stream);
|
||||
|
||||
/**
|
||||
* @brief Writes an image object to a stream
|
||||
*/
|
||||
static void write (tl::OutputStream &stream, const img::Object &img);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -26,6 +26,7 @@
|
|||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
|
|
@ -45,36 +46,36 @@ const double epsilon = 1e-6;
|
|||
|
||||
struct compare_first_of_node
|
||||
{
|
||||
bool operator() (const std::pair <double, QColor> &a, const std::pair <double, QColor> &b) const
|
||||
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, QColor> > &nodes, double x)
|
||||
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 nodes[0].second;
|
||||
return x < nodes[0].first ? nodes[0].second.first : nodes[0].second.second;
|
||||
} else {
|
||||
|
||||
std::vector<std::pair<double, QColor> >::const_iterator p = std::lower_bound (nodes.begin (), nodes.end (), std::make_pair (x, QColor ()), compare_first_of_node ());
|
||||
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;
|
||||
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<std::pair <double, QColor> > &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<QColor, QColor> 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<QColor, QColor> 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 <std::pair <double, QColor> > &nodes)
|
||||
ColorBar::set_nodes (const std::vector<std::pair<double, std::pair<QColor, QColor> > > &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 <std::pair <double, QColor> >::iterator w = m_nodes.begin ();
|
||||
std::vector <std::pair <double, QColor> >::const_iterator nn = m_nodes.begin ();
|
||||
for (std::vector <std::pair <double, QColor> >::const_iterator n = m_nodes.begin () + 1; n != m_nodes.end (); ++n) {
|
||||
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) {
|
||||
if (fabs (nn->first - n->first) > min_value_interval) {
|
||||
*w++ = *nn;
|
||||
nn = n;
|
||||
|
|
@ -207,7 +288,7 @@ ColorBar::set_nodes (const std::vector <std::pair <double, QColor> > &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<std::pair<double, QColor> >::const_iterator pmin = m_nodes.end ();
|
||||
for (std::vector<std::pair<double, QColor> >::const_iterator p = m_nodes.begin (); p != m_nodes.end (); ++p) {
|
||||
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) {
|
||||
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<std::pair<double, QColor> >::const_iterator (m_nodes.begin ()), pmin));
|
||||
m_selected = int (std::distance (std::vector<std::pair<double, std::pair<QColor, QColor> > >::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<std::pair<double, QColor> >::iterator p = std::lower_bound (m_nodes.begin (), m_nodes.end (), std::make_pair (xx, QColor ()), compare_first_of_node ());
|
||||
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 ());
|
||||
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 ();
|
||||
|
|
|
|||
|
|
@ -20,13 +20,15 @@
|
|||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_imgWidgets
|
||||
#define HDR_imgWidgets
|
||||
|
||||
#include "layWidgets.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QFrame>
|
||||
#include <QToolButton>
|
||||
#include <vector>
|
||||
|
||||
class QMouseEvent;
|
||||
|
|
@ -41,7 +43,37 @@ namespace img
|
|||
*
|
||||
* TODO: move this somewhere else.
|
||||
*/
|
||||
QColor interpolated_color (const std::vector<std::pair <double, QColor> > &nodes, double x);
|
||||
QColor interpolated_color (const std::vector<std::pair <double, std::pair<QColor, QColor> > > &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<QColor, QColor> c);
|
||||
|
||||
public slots:
|
||||
void set_color (std::pair<QColor, QColor> 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 <std::pair <double, QColor> > &nodes);
|
||||
void set_nodes (const std::vector <std::pair <double, std::pair<QColor, QColor> > > &nodes);
|
||||
|
||||
const std::vector <std::pair <double, QColor> > &nodes () const
|
||||
const std::vector <std::pair <double, std::pair<QColor, QColor> > > &nodes () const
|
||||
{
|
||||
return m_nodes;
|
||||
}
|
||||
|
|
@ -92,18 +124,18 @@ public:
|
|||
void set_histogram (const std::vector <size_t> &histogram);
|
||||
|
||||
public slots:
|
||||
void set_current_color (QColor c);
|
||||
void set_current_color (std::pair<QColor, QColor> 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<QColor, QColor> c);
|
||||
|
||||
private:
|
||||
bool m_dragging;
|
||||
int m_selected;
|
||||
std::vector <std::pair <double, QColor> > m_nodes;
|
||||
std::vector <std::pair <double, std::pair<QColor, QColor> > > m_nodes;
|
||||
std::vector <size_t> m_histogram;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,292 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2020 Matthias Koefferlein
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "imgStream.h"
|
||||
#include "tlUnitTest.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
TEST(1_FloatMono)
|
||||
{
|
||||
img::Object image (12, 8, db::DCplxTrans (1.5, 90.0, true, db::DVector (17, -42)), false, false);
|
||||
|
||||
image.set_min_value (-0.25);
|
||||
image.set_max_value (0.75);
|
||||
|
||||
std::vector<db::DPoint> lm;
|
||||
lm.push_back (db::DPoint (1, 2));
|
||||
lm.push_back (db::DPoint (-101, 102));
|
||||
image.set_landmarks (lm);
|
||||
|
||||
img::DataMapping dm;
|
||||
dm.blue_gain = 0.5;
|
||||
dm.green_gain = 0.75;
|
||||
dm.red_gain = 0.25;
|
||||
dm.contrast = -0.5;
|
||||
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))));
|
||||
image.set_data_mapping (dm);
|
||||
|
||||
image.set_pixel (0, 0, 0.25);
|
||||
image.set_pixel (2, 5, 0.25);
|
||||
image.set_pixel (7, 1, 0.125);
|
||||
|
||||
std::string path = tmp_file ("tmp.lyimg");
|
||||
{
|
||||
tl::OutputFile file (path);
|
||||
tl::OutputStream stream (file);
|
||||
img::ImageStreamer::write (stream, image);
|
||||
}
|
||||
|
||||
std::auto_ptr<img::Object> read;
|
||||
|
||||
{
|
||||
tl::InputFile file (path);
|
||||
tl::InputStream stream (file);
|
||||
read.reset (img::ImageStreamer::read (stream));
|
||||
}
|
||||
|
||||
EXPECT_EQ (image.to_string (), read->to_string ());
|
||||
}
|
||||
|
||||
TEST(2_FloatMonoWithMask)
|
||||
{
|
||||
img::Object image (12, 8, db::DCplxTrans (1.5, 90.0, true, db::DVector (17, -42)), false, false);
|
||||
|
||||
image.set_min_value (-0.25);
|
||||
image.set_max_value (0.75);
|
||||
|
||||
image.set_pixel (0, 0, 0.25);
|
||||
image.set_pixel (2, 5, 0.25);
|
||||
image.set_pixel (7, 1, 0.125);
|
||||
|
||||
image.set_mask (1, 0, 1);
|
||||
image.set_mask (1, 2, 1);
|
||||
image.set_mask (1, 3, 0);
|
||||
|
||||
std::string path = tmp_file ("tmp.lyimg");
|
||||
{
|
||||
tl::OutputFile file (path);
|
||||
tl::OutputStream stream (file);
|
||||
img::ImageStreamer::write (stream, image);
|
||||
}
|
||||
|
||||
std::auto_ptr<img::Object> read;
|
||||
|
||||
{
|
||||
tl::InputFile file (path);
|
||||
tl::InputStream stream (file);
|
||||
read.reset (img::ImageStreamer::read (stream));
|
||||
}
|
||||
|
||||
EXPECT_EQ (image.to_string (), read->to_string ());
|
||||
}
|
||||
|
||||
TEST(3_ByteMono)
|
||||
{
|
||||
img::Object image (12, 8, db::Matrix3d (db::DCplxTrans (1.5, 90.0, true, db::DVector (17, -42))), false, true);
|
||||
|
||||
image.set_min_value (10);
|
||||
image.set_max_value (240);
|
||||
|
||||
image.set_pixel (0, 0, 50);
|
||||
image.set_pixel (2, 5, 70);
|
||||
image.set_pixel (7, 1, 120);
|
||||
|
||||
std::string path = tmp_file ("tmp.lyimg");
|
||||
{
|
||||
tl::OutputFile file (path);
|
||||
tl::OutputStream stream (file);
|
||||
img::ImageStreamer::write (stream, image);
|
||||
}
|
||||
|
||||
std::auto_ptr<img::Object> read;
|
||||
|
||||
{
|
||||
tl::InputFile file (path);
|
||||
tl::InputStream stream (file);
|
||||
read.reset (img::ImageStreamer::read (stream));
|
||||
}
|
||||
|
||||
EXPECT_EQ (image.to_string (), read->to_string ());
|
||||
}
|
||||
|
||||
TEST(4_ByteMonoWithMask)
|
||||
{
|
||||
img::Object image (12, 8, db::DCplxTrans (1.5, 90.0, true, db::DVector (17, -42)), false, true);
|
||||
|
||||
image.set_min_value (10);
|
||||
image.set_max_value (240);
|
||||
|
||||
image.set_pixel (0, 0, 50);
|
||||
image.set_pixel (2, 5, 70);
|
||||
image.set_pixel (7, 1, 120);
|
||||
|
||||
image.set_mask (1, 0, 1);
|
||||
image.set_mask (1, 2, 1);
|
||||
image.set_mask (1, 3, 0);
|
||||
|
||||
std::string path = tmp_file ("tmp.lyimg");
|
||||
{
|
||||
tl::OutputFile file (path);
|
||||
tl::OutputStream stream (file);
|
||||
img::ImageStreamer::write (stream, image);
|
||||
}
|
||||
|
||||
std::auto_ptr<img::Object> read;
|
||||
|
||||
{
|
||||
tl::InputFile file (path);
|
||||
tl::InputStream stream (file);
|
||||
read.reset (img::ImageStreamer::read (stream));
|
||||
}
|
||||
|
||||
EXPECT_EQ (image.to_string (), read->to_string ());
|
||||
}
|
||||
|
||||
TEST(5_FloatColor)
|
||||
{
|
||||
img::Object image (12, 8, db::DCplxTrans (1.5, 90.0, true, db::DVector (17, -42)), true, false);
|
||||
|
||||
image.set_min_value (-0.25);
|
||||
image.set_max_value (0.75);
|
||||
|
||||
image.set_pixel (0, 0, 0.25, -0.25, -0.125);
|
||||
image.set_pixel (2, 5, 0.25, 0.125, 0.625);
|
||||
image.set_pixel (7, 1, 0.125, 0.25, 0.75);
|
||||
|
||||
std::string path = tmp_file ("tmp.lyimg");
|
||||
{
|
||||
tl::OutputFile file (path);
|
||||
tl::OutputStream stream (file);
|
||||
img::ImageStreamer::write (stream, image);
|
||||
}
|
||||
|
||||
std::auto_ptr<img::Object> read;
|
||||
|
||||
{
|
||||
tl::InputFile file (path);
|
||||
tl::InputStream stream (file);
|
||||
read.reset (img::ImageStreamer::read (stream));
|
||||
}
|
||||
|
||||
EXPECT_EQ (image.to_string (), read->to_string ());
|
||||
}
|
||||
|
||||
TEST(6_FloatColorWithMask)
|
||||
{
|
||||
img::Object image (12, 8, db::DCplxTrans (1.5, 90.0, true, db::DVector (17, -42)), true, false);
|
||||
|
||||
image.set_min_value (-0.25);
|
||||
image.set_max_value (0.75);
|
||||
|
||||
image.set_pixel (0, 0, 0.25, -0.25, -0.125);
|
||||
image.set_pixel (2, 5, 0.25, 0.125, 0.625);
|
||||
image.set_pixel (7, 1, 0.125, 0.25, 0.75);
|
||||
|
||||
image.set_mask (1, 0, 1);
|
||||
image.set_mask (1, 2, 1);
|
||||
image.set_mask (1, 3, 0);
|
||||
|
||||
std::string path = tmp_file ("tmp.lyimg");
|
||||
{
|
||||
tl::OutputFile file (path);
|
||||
tl::OutputStream stream (file);
|
||||
img::ImageStreamer::write (stream, image);
|
||||
}
|
||||
|
||||
std::auto_ptr<img::Object> read;
|
||||
|
||||
{
|
||||
tl::InputFile file (path);
|
||||
tl::InputStream stream (file);
|
||||
read.reset (img::ImageStreamer::read (stream));
|
||||
}
|
||||
|
||||
EXPECT_EQ (image.to_string (), read->to_string ());
|
||||
}
|
||||
|
||||
TEST(7_ByteColor)
|
||||
{
|
||||
img::Object image (12, 8, db::DCplxTrans (1.5, 90.0, true, db::DVector (17, -42)), true, true);
|
||||
|
||||
image.set_min_value (10);
|
||||
image.set_max_value (240);
|
||||
|
||||
image.set_pixel (0, 0, 10, 20, 30);
|
||||
image.set_pixel (2, 5, 11, 21, 31);
|
||||
image.set_pixel (7, 1, 12, 22, 32);
|
||||
|
||||
std::string path = tmp_file ("tmp.lyimg");
|
||||
{
|
||||
tl::OutputFile file (path);
|
||||
tl::OutputStream stream (file);
|
||||
img::ImageStreamer::write (stream, image);
|
||||
}
|
||||
|
||||
std::auto_ptr<img::Object> read;
|
||||
|
||||
{
|
||||
tl::InputFile file (path);
|
||||
tl::InputStream stream (file);
|
||||
read.reset (img::ImageStreamer::read (stream));
|
||||
}
|
||||
|
||||
EXPECT_EQ (image.to_string (), read->to_string ());
|
||||
}
|
||||
|
||||
TEST(8_ByteColorWithMask)
|
||||
{
|
||||
img::Object image (12, 8, db::DCplxTrans (1.5, 90.0, true, db::DVector (17, -42)), true, true);
|
||||
|
||||
image.set_min_value (10);
|
||||
image.set_max_value (240);
|
||||
|
||||
image.set_pixel (0, 0, 10, 20, 30);
|
||||
image.set_pixel (2, 5, 11, 21, 31);
|
||||
image.set_pixel (7, 1, 12, 22, 32);
|
||||
|
||||
image.set_mask (1, 0, 1);
|
||||
image.set_mask (1, 2, 1);
|
||||
image.set_mask (1, 3, 0);
|
||||
|
||||
std::string path = tmp_file ("tmp.lyimg");
|
||||
{
|
||||
tl::OutputFile file (path);
|
||||
tl::OutputStream stream (file);
|
||||
img::ImageStreamer::write (stream, image);
|
||||
}
|
||||
|
||||
std::auto_ptr<img::Object> read;
|
||||
|
||||
{
|
||||
tl::InputFile file (path);
|
||||
tl::InputStream stream (file);
|
||||
read.reset (img::ImageStreamer::read (stream));
|
||||
}
|
||||
|
||||
EXPECT_EQ (image.to_string (), read->to_string ());
|
||||
}
|
||||
|
|
@ -25,9 +25,16 @@
|
|||
#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);
|
||||
img::Object image (12, 8, db::DCplxTrans (), false, false);
|
||||
|
||||
EXPECT_EQ (image.is_color (), false);
|
||||
EXPECT_EQ (image.is_byte_data (), false);
|
||||
|
|
@ -95,25 +102,42 @@ 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);
|
||||
|
||||
img::Object copy3, empty;
|
||||
copy3.swap (copy2);
|
||||
EXPECT_EQ (copy3.equals (&image), true);
|
||||
EXPECT_EQ (copy2.equals (&empty), true);
|
||||
copy3.swap (copy2);
|
||||
EXPECT_EQ (copy2.equals (&image), true);
|
||||
EXPECT_EQ (copy3.equals (&empty), true);
|
||||
|
||||
EXPECT_EQ (image.to_string (), copy2.to_string ());
|
||||
|
||||
EXPECT_EQ (image.mask (1, 2), true);
|
||||
|
|
@ -134,7 +158,7 @@ TEST(1)
|
|||
TEST(2)
|
||||
{
|
||||
for (unsigned int channel = 0; channel < 3; ++channel) {
|
||||
img::Object image (12, 8, db::DCplxTrans (), true);
|
||||
img::Object image (12, 8, db::DCplxTrans (), true, false);
|
||||
|
||||
EXPECT_EQ (image.is_color (), true);
|
||||
|
||||
|
|
@ -203,7 +227,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);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri)
|
|||
|
||||
SOURCES = \
|
||||
imgObject.cc \
|
||||
imgFile.cc
|
||||
|
||||
INCLUDEPATH += $$IMG_INC $$DB_INC $$TL_INC $$LAYBASIC_INC $$GSI_INC
|
||||
DEPENDPATH += $$IMG_INC $$DB_INC $$TL_INC $$LAYBASIC_INC $$GSI_INC
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 318 B |
Binary file not shown.
|
After Width: | Height: | Size: 368 B |
|
|
@ -127,6 +127,8 @@
|
|||
<file alias="folder_12.png">images/folder_12.png</file>
|
||||
<file alias="file_12.png">images/file_12.png</file>
|
||||
<file alias="empty_12.png">images/empty_12.png</file>
|
||||
<file alias="unlocked_16.png">images/unlocked_16.png</file>
|
||||
<file alias="locked_16.png">images/locked_16.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/syntax">
|
||||
<file alias="ruby.xml">syntax/ruby.xml</file>
|
||||
|
|
|
|||
|
|
@ -1468,6 +1468,9 @@ AbstractMenu::find_item (tl::Extractor &extr)
|
|||
if (n.empty ()) {
|
||||
|
||||
// skip (avoids infinite loops on wrong paths)
|
||||
while (! extr.at_end () && *extr != ';') {
|
||||
++extr;
|
||||
}
|
||||
|
||||
} else if (n == "begin") {
|
||||
|
||||
|
|
|
|||
|
|
@ -940,6 +940,15 @@ Extractor::read (unsigned int &value)
|
|||
return *this;
|
||||
}
|
||||
|
||||
Extractor &
|
||||
Extractor::read (unsigned char &value)
|
||||
{
|
||||
if (! try_read (value)) {
|
||||
error (tl::to_string (tr ("Expected an unsigned byte value")));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Extractor &
|
||||
Extractor::read (unsigned long &value)
|
||||
{
|
||||
|
|
@ -967,6 +976,15 @@ Extractor::read (double &value)
|
|||
return *this;
|
||||
}
|
||||
|
||||
Extractor &
|
||||
Extractor::read (float &value)
|
||||
{
|
||||
if (! try_read (value)) {
|
||||
error (tl::to_string (tr ("Expected a real number")));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Extractor &
|
||||
Extractor::read (int &value)
|
||||
{
|
||||
|
|
@ -1099,6 +1117,14 @@ namespace
|
|||
return tl::to_string (tr ("Range overflow on unsigned integer"));
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct overflow_msg_func<unsigned char>
|
||||
{
|
||||
std::string operator() () const
|
||||
{
|
||||
return tl::to_string (tr ("Range overflow on unsigned byte"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <class T> bool
|
||||
|
|
@ -1167,6 +1193,12 @@ Extractor::try_read_unsigned_int (T &value)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Extractor::try_read (unsigned char &value)
|
||||
{
|
||||
return try_read_unsigned_int (value);
|
||||
}
|
||||
|
||||
bool
|
||||
Extractor::try_read (unsigned int &value)
|
||||
{
|
||||
|
|
@ -1202,6 +1234,19 @@ Extractor::try_read (long long &value)
|
|||
{
|
||||
return try_read_signed_int (value);
|
||||
}
|
||||
|
||||
bool
|
||||
Extractor::try_read (float &value)
|
||||
{
|
||||
double d = value;
|
||||
if (try_read (d)) {
|
||||
value = d;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Extractor::try_read (double &value)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -468,6 +468,11 @@ public:
|
|||
*/
|
||||
Extractor &read (unsigned int &value);
|
||||
|
||||
/**
|
||||
* @brief Read an unsigned char int (see read of an unsigned int)
|
||||
*/
|
||||
Extractor &read (unsigned char &value);
|
||||
|
||||
/**
|
||||
* @brief Read an unsigned long (see read of an unsigned int)
|
||||
*/
|
||||
|
|
@ -483,6 +488,11 @@ public:
|
|||
*/
|
||||
Extractor &read (double &value);
|
||||
|
||||
/**
|
||||
* @brief Read a float (see read of an unsigned int)
|
||||
*/
|
||||
Extractor &read (float &value);
|
||||
|
||||
/**
|
||||
* @brief Read a signed int (see read of an unsigned int)
|
||||
*/
|
||||
|
|
@ -576,6 +586,11 @@ public:
|
|||
*/
|
||||
bool try_read (int &value);
|
||||
|
||||
/**
|
||||
* @brief Try to read an unsigned char int (see try to read an unsigned int)
|
||||
*/
|
||||
bool try_read (unsigned char &value);
|
||||
|
||||
/**
|
||||
* @brief Try to read an unsigned long (see try to read an unsigned int)
|
||||
*/
|
||||
|
|
@ -601,6 +616,11 @@ public:
|
|||
*/
|
||||
bool try_read (double &value);
|
||||
|
||||
/**
|
||||
* @brief Try to read a float (see try to read an unsigned int)
|
||||
*/
|
||||
bool try_read (float &value);
|
||||
|
||||
/**
|
||||
* @brief Try to read a boolean value (see try to read an unsigned int)
|
||||
*
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
|
|
@ -53,14 +53,31 @@ class IMG_TestClass < TestBase
|
|||
assert_equal(dm.num_colormap_entries, 0)
|
||||
dm.add_colormap_entry(0, 0x123456);
|
||||
dm.add_colormap_entry(0.5, 0xff0000);
|
||||
dm.add_colormap_entry(0.75, 0x123456, 0x654321);
|
||||
dm.add_colormap_entry(1.0, 0x00ff00);
|
||||
assert_equal(dm.num_colormap_entries, 3)
|
||||
assert_equal(dm.num_colormap_entries, 4)
|
||||
assert_equal(dm.colormap_color(0) & 0xffffff, 0x123456)
|
||||
assert_equal(dm.colormap_value(0), 0.0)
|
||||
assert_equal(dm.colormap_color(1) & 0xffffff, 0xff0000)
|
||||
assert_equal(dm.colormap_lcolor(1) & 0xffffff, 0xff0000)
|
||||
assert_equal(dm.colormap_rcolor(1) & 0xffffff, 0xff0000)
|
||||
assert_equal(dm.colormap_value(1), 0.5)
|
||||
assert_equal(dm.colormap_color(2) & 0xffffff, 0x00ff00)
|
||||
assert_equal(dm.colormap_value(2), 1.0)
|
||||
assert_equal(dm.colormap_color(2) & 0xffffff, 0x123456)
|
||||
assert_equal(dm.colormap_lcolor(2) & 0xffffff, 0x123456)
|
||||
assert_equal(dm.colormap_rcolor(2) & 0xffffff, 0x654321)
|
||||
assert_equal(dm.colormap_value(2), 0.75)
|
||||
assert_equal(dm.colormap_color(3) & 0xffffff, 0x00ff00)
|
||||
assert_equal(dm.colormap_value(3), 1.0)
|
||||
|
||||
image = RBA::Image.new
|
||||
image.data_mapping = dm
|
||||
|
||||
dm2 = image.data_mapping
|
||||
assert_equal(dm2.num_colormap_entries, 4)
|
||||
|
||||
dm.clear_colormap
|
||||
assert_equal(dm2.num_colormap_entries, 4)
|
||||
assert_equal(dm.num_colormap_entries, 0)
|
||||
|
||||
end
|
||||
|
||||
|
|
@ -74,12 +91,21 @@ class IMG_TestClass < TestBase
|
|||
assert_equal(image.to_s, "empty:")
|
||||
assert_equal(image.is_empty?, true)
|
||||
|
||||
image2 = RBA::Image::from_s(image.to_s)
|
||||
assert_equal(image2.to_s, image.to_s)
|
||||
|
||||
image = RBA::Image.new(2, 3, [])
|
||||
assert_equal(image.to_s, "mono:matrix=(1,0,0) (0,1,0) (0,0,1);min_value=0;max_value=1;is_visible=true;z_position=0;brightness=0;contrast=0;gamma=1;red_gain=1;green_gain=1;blue_gain=1;color_mapping=[0,'#000000';1,'#ffffff';];width=2;height=3;data=[0;0;0;0;0;0;]")
|
||||
|
||||
image2 = RBA::Image::from_s(image.to_s)
|
||||
assert_equal(image2.to_s, image.to_s)
|
||||
|
||||
image = RBA::Image.new(2, 3, [], [], [])
|
||||
assert_equal(image.to_s, "color:matrix=(1,0,0) (0,1,0) (0,0,1);min_value=0;max_value=1;is_visible=true;z_position=0;brightness=0;contrast=0;gamma=1;red_gain=1;green_gain=1;blue_gain=1;color_mapping=[0,'#000000';1,'#ffffff';];width=2;height=3;data=[0,0,0;0,0,0;0,0,0;0,0,0;0,0,0;0,0,0;]")
|
||||
|
||||
image2 = RBA::Image::from_s(image.to_s)
|
||||
assert_equal(image2.to_s, image.to_s)
|
||||
|
||||
data = [0.0, 0.5, 1.5, 2.5, 10, 20]
|
||||
image = RBA::Image.new(2, 3, data)
|
||||
assert_equal(image.to_s, "mono:matrix=(1,0,0) (0,1,0) (0,0,1);min_value=0;max_value=1;is_visible=true;z_position=0;brightness=0;contrast=0;gamma=1;red_gain=1;green_gain=1;blue_gain=1;color_mapping=[0,'#000000';1,'#ffffff';];width=2;height=3;data=[0;0.5;1.5;2.5;10;20;]")
|
||||
|
|
@ -164,18 +190,18 @@ class IMG_TestClass < TestBase
|
|||
|
||||
assert_equal(image.filename, "")
|
||||
|
||||
if false
|
||||
image = RBA::Image.new("/home/matthias/a.png")
|
||||
assert_equal(image.trans.to_s, "r0 *1 0,0")
|
||||
assert_equal(image.width, 728)
|
||||
assert_equal(image.height, 762)
|
||||
assert_equal(image.filename, "/home/matthias/a.png")
|
||||
|
||||
image = RBA::Image.new("/home/matthias/a.png", t)
|
||||
assert_equal(image.trans.to_s, "r90 *2.5 1,5")
|
||||
assert_equal(image.width, 728)
|
||||
assert_equal(image.height, 762)
|
||||
end
|
||||
fn = ENV["TESTSRC"] + "/testdata/img/gs.png"
|
||||
|
||||
image = RBA::Image.new(fn)
|
||||
assert_equal(image.trans.to_s, "r0 *1 -513.5,-349")
|
||||
assert_equal(image.width, 1027)
|
||||
assert_equal(image.height, 698)
|
||||
assert_equal(image.filename, fn)
|
||||
|
||||
image = RBA::Image.new(fn, t)
|
||||
assert_equal(image.trans.to_s, "r90 *1 873.5,-1278.75")
|
||||
assert_equal(image.width, 1027)
|
||||
assert_equal(image.height, 698)
|
||||
|
||||
image.min_value = -12.5
|
||||
assert_equal(image.min_value, -12.5)
|
||||
|
|
@ -370,6 +396,25 @@ class IMG_TestClass < TestBase
|
|||
|
||||
end
|
||||
|
||||
def test_4
|
||||
|
||||
tmp = File::join($ut_testtmp, "tmp.lyimg")
|
||||
|
||||
t = RBA::DCplxTrans.new(2.5, 90, false, RBA::DPoint.new(1, 5))
|
||||
|
||||
image = RBA::Image.new(2, 3, t, [1,2,3,4,5,6])
|
||||
assert_equal(image.to_s, "mono:matrix=(0,-2.5,-2.75) (2.5,0,7.5) (0,0,1);min_value=0;max_value=1;is_visible=true;z_position=0;brightness=0;contrast=0;gamma=1;red_gain=1;green_gain=1;blue_gain=1;color_mapping=[0,'#000000';1,'#ffffff';];width=2;height=3;data=[1;2;3;4;5;6;]")
|
||||
|
||||
image.write(tmp)
|
||||
|
||||
image.clear
|
||||
assert_equal(image.to_s, "mono:matrix=(0,-2.5,-2.75) (2.5,0,7.5) (0,0,1);min_value=0;max_value=1;is_visible=true;z_position=0;brightness=0;contrast=0;gamma=1;red_gain=1;green_gain=1;blue_gain=1;color_mapping=[0,'#000000';1,'#ffffff';];width=2;height=3;data=[0;0;0;0;0;0;]")
|
||||
|
||||
image2 = RBA::Image::read(tmp)
|
||||
assert_equal(image2.to_s, "mono:matrix=(0,-2.5,-2.75) (2.5,0,7.5) (0,0,1);min_value=0;max_value=1;is_visible=true;z_position=0;brightness=0;contrast=0;gamma=1;red_gain=1;green_gain=1;blue_gain=1;color_mapping=[0,'#000000';1,'#ffffff';];width=2;height=3;data=[1;2;3;4;5;6;]")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
load("test_epilogue.rb")
|
||||
|
|
|
|||
Loading…
Reference in New Issue