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..846f7b4b6 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,8 @@ private: void validate_pixel_data () const; void allocate (bool color); void read_file (); + void create_from_qimage (const QImage &qimage); + void create_from_pixel_buffer (const tl::PixelBuffer &img); }; } 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")