diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 5402af6db..c4555ee99 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -128,6 +128,19 @@ size_t hash_value (const db::LayerProperties *l) return std::hfunc (*l); } +static bool log_equal_ext (const db::LayerProperties *lp1, const db::LayerProperties &lp2) +{ + if (lp1->log_equal (lp2)) { + return true; + } + // compare by name as fallback if one argument is named and the other is not + // (this gives a way to look up + if ((lp1->is_named () || lp2.is_named()) && ! lp1->name.empty () && ! lp2.name.empty ()) { + return lp1->name == lp2.name; + } + return false; +} + // since there already exists a "LayerProperties" object, we call this one "LayerInfo" Class decl_LayerInfo ("db", "LayerInfo", gsi::constructor ("new", &ctor_layer_info_default, @@ -194,14 +207,31 @@ Class decl_LayerInfo ("db", "LayerInfo", "\n" "This method was added in version 0.18.\n" ) + - gsi::method ("is_equivalent?", &db::LayerProperties::log_equal, gsi::arg ("b"), + gsi::method_ext ("is_equivalent?", &log_equal_ext, gsi::arg ("b"), "@brief Equivalence of two layer info objects\n" "@return True, if both are equivalent\n" "\n" - "First, layer and datatype are compared. The name is of second order and used only if no layer or datatype is given.\n" - "This is basically a weak comparison that reflects the search preferences.\n" + "First, layer and datatype are compared. The name is of second order and used only if no layer or datatype is given " + "for one of the operands.\n" + "This is basically a weak comparison that reflects the search preferences. It is the basis for \\Layout#find_layer.\n" + "Here are some examples:\n" "\n" - "This method was added in version 0.18.\n" + "@code\n" + "# no match as layer/datatypes or names differ:\n" + "RBA::LayerInfo::new(1, 17).is_equivalent?(RBA::LayerInfo::new(1, 18)) -> false\n" + "RBA::LayerInfo::new('metal1').is_equivalent?(RBA::LayerInfo::new('m1')) -> false\n" + "# exact match for numbered or named layers:\n" + "RBA::LayerInfo::new(1, 17).is_equivalent?(RBA::LayerInfo::new(1, 17)) -> true\n" + "RBA::LayerInfo::new('metal1').is_equivalent?(RBA::LayerInfo::new('metal1')) -> true\n" + "# match as names are second priority over layer/datatypes:\n" + "RBA::LayerInfo::new(1, 17, 'metal1').is_equivalent?(RBA::LayerInfo::new(1, 17, 'm1')) -> true\n" + "# match as name matching is fallback:\n" + "RBA::LayerInfo::new(1, 17, 'metal1').is_equivalent?(RBA::LayerInfo::new('metal1')) -> true\n" + "# no match as neither names or layer/datatypes match:\n" + "RBA::LayerInfo::new(1, 17, 'metal1').is_equivalent?(RBA::LayerInfo::new('m1')) -> false\n" + "@/code\n" + "\n" + "This method was added in version 0.18 and modified to compare non-named vs. named layers in version 0.28.11.\n" ) + gsi::method_ext ("hash", &hash_value, "@brief Computes a hash value\n" @@ -504,9 +534,15 @@ static tl::Variant find_layer (db::Layout *l, const db::LayerProperties &lp) // for a null layer info always return nil return tl::Variant (); } else { - // if we have a layer with the requested properties already, return this. + // if we have a layer with the requested properties already, return this one. + // first pass: exact match + int index = l->get_layer_maybe (lp); + if (index >= 0) { + return tl::Variant ((unsigned int) index); + } + // second pass: relaxed match for (db::Layout::layer_iterator li = l->begin_layers (); li != l->end_layers (); ++li) { - if ((*li).second->log_equal (lp)) { + if (log_equal_ext ((*li).second, lp)) { return tl::Variant ((*li).first); } } @@ -530,7 +566,7 @@ static tl::Variant find_layer3 (db::Layout *l, int ln, int dn, const std::string return find_layer (l, db::LayerProperties (ln, dn, name)); } -static std::vector layer_indices (const db::Layout *l) +static std::vector layer_indexes (const db::Layout *l) { std::vector layers; for (unsigned int i = 0; i < l->layers (); ++i) { @@ -1555,7 +1591,19 @@ Class decl_Layout ("db", "Layout", "If a layer with the given properties already exists, this method will return the index of that layer." "If no such layer exists, it will return nil.\n" "\n" - "This method has been introduced in version 0.23.\n" + "In contrast to \\layer, this method will also find layers matching by name only. " + "For example:\n" + "\n" + "@code\n" + "# finds layer '17/0' and 'name (17/0)':\n" + "index = layout.find_layer(RBA::LayerInfo::new(17, 0))\n" + "# finds layer 'name' (first priority), but also 'name (17/0)' (second priority):\n" + "index = layout.find_layer(RBA::LayerInfo::new('name'))\n" + "# note that this will not match layer 'name (17/0)' and create a new name-only layer:\n" + "index = layout.layer(RBA::LayerInfo::new('name'))\n" + "@/code\n" + "\n" + "This method has been introduced in version 0.23 and has been extended to name queries in version 0.28.11.\n" ) + gsi::method_ext ("find_layer", &find_layer1, gsi::arg ("name"), "@brief Finds a layer with the given name\n" @@ -1563,7 +1611,17 @@ Class decl_Layout ("db", "Layout", "If a layer with the given name already exists, this method will return the index of that layer." "If no such layer exists, it will return nil.\n" "\n" - "This method has been introduced in version 0.23.\n" + "In contrast to \\layer, this method will also find numbered layers if the name matches. " + "For example:\n" + "\n" + "@code\n" + "# finds layer 'name' (first priority), but also 'name (17/0)' (second priority):\n" + "index = layout.find_layer('name')\n" + "# note that this will not match layer 'name (17/0)' and create a new name-only layer:\n" + "index = layout.layer('name')\n" + "@/code\n" + "\n" + "This method has been introduced in version 0.23 and has been extended to name queries in version 0.28.11.\n" ) + gsi::method_ext ("find_layer", &find_layer2, gsi::arg ("layer"), gsi::arg ("datatype"), "@brief Finds a layer with the given layer and datatype number\n" @@ -1755,7 +1813,7 @@ Class decl_Layout ("db", "Layout", "\n" "@param layer_index The index of the layer to delete.\n" ) + - gsi::method_ext ("layer_indexes|#layer_indices", &layer_indices, + gsi::method_ext ("layer_indexes|#layer_indices", &layer_indexes, "@brief Gets a list of valid layer's indices\n" "This method returns an array with layer indices representing valid layers.\n" "\n" diff --git a/src/img/img/gsiDeclImg.cc b/src/img/img/gsiDeclImg.cc index 44f280da5..4a7ee22cb 100644 --- a/src/img/img/gsiDeclImg.cc +++ b/src/img/img/gsiDeclImg.cc @@ -30,6 +30,10 @@ #include "dbTilingProcessor.h" #include "layLayoutViewBase.h" +#if defined(HAVE_QT) +# include +#endif + namespace gsi { @@ -437,27 +441,22 @@ static ImageRef *new_image () return new ImageRef (); } -static ImageRef *new_image_f (const std::string &filename) -{ - return new ImageRef (img::Object (filename, db::DCplxTrans ())); -} - static ImageRef *new_image_ft (const std::string &filename, const db::DCplxTrans &trans) { return new ImageRef (img::Object (filename, trans)); } -/* -static ImageRef *new_image_whc (size_t w, size_t h, bool color) +static ImageRef *new_image_pbt (const tl::PixelBuffer &pixel_buffer, const db::DCplxTrans &trans) { - return new ImageRef (img::Object (w, h, db::DCplxTrans (), color)); + return new ImageRef (img::Object (pixel_buffer, trans)); } -static ImageRef *new_image_whtc (size_t w, size_t h, const db::DCplxTrans &trans, bool color) +#if HAVE_QT +static ImageRef *new_image_qit (const QImage &image, const db::DCplxTrans &trans) { - return new ImageRef (img::Object (w, h, trans, color)); + return new ImageRef (img::Object (image, trans)); } -*/ +#endif static ImageRef *new_image_whd (size_t w, size_t h, const std::vector &data) { @@ -576,17 +575,8 @@ gsi::Class decl_Image (decl_BasicImage, "lay", "Image", "This will create an empty image without data and no particular pixel width or related.\n" "Use the \\read_file or \\set_data methods to set image properties and pixel values.\n" ) + - gsi::constructor ("new", &gsi::new_image_f, gsi::arg ("filename"), - "@brief Constructor from a image file \n" - "\n" - "This constructor creates an image object from a file (which can have any format supported by Qt) and \n" - "a unit transformation. The image will originally be put to position 0,0 (lower left corner) and each pixel\n" - "will have a size of 1 (micron). \n" - "\n" - "@param filename The path to the image file to load.\n" - ) + - gsi::constructor ("new", &gsi::new_image_ft, gsi::arg ("filename"), gsi::arg ("trans"), - "@brief Constructor from a image file \n" + gsi::constructor ("new", &gsi::new_image_ft, gsi::arg ("filename"), gsi::arg ("trans", db::DCplxTrans (), "unity"), + "@brief Constructor from a image file\n" "\n" "This constructor creates an image object from a file (which can have any format supported by Qt) and \n" "a transformation. The image will originally be put to position 0,0 (lower left corner) and each pixel\n" @@ -595,6 +585,31 @@ gsi::Class decl_Image (decl_BasicImage, "lay", "Image", "@param filename The path to the image file to load.\n" "@param trans The transformation to apply to the image when displaying it.\n" ) + + gsi::constructor ("new", &gsi::new_image_pbt, gsi::arg ("pixels"), gsi::arg ("trans", db::DCplxTrans (), "unity"), + "@brief Constructor from a image pixel buffer\n" + "\n" + "This constructor creates an image object from a pixel buffer object. This object holds RGB or mono image data similar to " + "QImage, except it is available also when Qt is not available (e.g. inside the Python module).\n" + "\n" + "The image will originally be put to position 0,0 (lower left corner) and each pixel\n" + "will have a size of 1. The transformation describes how to transform this image into micron space.\n" + "\n" + "@param filename The path to the image file to load.\n" + "@param trans The transformation to apply to the image when displaying it.\n" + ) + +#if defined(HAVE_QT) + gsi::constructor ("new", &gsi::new_image_qit, gsi::arg ("image"), gsi::arg ("trans", db::DCplxTrans (), "unity"), + "@brief Constructor from a image pixel buffer\n" + "\n" + "This constructor creates an image object from a pixel QImage object and uses RGB or mono image data to generate the image.\n" + "\n" + "The image will originally be put to position 0,0 (lower left corner) and each pixel\n" + "will have a size of 1. The transformation describes how to transform this image into micron space.\n" + "\n" + "@param filename The path to the image file to load.\n" + "@param trans The transformation to apply to the image when displaying it.\n" + ) + +#endif gsi::constructor ("new", &gsi::new_image_whd, gsi::arg ("w"), gsi::arg ("h"), gsi::arg ("data"), "@brief Constructor for a monochrome image with the given pixel values\n" "\n" @@ -624,39 +639,6 @@ gsi::Class decl_Image (decl_BasicImage, "lay", "Image", "@param trans The transformation from pixel space to micron space\n" "@param d The data (see method description)\n" ) + - /* HINT: these declarations cannot be used currently since any array is case to the boolean color parameter - gsi::constructor ("new", &img::new_image_whc, gsi::arg ("w"), gsi::arg ("h"), gsi::arg ("color"), - "@brief Constructor for monochrome or color images with zero pixel values\n" - "\n" - "This constructor creates an image object from a data set describing one monochrome channel\n" - "or three color channels.\n" - "Each channel consists of an array of x*y values where the first \"x\" values describe the first (lowest!) row\n" - "and so on. Note, that the rows are oriented in the mathematical sense (first one is the lowest) contrary to \n" - "the common convention for image data.\n" - "The data fields can be accessed with the \"data\", \"set_data\", \"pixel\" or \"set_pixel\" methods.\n" - "Initially the pixel width and height will be 1 micron and the data range will be 0 to 1.0 (black to white level). \n" - "\n" - "@param w The width of the image\n" - "@param h The height of the image\n" - "@param color True to create a color image.\n" - ) + - gsi::constructor ("new", &img::new_image_whtc, gsi::arg ("w"), gsi::arg ("h"), gsi::arg ("trans"), gsi::arg ("color"), - "@brief Constructor for monochrome or color images with zero pixel values\n" - "\n" - "This constructor creates an image object from a data set describing one monochrome channel\n" - "or three color channels.\n" - "Each channel consists of an array of x*y values where the first \"x\" values describe the first (lowest!) row\n" - "and so on. Note, that the rows are oriented in the mathematical sense (first one is the lowest) contrary to \n" - "the common convention for image data.\n" - "The data fields can be accessed with the \"data\", \"set_data\", \"pixel\" or \"set_pixel\" methods.\n" - "Initially the pixel width and height will be 1 micron and the data range will be 0 to 1.0 (black to white level). \n" - "\n" - "@param w The width of the image\n" - "@param h The height of the image\n" - "@param trans The transformation to apply to the image when displaying it\n" - "@param color True to create a color image.\n" - ) + - */ gsi::constructor ("new", &gsi::new_image_whrgb, gsi::arg ("w"), gsi::arg ("h"), gsi::arg ("red"), gsi::arg ("green"), gsi::arg ("blue"), "@brief Constructor for a color image with the given pixel values\n" "\n" diff --git a/src/img/img/imgObject.cc b/src/img/img/imgObject.cc index e660df4ff..656dde1ec 100644 --- a/src/img/img/imgObject.cc +++ b/src/img/img/imgObject.cc @@ -851,6 +851,30 @@ Object::Object (const std::string &filename, const db::DCplxTrans &trans) m_updates_enabled = true; } +Object::Object (const tl::PixelBuffer &pixel_buffer, const db::DCplxTrans &trans) + : m_filename (""), 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 = 0; + create_from_pixel_buffer (pixel_buffer); + m_updates_enabled = true; +} + +#if defined(HAVE_QT) +Object::Object (const QImage &qimage, const db::DCplxTrans &trans) + : m_filename (""), 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 = 0; + create_from_qimage (qimage); + m_updates_enabled = true; +} +#endif + 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) { @@ -934,6 +958,31 @@ Object::Object (const std::string &filename, const db::Matrix3d &trans) m_updates_enabled = true; } +Object::Object (const tl::PixelBuffer &pixel_buffer, const db::Matrix3d &trans) + : m_filename (""), 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 = 0; + create_from_pixel_buffer (pixel_buffer); + read_file (); + m_updates_enabled = true; +} + +#if defined(HAVE_QT) +Object::Object (const QImage &qimage, const db::Matrix3d &trans) + : m_filename (""), 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 = 0; + create_from_qimage (qimage); + m_updates_enabled = true; +} +#endif + Object::Object (const img::Object &d) { m_updates_enabled = false; @@ -1566,65 +1615,7 @@ Object::read_file () #if defined(HAVE_QT) QImage qimage (tl::to_qstring (m_filename)); - - if (! qimage.isNull ()) { - - if (! m_min_value_set) { - m_min_value = 0.0; - } - - if (! m_max_value_set) { - m_max_value = 255.0; - } - - m_min_value_set = true; - m_max_value_set = true; - - size_t w = qimage.width (), h = qimage.height (); - - mp_data = new DataHeader (w, h, ! qimage.isGrayscale (), true); - mp_data->add_ref (); - - size_t i = 0; - - if (is_color ()) { - - unsigned char *red = mp_data->byte_data (0); - unsigned char *green = mp_data->byte_data (1); - unsigned char *blue = mp_data->byte_data (2); - unsigned char *msk = qimage.hasAlphaChannel () ? mp_data->set_mask () : 0; - - for (size_t y = 0; y < h; ++y) { - for (size_t x = 0; x < w; ++x) { - QRgb rgb = qimage.pixel (QPoint (int (x), int (h - y - 1))); - red[i] = qRed (rgb); - green[i] = qGreen (rgb); - blue[i] = qBlue (rgb); - if (msk) { - msk[i] = qAlpha (rgb) > 128; - } - ++i; - } - } - - } else { - - unsigned char *d = mp_data->byte_data (); - unsigned char *msk = qimage.hasAlphaChannel () ? mp_data->set_mask () : 0; - - for (size_t y = 0; y < h; ++y) { - for (size_t x = 0; x < w; ++x) { - QRgb rgb = qimage.pixel (QPoint (int (x), int (h - y - 1))); - *d++ = qGreen (rgb); - if (msk) { - msk[i] = qAlpha (rgb) > 128; - } - } - } - - } - - } + create_from_qimage (qimage); #elif defined(HAVE_PNG) @@ -1635,6 +1626,82 @@ Object::read_file () img = tl::PixelBuffer::read_png (stream); } + create_from_pixel_buffer (img); + +#else + throw tl::Exception ("No PNG support compiled in - cannot load PNG files"); +#endif +} + +#if defined(HAVE_QT) +void +Object::create_from_qimage (const QImage &qimage) +{ + if (qimage.isNull ()) { + return; + } + + if (! m_min_value_set) { + m_min_value = 0.0; + } + + if (! m_max_value_set) { + m_max_value = 255.0; + } + + m_min_value_set = true; + m_max_value_set = true; + + size_t w = qimage.width (), h = qimage.height (); + + mp_data = new DataHeader (w, h, ! qimage.isGrayscale (), true); + mp_data->add_ref (); + + size_t i = 0; + + if (is_color ()) { + + unsigned char *red = mp_data->byte_data (0); + unsigned char *green = mp_data->byte_data (1); + unsigned char *blue = mp_data->byte_data (2); + unsigned char *msk = qimage.hasAlphaChannel () ? mp_data->set_mask () : 0; + + for (size_t y = 0; y < h; ++y) { + for (size_t x = 0; x < w; ++x) { + QRgb rgb = qimage.pixel (QPoint (int (x), int (h - y - 1))); + red[i] = qRed (rgb); + green[i] = qGreen (rgb); + blue[i] = qBlue (rgb); + if (msk) { + msk[i] = qAlpha (rgb) > 128; + } + ++i; + } + } + + } else { + + unsigned char *d = mp_data->byte_data (); + unsigned char *msk = qimage.hasAlphaChannel () ? mp_data->set_mask () : 0; + + for (size_t y = 0; y < h; ++y) { + for (size_t x = 0; x < w; ++x) { + QRgb rgb = qimage.pixel (QPoint (int (x), int (h - y - 1))); + *d++ = qGreen (rgb); + if (msk) { + msk[i] = qAlpha (rgb) > 128; + } + } + } + + } + +} +#endif + +void +Object::create_from_pixel_buffer (const tl::PixelBuffer &img) +{ bool is_color = false; for (unsigned int i = 0; i < img.height () && ! is_color; ++i) { const tl::color_t *d = img.scan_line (i); @@ -1700,10 +1767,6 @@ Object::read_file () } } - -#else - throw tl::Exception ("No PNG support compiled in - cannot load PNG files"); -#endif } void diff --git a/src/img/img/imgObject.h b/src/img/img/imgObject.h index 4ebc6f61c..be81a3e54 100644 --- a/src/img/img/imgObject.h +++ b/src/img/img/imgObject.h @@ -21,7 +21,6 @@ */ - #ifndef HDR_imgObject #define HDR_imgObject @@ -34,10 +33,15 @@ #include "dbPolygon.h" #include "tlDataMapping.h" #include "tlColor.h" +#include "tlPixelBuffer.h" #include #include +#if defined(HAVE_QT) +class QImage; +#endif + namespace img { class DataHeader; @@ -293,6 +297,26 @@ public: */ Object (const std::string &filename, const db::DCplxTrans &trans); + /** + * @brief Constructor from a PixelBuffer object + * + * This constructor creates an image object from a PixelBuffer object. + * The image will originally be put to position 0, 0 (lower left corner) and each pixel + * will have a size of 1. The transformation describes how to transform this image into micron space. + */ + Object (const tl::PixelBuffer &pixel_buffer, const db::DCplxTrans &trans); + +#if defined(HAVE_QT) + /** + * @brief Constructor from a QImage object + * + * This constructor creates an image object from a QImage object. + * The image will originally be put to position 0, 0 (lower left corner) and each pixel + * will have a size of 1. The transformation describes how to transform this image into micron space. + */ + Object (const QImage &image, const db::DCplxTrans &trans); +#endif + /** * @brief Constructor for monochrome or color images with zero pixel values * @@ -418,6 +442,26 @@ public: */ Object (const std::string &filename, const db::Matrix3d &trans); + /** + * @brief Constructor from a PixelBuffer object + * + * This constructor creates an image object from a PixelBuffer object. + * The image will originally be put to position 0, 0 (lower left corner) and each pixel + * will have a size of 1. The transformation describes how to transform this image into micron space. + */ + Object (const tl::PixelBuffer &pixel_buffer, const db::Matrix3d &trans); + +#if defined(HAVE_QT) + /** + * @brief Constructor from a QImage object + * + * This constructor creates an image object from a QImage object. + * The image will originally be put to position 0, 0 (lower left corner) and each pixel + * will have a size of 1. The transformation describes how to transform this image into micron space. + */ + Object (const QImage &image, const db::Matrix3d &trans); +#endif + /** * @brief Copy constructor */ @@ -984,6 +1028,10 @@ private: void validate_pixel_data () const; void allocate (bool color); void read_file (); +#if defined(HAVE_QT) + void create_from_qimage (const QImage &qimage); +#endif + void create_from_pixel_buffer (const tl::PixelBuffer &img); }; } diff --git a/src/layui/layui/MarkerBrowserPage.ui b/src/layui/layui/MarkerBrowserPage.ui index 07bbf0344..18afcbe7f 100644 --- a/src/layui/layui/MarkerBrowserPage.ui +++ b/src/layui/layui/MarkerBrowserPage.ui @@ -62,7 +62,7 @@ ... - + :/run_16px.png:/run_16px.png @@ -480,27 +480,6 @@ 6 - - - - Info - - - - - - - Add snapshot - - - Photo - - - - :/photo_16px.png:/photo_16px.png - - - @@ -515,40 +494,6 @@ - - - - - - - - - - - Set or reset flag - - - Flag - - - QToolButton::MenuButtonPopup - - - - - - - Important - - - Imp - - - - :/important_16px.png:/important_16px.png - - - @@ -566,6 +511,54 @@ p, li { white-space: pre-wrap; } + + + + Set or reset flag + + + Flag + + + QToolButton::MenuButtonPopup + + + + + + + Add snapshot + + + Photo + + + + :/photo_16px.png:/photo_16px.png + + + + + + + Important + + + Imp + + + + :/important_16px.png:/important_16px.png + + + + + + + + + + @@ -612,6 +605,63 @@ p, li { white-space: pre-wrap; } + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Info + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + list shapes + + + + + + diff --git a/src/layui/layui/rdbMarkerBrowser.cc b/src/layui/layui/rdbMarkerBrowser.cc index 12a2a6919..e7bc8e8e3 100644 --- a/src/layui/layui/rdbMarkerBrowser.cc +++ b/src/layui/layui/rdbMarkerBrowser.cc @@ -42,6 +42,7 @@ namespace rdb std::string cfg_rdb_context_mode ("rdb-context-mode"); std::string cfg_rdb_show_all ("rdb-show-all"); +std::string cfg_rdb_list_shapes ("rdb-list-shapes"); std::string cfg_rdb_window_state ("rdb-window-state-v2"); // v2: 0.24++ std::string cfg_rdb_window_mode ("rdb-window-mode"); std::string cfg_rdb_window_dim ("rdb-window-dim"); diff --git a/src/layui/layui/rdbMarkerBrowserDialog.cc b/src/layui/layui/rdbMarkerBrowserDialog.cc index 082a7310a..6855ddd25 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.cc +++ b/src/layui/layui/rdbMarkerBrowserDialog.cc @@ -48,6 +48,7 @@ namespace rdb extern std::string cfg_rdb_context_mode; extern std::string cfg_rdb_show_all; +extern std::string cfg_rdb_list_shapes; extern std::string cfg_rdb_window_state; extern std::string cfg_rdb_window_mode; extern std::string cfg_rdb_window_dim; @@ -445,6 +446,7 @@ MarkerBrowserDialog::configure (const std::string &name, const std::string &valu bool need_update = false; bool taken = true; bool show_all = mp_ui->browser_frame->show_all (); + bool list_shapes = mp_ui->browser_frame->list_shapes (); if (name == cfg_rdb_context_mode) { @@ -452,6 +454,10 @@ MarkerBrowserDialog::configure (const std::string &name, const std::string &valu MarkerBrowserContextModeConverter ().from_string (value, context); need_update = lay::test_and_set (m_context, context); + } else if (name == cfg_rdb_list_shapes) { + + tl::from_string (value, list_shapes); + } else if (name == cfg_rdb_show_all) { tl::from_string (value, show_all); @@ -540,6 +546,7 @@ MarkerBrowserDialog::configure (const std::string &name, const std::string &valu } mp_ui->browser_frame->show_all (show_all); + mp_ui->browser_frame->list_shapes (list_shapes); return taken; } diff --git a/src/layui/layui/rdbMarkerBrowserPage.cc b/src/layui/layui/rdbMarkerBrowserPage.cc index f81e77bd9..822d19572 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.cc +++ b/src/layui/layui/rdbMarkerBrowserPage.cc @@ -44,6 +44,7 @@ namespace rdb { extern std::string cfg_rdb_show_all; +extern std::string cfg_rdb_list_shapes; struct FlagDescriptor { @@ -1447,6 +1448,7 @@ MarkerBrowserPage::MarkerBrowserPage (QWidget * /*parent*/) m_update_needed (false), mp_database (0), m_show_all (true), + m_list_shapes (true), mp_view (0), m_cv_index (0), m_num_items (0), @@ -1504,6 +1506,8 @@ MarkerBrowserPage::MarkerBrowserPage (QWidget * /*parent*/) markers_list->header ()->setSortIndicatorShown (true); markers_list->header ()->setMinimumSectionSize (24); + list_shapes_cb->setChecked (m_list_shapes); + connect (markers_list, SIGNAL (doubleClicked (const QModelIndex &)), this, SLOT (marker_double_clicked (const QModelIndex &))); connect (dir_up_pb, SIGNAL (clicked ()), this, SLOT (dir_up_clicked ())); @@ -1519,6 +1523,7 @@ MarkerBrowserPage::MarkerBrowserPage (QWidget * /*parent*/) connect (cat_filter, SIGNAL (textEdited (const QString &)), this, SLOT (filter_changed ())); connect (cell_filter, SIGNAL (textEdited (const QString &)), this, SLOT (filter_changed ())); connect (rerun_button, SIGNAL (pressed ()), this, SLOT (rerun_button_pressed ())); + connect (list_shapes_cb, SIGNAL (clicked ()), this, SLOT (list_shapes_clicked ())); m_show_all_action = new QAction (QObject::tr ("Show All"), this); m_show_all_action->setCheckable (true); @@ -1674,7 +1679,20 @@ MarkerBrowserPage::show_all (bool f) } } -void +void +MarkerBrowserPage::list_shapes (bool f) +{ + if (f != m_list_shapes) { + + m_list_shapes = f; + list_shapes_cb->setChecked (f); + + update_info_text (); + + } +} + +void MarkerBrowserPage::set_rdb (rdb::Database *database) { if (database != mp_database) { @@ -1975,7 +1993,7 @@ MarkerBrowserPage::update_info_text () for (rdb::Values::const_iterator v = item->values ().begin (); v != item->values ().end (); ++v) { - if (v->get () != 0) { + if (v->get () != 0 && (m_list_shapes || ! v->get ()->is_shape ())) { if (v->tag_id () != 0) { const rdb::Tag &tag = mp_database->tags ().tag (v->tag_id ()); @@ -2851,7 +2869,15 @@ MarkerBrowserPage::show_all_clicked () } } -void +void +MarkerBrowserPage::list_shapes_clicked () +{ + if (mp_plugin_root) { + mp_plugin_root->config_set (cfg_rdb_list_shapes, tl::to_string (list_shapes_cb->isChecked ())); + } +} + +void MarkerBrowserPage::unwaive_all () { if (! mp_database) { diff --git a/src/layui/layui/rdbMarkerBrowserPage.h b/src/layui/layui/rdbMarkerBrowserPage.h index c2e11dd11..68832f04c 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.h +++ b/src/layui/layui/rdbMarkerBrowserPage.h @@ -103,6 +103,21 @@ public: */ void show_all (bool f); + /** + * @brief Gets a value indicating whether to list the shapes in the info panel + */ + bool list_shapes () const + { + return m_list_shapes; + } + + /** + * @brief Sets a value indicating whether to list the shapes in the info panel + * + * If this property is set to false, shapes will not be listed in the info panel. + */ + void list_shapes (bool f); + /** * @brief Update the contents * @@ -176,6 +191,7 @@ public slots: void waive (); void unwaive (); void show_all_clicked (); + void list_shapes_clicked (); void info_anchor_clicked (const QUrl &link); void filter_changed (); @@ -184,6 +200,7 @@ private: bool m_update_needed; rdb::Database *mp_database; bool m_show_all; + bool m_list_shapes; QAction *m_show_all_action; lay::LayoutViewBase *mp_view; unsigned int m_cv_index; diff --git a/src/lym/lym/lymMacro.cc b/src/lym/lym/lymMacro.cc index e6e83f4b3..ac179c0ea 100644 --- a/src/lym/lym/lymMacro.cc +++ b/src/lym/lym/lymMacro.cc @@ -227,6 +227,7 @@ void Macro::save_to (const std::string &path) void Macro::load_from (const std::string &fn) { m_format = NoFormat; + m_interpreter = None; std::pair f = format_from_filename (fn, m_interpreter, m_dsl_interpreter, m_autorun_default, m_format); if (f.first) { @@ -252,12 +253,23 @@ void Macro::load_from (const std::string &fn) tl::InputStream stream (path); tl::TextInputStream text_stream (stream); m_text = text_stream.read_all (); - sync_properties_with_text (); + + if (m_format == PlainTextWithHashAnnotationsFormat) { + sync_properties_with_text (); + } } } else { - throw tl::Exception (tl::to_string (tr ("Unable to determine format for file from suffix or format spec ")) + fn); + + if (tl::verbosity () >= 20) { + tl::log << "Loading macro from " << fn; + } + + tl::InputStream stream (fn); + tl::TextInputStream text_stream (stream); + m_text = text_stream.read_all (); + } m_modified = true; @@ -268,6 +280,7 @@ void Macro::load_from (const std::string &fn) void Macro::load_from_string (const std::string &text, const std::string &url) { m_format = NoFormat; + m_interpreter = None; if (tl::verbosity () >= 20) { tl::log << "Loading macro from " << url; @@ -294,7 +307,7 @@ void Macro::load_from_string (const std::string &text, const std::string &url) } } else { - throw tl::Exception (tl::to_string (tr ("Unable to determine format for file from suffix ")) + url); + m_text = text; } m_modified = true; diff --git a/src/lym/lym/lymMacroInterpreter.cc b/src/lym/lym/lymMacroInterpreter.cc index cbb8bb8c2..8cb7a9f34 100644 --- a/src/lym/lym/lymMacroInterpreter.cc +++ b/src/lym/lym/lymMacroInterpreter.cc @@ -51,11 +51,35 @@ MacroInterpreter::can_run (const lym::Macro *macro) return false; } +namespace +{ + +class MacroIncludeFileResolver + : public tl::IncludeFileResolver +{ +public: + MacroIncludeFileResolver () { } + + std::string get_text (const std::string &path) const + { + // Use lym::Macro to resolve texts - this strips the XML envelope. + // Intentionally not compatibility check is made to allow using any + // type of input and specifically any extension. + lym::Macro macro; + macro.load_from (path); + return macro.text (); + } +}; + +} + std::pair MacroInterpreter::include_expansion (const lym::Macro *macro) { + MacroIncludeFileResolver include_file_resolver; + std::pair res; - res.first = tl::IncludeExpander::expand (macro->path (), macro->text (), res.second).to_string (); + res.first = tl::IncludeExpander::expand (macro->path (), macro->text (), res.second, &include_file_resolver).to_string (); if (res.first != macro->path ()) { diff --git a/src/lym/unit_tests/lymBasicTests.cc b/src/lym/unit_tests/lymBasicTests.cc index 90fac66bb..458421789 100644 --- a/src/lym/unit_tests/lymBasicTests.cc +++ b/src/lym/unit_tests/lymBasicTests.cc @@ -129,6 +129,29 @@ TEST(3_RubyInclude) EXPECT_EQ (np (console.text ()), np ("An error in " + tl::testsrc () + "/testdata/lym/b_inc.rb:3\n")); } +TEST(4_RubyIncludeFromXML) +{ + tl_assert (rba::RubyInterpreter::instance () != 0); + + lym::Macro macro; + + macro.set_file_path (tl::testsrc () + "/testdata/lym/m4.rb"); + macro.set_interpreter (lym::Macro::Ruby); + macro.load (); + + TestCollectorConsole console; + rba::RubyInterpreter::instance ()->push_console (&console); + try { + EXPECT_EQ (macro.run (), 0); + rba::RubyInterpreter::instance ()->remove_console (&console); + } catch (...) { + rba::RubyInterpreter::instance ()->remove_console (&console); + throw; + } + + EXPECT_EQ (np (console.text ()), np ("An error in " + tl::testsrc () + "/testdata/lym/b_inc.lym:3\n")); +} + TEST(11_DRCBasic) { tl_assert (rba::RubyInterpreter::instance () != 0); diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc index c72efceee..1a58a5061 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc @@ -588,9 +588,6 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C error (tl::to_string (tr ("RECT routing specification not followed by coordinate list"))); } - // breaks wiring - pts.clear (); - // rect spec double x1 = get_double (); diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index 142b1d92d..788770b82 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -986,3 +986,9 @@ TEST(207_joined_paths) run_test (_this, "issue-1345", "lef:in.lef+def:in.def", "au-nojoin.oas.gz", default_options (), false); } +// issue-1432 +TEST(208_nets_and_rects) +{ + run_test (_this, "issue-1432", "map:test.map+lef:test.lef+def:test.def", "au.oas", default_options (), false); +} + diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index eab2428a0..0c38bba86 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -156,6 +156,49 @@ template <> RDB_PUBLIC std::string Value::to_display_string () const return to_string (); } +// is_shape implementations + +template <> RDB_PUBLIC bool Value::is_shape () const +{ + return false; +} + +template <> RDB_PUBLIC bool Value::is_shape () const +{ + return false; +} + +template <> RDB_PUBLIC bool Value::is_shape () const +{ + return true; +} + +template <> RDB_PUBLIC bool Value::is_shape () const +{ + return true; +} + +template <> RDB_PUBLIC bool Value::is_shape () const +{ + return true; +} + +template <> RDB_PUBLIC bool Value::is_shape () const +{ + return true; +} + +template <> RDB_PUBLIC bool Value::is_shape () const +{ + return true; +} + +template <> RDB_PUBLIC bool Value::is_shape () const +{ + return true; +} + + bool ValueBase::compare (const ValueBase *a, const ValueBase *b) { // compare is the intrinsic compare of equal type and type index for different types. diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index ac9535627..f27f257ae 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -438,6 +438,8 @@ public: virtual std::string to_display_string () const = 0; + virtual bool is_shape () const = 0; + virtual ValueBase *clone () const = 0; virtual int type_index () const = 0; @@ -508,6 +510,8 @@ public: return m_value < static_cast *> (other)->m_value; } + bool is_shape () const; + std::string to_string () const; std::string to_display_string () const; diff --git a/src/tl/tl/tlInclude.cc b/src/tl/tl/tlInclude.cc index b172d6a6b..4d2fc5a09 100644 --- a/src/tl/tl/tlInclude.cc +++ b/src/tl/tl/tlInclude.cc @@ -32,6 +32,9 @@ namespace tl { +// ----------------------------------------------------------------------------------------------------- +// IncludeExpander implementation + static const char *valid_fn_chars = "@_:,.\\/-+"; IncludeExpander::IncludeExpander () @@ -40,30 +43,30 @@ IncludeExpander::IncludeExpander () } IncludeExpander -IncludeExpander::expand (const std::string &path, std::string &expanded_text) +IncludeExpander::expand (const std::string &path, std::string &expanded_text, const IncludeFileResolver *resolver) { IncludeExpander ie; int lc = 1; tl::InputStream is (path); - ie.read (path, is, expanded_text, ie, lc); + ie.read (path, is, expanded_text, lc, resolver); return ie; } IncludeExpander -IncludeExpander::expand (const std::string &path, const std::string &original_text, std::string &expanded_text) +IncludeExpander::expand (const std::string &path, const std::string &original_text, std::string &expanded_text, const IncludeFileResolver *resolver) { IncludeExpander ie; int lc = 1; tl::InputMemoryStream ms (original_text.c_str (), original_text.size ()); tl::InputStream is (ms); - ie.read (path, is, expanded_text, ie, lc); + ie.read (path, is, expanded_text, lc, resolver); return ie; } void -IncludeExpander::read (const std::string &path, tl::InputStream &is, std::string &expanded_text, IncludeExpander &ie, int &line_counter) +IncludeExpander::read (const std::string &path, tl::InputStream &is, std::string &expanded_text, int &line_counter, const IncludeFileResolver *resolver) { - ie.m_sections [line_counter] = std::make_pair (path, 1 - line_counter); + m_sections [line_counter] = std::make_pair (path, 1 - line_counter); tl::TextInputStream text (is); @@ -100,8 +103,17 @@ IncludeExpander::read (const std::string &path, tl::InputStream &is, std::string include_path = current_uri.resolved (new_uri).to_abstract_path (); } - tl::InputStream is (include_path); - read (include_path, is, expanded_text, ie, line_counter); + std::string include_text; + if (resolver) { + include_text = resolver->get_text (include_path); + } else { + tl::InputStream iis (include_path); + include_text = iis.read_all (); + } + + tl::InputMemoryStream ms (include_text.c_str (), include_text.size ()); + tl::InputStream is (ms); + read (include_path, is, expanded_text, line_counter, resolver); emit_section = true; @@ -109,7 +121,7 @@ IncludeExpander::read (const std::string &path, tl::InputStream &is, std::string if (emit_section) { emit_section = false; - ie.m_sections [line_counter] = std::make_pair (path, lnum - line_counter); + m_sections [line_counter] = std::make_pair (path, lnum - line_counter); } expanded_text += l; diff --git a/src/tl/tl/tlInclude.h b/src/tl/tl/tlInclude.h index b465675dd..687dd950e 100644 --- a/src/tl/tl/tlInclude.h +++ b/src/tl/tl/tlInclude.h @@ -34,6 +34,21 @@ namespace tl class InputStream; +/** + * @brief An interface providing the include file resolver + * + * The task of this object is to obtain the text for an include file path. + * The path already underwent variable interpolation and relative path resolution. + */ +class TL_PUBLIC IncludeFileResolver +{ +public: + IncludeFileResolver () { } + virtual ~IncludeFileResolver () { } + + virtual std::string get_text (const std::string &path) const = 0; +}; + /** * @brief Provide the basic include expansion and file/line mapping mechanism * @@ -59,7 +74,7 @@ public: * * This method will deliver the expanded text and the include expander object. */ - static IncludeExpander expand (const std::string &path, std::string &expanded_text); + static IncludeExpander expand (const std::string &path, std::string &expanded_text, const IncludeFileResolver *resolver = 0); /** * @brief Provides include expansion @@ -67,7 +82,7 @@ public: * This method will deliver the expanded text and the include expander object. * This version also takes the actual text of the original file. */ - static IncludeExpander expand (const std::string &path, const std::string &original_text, std::string &expanded_text); + static IncludeExpander expand (const std::string &path, const std::string &original_text, std::string &expanded_text, const IncludeFileResolver *resolver = 0); /** * @brief Serializes the include expander information into a string @@ -98,7 +113,7 @@ public: private: std::map > m_sections; - void read (const std::string &path, tl::InputStream &is, std::string &expanded_text, IncludeExpander &ie, int &line_counter); + void read (const std::string &path, tl::InputStream &is, std::string &expanded_text, int &line_counter, const IncludeFileResolver *mp_resolver); }; } diff --git a/testdata/lefdef/issue-1432/au.oas b/testdata/lefdef/issue-1432/au.oas new file mode 100644 index 000000000..a0e587298 Binary files /dev/null and b/testdata/lefdef/issue-1432/au.oas differ diff --git a/testdata/lefdef/issue-1432/test.def b/testdata/lefdef/issue-1432/test.def new file mode 100644 index 000000000..6c27d93d2 --- /dev/null +++ b/testdata/lefdef/issue-1432/test.def @@ -0,0 +1,8 @@ +VERSION 5.8 ; +DESIGN test ; +UNITS DISTANCE MICRONS 1000 ; +DIEAREA ( -1 -1 ) ( 8 8 ) ; +NETS 1 ; +- dummy + ROUTED M1 ( 0 0 ) ( 5 0 ) VIRTUAL ( 7 1 ) RECT ( -3 0 -1 2 ) ( 7 7 ) ; +END NETS +END DESIGN diff --git a/testdata/lefdef/issue-1432/test.lef b/testdata/lefdef/issue-1432/test.lef new file mode 100644 index 000000000..3a62839f8 --- /dev/null +++ b/testdata/lefdef/issue-1432/test.lef @@ -0,0 +1,11 @@ +UNITS + DATABASE MICRONS 1000 ; +END UNITS + +MANUFACTURINGGRID 0.001 ; + +LAYER M1 + TYPE ROUTING ; + WIDTH 0.002 ; +END M1 + diff --git a/testdata/lefdef/issue-1432/test.map b/testdata/lefdef/issue-1432/test.map new file mode 100644 index 000000000..769acbcb4 --- /dev/null +++ b/testdata/lefdef/issue-1432/test.map @@ -0,0 +1,2 @@ +DIEAREA ALL 108 0 +M1 NET 31 0 diff --git a/testdata/lym/b_inc.lym b/testdata/lym/b_inc.lym new file mode 100644 index 000000000..fae9e4889 --- /dev/null +++ b/testdata/lym/b_inc.lym @@ -0,0 +1,23 @@ + + + + + + + + + false + false + 0 + + false + + + ruby + + +def f + raise("An error") +end + + diff --git a/testdata/lym/m4.rb b/testdata/lym/m4.rb new file mode 100644 index 000000000..a78adf610 --- /dev/null +++ b/testdata/lym/m4.rb @@ -0,0 +1,12 @@ + +# %include b_inc.lym + +begin + puts f +rescue => ex + ln = ex.backtrace[0].split(":") + # NOTE: as the backtrace is a native Ruby feature, include file translation + # does not happen. We need to do this explicitly here: + puts ex.to_s + " in " + RBA::Macro::real_path(ln[0], ln[1].to_i) + ":" + RBA::Macro::real_line(ln[0], ln[1].to_i).to_s +end + diff --git a/testdata/ruby/dbLayoutTests1.rb b/testdata/ruby/dbLayoutTests1.rb index fa7c0ebe6..284e1fea4 100644 --- a/testdata/ruby/dbLayoutTests1.rb +++ b/testdata/ruby/dbLayoutTests1.rb @@ -64,16 +64,38 @@ class DBLayoutTests1_TestClass < TestBase assert_equal(l3.anonymous?, false) assert_equal(l3.is_named?, true) assert_equal(l3.is_equivalent?(l2), false) - l3.layer = 1 - l3.datatype = 100 - assert_equal(l3.is_named?, false) - assert_equal(l3.is_equivalent?(l2), true) + assert_equal(l2.is_equivalent?(l3), false) + l4 = RBA::LayerInfo::new(1, 100, "aber") + assert_equal(l4.to_s, "aber (1/100)") + assert_equal(l4.anonymous?, false) + assert_equal(l4.is_named?, false) + assert_equal(l4.is_equivalent?(l2), true) + assert_equal(l2.is_equivalent?(l4), true) + assert_equal(l4.is_equivalent?(l3), true) + assert_equal(l3.is_equivalent?(l4), true) + assert_equal(l4.is_equivalent?(RBA::LayerInfo::new(1, 100, "xyz")), true) + assert_equal(l4.is_equivalent?(RBA::LayerInfo::new(1, 101, "aber")), false) + + l1.assign(l4) - l1.assign(l3) assert_equal(l1.to_s, "aber (1/100)") assert_equal(l1.is_named?, false) - assert_equal(l1.is_equivalent?(l3), true) - assert_equal(l1 == l3, true) + assert_equal(l1.is_equivalent?(l4), true) + assert_equal(l1 == l4, true) + + l1.layer = -1 + l1.datatype = -1 + assert_equal(l1.is_named?, true) + assert_equal(l1.to_s, "aber") + + l1.name = "xyz" + assert_equal(l1.is_named?, true) + assert_equal(l1.to_s, "xyz") + + l1.layer = 100 + l1.datatype = 0 + assert_equal(l1.is_named?, false) + assert_equal(l1.to_s, "xyz (100/0)") end @@ -164,16 +186,50 @@ class DBLayoutTests1_TestClass < TestBase assert_equal(a, nil) a = ly.find_layer(RBA::LayerInfo.new(2, 0)) assert_equal(a, li) + a = ly.find_layer(3, 0, "hallo") + assert_equal(a, nil) li2 = ly.layer("hallo") a = ly.find_layer("hillo") assert_equal(a, nil) a = ly.find_layer("hallo") assert_equal(a, li2) a = ly.find_layer(3, 0, "hallo") - assert_equal(a, nil) + assert_equal(a, li2) a = ly.find_layer(2, 0, "hallo") assert_equal(a, li) + ly = RBA::Layout.new + li = ly.layer(2, 0, "hallo") + a = ly.find_layer(3, 0) + assert_equal(a, nil) + a = ly.find_layer(2, 0) + assert_equal(a, li) + a = ly.find_layer("hallo") + assert_equal(a, li) + a = ly.find_layer(2, 0, "hillo") + assert_equal(a, li) + a = ly.find_layer(1, 0, "hallo") + assert_equal(a, nil) + + ly = RBA::Layout.new + li0 = ly.layer("hello") + li = ly.layer("hallo") + a = ly.find_layer(3, 0) + assert_equal(a, nil) + a = ly.find_layer(2, 0) + assert_equal(a, nil) + a = ly.find_layer("hallo") + assert_equal(a, li) + a = ly.find_layer(2, 0, "hillo") + assert_equal(a, nil) + a = ly.find_layer(1, 0, "hallo") + assert_equal(a, li) + li2 = ly.layer(1, 0, "hello") + a = ly.find_layer(1, 0, "hello") + assert_equal(a, li2) + a = ly.find_layer("hello") + assert_equal(a, li0) + end def collect(s, l) diff --git a/testdata/ruby/imgObject.rb b/testdata/ruby/imgObject.rb index c1986b28c..df598f5f1 100644 --- a/testdata/ruby/imgObject.rb +++ b/testdata/ruby/imgObject.rb @@ -415,6 +415,38 @@ class IMG_TestClass < TestBase end + # Construction from PixelBuffer + def test_6 + + fn = ENV["TESTSRC"] + "/testdata/img/gs.png" + pb = RBA::PixelBuffer::read_png(fn) + + image = RBA::Image.new(pb) + assert_equal(image.trans.to_s, "r0 *1 -513.5,-349") + + assert_equal(image.width, 1027) + assert_equal(image.height, 698) + + end + + # Construction from QImage + def test_7 + + if RBA.constants.find { |x| x == :QImage } + + fn = ENV["TESTSRC"] + "/testdata/img/gs.png" + qimage = RBA::QImage::new(fn) + + image = RBA::Image.new(qimage) + assert_equal(image.trans.to_s, "r0 *1 -513.5,-349") + + assert_equal(image.width, 1027) + assert_equal(image.height, 698) + + end + + end + end load("test_epilogue.rb")