diff --git a/src/img/img/gsiDeclImg.cc b/src/img/img/gsiDeclImg.cc index 174887712..724befcb5 100644 --- a/src/img/img/gsiDeclImg.cc +++ b/src/img/img/gsiDeclImg.cc @@ -1344,7 +1344,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); } } diff --git a/src/img/img/img.pro b/src/img/img/img.pro index 2f667acbb..c5337e156 100644 --- a/src/img/img/img.pro +++ b/src/img/img/img.pro @@ -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 diff --git a/src/img/img/imgObject.cc b/src/img/img/imgObject.cc index 1d3bb0386..aed1b1c4d 100644 --- a/src/img/img/imgObject.cc +++ b/src/img/img/imgObject.cc @@ -710,31 +710,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) @@ -808,32 +793,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; } @@ -1084,6 +1052,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 { diff --git a/src/img/img/imgObject.h b/src/img/img/imgObject.h index edbe85ac6..d10348204 100644 --- a/src/img/img/imgObject.h +++ b/src/img/img/imgObject.h @@ -52,6 +52,8 @@ class DataHeader; struct IMG_PUBLIC DataMapping { public: + typedef std::vector< std::pair > > 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 > > 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 &red, const std::vector &green, const std::vector &blue); + /** + * @brief Clears the pixel data (sets the values to 0) + */ + void clear (); + /** * @brief Set the transformation matrix * diff --git a/src/img/img/imgStream.cc b/src/img/img/imgStream.cc new file mode 100644 index 000000000..5b605881c --- /dev/null +++ b/src/img/img/imgStream.cc @@ -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 + +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::const_iterator begin_byte_data () const + { + return m_byte_data.begin (); + } + + std::list::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::const_iterator begin_data () const + { + return m_data.begin (); + } + + std::list::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 m_byte_data, m_data; + bool m_color; + + void init (); +}; + +template +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 (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::const_iterator s = m_byte_data.begin (); + for (size_t i = 0; i < m_height; ++i) { + string_to_pixels (img.get (), *s++, i, m_width, m_color); + } + + } else { + + std::list::const_iterator s = m_data.begin (); + for (size_t i = 0; i < m_height; ++i) { + string_to_pixels (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 +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 > &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 > &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 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 + diff --git a/src/img/img/imgStream.h b/src/img/img/imgStream.h new file mode 100644 index 000000000..e6c5dfca2 --- /dev/null +++ b/src/img/img/imgStream.h @@ -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 + diff --git a/src/img/unit_tests/imgFile.cc b/src/img/unit_tests/imgFile.cc new file mode 100644 index 000000000..7a292a9c8 --- /dev/null +++ b/src/img/unit_tests/imgFile.cc @@ -0,0 +1,291 @@ + +/* + + 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 + +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 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.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 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 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 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 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 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 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 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 read; + + { + tl::InputFile file (path); + tl::InputStream stream (file); + read.reset (img::ImageStreamer::read (stream)); + } + + EXPECT_EQ (image.to_string (), read->to_string ()); +} diff --git a/src/img/unit_tests/imgObject.cc b/src/img/unit_tests/imgObject.cc index 1786d2006..4cfa10e75 100644 --- a/src/img/unit_tests/imgObject.cc +++ b/src/img/unit_tests/imgObject.cc @@ -34,7 +34,7 @@ static img::Object from_s (const std::string &s) 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); @@ -150,7 +150,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); diff --git a/src/img/unit_tests/unit_tests.pro b/src/img/unit_tests/unit_tests.pro index fec02cec1..63793df87 100644 --- a/src/img/unit_tests/unit_tests.pro +++ b/src/img/unit_tests/unit_tests.pro @@ -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 diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index 2319008d0..16f912060 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -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 + { + std::string operator() () const + { + return tl::to_string (tr ("Range overflow on unsigned byte")); + } + }; } template 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) { diff --git a/src/tl/tl/tlString.h b/src/tl/tl/tlString.h index 521c1b353..0bdbf2792 100644 --- a/src/tl/tl/tlString.h +++ b/src/tl/tl/tlString.h @@ -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) *