diff --git a/modules/highgui/include/opencv2/highgui.hpp b/modules/highgui/include/opencv2/highgui.hpp index 8175bd0871..35b64bceae 100644 --- a/modules/highgui/include/opencv2/highgui.hpp +++ b/modules/highgui/include/opencv2/highgui.hpp @@ -366,6 +366,11 @@ for image display). **waitKey(25)** will display a frame and wait approximately press (suitable for displaying a video frame-by-frame). To remove the window, use cv::destroyWindow. @note [__Windows Backend Only__] Pressing Ctrl+C will copy the image to the clipboard. Pressing Ctrl+S will show a dialog to save the image. +@note [__Wayland Backend Only__] Supoorting format is extended. +- If the image is 8-bit signed, the pixels are biased by 128. That is, the + value range [-128,127] is mapped to [0,255]. +- If the image is 16-bit signed, the pixels are divided by 256 and biased by 128. That is, the + value range [-32768,32767] is mapped to [0,255]. @param winname Name of the window. @param mat Image to be shown. @@ -394,6 +399,8 @@ CV_EXPORTS_W void resizeWindow(const String& winname, const cv::Size& size); @param winname Name of the window. @param x The new x-coordinate of the window. @param y The new y-coordinate of the window. + +@note [__Wayland Backend Only__] This function is not supported by the Wayland protocol limitation. */ CV_EXPORTS_W void moveWindow(const String& winname, int x, int y); @@ -404,6 +411,8 @@ The function setWindowProperty enables changing properties of a window. @param winname Name of the window. @param prop_id Window property to edit. The supported operation flags are: (cv::WindowPropertyFlags) @param prop_value New value of the window property. The supported flags are: (cv::WindowFlags) + +@note [__Wayland Backend Only__] This function is not supported. */ CV_EXPORTS_W void setWindowProperty(const String& winname, int prop_id, double prop_value); @@ -421,6 +430,8 @@ The function getWindowProperty returns properties of a window. @param prop_id Window property to retrieve. The following operation flags are available: (cv::WindowPropertyFlags) @sa setWindowProperty + +@note [__Wayland Backend Only__] This function is not supported. */ CV_EXPORTS_W double getWindowProperty(const String& winname, int prop_id); @@ -431,6 +442,8 @@ The function getWindowImageRect returns the client screen coordinates, width and @param winname Name of the window. @sa resizeWindow moveWindow + +@note [__Wayland Backend Only__] This function is not supported by the Wayland protocol limitation. */ CV_EXPORTS_W Rect getWindowImageRect(const String& winname); diff --git a/modules/highgui/src/precomp.hpp b/modules/highgui/src/precomp.hpp index 9f0ea59b60..2bbfd6c14a 100644 --- a/modules/highgui/src/precomp.hpp +++ b/modules/highgui/src/precomp.hpp @@ -98,6 +98,7 @@ void cvSetModeWindow_WinRT(const char* name, double prop_value); CvRect cvGetWindowRect_W32(const char* name); CvRect cvGetWindowRect_GTK(const char* name); CvRect cvGetWindowRect_COCOA(const char* name); +CvRect cvGetWindowRect_WAYLAND(const char* name); double cvGetModeWindow_W32(const char* name); double cvGetModeWindow_GTK(const char* name); diff --git a/modules/highgui/src/window.cpp b/modules/highgui/src/window.cpp index a41e2313c8..e6972b300b 100644 --- a/modules/highgui/src/window.cpp +++ b/modules/highgui/src/window.cpp @@ -436,6 +436,8 @@ cv::Rect cv::getWindowImageRect(const String& winname) return cvGetWindowRect_GTK(winname.c_str()); #elif defined (HAVE_COCOA) return cvGetWindowRect_COCOA(winname.c_str()); + #elif defined (HAVE_WAYLAND) + return cvGetWindowRect_WAYLAND(winname.c_str()); #else return Rect(-1, -1, -1, -1); #endif diff --git a/modules/highgui/src/window_wayland.cpp b/modules/highgui/src/window_wayland.cpp index 44f30bd0be..ee1d50207d 100644 --- a/modules/highgui/src/window_wayland.cpp +++ b/modules/highgui/src/window_wayland.cpp @@ -84,20 +84,73 @@ static int xkb_keysym_to_ascii(xkb_keysym_t keysym) { return static_cast(keysym & 0xff); } +static void write_mat_to_xrgb8888(cv::Mat const &img_, void *data) { + // Validate destination data. + CV_CheckFalse((data == nullptr), "Destination Address must not be nullptr."); -static void draw_xrgb8888(void *d, uint8_t a, uint8_t r, uint8_t g, uint8_t b) { - *((uint32_t *) d) = ((a << 24) | (r << 16) | (g << 8) | b); -} + // Validate source img parameters. + CV_CheckFalse(img_.empty(), "Source Mat must not be empty."); + const int ncn = img_.channels(); + CV_CheckType(img_.type(), + ( (ncn == 1) || (ncn == 3) || (ncn == 4)), + "Unsupported channels, please convert to 1, 3 or 4 channels" + ); -static void write_mat_to_xrgb8888(cv::Mat const &img, void *data) { - CV_Assert(data != nullptr); - CV_Assert(img.isContinuous()); + // The supported Mat depth is according to imshow() specification. + const int depth = CV_MAT_DEPTH(img_.type()); + CV_CheckDepth(img_.type(), + ( (depth == CV_8U) || (depth == CV_8S) || + (depth == CV_16U) || (depth == CV_16S) || + (depth == CV_32F) || (depth == CV_64F) ), + "Unsupported depth, please convert to CV_8U" + ); - for (int y = 0; y < img.rows; y++) { - for (int x = 0; x < img.cols; x++) { - auto p = img.at(y, x); - draw_xrgb8888((char *) data + (y * img.cols + x) * 4, 0x00, p[2], p[1], p[0]); - } + // Convert to CV_8U + cv::Mat img; + const int mtype = CV_MAKE_TYPE(CV_8U, ncn); + switch(CV_MAT_DEPTH(depth)) + { + case CV_8U: + img = img_; // do nothing. + break; + case CV_8S: + // [-128,127] -> [0,255] + img_.convertTo(img, mtype, 1.0, 128); + break; + case CV_16U: + // [0,65535] -> [0,255] + img_.convertTo(img, mtype, 1.0/255. ); + break; + case CV_16S: + // [-32768,32767] -> [0,255] + img_.convertTo(img, mtype, 1.0/255. , 128); + break; + case CV_32F: + case CV_64F: + // [0, 1] -> [0,255] + img_.convertTo(img, mtype, 255.); + break; + default: + // it cannot be reachable. + break; + } + CV_CheckDepthEQ(CV_MAT_DEPTH(img.type()), CV_8U, "img should be CV_8U"); + + // XRGB8888 in Little Endian(Wayland Request) = [B8:G8:R8:X8] in data array. + // X is not used to show. So we can use cvtColor() with GRAY2BGRA or BGR2BGRA or copyTo(). + cv::Mat dst(img.size(), CV_MAKE_TYPE(CV_8U, 4), (uint8_t*)data); + if(ncn == 1) + { + cvtColor(img, dst, cv::COLOR_GRAY2BGRA); + } + else if(ncn == 3) + { + cvtColor(img, dst, cv::COLOR_BGR2BGRA); + } + else + { + CV_CheckTrue(ncn==4, "Unexpected channels"); + img.copyTo(dst); } } @@ -232,10 +285,10 @@ private: &handle_pointer_axis, &handle_pointer_frame, &handle_pointer_axis_source, &handle_pointer_axis_stop, &handle_pointer_axis_discrete, -#if WL_POINTER_AXIS_VALUE120_SINCE_VERSION >= 8 +#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION &handle_axis_value120, #endif -#if WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION >= 9 +#ifdef WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION &handle_axis_relative_direction, #endif }; @@ -284,7 +337,7 @@ private: CV_UNUSED(discrete); } -#if WL_POINTER_AXIS_VALUE120_SINCE_VERSION >= 8 +#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION static void handle_axis_value120(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120) { CV_UNUSED(data); @@ -294,7 +347,7 @@ private: } #endif -#if WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION >= 9 +#ifdef WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION static void handle_axis_relative_direction(void *data, struct wl_pointer *wl_pointer, uint32_t axis, uint32_t direction) { CV_UNUSED(data); @@ -686,7 +739,7 @@ public: void show_image(cv::Mat const &image); - void create_trackbar(std::string const &name, int *value, int count, CvTrackbarCallback2 on_change, void *userdata); + int create_trackbar(std::string const &name, int *value, int count, CvTrackbarCallback2 on_change, void *userdata); weak_ptr get_trackbar(std::string const &) const; @@ -723,10 +776,10 @@ private: struct xdg_toplevel *xdg_toplevel_; struct xdg_toplevel_listener xdgtop_listener_{ &handle_toplevel_configure, &handle_toplevel_close, -#if XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION >= 4 +#ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION &handle_toplevel_configure_bounds, #endif -#if XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION >= 5 +#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION &handle_toplevel_wm_capabilities, #endif }; @@ -775,7 +828,7 @@ private: static void handle_toplevel_close(void *data, struct xdg_toplevel *surface); -#if XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION >= 4 +#ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION static void handle_toplevel_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) { @@ -786,7 +839,7 @@ private: } #endif -#if XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION >= 5 +#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION static void handle_toplevel_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, struct wl_array *capabilities) { @@ -1527,13 +1580,7 @@ cv_wl_viewer::cv_wl_viewer(cv_wl_window *window, int flags) } void cv_wl_viewer::set_image(cv::Mat const &image) { - if (image.type() == CV_8UC1) { - cv::Mat bgr; - cv::cvtColor(image, bgr, CV_GRAY2BGR); - image_ = bgr.clone(); - } else { - image_ = image.clone(); - } + image_ = image.clone(); image_changed_ = true; } @@ -1642,6 +1689,11 @@ cv_wl_trackbar::cv_wl_trackbar(cv_wl_window *window, std::string name, on_change_.value = value; on_change_.data = data; on_change_.callback = on_change; + + // initilize slider_.value if value is not nullptr. + if (value != nullptr){ + set_pos(*value); + } } std::string const &cv_wl_trackbar::name() const { @@ -1657,6 +1709,12 @@ void cv_wl_trackbar::set_pos(int value) { slider_.value = value; slider_moved_ = true; window_->show(); + + // Update user-ptr value and call on_change() function if cv_wl_trackbar::draw() is not called. + if(slider_moved_) { + on_change_.update(slider_.value); + on_change_.call(slider_.value); + } } } @@ -1666,6 +1724,12 @@ void cv_wl_trackbar::set_max(int maxval) { slider_.value = maxval; slider_moved_ = true; window_->show(); + + // Update user-ptr and call on_change() function if cv_wl_trackbar::draw() is not called. + if(slider_moved_) { + on_change_.update(slider_.value); + on_change_.call(slider_.value); + } } } @@ -1836,8 +1900,9 @@ void cv_wl_window::show_image(cv::Mat const &image) { this->show(); } -void cv_wl_window::create_trackbar(std::string const &name, int *value, int count, CvTrackbarCallback2 on_change, +int cv_wl_window::create_trackbar(std::string const &name, int *value, int count, CvTrackbarCallback2 on_change, void *userdata) { + int ret = 0; auto exists = this->get_trackbar(name).lock(); if (!exists) { auto trackbar = @@ -1846,7 +1911,9 @@ void cv_wl_window::create_trackbar(std::string const &name, int *value, int coun ); widgets_.emplace_back(trackbar); widget_geometries_.emplace_back(0, 0, 0, 0); + ret = 1; } + return ret; } weak_ptr cv_wl_window::get_trackbar(std::string const &trackbar_name) const { @@ -2324,6 +2391,7 @@ std::string const &cv_wl_core::get_window_name(void *handle) { } bool cv_wl_core::create_window(std::string const &name, int flags) { + CV_CheckTrue(display_ != nullptr, "Display is not connected."); auto window = std::make_shared(display_, name, flags); auto result = windows_.insert(std::make_pair(name, window)); handles_[window.get()] = window->get_title(); @@ -2402,6 +2470,13 @@ CV_IMPL void cvResizeWindow(const char *name, int width, int height) { throw_system_error("Could not get window name", errno) } +CvRect cvGetWindowRect_WAYLAND(const char* name) +{ + CV_UNUSED(name); + CV_LOG_ONCE_WARNING(nullptr, "Function not implemented: User cannot get window rect in Wayland"); + return cvRect(-1, -1, -1, -1); +} + CV_IMPL int cvCreateTrackbar(const char *name_bar, const char *window_name, int *value, int count, CvTrackbarCallback on_change) { CV_UNUSED(name_bar); @@ -2416,10 +2491,11 @@ CV_IMPL int cvCreateTrackbar(const char *name_bar, const char *window_name, int CV_IMPL int cvCreateTrackbar2(const char *trackbar_name, const char *window_name, int *val, int count, CvTrackbarCallback2 on_notify, void *userdata) { + int ret = 0; if (auto window = CvWlCore::getInstance().get_window(window_name)) - window->create_trackbar(trackbar_name, val, count, on_notify, userdata); + ret = window->create_trackbar(trackbar_name, val, count, on_notify, userdata); - return 0; + return ret; } CV_IMPL int cvGetTrackbarPos(const char *trackbar_name, const char *window_name) { diff --git a/modules/highgui/test/test_gui.cpp b/modules/highgui/test/test_gui.cpp index 8991e8072b..41304ddf43 100644 --- a/modules/highgui/test/test_gui.cpp +++ b/modules/highgui/test/test_gui.cpp @@ -48,6 +48,16 @@ inline void verify_size(const std::string &nm, const cv::Mat &img) { EXPECT_NO_THROW(imshow(nm, img)); EXPECT_EQ(-1, waitKey(200)); + + // see https://github.com/opencv/opencv/issues/25550 + // Wayland backend is not supported getWindowImageRect(). + string framework; + EXPECT_NO_THROW(framework = currentUIFramework()); + if(framework == "WAYLAND") + { + return; + } + Rect rc; EXPECT_NO_THROW(rc = getWindowImageRect(nm)); EXPECT_EQ(rc.size(), img.size());