diff --git a/CMakeLists.txt b/CMakeLists.txt index a61b43abe4..58437a37b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -306,6 +306,10 @@ OCV_OPTION(WITH_GTK "Include GTK support" ON OCV_OPTION(WITH_GTK_2_X "Use GTK version 2" OFF VISIBLE_IF UNIX AND NOT APPLE AND NOT ANDROID VERIFY HAVE_GTK AND NOT HAVE_GTK3) +OCV_OPTION(WITH_FRAMEBUFFER "Include framebuffer support" OFF + VISIBLE_IF UNIX AND NOT APPLE AND NOT ANDROID) +OCV_OPTION(WITH_FRAMEBUFFER_XVFB "Include virtual framebuffer support" OFF + VISIBLE_IF UNIX AND NOT APPLE AND NOT ANDROID) OCV_OPTION(WITH_WAYLAND "Include Wayland support" OFF VISIBLE_IF UNIX AND NOT APPLE AND NOT ANDROID VERIFY HAVE_WAYLAND) @@ -1462,6 +1466,13 @@ if(WITH_GTK OR HAVE_GTK) endif() endif() +if(WITH_FRAMEBUFFER OR HAVE_FRAMEBUFFER) + status(" Framebuffer UI:" HAVE_FRAMEBUFFER THEN YES ELSE NO) + if(WITH_FRAMEBUFFER_XVFB OR HAVE_FRAMEBUFFER_XVFB) + status(" Virtual framebuffer UI:" HAVE_FRAMEBUFFER_XVFB THEN YES ELSE NO) + endif() +endif() + if(WITH_OPENGL OR HAVE_OPENGL) status(" OpenGL support:" HAVE_OPENGL THEN "YES (${OPENGL_LIBRARIES})" ELSE NO) endif() diff --git a/cmake/checks/framebuffer.cpp b/cmake/checks/framebuffer.cpp new file mode 100644 index 0000000000..77bb60275c --- /dev/null +++ b/cmake/checks/framebuffer.cpp @@ -0,0 +1,9 @@ +#include +#include + +int main(void) +{ + XWDFileHeader *xwd_header; + XWDColor *xwd_colors; + return 0; +} diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index d8a7bb050a..0108626dfd 100644 --- a/modules/highgui/CMakeLists.txt +++ b/modules/highgui/CMakeLists.txt @@ -49,6 +49,16 @@ list(REMOVE_ITEM highgui_ext_hdrs "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${n set(OPENCV_HIGHGUI_BUILTIN_BACKEND "") +if(WITH_FRAMEBUFFER AND HAVE_FRAMEBUFFER) + set(OPENCV_HIGHGUI_BUILTIN_BACKEND "FB") + add_definitions(-DHAVE_FRAMEBUFFER) + list(APPEND highgui_srcs ${CMAKE_CURRENT_LIST_DIR}/src/window_framebuffer.cpp) + list(APPEND highgui_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/window_framebuffer.hpp) + if(HAVE_FRAMEBUFFER_XVFB) + add_definitions(-DHAVE_FRAMEBUFFER_XVFB) + endif() +endif() + if(WITH_WAYLAND AND HAVE_WAYLAND) set(OPENCV_HIGHGUI_BUILTIN_BACKEND "Wayland") add_definitions(-DHAVE_WAYLAND) diff --git a/modules/highgui/cmake/detect_framebuffer.cmake b/modules/highgui/cmake/detect_framebuffer.cmake new file mode 100644 index 0000000000..8e292f72ac --- /dev/null +++ b/modules/highgui/cmake/detect_framebuffer.cmake @@ -0,0 +1,14 @@ +# --- FB --- +set(HAVE_FRAMEBUFFER ON) +if(WITH_FRAMEBUFFER_XVFB) + try_compile(HAVE_FRAMEBUFFER_XVFB + "${CMAKE_CURRENT_BINARY_DIR}" + "${OpenCV_SOURCE_DIR}/cmake/checks/framebuffer.cpp") + if(HAVE_FRAMEBUFFER_XVFB) + message(STATUS "Check virtual framebuffer - done") + else() + message(STATUS + "Check virtual framebuffer - failed\n" + "Please install the xorg-x11-proto-devel or x11proto-dev package\n") + endif() +endif() diff --git a/modules/highgui/cmake/init.cmake b/modules/highgui/cmake/init.cmake index 49d4799b30..f52ce2a4a8 100644 --- a/modules/highgui/cmake/init.cmake +++ b/modules/highgui/cmake/init.cmake @@ -39,6 +39,8 @@ endmacro() add_backend("gtk" WITH_GTK) add_backend("win32ui" WITH_WIN32UI) add_backend("wayland" WITH_WAYLAND) +add_backend("framebuffer" WITH_FRAMEBUFFER) + # TODO cocoa # TODO qt # TODO opengl diff --git a/modules/highgui/src/backend.hpp b/modules/highgui/src/backend.hpp index 93d51da119..49878baefc 100644 --- a/modules/highgui/src/backend.hpp +++ b/modules/highgui/src/backend.hpp @@ -127,6 +127,10 @@ std::shared_ptr createUIBackendGTK(); std::shared_ptr createUIBackendQT(); #endif +#ifdef HAVE_FRAMEBUFFER +std::shared_ptr createUIBackendFramebuffer(); +#endif + #endif // BUILD_PLUGIN } // namespace highgui_backend diff --git a/modules/highgui/src/registry.impl.hpp b/modules/highgui/src/registry.impl.hpp index 23f4e9f4e1..782738a820 100644 --- a/modules/highgui/src/registry.impl.hpp +++ b/modules/highgui/src/registry.impl.hpp @@ -44,6 +44,10 @@ std::vector& getBuiltinBackendsInfo() DECLARE_DYNAMIC_BACKEND("GTK2") #endif +#ifdef HAVE_FRAMEBUFFER + DECLARE_STATIC_BACKEND("FB", createUIBackendFramebuffer) +#endif + #if 0 // TODO #ifdef HAVE_QT DECLARE_STATIC_BACKEND("QT", createUIBackendQT) diff --git a/modules/highgui/src/window_framebuffer.cpp b/modules/highgui/src/window_framebuffer.cpp new file mode 100644 index 0000000000..687bf91724 --- /dev/null +++ b/modules/highgui/src/window_framebuffer.cpp @@ -0,0 +1,798 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "precomp.hpp" + +#include "window_framebuffer.hpp" + +#include +#include +#ifdef NDEBUG +#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_DEBUG + 1 +#else +#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE + 1 +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "opencv2/imgproc.hpp" + +#ifdef HAVE_FRAMEBUFFER_XVFB +#include +#include + +#define C32INT(ptr) ((((unsigned char*)ptr)[0] << 24) | (((unsigned char*)ptr)[1] << 16) | \ + (((unsigned char*)ptr)[2] << 8) | (((unsigned char*)ptr)[3] << 0)) +#endif + + +namespace cv { +namespace highgui_backend { + +std::shared_ptr createUIBackendFramebuffer() +{ + return std::make_shared(); +} + +static std::string& getFBMode() +{ + static std::string fbModeOpenCV = + cv::utils::getConfigurationParameterString("OPENCV_HIGHGUI_FB_MODE", "FB"); + return fbModeOpenCV; +} + +static std::string& getFBFileName() +{ + static std::string fbFileNameFB = + cv::utils::getConfigurationParameterString("FRAMEBUFFER", "/dev/fb0"); + static std::string fbFileNameOpenCV = + cv::utils::getConfigurationParameterString("OPENCV_HIGHGUI_FB_DEVICE", ""); + + if (!fbFileNameOpenCV.empty()) return fbFileNameOpenCV; + return fbFileNameFB; +} + +FramebufferWindow::FramebufferWindow(FramebufferBackend &_backend, int _flags): + backend(_backend), flags(_flags) +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::FramebufferWindow()"); + FB_ID = "FramebufferWindow"; + windowRect = Rect(0,0, backend.getFBWidth(), backend.getFBHeight()); +} + +FramebufferWindow::~FramebufferWindow() +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::~FramebufferWindow()"); +} + +void FramebufferWindow::imshow(InputArray image) +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::imshow(InputArray image)"); + currentImg = image.getMat().clone(); + + CV_LOG_INFO(NULL, "UI: InputArray image: " + << cv::typeToString(image.type()) << " size " << image.size()); + + if (currentImg.empty()) + { + CV_LOG_WARNING(NULL, "UI: image is empty"); + return; + } + CV_CheckEQ(currentImg.dims, 2, "UI: dims != 2"); + + Mat img = image.getMat(); + switch (img.channels()) + { + case 1: + { + Mat tmp; + switch(img.type()) + { + case CV_8U: + tmp = img; + break; + case CV_8S: + cv::convertScaleAbs(img, tmp, 1, 127); + break; + case CV_16S: + cv::convertScaleAbs(img, tmp, 1/255., 127); + break; + case CV_16U: + cv::convertScaleAbs(img, tmp, 1/255.); + break; + case CV_32F: + case CV_64F: // assuming image has values in range [0, 1) + img.convertTo(tmp, CV_8U, 255., 0.); + break; + } + Mat rgb(img.rows, img.cols, CV_8UC3); + cvtColor(tmp, rgb, COLOR_GRAY2RGB); + img = rgb; + } + break; + case 3: + case 4: + { + Mat tmp(img.rows, img.cols, CV_8UC3); + convertToShow(img, tmp, true); + img = tmp; + } + break; + default: + CV_Error(cv::Error::StsBadArg, "Bad image: wrong number of channels"); + } + { + Mat bgra(img.rows, img.cols, CV_8UC4); + cvtColor(img, bgra, COLOR_RGB2BGRA, bgra.channels()); + img = bgra; + } + + int newWidth = windowRect.width; + int newHeight = windowRect.height; + int cntChannel = img.channels(); + cv::Size imgSize = currentImg.size(); + + if (flags & WINDOW_AUTOSIZE) + { + windowRect.width = imgSize.width; + windowRect.height = imgSize.height; + newWidth = windowRect.width; + newHeight = windowRect.height; + } + + if (flags & WINDOW_FREERATIO) + { + newWidth = windowRect.width; + newHeight = windowRect.height; + } + else //WINDOW_KEEPRATIO + { + double aspect_ratio = ((double)img.cols) / img.rows; + newWidth = windowRect.width; + newHeight = (int)(windowRect.width / aspect_ratio); + + if (newHeight > windowRect.height) + { + newWidth = (int)(windowRect.height * aspect_ratio); + newHeight = windowRect.height; + } + } + + if ((newWidth != img.cols) && (newHeight != img.rows)) + { + Mat imResize; + cv::resize(img, imResize, cv::Size(newWidth, newHeight), INTER_LINEAR); + img = imResize; + } + + CV_LOG_INFO(NULL, "UI: Formated image: " + << cv::typeToString(img.type()) << " size " << img.size()); + + if (backend.getMode() == FB_MODE_EMU) + { + CV_LOG_WARNING(NULL, "UI: FramebufferWindow::imshow is used in EMU mode"); + return; + } + + if (backend.getFBPointer() == MAP_FAILED) + { + CV_LOG_ERROR(NULL, "UI: Framebuffer is not mapped"); + return; + } + + int xOffset = backend.getFBXOffset(); + int yOffset = backend.getFBYOffset(); + int fbHeight = backend.getFBHeight(); + int fbWidth = backend.getFBWidth(); + int lineLength = backend.getFBLineLength(); + + int img_start_x; + int img_start_y; + int img_end_x; + int img_end_y; + int fb_start_x; + int fb_start_y; + + if (windowRect.y - yOffset < 0) + { + img_start_y = - (windowRect.y - yOffset); + } + else + { + img_start_y = 0; + } + if (windowRect.x - xOffset < 0) + { + img_start_x = - (windowRect.x - xOffset); + } + else + { + img_start_x = 0; + } + + if (windowRect.y + yOffset + img.rows > fbHeight) + { + img_end_y = fbHeight - windowRect.y - yOffset; + } + else + { + img_end_y = img.rows; + } + if (windowRect.x + xOffset + img.cols > fbWidth) + { + img_end_x = fbWidth - windowRect.x - xOffset; + } + else + { + img_end_x = img.cols; + } + + if (windowRect.y + yOffset >= 0) + { + fb_start_y = windowRect.y + yOffset; + } + else + { + fb_start_y = 0; + } + if (windowRect.x + xOffset >= 0) + { + fb_start_x = windowRect.x + xOffset; + } + else + { + fb_start_x = 0; + } + + for (int y = img_start_y; y < img_end_y; y++) + { + std::memcpy(backend.getFBPointer() + + (fb_start_y + y - img_start_y) * lineLength + fb_start_x * cntChannel, + img.ptr(y) + img_start_x * cntChannel, + (img_end_x - img_start_x) * cntChannel); + } +} + +double FramebufferWindow::getProperty(int /*prop*/) const +{ + CV_LOG_WARNING(NULL, "UI: getProperty (not supported)"); + return 0.0; +} + +bool FramebufferWindow::setProperty(int /*prop*/, double /*value*/) +{ + CV_LOG_WARNING(NULL, "UI: setProperty (not supported)"); + return false; +} + +void FramebufferWindow::resize(int width, int height) +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::resize(int width " + << width <<", height " << height << ")"); + + CV_Assert(width > 0); + CV_Assert(height > 0); + + if (!(flags & WINDOW_AUTOSIZE)) + { + windowRect.width = width; + windowRect.height = height; + + if (!currentImg.empty()) + { + imshow(currentImg); + } + } +} + +void FramebufferWindow::move(int x, int y) +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::move(int x " << x << ", y " << y <<")"); + + windowRect.x = x; + windowRect.y = y; + + if (!currentImg.empty()) + { + imshow(currentImg); + } +} + +Rect FramebufferWindow::getImageRect() const +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::getImageRect()"); + return windowRect; +} + +void FramebufferWindow::setTitle(const std::string& /*title*/) +{ + CV_LOG_WARNING(NULL, "UI: setTitle (not supported)"); +} + +void FramebufferWindow::setMouseCallback(MouseCallback /*onMouse*/, void* /*userdata*/) +{ + CV_LOG_WARNING(NULL, "UI: setMouseCallback (not supported)"); +} + +std::shared_ptr FramebufferWindow::createTrackbar( + const std::string& /*name*/, + int /*count*/, + TrackbarCallback /*onChange*/, + void* /*userdata*/) +{ + CV_LOG_WARNING(NULL, "UI: createTrackbar (not supported)"); + return nullptr; +} + +std::shared_ptr FramebufferWindow::findTrackbar(const std::string& /*name*/) +{ + CV_LOG_WARNING(NULL, "UI: findTrackbar (not supported)"); + return nullptr; +} + +const std::string& FramebufferWindow::getID() const +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::getID()"); + return FB_ID; +} + +bool FramebufferWindow::isActive() const +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::isActive()"); + return true; +} + +void FramebufferWindow::destroy() +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::destroy()"); +} + +int FramebufferBackend::fbOpenAndGetInfo() +{ + std::string fbFileName = getFBFileName(); + CV_LOG_INFO(NULL, "UI: FramebufferWindow::The following is used as a framebuffer file: \n" + << fbFileName); + + int fb_fd = open(fbFileName.c_str(), O_RDWR); + if (fb_fd == -1) + { + CV_LOG_ERROR(NULL, "UI: can't open framebuffer"); + return -1; + } + + if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &fixInfo)) + { + CV_LOG_ERROR(NULL, "UI: can't read fix info for framebuffer"); + return -1; + } + + if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &varInfo)) + { + CV_LOG_ERROR(NULL, "UI: can't read var info for framebuffer"); + return -1; + } + + CV_LOG_INFO(NULL, "UI: framebuffer info: \n" + << " red offset " << varInfo.red.offset << " length " << varInfo.red.length << "\n" + << " green offset " << varInfo.green.offset << " length " << varInfo.green.length << "\n" + << " blue offset " << varInfo.blue.offset << " length " << varInfo.blue.length << "\n" + << "transp offset " << varInfo.transp.offset << " length " <pixmap_format)) != ZPixmap) + { + CV_LOG_ERROR(NULL, "Unsupported pixmap format: " << xwd_header->pixmap_format); + return -1; + } + + if (xwd_header->xoffset != 0) + { + CV_LOG_ERROR(NULL, "UI: Unsupported xoffset value: " << xwd_header->xoffset ); + return -1; + } + + unsigned int r = C32INT(&(xwd_header->red_mask)); + unsigned int g = C32INT(&(xwd_header->green_mask)); + unsigned int b = C32INT(&(xwd_header->blue_mask)); + + fbWidth = C32INT(&(xwd_header->pixmap_width)); + fbHeight = C32INT(&(xwd_header->pixmap_height)); + fbXOffset = 0; + fbYOffset = 0; + fbLineLength = C32INT(&(xwd_header->bytes_per_line)); + fbBitsPerPixel = C32INT(&(xwd_header->bits_per_pixel)); + + CV_LOG_INFO(NULL, "UI: XVFB info: \n" + << " red_mask " << r << "\n" + << " green_mask " << g << "\n" + << " blue_mask " << b << "\n" + << "bits_per_pixel " << fbBitsPerPixel); + + if ((r != 16711680 ) && (g != 65280 ) && (b != 255 ) && + (fbBitsPerPixel != 32)) + { + CV_LOG_ERROR(NULL, "UI: Framebuffer format is not supported " + << "(use BGRA format with bits_per_pixel = 32)"); + return -1; + } + + xvfb_len_header = C32INT(&(xwd_header->header_size)); + xvfb_len_colors = sizeof(XWDColor) * C32INT(&(xwd_header->ncolors)); + xvfb_len_pixmap = C32INT(&(xwd_header->bytes_per_line)) * + C32INT(&(xwd_header->pixmap_height)); + + munmap(xwd_header, sizeof(XWDFileHeader)); + + fbScreenSize = xvfb_len_header + xvfb_len_colors + xvfb_len_pixmap; + xwd_header = (XWDFileHeader*) + mmap(NULL, fbScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0); + + fbPointer = (unsigned char*)xwd_header; + fbPointer_dist = xvfb_len_header + xvfb_len_colors; + +#else + CV_LOG_WARNING(NULL, "UI: To use virtual framebuffer, " + << "compile OpenCV with the WITH_FRAMEBUFFER_XVFB=ON"); +#endif + + return fb_fd; +} + +fb_var_screeninfo &FramebufferBackend::getVarInfo() +{ + return varInfo; +} + +fb_fix_screeninfo &FramebufferBackend::getFixInfo() +{ + return fixInfo; +} + +int FramebufferBackend::getFramebuffrerID() +{ + return fbID; +} + +int FramebufferBackend::getFBWidth() +{ + return fbWidth; +} + +int FramebufferBackend::getFBHeight() +{ + return fbHeight; +} + +int FramebufferBackend::getFBXOffset() +{ + return fbXOffset; +} + +int FramebufferBackend::getFBYOffset() +{ + return fbYOffset; +} + +int FramebufferBackend::getFBBitsPerPixel() +{ + return fbBitsPerPixel; +} + +int FramebufferBackend::getFBLineLength() +{ + return fbLineLength; +} + +unsigned char* FramebufferBackend::getFBPointer() +{ + return fbPointer + fbPointer_dist; +} + +Mat& FramebufferBackend::getBackgroundBuff() +{ + return backgroundBuff; +} + +OpenCVFBMode FramebufferBackend::getMode() +{ + return mode; +} + +FramebufferBackend::FramebufferBackend():mode(FB_MODE_FB), fbPointer_dist(0) +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::FramebufferBackend()"); + + std::string fbModeStr = getFBMode(); + + if (fbModeStr == "EMU") + { + mode = FB_MODE_EMU; + CV_LOG_WARNING(NULL, "UI: FramebufferWindow is trying to use EMU mode"); + } + if (fbModeStr == "FB") + { + mode = FB_MODE_FB; + CV_LOG_WARNING(NULL, "UI: FramebufferWindow is trying to use FB mode"); + } + if (fbModeStr == "XVFB") + { + mode = FB_MODE_XVFB; + CV_LOG_WARNING(NULL, "UI: FramebufferWindow is trying to use XVFB mode"); + } + + fbID = -1; + if (mode == FB_MODE_FB) + { + fbID = fbOpenAndGetInfo(); + } + if (mode == FB_MODE_XVFB) + { + fbID = XvfbOpenAndGetInfo(); + } + + CV_LOG_INFO(NULL, "UI: FramebufferWindow::fbID " << fbID); + + if (fbID == -1) + { + mode = FB_MODE_EMU; + fbWidth = 640; + fbHeight = 480; + fbXOffset = 0; + fbYOffset = 0; + fbBitsPerPixel = 0; + fbLineLength = 0; + + CV_LOG_WARNING(NULL, "UI: FramebufferWindow is used in EMU mode"); + return; + } + + CV_LOG_INFO(NULL, "UI: Framebuffer's width, height, bits per pix: " + << fbWidth << " " << fbHeight << " " << fbBitsPerPixel); + + CV_LOG_INFO(NULL, "UI: Framebuffer's offsets (x, y), line length: " + << fbXOffset << " " << fbYOffset << " " << fbLineLength); + + backgroundBuff = Mat(fbHeight, fbWidth, CV_8UC4); + int cntChannel = 4; + for (int y = fbYOffset; y < backgroundBuff.rows + fbYOffset; y++) + { + std::memcpy(backgroundBuff.ptr(y - fbYOffset), + getFBPointer() + y * fbLineLength + fbXOffset * cntChannel, + backgroundBuff.cols * cntChannel); + } +} + +FramebufferBackend::~FramebufferBackend() +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferBackend::~FramebufferBackend()"); + if(fbID == -1) return; + + if (fbPointer != MAP_FAILED) + { + int cntChannel = 4; + for (int y = fbYOffset; y < backgroundBuff.rows + fbYOffset; y++) + { + std::memcpy(getFBPointer() + y * fbLineLength + fbXOffset * cntChannel, + backgroundBuff.ptr(y - fbYOffset), + backgroundBuff.cols * cntChannel); + } + + munmap(fbPointer, fbScreenSize); + } + close(fbID); +} + +void FramebufferBackend::destroyAllWindows() { + CV_LOG_DEBUG(NULL, "UI: FramebufferBackend::destroyAllWindows()"); +} + +// namedWindow +std::shared_ptr FramebufferBackend::createWindow( + const std::string& winname, + int flags) +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferBackend::createWindow(" + << winname << ", " << flags << ")"); + return std::make_shared(*this, flags); +} + +void FramebufferBackend::initTermios(int echo, int wait) +{ + tcgetattr(0, &old); + current = old; + current.c_lflag &= ~ICANON; + current.c_lflag &= ~ISIG; + current.c_cc[VMIN] = wait; + if (echo) + { + current.c_lflag |= ECHO; + } + else + { + current.c_lflag &= ~ECHO; + } + tcsetattr(0, TCSANOW, ¤t); +} + +void FramebufferBackend::resetTermios(void) +{ + tcsetattr(0, TCSANOW, &old); +} + +int FramebufferBackend::getch_(int echo, int wait) +{ + int ch; + initTermios(echo, wait); + ch = getchar(); + if (ch < 0) + { + rewind(stdin); + } + resetTermios(); + return ch; +} + +bool FramebufferBackend::kbhit() +{ + int byteswaiting = 0; + initTermios(0, 1); + if (ioctl(0, FIONREAD, &byteswaiting) < 0) + { + CV_LOG_ERROR(NULL, "UI: Framebuffer ERR byteswaiting" ); + } + resetTermios(); + + return byteswaiting > 0; +} + +int FramebufferBackend::waitKeyEx(int delay) +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferBackend::waitKeyEx(int delay = " << delay << ")"); + + int code = -1; + + if (delay <= 0) + { + int ch = getch_(0, 1); + CV_LOG_INFO(NULL, "UI: FramebufferBackend::getch_() take value = " << (int)ch); + code = ch; + + while ((ch = getch_(0, 0)) >= 0) + { + CV_LOG_INFO(NULL, "UI: FramebufferBackend::getch_() take value = " + << (int)ch << " (additional code on )"); + code = ch; + } + } + else + { + bool f_kbhit = false; + while (!(f_kbhit = kbhit()) && (delay > 0)) + { + delay -= 1; + usleep(1000); + } + if (f_kbhit) + { + CV_LOG_INFO(NULL, "UI: FramebufferBackend kbhit is True "); + + int ch = getch_(0, 1); + CV_LOG_INFO(NULL, "UI: FramebufferBackend::getch_() take value = " << (int)ch); + code = ch; + + while ((ch = getch_(0, 0)) >= 0) + { + CV_LOG_INFO(NULL, "UI: FramebufferBackend::getch_() take value = " + << (int)ch << " (additional code on )"); + code = ch; + } + } + } + + CV_LOG_INFO(NULL, "UI: FramebufferBackend::waitKeyEx() result code = " << code); + return code; +} + +int FramebufferBackend::pollKey() +{ + CV_LOG_DEBUG(NULL, "UI: FramebufferBackend::pollKey()"); + int code = -1; + bool f_kbhit = false; + f_kbhit = kbhit(); + + if (f_kbhit) + { + CV_LOG_INFO(NULL, "UI: FramebufferBackend kbhit is True "); + + int ch = getch_(0, 1); + CV_LOG_INFO(NULL, "UI: FramebufferBackend::getch_() take value = " << (int)ch); + code = ch; + + while ((ch = getch_(0, 0)) >= 0) + { + CV_LOG_INFO(NULL, "UI: FramebufferBackend::getch_() take value = " + << (int)ch << " (additional code on )"); + code = ch; + } + } + + return code; +} + +const std::string FramebufferBackend::getName() const +{ + return "FB"; +} + +}} // cv::highgui_backend:: diff --git a/modules/highgui/src/window_framebuffer.hpp b/modules/highgui/src/window_framebuffer.hpp new file mode 100644 index 0000000000..e7bf50dff6 --- /dev/null +++ b/modules/highgui/src/window_framebuffer.hpp @@ -0,0 +1,135 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_HIGHGUI_WINDOWS_FRAMEBUFFER_HPP +#define OPENCV_HIGHGUI_WINDOWS_FRAMEBUFFER_HPP + +#include "backend.hpp" + +#include +#include + +#include + +namespace cv { +namespace highgui_backend { + +enum OpenCVFBMode{ + FB_MODE_EMU, + FB_MODE_FB, + FB_MODE_XVFB +}; + +class FramebufferBackend; +class FramebufferWindow : public UIWindow +{ + FramebufferBackend &backend; + std::string FB_ID; + Rect windowRect; + + int flags; + Mat currentImg; + +public: + FramebufferWindow(FramebufferBackend &backend, int flags); + virtual ~FramebufferWindow(); + + virtual void imshow(InputArray image) override; + + virtual double getProperty(int prop) const override; + virtual bool setProperty(int prop, double value) override; + + virtual void resize(int width, int height) override; + virtual void move(int x, int y) override; + + virtual Rect getImageRect() const override; + + virtual void setTitle(const std::string& title) override; + + virtual void setMouseCallback(MouseCallback onMouse, void* userdata /*= 0*/) override; + + virtual std::shared_ptr createTrackbar( + const std::string& name, + int count, + TrackbarCallback onChange /*= 0*/, + void* userdata /*= 0*/ + ) override; + + virtual std::shared_ptr findTrackbar(const std::string& name) override; + + virtual const std::string& getID() const override; + + virtual bool isActive() const override; + + virtual void destroy() override; +}; // FramebufferWindow + +class FramebufferBackend: public UIBackend +{ + OpenCVFBMode mode; + + struct termios old, current; + + void initTermios(int echo, int wait); + void resetTermios(void); + int getch_(int echo, int wait); + bool kbhit(); + + fb_var_screeninfo varInfo; + fb_fix_screeninfo fixInfo; + int fbWidth; + int fbHeight; + int fbXOffset; + int fbYOffset; + int fbBitsPerPixel; + int fbLineLength; + long int fbScreenSize; + unsigned char* fbPointer; + unsigned int fbPointer_dist; + Mat backgroundBuff; + + int fbOpenAndGetInfo(); + int fbID; + + unsigned int xvfb_len_header; + unsigned int xvfb_len_colors; + unsigned int xvfb_len_pixmap; + int XvfbOpenAndGetInfo(); + +public: + + fb_var_screeninfo &getVarInfo(); + fb_fix_screeninfo &getFixInfo(); + int getFramebuffrerID(); + int getFBWidth(); + int getFBHeight(); + int getFBXOffset(); + int getFBYOffset(); + int getFBBitsPerPixel(); + int getFBLineLength(); + unsigned char* getFBPointer(); + Mat& getBackgroundBuff(); + OpenCVFBMode getMode(); + + FramebufferBackend(); + + virtual ~FramebufferBackend(); + + virtual void destroyAllWindows()override; + + // namedWindow + virtual std::shared_ptr createWindow( + const std::string& winname, + int flags + )override; + + virtual int waitKeyEx(int delay /*= 0*/)override; + virtual int pollKey() override; + + virtual const std::string getName() const override; +}; + +}} // cv::highgui_backend:: + +#endif diff --git a/modules/highgui/test/test_gui.cpp b/modules/highgui/test/test_gui.cpp index 5b72545faf..b8093e07ec 100644 --- a/modules/highgui/test/test_gui.cpp +++ b/modules/highgui/test/test_gui.cpp @@ -148,7 +148,8 @@ static void Foo(int, void* counter) && !defined HAVE_WIN32UI \ && !defined HAVE_WAYLAND \ ) \ - || defined(__APPLE__) // test fails on Mac (cocoa) + || defined(__APPLE__) /* test fails on Mac (cocoa) */ \ + || defined HAVE_FRAMEBUFFER /* trackbar is not supported */ TEST(Highgui_GUI, DISABLED_trackbar_unsafe) #else TEST(Highgui_GUI, trackbar_unsafe) @@ -188,7 +189,8 @@ void testTrackbarCallback(int pos, void* param) && !defined HAVE_WIN32UI \ && !defined HAVE_WAYLAND \ ) \ - || defined(__APPLE__) // test fails on Mac (cocoa) + || defined(__APPLE__) /* test fails on Mac (cocoa) */ \ + || defined HAVE_FRAMEBUFFER /* trackbar is not supported */ TEST(Highgui_GUI, DISABLED_trackbar) #else TEST(Highgui_GUI, trackbar)