CMAKE_FLAGS "-DLINK_LIBRARIES:STRING=user32;gdi32")
endif()
-# --- QT4 ---
+# --- QT4/5 ---
ocv_clear_vars(HAVE_QT HAVE_QT5)
if(WITH_QT)
if(NOT WITH_QT EQUAL 4)
endif()
endif()
-# --- GTK ---
-ocv_clear_vars(HAVE_GTK HAVE_GTK3 HAVE_GTHREAD HAVE_GTKGLEXT)
-if(WITH_GTK AND NOT HAVE_QT)
- if(NOT WITH_GTK_2_X)
- ocv_check_modules(GTK3 gtk+-3.0)
- if(HAVE_GTK3)
- ocv_append_build_options(HIGHGUI GTK3)
- set(HAVE_GTK TRUE)
- endif()
- endif()
- if(NOT HAVE_GTK)
- ocv_check_modules(GTK2 gtk+-2.0)
- if(HAVE_GTK2)
- if (GTK2_VERSION VERSION_LESS MIN_VER_GTK)
- message (FATAL_ERROR "GTK support requires a minimum version of ${MIN_VER_GTK} (${GTK2_VERSION} found)")
- else()
- ocv_append_build_options(HIGHGUI GTK2)
- set(HAVE_GTK TRUE)
- endif()
- endif()
- endif()
- ocv_check_modules(GTHREAD gthread-2.0)
- if(HAVE_GTK AND NOT HAVE_GTHREAD)
- message(FATAL_ERROR "gthread not found. This library is required when building with GTK support")
- else()
- ocv_append_build_options(HIGHGUI GTHREAD)
- endif()
- if(WITH_OPENGL AND NOT HAVE_GTK3)
- ocv_check_modules(GTKGLEXT gtkglext-1.0)
- if(HAVE_GTKGLEXT)
- ocv_append_build_options(HIGHGUI GTKGLEXT)
- endif()
- endif()
-endif()
-
# --- OpenGl ---
ocv_clear_vars(HAVE_OPENGL HAVE_QT_OPENGL)
if(WITH_OPENGL)
set(MIN_VER_PYTHON2 2.7)
set(MIN_VER_PYTHON3 3.2)
set(MIN_VER_ZLIB 1.2.3)
-set(MIN_VER_GTK 2.18.0)
if(TARGET opencv_videoio_plugins)
add_dependencies(${the_target} opencv_videoio_plugins)
endif()
+ if(TARGET opencv_highgui_plugins)
+ add_dependencies(${the_target} opencv_highgui_plugins)
+ endif()
if(HAVE_HPX)
message("Linking HPX to Perf test of module ${name}")
if(TARGET opencv_videoio_plugins)
add_dependencies(${the_target} opencv_videoio_plugins)
endif()
+ if(TARGET opencv_highgui_plugins)
+ add_dependencies(${the_target} opencv_highgui_plugins)
+ endif()
if(HAVE_HPX)
message("Linking HPX to Perf test of module ${name}")
if(TARGET opencv_videoio_plugins)
add_dependencies(${the_target} opencv_videoio_plugins)
endif()
+ if(TARGET opencv_highgui_plugins)
+ add_dependencies(${the_target} opencv_highgui_plugins)
+ endif()
if(INSTALL_BIN_EXAMPLES)
install(TARGETS ${the_target} RUNTIME DESTINATION "${OPENCV_SAMPLES_BIN_INSTALL_PATH}/${module_id}" COMPONENT samples)
/* Clp support */
#cmakedefine HAVE_CLP
-/* Cocoa API */
-#cmakedefine HAVE_COCOA
-
/* NVIDIA CUDA Runtime API*/
#cmakedefine HAVE_CUDA
/* Geospatial Data Abstraction Library */
#cmakedefine HAVE_GDAL
-/* GTK+ 2.0 Thread support */
-#cmakedefine HAVE_GTHREAD
-
-/* GTK+ 2.x toolkit */
-#cmakedefine HAVE_GTK
-
/* Halide support */
#cmakedefine HAVE_HALIDE
/* parallel_for with pthreads */
#cmakedefine HAVE_PTHREADS_PF
-/* Qt support */
-#cmakedefine HAVE_QT
-
-/* Qt OpenGL support */
-#cmakedefine HAVE_QT_OPENGL
-
/* Intel Threading Building Blocks */
#cmakedefine HAVE_TBB
+ocv_cmake_dump_vars("" TOFILE "CMakeVars2.txt")
+set(OCV_TEST_VAR 123)
add_definitions(-D__OPENCV_BUILD=1)
if(NOT OPENCV_MODULES_PATH)
return LoadLibraryW(filename.c_str());
#endif
#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__)
- return dlopen(filename.c_str(), RTLD_NOW);
+ void* handle = dlopen(filename.c_str(), RTLD_NOW);
+ CV_LOG_IF_DEBUG(NULL, !handle, "dlopen() error: " << dlerror());
+ return handle;
#endif
}
set(the_description "High-level GUI")
+
+set(ENABLE_PLUGINS_DEFAULT ON)
+if(EMSCRIPTEN OR IOS OR WINRT)
+ set(ENABLE_PLUGINS_DEFAULT OFF)
+endif()
+set(HIGHGUI_PLUGIN_LIST "" CACHE STRING "List of GUI backends to be compiled as plugins (gtk, gtk2/gtk3, qt, win32 or special value 'all')")
+set(HIGHGUI_ENABLE_PLUGINS "${ENABLE_PLUGINS_DEFAULT}" CACHE BOOL "Allow building and using of GUI plugins")
+mark_as_advanced(HIGHGUI_PLUGIN_LIST HIGHGUI_ENABLE_PLUGINS)
+
+string(REPLACE "," ";" HIGHGUI_PLUGIN_LIST "${HIGHGUI_PLUGIN_LIST}") # support comma-separated list (,) too
+if(NOT HIGHGUI_ENABLE_PLUGINS)
+ if(HIGHGUI_PLUGIN_LIST)
+ message(WARNING "HighGUI: plugins are disabled through HIGHGUI_ENABLE_PLUGINS, so HIGHGUI_PLUGIN_LIST='${HIGHGUI_PLUGIN_LIST}' is ignored")
+ set(HIGHGUI_PLUGIN_LIST "")
+ endif()
+else()
+ # Make virtual plugins target
+ if(NOT TARGET opencv_highgui_plugins)
+ add_custom_target(opencv_highgui_plugins ALL)
+ endif()
+endif()
+
if(ANDROID)
ocv_add_module(highgui opencv_imgproc opencv_imgcodecs OPTIONAL opencv_videoio WRAP python)
else()
ocv_add_module(highgui opencv_imgproc opencv_imgcodecs OPTIONAL opencv_videoio WRAP python java)
endif()
+include(${CMAKE_CURRENT_LIST_DIR}/cmake/plugin.cmake)
+
+set(tgts "PRIVATE")
+
+set(highgui_hdrs
+ ${CMAKE_CURRENT_LIST_DIR}/src/precomp.hpp
+ )
+
+set(highgui_srcs
+ ${CMAKE_CURRENT_LIST_DIR}/src/backend.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/src/window.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/src/roiSelector.cpp
+ )
+
# ----------------------------------------------------------------------------
# CMake file for highgui. See root CMakeLists.txt
# Some parts taken from version of Hartmut Seichter, HIT Lab NZ.
add_definitions(-DHAVE_WEBP)
endif()
-set(highgui_hdrs
- ${CMAKE_CURRENT_LIST_DIR}/src/precomp.hpp
- )
-
-set(highgui_srcs
- ${CMAKE_CURRENT_LIST_DIR}/src/window.cpp
- ${CMAKE_CURRENT_LIST_DIR}/src/roiSelector.cpp
- )
-
file(GLOB highgui_ext_hdrs
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/*.hpp"
list(REMOVE_ITEM highgui_ext_hdrs "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/highgui_winrt.hpp")
if(HAVE_QT5)
+ add_definitions(-DHAVE_QT)
+
# "Automoc" doesn't work properly with opencv_world build, use QT5_WRAP_CPP() directly
#set(CMAKE_AUTOMOC ON)
endforeach()
if(HAVE_QT_OPENGL)
+ add_definitions(-DHAVE_QT_OPENGL)
add_definitions(${Qt5OpenGL_DEFINITIONS})
include_directories(${Qt5OpenGL_INCLUDE_DIRS})
list(APPEND HIGHGUI_LIBRARIES ${Qt5OpenGL_LIBRARIES})
endif()
elseif(HAVE_QT)
- if (HAVE_QT_OPENGL)
+ add_definitions(-DHAVE_QT)
+ if(HAVE_QT_OPENGL)
+ add_definitions(-DHAVE_QT_OPENGL)
set(QT_USE_QTOPENGL TRUE)
endif()
include(${QT_USE_FILE})
if(OpenCV_ARCH STREQUAL "ARM64")
list(APPEND HIGHGUI_LIBRARIES "comdlg32" "advapi32")
endif()
-elseif(HAVE_GTK OR HAVE_GTK3)
- list(APPEND highgui_srcs ${CMAKE_CURRENT_LIST_DIR}/src/window_gtk.cpp)
elseif(HAVE_COCOA)
+ add_definitions(-DHAVE_COCOA)
list(APPEND highgui_srcs ${CMAKE_CURRENT_LIST_DIR}/src/window_cocoa.mm)
list(APPEND HIGHGUI_LIBRARIES "-framework Cocoa")
endif()
+if(TARGET ocv.3rdparty.gtk3 OR TARGET ocv.3rdparty.gtk2)
+ if(TARGET ocv.3rdparty.gtk3 AND NOT WITH_GTK_2_X)
+ set(__gtk_dependency "ocv.3rdparty.gtk3")
+ else()
+ set(__gtk_dependency "ocv.3rdparty.gtk2")
+ endif()
+ if(
+ NOT HIGHGUI_PLUGIN_LIST STREQUAL "all"
+ AND NOT "gtk" IN_LIST HIGHGUI_PLUGIN_LIST
+ AND NOT "gtk2" IN_LIST HIGHGUI_PLUGIN_LIST
+ AND NOT "gtk3" IN_LIST HIGHGUI_PLUGIN_LIST
+ )
+ list(APPEND highgui_srcs ${CMAKE_CURRENT_LIST_DIR}/src/window_gtk.cpp)
+ list(APPEND tgts ${__gtk_dependency})
+ if(TARGET ocv.3rdparty.gthread)
+ list(APPEND tgts ocv.3rdparty.gthread)
+ endif()
+ if(TARGET ocv.3rdparty.gtkglext
+ AND NOT OPENCV_GTK_DISABLE_GTKGLEXT
+ )
+ list(APPEND tgts ocv.3rdparty.gtkglext)
+ endif()
+ elseif("gtk" IN_LIST HIGHGUI_PLUGIN_LIST)
+ ocv_create_builtin_highgui_plugin(opencv_highgui_gtk ${__gtk_dependency} "window_gtk.cpp")
+ if(TARGET ocv.3rdparty.gthread)
+ ocv_target_link_libraries(opencv_highgui_gtk ocv.3rdparty.gthread)
+ endif()
+ if(TARGET ocv.3rdparty.gtkglext)
+ ocv_target_link_libraries(opencv_highgui_gtk ocv.3rdparty.gtkglext)
+ endif()
+ else()
+ if(TARGET ocv.3rdparty.gtk3 AND ("gtk3" IN_LIST HIGHGUI_PLUGIN_LIST OR HIGHGUI_PLUGIN_LIST STREQUAL "all"))
+ ocv_create_builtin_highgui_plugin(opencv_highgui_gtk3 ocv.3rdparty.gtk3 "window_gtk.cpp")
+ if(TARGET ocv.3rdparty.gthread)
+ ocv_target_link_libraries(opencv_highgui_gtk3 ocv.3rdparty.gthread)
+ endif()
+ if(TARGET ocv.3rdparty.gtkglext)
+ ocv_target_link_libraries(opencv_highgui_gtk3 ocv.3rdparty.gtkglext)
+ endif()
+ endif()
+ if(TARGET ocv.3rdparty.gtk2 AND ("gtk2" IN_LIST HIGHGUI_PLUGIN_LIST OR HIGHGUI_PLUGIN_LIST STREQUAL "all"))
+ ocv_create_builtin_highgui_plugin(opencv_highgui_gtk2 ocv.3rdparty.gtk2 "window_gtk.cpp")
+ if(TARGET ocv.3rdparty.gthread)
+ ocv_target_link_libraries(opencv_highgui_gtk2 ocv.3rdparty.gthread)
+ endif()
+ if(TARGET ocv.3rdparty.gtkglext)
+ ocv_target_link_libraries(opencv_highgui_gtk2 ocv.3rdparty.gtkglext)
+ endif()
+ endif()
+ endif()
+endif()
+
if(TRUE)
# these variables are set by 'ocv_append_build_options(HIGHGUI ...)'
foreach(P ${HIGHGUI_INCLUDE_DIRS})
endforeach()
endif()
+if(tgts STREQUAL "PRIVATE")
+ set(tgts "")
+endif()
+
+# install used dependencies only
+if(NOT BUILD_SHARED_LIBS
+ AND NOT (CMAKE_VERSION VERSION_LESS "3.13.0") # upgrade CMake: https://gitlab.kitware.com/cmake/cmake/-/merge_requests/2152
+)
+ foreach(tgt in ${tgts})
+ if(tgt MATCHES "^ocv\.3rdparty\.")
+ install(TARGETS ${tgt} EXPORT OpenCVModules)
+ endif()
+ endforeach()
+endif()
+
source_group("Src" FILES ${highgui_srcs} ${highgui_hdrs})
source_group("Include" FILES ${highgui_ext_hdrs})
ocv_set_module_sources(HEADERS ${highgui_ext_hdrs} SOURCES ${highgui_srcs} ${highgui_hdrs})
ocv_highgui_configure_target()
endif()
-ocv_add_accuracy_tests()
-ocv_add_perf_tests()
+ocv_add_accuracy_tests(${tgts})
+#ocv_add_perf_tests(${tgts})
+
+if(HIGHGUI_ENABLE_PLUGINS)
+ ocv_target_compile_definitions(${the_module} PRIVATE ENABLE_PLUGINS)
+ if(TARGET opencv_test_highgui)
+ ocv_target_compile_definitions(opencv_test_highgui PRIVATE ENABLE_PLUGINS)
+ endif()
+endif()
+
+ocv_target_link_libraries(${the_module} LINK_PRIVATE ${tgts})
--- /dev/null
+# --- GTK ---
+ocv_clear_vars(HAVE_GTK HAVE_GTK3 HAVE_GTHREAD HAVE_GTKGLEXT)
+if(WITH_GTK AND NOT HAVE_QT)
+ if(NOT WITH_GTK_2_X)
+ ocv_check_modules(GTK3 gtk+-3.0)
+ if(HAVE_GTK3)
+ ocv_add_external_target(gtk3 "${GTK3_INCLUDE_DIRS}" "${GTK3_LIBRARIES}" "HAVE_GTK3;HAVE_GTK")
+ set(HAVE_GTK TRUE)
+ set(GTK3_VERSION "${GTK3_VERSION}" PARENT_SCOPE) # informational
+ endif()
+ endif()
+ if(TRUE)
+ ocv_check_modules(GTK2 gtk+-2.0)
+ if(HAVE_GTK2)
+ set(MIN_VER_GTK "2.18.0")
+ if(GTK2_VERSION VERSION_LESS MIN_VER_GTK)
+ message(FATAL_ERROR "GTK support requires a minimum version of ${MIN_VER_GTK} (${GTK2_VERSION} found)")
+ else()
+ ocv_add_external_target(gtk2 "${GTK2_INCLUDE_DIRS}" "${GTK2_LIBRARIES}" "HAVE_GTK2;HAVE_GTK")
+ set(HAVE_GTK TRUE)
+ set(GTK2_VERSION "${GTK2_VERSION}" PARENT_SCOPE) # informational
+ endif()
+ endif()
+ endif()
+ ocv_check_modules(GTHREAD gthread-2.0)
+ if(HAVE_GTK AND NOT HAVE_GTHREAD)
+ message(FATAL_ERROR "gthread not found. This library is required when building with GTK support")
+ else()
+ ocv_add_external_target(gthread "${GTHREAD_INCLUDE_DIRS}" "${GTHREAD_LIBRARIES}" "HAVE_GTHREAD")
+ set(HAVE_GTHREAD "${HAVE_GTHREAD}" PARENT_SCOPE) # informational
+ set(GTHREAD_VERSION "${GTHREAD_VERSION}" PARENT_SCOPE) # informational
+ endif()
+ if(WITH_OPENGL AND NOT HAVE_GTK3)
+ ocv_check_modules(GTKGLEXT gtkglext-1.0)
+ if(HAVE_GTKGLEXT)
+ ocv_add_external_target(gtkglext "${GTKGLEXT_INCLUDE_DIRS}" "${GTKGLEXT_LIBRARIES}" "HAVE_GTKGLEXT")
+ set(HAVE_GTKGLEXT "${HAVE_GTKGLEXT}" PARENT_SCOPE) # informational
+ set(GTKGLEXT_VERSION "${GTKGLEXT_VERSION}" PARENT_SCOPE) # informational
+ endif()
+ endif()
+endif()
+
+set(HAVE_GTK ${HAVE_GTK} PARENT_SCOPE)
--- /dev/null
+include(FindPkgConfig)
+
+# FIXIT: stop using PARENT_SCOPE in dependencies
+if(PROJECT_NAME STREQUAL "OpenCV")
+ macro(add_backend backend_id cond_var)
+ if(${cond_var})
+ include("${CMAKE_CURRENT_LIST_DIR}/detect_${backend_id}.cmake")
+ endif()
+ endmacro()
+else()
+ function(add_backend backend_id cond_var)
+ if(${cond_var})
+ include("${CMAKE_CURRENT_LIST_DIR}/detect_${backend_id}.cmake")
+ endif()
+ endfunction()
+endif()
+
+add_backend("gtk" WITH_GTK)
+
+# TODO win32
+# TODO cocoa
+# TODO qt
+# TODO opengl
+
+# FIXIT: move content of cmake/OpenCVFindLibsGUI.cmake here (need to resolve CMake scope issues)
--- /dev/null
+function(ocv_create_builtin_highgui_plugin name target)
+
+ ocv_debug_message("ocv_create_builtin_highgui_plugin(${ARGV})")
+
+ if(NOT TARGET ${target})
+ message(FATAL_ERROR "${target} does not exist!")
+ endif()
+ if(NOT OpenCV_SOURCE_DIR)
+ message(FATAL_ERROR "OpenCV_SOURCE_DIR must be set to build the plugin!")
+ endif()
+
+ message(STATUS "HighGUI: add builtin plugin '${name}'")
+
+ foreach(src ${ARGN})
+ list(APPEND sources "${CMAKE_CURRENT_LIST_DIR}/src/${src}")
+ endforeach()
+
+ add_library(${name} MODULE ${sources})
+ target_include_directories(${name} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
+ target_compile_definitions(${name} PRIVATE BUILD_PLUGIN)
+ target_link_libraries(${name} PRIVATE ${target})
+
+ foreach(mod opencv_highgui
+ opencv_core
+ opencv_imgproc
+ opencv_imgcodecs
+ opencv_videoio # TODO remove this dependency
+ )
+ ocv_target_link_libraries(${name} LINK_PRIVATE ${mod})
+ ocv_target_include_directories(${name} "${OPENCV_MODULE_${mod}_LOCATION}/include")
+ endforeach()
+
+ if(WIN32)
+ set(OPENCV_PLUGIN_VERSION "${OPENCV_DLLVERSION}" CACHE STRING "")
+ if(CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8)
+ set(OPENCV_PLUGIN_ARCH "_64" CACHE STRING "")
+ else()
+ set(OPENCV_PLUGIN_ARCH "" CACHE STRING "")
+ endif()
+ else()
+ set(OPENCV_PLUGIN_VERSION "" CACHE STRING "")
+ set(OPENCV_PLUGIN_ARCH "" CACHE STRING "")
+ endif()
+
+ set_target_properties(${name} PROPERTIES
+ CXX_STANDARD 11
+ CXX_VISIBILITY_PRESET hidden
+ DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
+ OUTPUT_NAME "${name}${OPENCV_PLUGIN_VERSION}${OPENCV_PLUGIN_ARCH}"
+ )
+
+ if(WIN32)
+ set_target_properties(${name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
+ install(TARGETS ${name} OPTIONAL LIBRARY DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT plugins)
+ else()
+ install(TARGETS ${name} OPTIONAL LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT plugins)
+ endif()
+
+ add_dependencies(opencv_highgui_plugins ${name})
+
+endfunction()
--- /dev/null
+#!/bin/bash
+
+set -e
+
+if [ -z $1 ] ; then
+ echo "$0 <destination directory>"
+ exit 1
+fi
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+OCV="$( cd "${DIR}/../../../.." >/dev/null 2>&1 && pwd )"
+mkdir -p "${1}" # Docker creates non-existed mounts with 'root' owner, lets ensure that dir exists under the current user to avoid "Permission denied" problem
+DST="$( cd "$1" >/dev/null 2>&1 && pwd )"
+CFG=$2
+
+do_build()
+{
+TAG=$1
+D=$2
+F=$3
+shift 3
+docker build \
+ --build-arg http_proxy \
+ --build-arg https_proxy \
+ $@ \
+ -t $TAG \
+ -f "${D}/${F}" \
+ "${D}"
+}
+
+do_run()
+{
+TAG=$1
+shift 1
+docker run \
+ -it \
+ --rm \
+ -v "${OCV}":/opencv:ro \
+ -v "${DST}":/dst \
+ -e CFG=$CFG \
+ --user $(id -u):$(id -g) \
+ $TAG \
+ $@
+}
+
+build_gtk2_ubuntu()
+{
+VER=$1
+TAG=opencv_highgui_ubuntu_gtk2_builder:${VER}
+do_build $TAG "${DIR}/plugin_gtk" Dockerfile-ubuntu-gtk2 --build-arg VER=${VER}
+do_run $TAG /opencv/modules/highgui/misc/plugins/plugin_gtk/build.sh /dst gtk2_ubuntu${VER} ${CFG}
+
+}
+
+build_gtk3_ubuntu()
+{
+VER=$1
+TAG=opencv_highgui_ubuntu_gtk3_builder:${VER}
+do_build $TAG "${DIR}/plugin_gtk" Dockerfile-ubuntu-gtk3 --build-arg VER=${VER}
+do_run $TAG /opencv/modules/highgui/misc/plugins/plugin_gtk/build.sh /dst gtk3_ubuntu${VER} ${CFG}
+}
+
+echo "OpenCV: ${OCV}"
+echo "Destination: ${DST}"
+
+build_gtk2_ubuntu 16.04
+build_gtk2_ubuntu 18.04
+build_gtk3_ubuntu 18.04
+build_gtk3_ubuntu 20.04
--- /dev/null
+cmake_minimum_required(VERSION 3.5)
+project(opencv_highgui_gtk)
+
+get_filename_component(OpenCV_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../../.." ABSOLUTE)
+include("${OpenCV_SOURCE_DIR}/cmake/OpenCVPluginStandalone.cmake")
+
+# scan dependencies
+set(WITH_GTK ON)
+include("${OpenCV_SOURCE_DIR}/modules/highgui/cmake/init.cmake")
+
+ocv_warnings_disable(CMAKE_CXX_FLAGS -Wno-deprecated-declarations)
+
+set(OPENCV_PLUGIN_DEPS core imgproc imgcodecs)
+if(TARGET ocv.3rdparty.gtk3)
+ set(__deps ocv.3rdparty.gtk3)
+elseif(TARGET ocv.3rdparty.gtk2)
+ set(__deps ocv.3rdparty.gtk2)
+elseif(TARGET ocv.3rdparty.gtk)
+ set(__deps ocv.3rdparty.gtk)
+else()
+ message(FATAL_ERROR "Missing dependency target for GTK libraries")
+endif()
+ocv_create_plugin(highgui "opencv_highgui_gtk" "${__deps}" "GTK" "src/window_gtk.cpp")
+
+message(STATUS "GTK: ${GTK2_VERSION}")
+if(HAVE_GTK3)
+ message(STATUS "GTK+: ver ${GTK3_VERSION}")
+elseif(HAVE_GTK)
+ message(STATUS "GTK+: ver ${GTK2_VERSION}")
+else()
+ message(FATAL_ERROR "GTK+: NO")
+endif()
+if(HAVE_GTK)
+ if(HAVE_GTHREAD)
+ message(STATUS "GThread : YES (ver ${GTHREAD_VERSION})")
+ else()
+ message(STATUS "GThread : NO")
+ endif()
+ if(HAVE_GTKGLEXT)
+ message(STATUS "GtkGlExt: YES (ver ${GTKGLEXT_VERSION})")
+ else()
+ message(STATUS "GtkGlExt: NO")
+ endif()
+endif()
--- /dev/null
+ARG VER
+FROM ubuntu:$VER
+
+RUN \
+ apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ pkg-config \
+ cmake \
+ g++ \
+ ninja-build \
+ && \
+ rm -rf /var/lib/apt/lists/*
+
+RUN \
+ apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ libgtk2.0-dev \
+ && \
+ rm -rf /var/lib/apt/lists/*
+
+WORKDIR /tmp
--- /dev/null
+ARG VER
+FROM ubuntu:$VER
+
+RUN \
+ apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ pkg-config \
+ cmake \
+ g++ \
+ ninja-build \
+ && \
+ rm -rf /var/lib/apt/lists/*
+
+RUN \
+ apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ libgtk-3-dev \
+ && \
+ rm -rf /var/lib/apt/lists/*
+
+WORKDIR /tmp
--- /dev/null
+#!/bin/bash
+
+set -e
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+
+cmake -GNinja \
+ -DOPENCV_PLUGIN_NAME=opencv_highgui_$2 \
+ -DOPENCV_PLUGIN_DESTINATION=$1 \
+ -DCMAKE_BUILD_TYPE=$3 \
+ $DIR
+
+ninja -v
--- /dev/null
+// 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 "backend.hpp"
+
+#include <opencv2/core/utils/configuration.private.hpp>
+#include <opencv2/core/utils/logger.defines.hpp>
+#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 <opencv2/core/utils/logger.hpp>
+
+
+#include "registry.hpp"
+#include "registry.impl.hpp"
+
+#include "plugin_api.hpp"
+#include "plugin_wrapper.impl.hpp"
+
+
+namespace cv { namespace highgui_backend {
+
+UIBackend::~UIBackend()
+{
+ // nothing
+}
+
+UIWindowBase::~UIWindowBase()
+{
+ // nothing
+}
+
+UIWindow::~UIWindow()
+{
+ // nothing
+}
+
+UITrackbar::~UITrackbar()
+{
+ // nothing
+}
+
+static
+std::string& getUIBackendName()
+{
+ static std::string g_backendName = toUpperCase(cv::utils::getConfigurationParameterString("OPENCV_UI_BACKEND", ""));
+ return g_backendName;
+}
+
+static bool g_initializedUIBackend = false;
+
+static
+std::shared_ptr<UIBackend> createUIBackend()
+{
+ const std::string& name = getUIBackendName();
+ bool isKnown = false;
+ const auto& backends = getBackendsInfo();
+ if (!name.empty())
+ {
+ CV_LOG_INFO(NULL, "UI: requested backend name: " << name);
+ }
+ for (size_t i = 0; i < backends.size(); i++)
+ {
+ const auto& info = backends[i];
+ if (!name.empty())
+ {
+ if (name != info.name)
+ {
+ continue;
+ }
+ isKnown = true;
+ }
+ try
+ {
+ CV_LOG_DEBUG(NULL, "UI: trying backend: " << info.name << " (priority=" << info.priority << ")");
+ if (!info.backendFactory)
+ {
+ CV_LOG_DEBUG(NULL, "UI: factory is not available (plugins require filesystem support): " << info.name);
+ continue;
+ }
+ std::shared_ptr<UIBackend> backend = info.backendFactory->create();
+ if (!backend)
+ {
+ CV_LOG_VERBOSE(NULL, 0, "UI: not available: " << info.name);
+ continue;
+ }
+ CV_LOG_INFO(NULL, "UI: using backend: " << info.name << " (priority=" << info.priority << ")");
+ g_initializedUIBackend = true;
+ getUIBackendName() = info.name;
+ return backend;
+ }
+ catch (const std::exception& e)
+ {
+ CV_LOG_WARNING(NULL, "UI: can't initialize " << info.name << " backend: " << e.what());
+ }
+ catch (...)
+ {
+ CV_LOG_WARNING(NULL, "UI: can't initialize " << info.name << " backend: Unknown C++ exception");
+ }
+ }
+ if (name.empty())
+ {
+ CV_LOG_DEBUG(NULL, "UI: fallback on builtin code");
+ }
+ else
+ {
+ if (!isKnown)
+ CV_LOG_INFO(NULL, "UI: unknown backend: " << name);
+ }
+ g_initializedUIBackend = true;
+ return std::shared_ptr<UIBackend>();
+}
+
+static inline
+std::shared_ptr<UIBackend> createDefaultUIBackend()
+{
+ CV_LOG_DEBUG(NULL, "UI: Initializing backend...");
+ return createUIBackend();
+}
+
+std::shared_ptr<UIBackend>& getCurrentUIBackend()
+{
+ static std::shared_ptr<UIBackend> g_currentUIBackend = createDefaultUIBackend();
+ return g_currentUIBackend;
+}
+
+void setUIBackend(const std::shared_ptr<UIBackend>& api)
+{
+ getCurrentUIBackend() = api;
+}
+
+bool setUIBackend(const std::string& backendName)
+{
+ CV_TRACE_FUNCTION();
+
+ std::string backendName_u = toUpperCase(backendName);
+ if (g_initializedUIBackend)
+ {
+ // ... already initialized
+ if (getUIBackendName() == backendName_u)
+ {
+ CV_LOG_INFO(NULL, "UI: backend is already activated: " << (backendName.empty() ? "builtin(legacy)" : backendName));
+ return true;
+ }
+ else
+ {
+ // ... re-create new
+ CV_LOG_DEBUG(NULL, "UI: replacing backend...");
+ getUIBackendName() = backendName_u;
+ getCurrentUIBackend() = createUIBackend();
+ }
+ }
+ else
+ {
+ // ... no backend exists, just specify the name (initialization is triggered by getCurrentUIBackend() call)
+ getUIBackendName() = backendName_u;
+ }
+ std::shared_ptr<UIBackend> api = getCurrentUIBackend();
+ if (!api)
+ {
+ if (!backendName.empty())
+ {
+ CV_LOG_WARNING(NULL, "UI: backend is not available: " << backendName << " (using builtin legacy code)");
+ return false;
+ }
+ else
+ {
+ CV_LOG_WARNING(NULL, "UI: switched to builtin code (legacy)");
+ }
+ }
+ if (!backendName_u.empty())
+ {
+ CV_Assert(backendName_u == getUIBackendName()); // data race?
+ }
+ return true;
+}
+
+}} // namespace cv::highgui_backend
--- /dev/null
+// 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_BACKEND_HPP
+#define OPENCV_HIGHGUI_BACKEND_HPP
+
+#include <memory>
+#include <map>
+
+namespace cv { namespace highgui_backend {
+
+class CV_EXPORTS UIWindowBase
+{
+public:
+ typedef std::shared_ptr<UIWindowBase> Ptr;
+ typedef std::weak_ptr<UIWindowBase> WeakPtr;
+
+ virtual ~UIWindowBase();
+
+ virtual const std::string& getID() const = 0; // internal name, used for logging
+
+ virtual bool isActive() const = 0;
+
+ virtual void destroy() = 0;
+}; // UIWindowBase
+
+class UITrackbar;
+
+class CV_EXPORTS UIWindow : public UIWindowBase
+{
+public:
+ virtual ~UIWindow();
+
+ virtual void imshow(InputArray image) = 0;
+
+ virtual double getProperty(int prop) const = 0;
+ virtual bool setProperty(int prop, double value) = 0;
+
+ virtual void resize(int width, int height) = 0;
+ virtual void move(int x, int y) = 0;
+
+ virtual Rect getImageRect() const = 0;
+
+ virtual void setTitle(const std::string& title) = 0;
+
+ virtual void setMouseCallback(MouseCallback onMouse, void* userdata /*= 0*/) = 0;
+
+ //TODO: handle both keys and mouse events (both with mouse coordinates)
+ //virtual void setInputCallback(InputCallback onInputEvent, void* userdata /*= 0*/) = 0;
+
+ virtual std::shared_ptr<UITrackbar> createTrackbar(
+ const std::string& name,
+ int count,
+ TrackbarCallback onChange /*= 0*/,
+ void* userdata /*= 0*/
+ ) = 0;
+
+ virtual std::shared_ptr<UITrackbar> findTrackbar(const std::string& name) = 0;
+
+#if 0 // QT only
+ virtual void displayOverlay(const std::string& text, int delayms = 0) = 0;
+ virtual void displayStatusBar(const std::string& text, int delayms /*= 0*/) = 0;
+ virtual int createButton(
+ const std::string& bar_name, ButtonCallback on_change,
+ void* userdata = 0, int type /*= QT_PUSH_BUTTON*/,
+ bool initial_button_state /*= false*/
+ ) = 0;
+ // addText, QtFont stuff
+#endif
+
+#if 0 // OpenGL
+ virtual void imshow(const ogl::Texture2D& tex) = 0;
+ virtual void setOpenGlDrawCallback(OpenGlDrawCallback onOpenGlDraw, void* userdata = 0) = 0;
+ virtual void setOpenGlContext() = 0;
+ virtual void updateWindow() = 0;
+#endif
+
+}; // UIWindow
+
+
+class CV_EXPORTS UITrackbar : public UIWindowBase
+{
+public:
+ virtual ~UITrackbar();
+
+ virtual int getPos() const = 0;
+ virtual void setPos(int pos) = 0;
+
+ virtual cv::Range getRange() const = 0;
+ virtual void setRange(const cv::Range& range) = 0;
+}; // UITrackbar
+
+
+class CV_EXPORTS UIBackend
+{
+public:
+ virtual ~UIBackend();
+
+ virtual void destroyAllWindows() = 0;
+
+ // namedWindow
+ virtual std::shared_ptr<UIWindow> createWindow(
+ const std::string& winname,
+ int flags
+ ) = 0;
+
+ virtual int waitKeyEx(int delay /*= 0*/) = 0;
+ virtual int pollKey() = 0;
+};
+
+std::shared_ptr<UIBackend>& getCurrentUIBackend();
+void setUIBackend(const std::shared_ptr<UIBackend>& api);
+bool setUIBackend(const std::string& backendName);
+
+#ifndef BUILD_PLUGIN
+
+#ifdef HAVE_GTK
+std::shared_ptr<UIBackend> createUIBackendGTK();
+#endif
+
+#if 0 // TODO: defined HAVE_QT
+std::shared_ptr<UIBackend> createUIBackendQT();
+#endif
+
+#endif // BUILD_PLUGIN
+
+} // namespace highgui_backend
+
+} // namespace cv
+
+#endif // OPENCV_HIGHGUI_BACKEND_HPP
--- /dev/null
+// 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_UI_FACTORY_HPP
+#define OPENCV_UI_FACTORY_HPP
+
+#include "backend.hpp"
+
+namespace cv { namespace highgui_backend {
+
+class IUIBackendFactory
+{
+public:
+ virtual ~IUIBackendFactory() {}
+ virtual std::shared_ptr<cv::highgui_backend::UIBackend> create() const = 0;
+};
+
+
+class StaticBackendFactory CV_FINAL: public IUIBackendFactory
+{
+protected:
+ std::function<std::shared_ptr<cv::highgui_backend::UIBackend>(void)> create_fn_;
+
+public:
+ StaticBackendFactory(std::function<std::shared_ptr<cv::highgui_backend::UIBackend>(void)>&& create_fn)
+ : create_fn_(create_fn)
+ {
+ // nothing
+ }
+
+ ~StaticBackendFactory() CV_OVERRIDE {}
+
+ std::shared_ptr<cv::highgui_backend::UIBackend> create() const CV_OVERRIDE
+ {
+ return create_fn_();
+ }
+};
+
+//
+// PluginUIBackendFactory is implemented in plugin_wrapper
+//
+
+std::shared_ptr<IUIBackendFactory> createPluginUIBackendFactory(const std::string& baseName);
+
+}} // namespace
+
+#endif // OPENCV_UI_FACTORY_HPP
--- /dev/null
+// 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 UI_PLUGIN_API_HPP
+#define UI_PLUGIN_API_HPP
+
+#include <opencv2/core/cvdef.h>
+#include <opencv2/core/llapi/llapi.h>
+
+#include "backend.hpp"
+
+#if !defined(BUILD_PLUGIN)
+
+/// increased for backward-compatible changes, e.g. add new function
+/// Caller API <= Plugin API -> plugin is fully compatible
+/// Caller API > Plugin API -> plugin is not fully compatible, caller should use extra checks to use plugins with older API
+#define API_VERSION 0 // preview
+
+/// increased for incompatible changes, e.g. remove function argument
+/// Caller ABI == Plugin ABI -> plugin is compatible
+/// Caller ABI > Plugin ABI -> plugin is not compatible, caller should use shim code to use old ABI plugins (caller may know how lower ABI works, so it is possible)
+/// Caller ABI < Plugin ABI -> plugin can't be used (plugin should provide interface with lower ABI to handle that)
+#define ABI_VERSION 0 // preview
+
+#else // !defined(BUILD_PLUGIN)
+
+#if !defined(ABI_VERSION) || !defined(API_VERSION)
+#error "Plugin must define ABI_VERSION and API_VERSION before including plugin_api.hpp"
+#endif
+
+#endif // !defined(BUILD_PLUGIN)
+
+typedef cv::highgui_backend::UIBackend* CvPluginUIBackend;
+
+struct OpenCV_UI_Plugin_API_v0_0_api_entries
+{
+ /** @brief Get backend API instance
+
+ @param[out] handle pointer on backend API handle
+
+ @note API-CALL 1, API-Version == 0
+ */
+ CvResult (CV_API_CALL *getInstance)(CV_OUT CvPluginUIBackend* handle) CV_NOEXCEPT;
+}; // OpenCV_UI_Plugin_API_v0_0_api_entries
+
+typedef struct OpenCV_UI_Plugin_API_v0
+{
+ OpenCV_API_Header api_header;
+ struct OpenCV_UI_Plugin_API_v0_0_api_entries v0;
+} OpenCV_UI_Plugin_API_v0;
+
+#if ABI_VERSION == 0 && API_VERSION == 0
+typedef OpenCV_UI_Plugin_API_v0 OpenCV_UI_Plugin_API;
+#else
+#error "Not supported configuration: check ABI_VERSION/API_VERSION"
+#endif
+
+#ifdef BUILD_PLUGIN
+extern "C" {
+
+CV_PLUGIN_EXPORTS
+const OpenCV_UI_Plugin_API* CV_API_CALL opencv_ui_plugin_init_v0
+ (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/) CV_NOEXCEPT;
+
+} // extern "C"
+#else // BUILD_PLUGIN
+typedef const OpenCV_UI_Plugin_API* (CV_API_CALL *FN_opencv_ui_plugin_init_t)
+ (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/);
+#endif // BUILD_PLUGIN
+
+#endif // UI_PLUGIN_API_HPP
--- /dev/null
+// 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.
+
+//
+// Not a standalone header, part of backend.cpp
+//
+
+//==================================================================================================
+// Dynamic backend implementation
+
+#include "opencv2/core/utils/plugin_loader.private.hpp"
+
+namespace cv { namespace impl {
+
+using namespace cv::highgui_backend;
+
+#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS)
+
+using namespace cv::plugin::impl; // plugin_loader.hpp
+
+class PluginUIBackend CV_FINAL: public std::enable_shared_from_this<PluginUIBackend>
+{
+protected:
+ void initPluginAPI()
+ {
+ const char* init_name = "opencv_ui_plugin_init_v0";
+ FN_opencv_ui_plugin_init_t fn_init = reinterpret_cast<FN_opencv_ui_plugin_init_t>(lib_->getSymbol(init_name));
+ if (fn_init)
+ {
+ CV_LOG_DEBUG(NULL, "Found entry: '" << init_name << "'");
+ for (int supported_api_version = API_VERSION; supported_api_version >= 0; supported_api_version--)
+ {
+ plugin_api_ = fn_init(ABI_VERSION, supported_api_version, NULL);
+ if (plugin_api_)
+ break;
+ }
+ if (!plugin_api_)
+ {
+ CV_LOG_INFO(NULL, "UI: plugin is incompatible (can't be initialized): " << lib_->getName());
+ return;
+ }
+ if (!checkCompatibility(plugin_api_->api_header, ABI_VERSION, API_VERSION, false))
+ {
+ plugin_api_ = NULL;
+ return;
+ }
+ CV_LOG_INFO(NULL, "UI: plugin is ready to use '" << plugin_api_->api_header.api_description << "'");
+ }
+ else
+ {
+ CV_LOG_INFO(NULL, "UI: plugin is incompatible, missing init function: '" << init_name << "', file: " << lib_->getName());
+ }
+ }
+
+
+ bool checkCompatibility(const OpenCV_API_Header& api_header, unsigned int abi_version, unsigned int api_version, bool checkMinorOpenCVVersion)
+ {
+ if (api_header.opencv_version_major != CV_VERSION_MAJOR)
+ {
+ CV_LOG_ERROR(NULL, "UI: wrong OpenCV major version used by plugin '" << api_header.api_description << "': " <<
+ cv::format("%d.%d, OpenCV version is '" CV_VERSION "'", api_header.opencv_version_major, api_header.opencv_version_minor))
+ return false;
+ }
+ if (!checkMinorOpenCVVersion)
+ {
+ // no checks for OpenCV minor version
+ }
+ else if (api_header.opencv_version_minor != CV_VERSION_MINOR)
+ {
+ CV_LOG_ERROR(NULL, "UI: wrong OpenCV minor version used by plugin '" << api_header.api_description << "': " <<
+ cv::format("%d.%d, OpenCV version is '" CV_VERSION "'", api_header.opencv_version_major, api_header.opencv_version_minor))
+ return false;
+ }
+ CV_LOG_DEBUG(NULL, "UI: initialized '" << api_header.api_description << "': built with "
+ << cv::format("OpenCV %d.%d (ABI/API = %d/%d)",
+ api_header.opencv_version_major, api_header.opencv_version_minor,
+ api_header.min_api_version, api_header.api_version)
+ << ", current OpenCV version is '" CV_VERSION "' (ABI/API = " << abi_version << "/" << api_version << ")"
+ );
+ if (api_header.min_api_version != abi_version) // future: range can be here
+ {
+ // actually this should never happen due to checks in plugin's init() function
+ CV_LOG_ERROR(NULL, "UI: plugin is not supported due to incompatible ABI = " << api_header.min_api_version);
+ return false;
+ }
+ if (api_header.api_version != api_version)
+ {
+ CV_LOG_INFO(NULL, "UI: NOTE: plugin is supported, but there is API version mismath: "
+ << cv::format("plugin API level (%d) != OpenCV API level (%d)", api_header.api_version, api_version));
+ if (api_header.api_version < api_version)
+ {
+ CV_LOG_INFO(NULL, "UI: NOTE: some functionality may be unavailable due to lack of support by plugin implementation");
+ }
+ }
+ return true;
+ }
+
+public:
+ std::shared_ptr<cv::plugin::impl::DynamicLib> lib_;
+ const OpenCV_UI_Plugin_API* plugin_api_;
+
+ PluginUIBackend(const std::shared_ptr<cv::plugin::impl::DynamicLib>& lib)
+ : lib_(lib)
+ , plugin_api_(NULL)
+ {
+ initPluginAPI();
+ }
+
+ std::shared_ptr<cv::highgui_backend::UIBackend> create() const
+ {
+ CV_Assert(plugin_api_);
+
+ CvPluginUIBackend instancePtr = NULL;
+
+ if (plugin_api_->v0.getInstance)
+ {
+ if (CV_ERROR_OK == plugin_api_->v0.getInstance(&instancePtr))
+ {
+ CV_Assert(instancePtr);
+ // TODO C++20 "aliasing constructor"
+ return std::shared_ptr<cv::highgui_backend::UIBackend>(instancePtr, [](cv::highgui_backend::UIBackend*){}); // empty deleter
+ }
+ }
+ return std::shared_ptr<cv::highgui_backend::UIBackend>();
+ }
+};
+
+
+class PluginUIBackendFactory CV_FINAL: public IUIBackendFactory
+{
+public:
+ std::string baseName_;
+ std::shared_ptr<PluginUIBackend> backend;
+ bool initialized;
+public:
+ PluginUIBackendFactory(const std::string& baseName)
+ : baseName_(baseName)
+ , initialized(false)
+ {
+ // nothing, plugins are loaded on demand
+ }
+
+ std::shared_ptr<cv::highgui_backend::UIBackend> create() const CV_OVERRIDE
+ {
+ if (!initialized)
+ {
+ const_cast<PluginUIBackendFactory*>(this)->initBackend();
+ }
+ if (backend)
+ return backend->create();
+ return std::shared_ptr<cv::highgui_backend::UIBackend>();
+ }
+protected:
+ void initBackend()
+ {
+ AutoLock lock(getInitializationMutex());
+ try
+ {
+ if (!initialized)
+ loadPlugin();
+ }
+ catch (...)
+ {
+ CV_LOG_INFO(NULL, "UI: exception during plugin loading: " << baseName_ << ". SKIP");
+ }
+ initialized = true;
+ }
+ void loadPlugin();
+};
+
+static
+std::vector<FileSystemPath_t> getPluginCandidates(const std::string& baseName)
+{
+ using namespace cv::utils;
+ using namespace cv::utils::fs;
+ const std::string baseName_l = toLowerCase(baseName);
+ const std::string baseName_u = toUpperCase(baseName);
+ const FileSystemPath_t baseName_l_fs = toFileSystemPath(baseName_l);
+ std::vector<FileSystemPath_t> paths;
+ // TODO OPENCV_PLUGIN_PATH
+ const std::vector<std::string> paths_ = getConfigurationParameterPaths("OPENCV_CORE_PLUGIN_PATH", std::vector<std::string>());
+ if (paths_.size() != 0)
+ {
+ for (size_t i = 0; i < paths_.size(); i++)
+ {
+ paths.push_back(toFileSystemPath(paths_[i]));
+ }
+ }
+ else
+ {
+ FileSystemPath_t binaryLocation;
+ if (getBinLocation(binaryLocation))
+ {
+ binaryLocation = getParent(binaryLocation);
+#ifndef CV_UI_PLUGIN_SUBDIRECTORY
+ paths.push_back(binaryLocation);
+#else
+ paths.push_back(binaryLocation + toFileSystemPath("/") + toFileSystemPath(CV_UI_PLUGIN_SUBDIRECTORY_STR));
+#endif
+ }
+ }
+ const std::string default_expr = libraryPrefix() + "opencv_highgui_" + baseName_l + "*" + librarySuffix();
+ const std::string plugin_expr = getConfigurationParameterString((std::string("OPENCV_UI_PLUGIN_") + baseName_u).c_str(), default_expr.c_str());
+ std::vector<FileSystemPath_t> results;
+#ifdef _WIN32
+ FileSystemPath_t moduleName = toFileSystemPath(libraryPrefix() + "opencv_highgui_" + baseName_l + librarySuffix());
+ if (plugin_expr != default_expr)
+ {
+ moduleName = toFileSystemPath(plugin_expr);
+ results.push_back(moduleName);
+ }
+ for (const FileSystemPath_t& path : paths)
+ {
+ results.push_back(path + L"\\" + moduleName);
+ }
+ results.push_back(moduleName);
+#else
+ CV_LOG_DEBUG(NULL, "UI: " << baseName << " plugin's glob is '" << plugin_expr << "', " << paths.size() << " location(s)");
+ for (const std::string& path : paths)
+ {
+ if (path.empty())
+ continue;
+ std::vector<std::string> candidates;
+ cv::glob(utils::fs::join(path, plugin_expr), candidates);
+ CV_LOG_DEBUG(NULL, " - " << path << ": " << candidates.size());
+ copy(candidates.begin(), candidates.end(), back_inserter(results));
+ }
+#endif
+ CV_LOG_DEBUG(NULL, "Found " << results.size() << " plugin(s) for " << baseName);
+ return results;
+}
+
+void PluginUIBackendFactory::loadPlugin()
+{
+ for (const FileSystemPath_t& plugin : getPluginCandidates(baseName_))
+ {
+ auto lib = std::make_shared<cv::plugin::impl::DynamicLib>(plugin);
+ if (!lib->isLoaded())
+ {
+ continue;
+ }
+ try
+ {
+ auto pluginBackend = std::make_shared<PluginUIBackend>(lib);
+ if (!pluginBackend)
+ {
+ continue;
+ }
+ if (pluginBackend->plugin_api_ == NULL)
+ {
+ CV_LOG_ERROR(NULL, "UI: no compatible plugin API for backend: " << baseName_ << " in " << toPrintablePath(plugin));
+ continue;
+ }
+ // NB: we are going to use UI backend, so prevent automatic library unloading
+ lib->disableAutomaticLibraryUnloading();
+ backend = pluginBackend;
+ return;
+ }
+ catch (...)
+ {
+ CV_LOG_WARNING(NULL, "UI: exception during plugin initialization: " << toPrintablePath(plugin) << ". SKIP");
+ }
+ }
+}
+
+#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS)
+
+} // namespace
+
+namespace highgui_backend {
+
+std::shared_ptr<IUIBackendFactory> createPluginUIBackendFactory(const std::string& baseName)
+{
+#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS)
+ return std::make_shared<impl::PluginUIBackendFactory>(baseName);
+#else
+ CV_UNUSED(baseName);
+ return std::shared_ptr<IUIBackendFactory>();
+#endif
+}
+
+}} // namespace
#ifndef __HIGHGUI_H_
#define __HIGHGUI_H_
+#if defined(__OPENCV_BUILD) && defined(BUILD_PLUGIN)
+#undef __OPENCV_BUILD // allow public API only
+#endif
+
#include "opencv2/highgui.hpp"
#include "opencv2/core/utility.hpp"
+#if defined(__OPENCV_BUILD)
#include "opencv2/core/private.hpp"
+#endif
#include "opencv2/imgproc.hpp"
#include "opencv2/imgproc/imgproc_c.h"
}
+namespace cv {
+
+CV_EXPORTS Mutex& getWindowMutex();
+static inline Mutex& getInitializationMutex() { return getWindowMutex(); }
+
+} // namespace
+
#endif /* __HIGHGUI_H_ */
--- /dev/null
+// 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_REGISTRY_HPP
+#define OPENCV_HIGHGUI_REGISTRY_HPP
+
+#include "factory.hpp"
+
+namespace cv { namespace highgui_backend {
+
+struct BackendInfo
+{
+ int priority; // 1000-<index*10> - default builtin priority
+ // 0 - disabled (OPENCV_UI_PRIORITY_<name> = 0)
+ // >10000 - prioritized list (OPENCV_UI_PRIORITY_LIST)
+ std::string name;
+ std::shared_ptr<IUIBackendFactory> backendFactory;
+};
+
+const std::vector<BackendInfo>& getBackendsInfo();
+
+}} // namespace
+
+#endif // OPENCV_HIGHGUI_REGISTRY_HPP
--- /dev/null
+// 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.
+
+//
+// Not a standalone header, part of backend.cpp
+//
+
+#include "opencv2/core/utils/filesystem.private.hpp" // OPENCV_HAVE_FILESYSTEM_SUPPORT
+
+namespace cv { namespace highgui_backend {
+
+#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS)
+#define DECLARE_DYNAMIC_BACKEND(name) \
+BackendInfo { \
+ 1000, name, createPluginUIBackendFactory(name) \
+},
+#else
+#define DECLARE_DYNAMIC_BACKEND(name) /* nothing */
+#endif
+
+#define DECLARE_STATIC_BACKEND(name, createBackendAPI) \
+BackendInfo { \
+ 1000, name, std::make_shared<cv::highgui_backend::StaticBackendFactory>([=] () -> std::shared_ptr<cv::highgui_backend::UIBackend> { return createBackendAPI(); }) \
+},
+
+static
+std::vector<BackendInfo>& getBuiltinBackendsInfo()
+{
+ static std::vector<BackendInfo> g_backends
+ {
+#ifdef HAVE_GTK
+ DECLARE_STATIC_BACKEND("GTK", createUIBackendGTK)
+#if defined(HAVE_GTK3)
+ DECLARE_STATIC_BACKEND("GTK3", createUIBackendGTK)
+#elif defined(HAVE_GTK2)
+ DECLARE_STATIC_BACKEND("GTK2", createUIBackendGTK)
+#else
+#warning "HAVE_GTK definition issue. Register new GTK backend"
+#endif
+#elif defined(ENABLE_PLUGINS)
+ DECLARE_DYNAMIC_BACKEND("GTK")
+ DECLARE_DYNAMIC_BACKEND("GTK3")
+ DECLARE_DYNAMIC_BACKEND("GTK2")
+#endif
+
+#if 0 // TODO
+#ifdef HAVE_QT
+ DECLARE_STATIC_BACKEND("QT", createUIBackendQT)
+#elif defined(ENABLE_PLUGINS)
+ DECLARE_DYNAMIC_BACKEND("QT")
+#endif
+#endif
+ };
+ return g_backends;
+};
+
+static
+bool sortByPriority(const BackendInfo &lhs, const BackendInfo &rhs)
+{
+ return lhs.priority > rhs.priority;
+}
+
+/** @brief Manages list of enabled backends
+ */
+class UIBackendRegistry
+{
+protected:
+ std::vector<BackendInfo> enabledBackends;
+ UIBackendRegistry()
+ {
+ enabledBackends = getBuiltinBackendsInfo();
+ int N = (int)enabledBackends.size();
+ for (int i = 0; i < N; i++)
+ {
+ BackendInfo& info = enabledBackends[i];
+ info.priority = 1000 - i * 10;
+ }
+ CV_LOG_DEBUG(NULL, "UI: Builtin backends(" << N << "): " << dumpBackends());
+ if (readPrioritySettings())
+ {
+ CV_LOG_INFO(NULL, "UI: Updated backends priorities: " << dumpBackends());
+ N = (int)enabledBackends.size();
+ }
+ int enabled = 0;
+ for (int i = 0; i < N; i++)
+ {
+ BackendInfo& info = enabledBackends[enabled];
+ if (enabled != i)
+ info = enabledBackends[i];
+ size_t param_priority = utils::getConfigurationParameterSizeT(cv::format("OPENCV_UI_PRIORITY_%s", info.name.c_str()).c_str(), (size_t)info.priority);
+ CV_Assert(param_priority == (size_t)(int)param_priority); // overflow check
+ if (param_priority > 0)
+ {
+ info.priority = (int)param_priority;
+ enabled++;
+ }
+ else
+ {
+ CV_LOG_INFO(NULL, "UI: Disable backend: " << info.name);
+ }
+ }
+ enabledBackends.resize(enabled);
+ CV_LOG_DEBUG(NULL, "UI: Available backends(" << enabled << "): " << dumpBackends());
+ std::sort(enabledBackends.begin(), enabledBackends.end(), sortByPriority);
+ CV_LOG_INFO(NULL, "UI: Enabled backends(" << enabled << ", sorted by priority): " << (enabledBackends.empty() ? std::string("N/A") : dumpBackends()));
+ }
+
+ static std::vector<std::string> tokenize_string(const std::string& input, char token)
+ {
+ std::vector<std::string> result;
+ std::string::size_type prev_pos = 0, pos = 0;
+ while((pos = input.find(token, pos)) != std::string::npos)
+ {
+ result.push_back(input.substr(prev_pos, pos-prev_pos));
+ prev_pos = ++pos;
+ }
+ result.push_back(input.substr(prev_pos));
+ return result;
+ }
+ bool readPrioritySettings()
+ {
+ bool hasChanges = false;
+ cv::String prioritized_backends = utils::getConfigurationParameterString("OPENCV_UI_PRIORITY_LIST", NULL);
+ if (prioritized_backends.empty())
+ return hasChanges;
+ CV_LOG_INFO(NULL, "UI: Configured priority list (OPENCV_UI_PRIORITY_LIST): " << prioritized_backends);
+ const std::vector<std::string> names = tokenize_string(prioritized_backends, ',');
+ for (size_t i = 0; i < names.size(); i++)
+ {
+ const std::string& name = names[i];
+ int priority = (int)(100000 + (names.size() - i) * 1000);
+ bool found = false;
+ for (size_t k = 0; k < enabledBackends.size(); k++)
+ {
+ BackendInfo& info = enabledBackends[k];
+ if (name == info.name)
+ {
+ info.priority = priority;
+ CV_LOG_DEBUG(NULL, "UI: New backend priority: '" << name << "' => " << info.priority);
+ found = true;
+ hasChanges = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ CV_LOG_INFO(NULL, "UI: Adding backend (plugin): '" << name << "'");
+ enabledBackends.push_back(BackendInfo{priority, name, createPluginUIBackendFactory(name)});
+ hasChanges = true;
+ }
+ }
+ return hasChanges;
+ }
+public:
+ std::string dumpBackends() const
+ {
+ std::ostringstream os;
+ for (size_t i = 0; i < enabledBackends.size(); i++)
+ {
+ if (i > 0) os << "; ";
+ const BackendInfo& info = enabledBackends[i];
+ os << info.name << '(' << info.priority << ')';
+ }
+ return os.str();
+ }
+
+ static UIBackendRegistry& getInstance()
+ {
+ static UIBackendRegistry g_instance;
+ return g_instance;
+ }
+
+ inline const std::vector<BackendInfo>& getEnabledBackends() const { return enabledBackends; }
+};
+
+
+const std::vector<BackendInfo>& getBackendsInfo()
+{
+ return cv::highgui_backend::UIBackendRegistry::getInstance().getEnabledBackends();
+}
+
+}} // namespace
//M*/
#include "precomp.hpp"
-#include <map>
+#include "backend.hpp"
+
#include "opencv2/core/opengl.hpp"
#include "opencv2/core/utils/logger.hpp"
// in later times, use this file as a dispatcher to implementations like cvcap.cpp
+
+using namespace cv;
+using namespace cv::highgui_backend;
+
+namespace cv {
+
+Mutex& getWindowMutex()
+{
+ static Mutex* g_window_mutex = new Mutex();
+ return *g_window_mutex;
+}
+
+namespace impl {
+
+typedef std::map<std::string, highgui_backend::UIWindowBase::Ptr> WindowsMap_t;
+static WindowsMap_t& getWindowsMap()
+{
+ static WindowsMap_t g_windowsMap;
+ return g_windowsMap;
+}
+
+static std::shared_ptr<UIWindow> findWindow_(const std::string& name)
+{
+ cv::AutoLock lock(cv::getWindowMutex());
+ auto& windowsMap = getWindowsMap();
+ auto i = windowsMap.find(name);
+ if (i != windowsMap.end())
+ {
+ const auto& ui_base = i->second;
+ if (ui_base)
+ {
+ if (!ui_base->isActive())
+ {
+ windowsMap.erase(i);
+ return std::shared_ptr<UIWindow>();
+ }
+ auto window = std::dynamic_pointer_cast<UIWindow>(ui_base);
+ return window;
+ }
+ }
+ return std::shared_ptr<UIWindow>();
+}
+
+static void cleanupTrackbarCallbacksWithData_(); // forward declaration
+
+static void cleanupClosedWindows_()
+{
+ cv::AutoLock lock(cv::getWindowMutex());
+ auto& windowsMap = getWindowsMap();
+ for (auto it = windowsMap.begin(); it != windowsMap.end();)
+ {
+ const auto& ui_base = it->second;
+ bool erase = (!ui_base || !ui_base->isActive());
+ if (erase)
+ {
+ it = windowsMap.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ cleanupTrackbarCallbacksWithData_();
+}
+
+// Just to support deprecated API, to be removed
+struct TrackbarCallbackWithData
+{
+ std::weak_ptr<UITrackbar> trackbar_;
+ int* data_;
+ TrackbarCallback callback_;
+ void* userdata_;
+
+ TrackbarCallbackWithData(int* data, TrackbarCallback callback, void* userdata)
+ : data_(data)
+ , callback_(callback), userdata_(userdata)
+ {
+ // trackbar_ is initialized separatelly
+ }
+
+ ~TrackbarCallbackWithData()
+ {
+ CV_LOG_DEBUG(NULL, "UI/Trackbar: Cleanup deprecated TrackbarCallbackWithData");
+ }
+
+ void onChange(int pos)
+ {
+ if (data_)
+ *data_ = pos;
+ if (callback_)
+ callback_(pos, userdata_);
+ }
+
+ static void onChangeCallback(int pos, void* userdata)
+ {
+ TrackbarCallbackWithData* thiz = (TrackbarCallbackWithData*)userdata;
+ CV_Assert(thiz);
+ return thiz->onChange(pos);
+ }
+};
+
+typedef std::vector< std::shared_ptr<TrackbarCallbackWithData> > TrackbarCallbacksWithData_t;
+static TrackbarCallbacksWithData_t& getTrackbarCallbacksWithData()
+{
+ static TrackbarCallbacksWithData_t g_trackbarCallbacksWithData;
+ return g_trackbarCallbacksWithData;
+}
+
+static void cleanupTrackbarCallbacksWithData_()
+{
+ cv::AutoLock lock(cv::getWindowMutex());
+ auto& callbacks = getTrackbarCallbacksWithData();
+ for (auto it = callbacks.begin(); it != callbacks.end();)
+ {
+ const auto& cb = *it;
+ bool erase = (!cb || cb->trackbar_.expired());
+ if (erase)
+ {
+ it = callbacks.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
+}} // namespace cv::impl
+
+using namespace cv::impl;
+
CV_IMPL void cvSetWindowProperty(const char* name, int prop_id, double prop_value)
{
+ CV_TRACE_FUNCTION();
+ CV_Assert(name);
+
+ {
+ auto window = findWindow_(name);
+ if (window)
+ {
+ /*bool res = */window->setProperty(prop_id, prop_value);
+ return;
+ }
+ }
+
switch(prop_id)
{
//change between fullscreen or not.
/* return -1 if error */
CV_IMPL double cvGetWindowProperty(const char* name, int prop_id)
{
- if (!name)
- return -1;
+ CV_TRACE_FUNCTION();
+ CV_Assert(name);
+
+ {
+ auto window = findWindow_(name);
+ if (window)
+ {
+ double v = window->getProperty(prop_id);
+ if (cvIsNaN(v))
+ return -1;
+ return v;
+ }
+ }
switch(prop_id)
{
cv::Rect cvGetWindowImageRect(const char* name)
{
+ CV_TRACE_FUNCTION();
if (!name)
return cv::Rect(-1, -1, -1, -1);
+ {
+ auto window = findWindow_(name);
+ if (window)
+ {
+ return window->getImageRect();
+ }
+ }
+
#if defined (HAVE_QT)
return cvGetWindowRect_QT(name);
#elif defined(HAVE_WIN32UI)
void cv::namedWindow( const String& winname, int flags )
{
CV_TRACE_FUNCTION();
+ CV_Assert(!winname.empty());
+
+ {
+ cv::AutoLock lock(cv::getWindowMutex());
+ cleanupClosedWindows_();
+ auto& windowsMap = getWindowsMap();
+ auto i = windowsMap.find(winname);
+ if (i != windowsMap.end())
+ {
+ auto ui_base = i->second;
+ if (ui_base)
+ {
+ auto window = std::dynamic_pointer_cast<UIWindow>(ui_base);
+ if (!window)
+ {
+ CV_LOG_ERROR(NULL, "OpenCV/UI: Can't create window: '" << winname << "'");
+ }
+ return;
+ }
+ }
+ auto backend = getCurrentUIBackend();
+ if (backend)
+ {
+ auto window = backend->createWindow(winname, flags);
+ if (!window)
+ {
+ CV_LOG_ERROR(NULL, "OpenCV/UI: Can't create window: '" << winname << "'");
+ return;
+ }
+ windowsMap.emplace(winname, window);
+ return;
+ }
+ }
+
cvNamedWindow( winname.c_str(), flags );
}
void cv::destroyWindow( const String& winname )
{
CV_TRACE_FUNCTION();
+
+ {
+ auto window = findWindow_(winname);
+ if (window)
+ {
+ window->destroy();
+ cleanupClosedWindows_();
+ return;
+ }
+ }
+
cvDestroyWindow( winname.c_str() );
}
void cv::destroyAllWindows()
{
CV_TRACE_FUNCTION();
+
+ {
+ cv::AutoLock lock(cv::getWindowMutex());
+ auto backend = getCurrentUIBackend();
+ if (backend)
+ {
+ backend->destroyAllWindows();
+ cleanupClosedWindows_();
+ return;
+ }
+ }
+
cvDestroyAllWindows();
}
void cv::resizeWindow( const String& winname, int width, int height )
{
CV_TRACE_FUNCTION();
+
+ {
+ auto window = findWindow_(winname);
+ if (window)
+ {
+ return window->resize(width, height);
+ }
+ }
+
cvResizeWindow( winname.c_str(), width, height );
}
void cv::moveWindow( const String& winname, int x, int y )
{
CV_TRACE_FUNCTION();
+
+ {
+ auto window = findWindow_(winname);
+ if (window)
+ {
+ return window->move(x, y);
+ }
+ }
+
cvMoveWindow( winname.c_str(), x, y );
}
int cv::waitKeyEx(int delay)
{
CV_TRACE_FUNCTION();
+
+ {
+ cv::AutoLock lock(cv::getWindowMutex());
+ auto backend = getCurrentUIBackend();
+ if (backend)
+ {
+ return backend->waitKeyEx(delay);
+ }
+ }
+
return cvWaitKey(delay);
}
int cv::pollKey()
{
CV_TRACE_FUNCTION();
+
+ {
+ cv::AutoLock lock(cv::getWindowMutex());
+ auto backend = getCurrentUIBackend();
+ if (backend)
+ {
+ return backend->pollKey();
+ }
+ }
+
// fallback. please implement a proper polling function
return cvWaitKey(1);
}
void* userdata)
{
CV_TRACE_FUNCTION();
+
+ CV_LOG_IF_WARNING(NULL, value, "UI/Trackbar(" << trackbarName << "@" << winName << "): Using 'value' pointer is unsafe and deprecated. Use NULL as value pointer. "
+ "To fetch trackbar value setup callback.");
+
+ {
+ cv::AutoLock lock(cv::getWindowMutex());
+ auto window = findWindow_(winName);
+ if (window)
+ {
+ if (value)
+ {
+ auto cb = std::make_shared<TrackbarCallbackWithData>(value, callback, userdata);
+ auto trackbar = window->createTrackbar(trackbarName, count, TrackbarCallbackWithData::onChangeCallback, cb.get());
+ if (!trackbar)
+ {
+ CV_LOG_ERROR(NULL, "OpenCV/UI: Can't create trackbar: '" << trackbarName << "'@'" << winName << "'");
+ return 0;
+ }
+ cb->trackbar_ = trackbar;
+ getTrackbarCallbacksWithData().emplace_back(cb);
+ getWindowsMap().emplace(trackbar->getID(), trackbar);
+ trackbar->setPos(*value);
+ return 1;
+ }
+ else
+ {
+ auto trackbar = window->createTrackbar(trackbarName, count, callback, userdata);
+ if (!trackbar)
+ {
+ CV_LOG_ERROR(NULL, "OpenCV/UI: Can't create trackbar: '" << trackbarName << "'@'" << winName << "'");
+ return 0;
+ }
+ getWindowsMap().emplace(trackbar->getID(), trackbar);
+ return 1;
+ }
+ }
+ }
+
return cvCreateTrackbar2(trackbarName.c_str(), winName.c_str(),
value, count, callback, userdata);
}
void cv::setTrackbarPos( const String& trackbarName, const String& winName, int value )
{
CV_TRACE_FUNCTION();
+
+ {
+ cv::AutoLock lock(cv::getWindowMutex());
+ auto window = findWindow_(winName);
+ if (window)
+ {
+ auto trackbar = window->findTrackbar(trackbarName);
+ CV_Assert(trackbar);
+ return trackbar->setPos(value);
+ }
+ }
+
cvSetTrackbarPos(trackbarName.c_str(), winName.c_str(), value );
}
void cv::setTrackbarMax(const String& trackbarName, const String& winName, int maxval)
{
CV_TRACE_FUNCTION();
+
+ {
+ cv::AutoLock lock(cv::getWindowMutex());
+ auto window = findWindow_(winName);
+ if (window)
+ {
+ auto trackbar = window->findTrackbar(trackbarName);
+ CV_Assert(trackbar);
+ Range old_range = trackbar->getRange();
+ Range range(std::min(old_range.start, maxval), maxval);
+ return trackbar->setRange(range);
+ }
+ }
+
cvSetTrackbarMax(trackbarName.c_str(), winName.c_str(), maxval);
}
void cv::setTrackbarMin(const String& trackbarName, const String& winName, int minval)
{
CV_TRACE_FUNCTION();
+
+ {
+ cv::AutoLock lock(cv::getWindowMutex());
+ auto window = findWindow_(winName);
+ if (window)
+ {
+ auto trackbar = window->findTrackbar(trackbarName);
+ CV_Assert(trackbar);
+ Range old_range = trackbar->getRange();
+ Range range(minval, std::max(minval, old_range.end));
+ return trackbar->setRange(range);
+ }
+ }
+
cvSetTrackbarMin(trackbarName.c_str(), winName.c_str(), minval);
}
int cv::getTrackbarPos( const String& trackbarName, const String& winName )
{
CV_TRACE_FUNCTION();
+
+ {
+ cv::AutoLock lock(cv::getWindowMutex());
+ auto window = findWindow_(winName);
+ if (window)
+ {
+ auto trackbar = window->findTrackbar(trackbarName);
+ CV_Assert(trackbar);
+ return trackbar->getPos();
+ }
+ }
+
return cvGetTrackbarPos(trackbarName.c_str(), winName.c_str());
}
void cv::setMouseCallback( const String& windowName, MouseCallback onMouse, void* param)
{
CV_TRACE_FUNCTION();
+
+ {
+ cv::AutoLock lock(cv::getWindowMutex());
+ auto window = findWindow_(windowName);
+ if (window)
+ {
+ return window->setMouseCallback(onMouse, param);
+ }
+ }
+
cvSetMouseCallback(windowName.c_str(), onMouse, param);
}
void cv::imshow( const String& winname, InputArray _img )
{
CV_TRACE_FUNCTION();
+
+ {
+ cv::AutoLock lock(cv::getWindowMutex());
+ cleanupClosedWindows_();
+ auto& windowsMap = getWindowsMap();
+ auto i = windowsMap.find(winname);
+ if (i != windowsMap.end())
+ {
+ auto ui_base = i->second;
+ if (ui_base)
+ {
+ auto window = std::dynamic_pointer_cast<UIWindow>(ui_base);
+ if (!window)
+ {
+ CV_LOG_ERROR(NULL, "OpenCV/UI: invalid window name: '" << winname << "'");
+ }
+ return window->imshow(_img);
+ }
+ }
+ auto backend = getCurrentUIBackend();
+ if (backend)
+ {
+ auto window = backend->createWindow(winname, WINDOW_NORMAL);
+ if (!window)
+ {
+ CV_LOG_ERROR(NULL, "OpenCV/UI: Can't create window: '" << winname << "'");
+ return;
+ }
+ windowsMap.emplace(winname, window);
+ return window->imshow(_img);
+ }
+ }
+
const Size size = _img.size();
#ifndef HAVE_OPENGL
CV_Assert(size.width>0 && size.height>0);
if (t) //trackbar exists
return;
- if (!value)
- CV_Error(CV_StsNullPtr, "NULL value pointer" );
-
if (count <= 0) //count is the max value of the slider, so must be bigger than 0
CV_Error(CV_StsNullPtr, "Max value of the slider must be bigger than 0" );
slider->setMinimum(0);
slider->setMaximum(_count);
slider->setPageStep(5);
- slider->setValue(*value);
+ if (dataSlider)
+ slider->setValue(*dataSlider);
slider->setTickPosition(QSlider::TicksBelow);
{
setLabel(myvalue);
- *dataSlider = myvalue;
+ if (dataSlider)
+ *dataSlider = myvalue;
if (callback)
{
callback(myvalue);
QPointer<QPushButton > label;
CvTrackbarCallback callback;
CvTrackbarCallback2 callback2;//look like it is use by python binding
- int* dataSlider;
+ int* dataSlider; // deprecated
void* userdata;
};
//M*/
#include "precomp.hpp"
-
-#ifndef _WIN32
+#include "backend.hpp"
#if defined (HAVE_GTK)
/** Allocate new image viewer widget */
GtkWidget* cvImageWidgetNew (int flags);
-/** Set the image to display in the widget */
-void cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr);
-
// standard GTK object macros
#define CV_IMAGE_WIDGET(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, cvImageWidget_get_type (), CvImageWidget)
#define CV_IMAGE_WIDGET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, cvImageWidget_get_type (), CvImageWidgetClass)
// flag to help size initial window
#define CV_WINDOW_NO_IMAGE 2
-void cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr){
+/** Set the image to display in the widget */
+static
+void cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr)
+{
CvMat * mat, stub;
int origin=0;
CvImageWidget *image_widget;
image_widget = CV_IMAGE_WIDGET( gtk_widget_new (cvImageWidget_get_type (), NULL) );
+ CV_Assert(image_widget && "GTK widget creation is failed. Ensure that there is no GTK2/GTK3 libraries conflict");
image_widget->original_image = 0;
image_widget->scaled_image = 0;
image_widget->flags = flags | CV_WINDOW_NO_IMAGE;
struct CvTrackbar : CvUIBase
{
- CvTrackbar(const char* trackbar_name) :
+ CvTrackbar(const std::string& trackbar_name) :
CvUIBase(CV_TRACKBAR_MAGIC_VAL),
widget(NULL), name(trackbar_name),
parent(NULL), data(NULL),
pos(0), maxval(0), minval(0),
- notify(NULL), notify2(NULL), userdata(NULL)
+ notify(NULL), notify2(NULL), // deprecated
+ onChangeCallback(NULL), userdata(NULL)
{
// nothing
}
GtkWidget* widget;
std::string name;
- CvWindow* parent;
+ CvWindow* parent; // TODO weak_ptr
int* data;
int pos;
int maxval;
int minval;
- CvTrackbarCallback notify;
- CvTrackbarCallback2 notify2;
+ CvTrackbarCallback notify; // deprecated
+ CvTrackbarCallback2 notify2; // deprecated
+ TrackbarCallback onChangeCallback;
void* userdata;
};
struct CvWindow : CvUIBase
{
- CvWindow(const char* window_name) :
+ CvWindow(const std::string& window_name) :
CvUIBase(CV_WINDOW_MAGIC_VAL),
widget(NULL), frame(NULL), paned(NULL), name(window_name),
last_key(0), flags(0), status(0),
,useGl(false), glDrawCallback(NULL), glDrawData(NULL)
#endif
{
- // nothing
+ CV_LOG_INFO(NULL, "OpenCV/UI: creating GTK window: " << window_name);
}
~CvWindow();
+ void destroy();
GtkWidget* widget;
GtkWidget* frame;
CvMouseCallback on_mouse;
void* on_mouse_param;
- std::vector< Ptr<CvTrackbar> > trackbars;
+ std::vector< std::shared_ptr<CvTrackbar> > trackbars;
#ifdef HAVE_OPENGL
bool useGl;
GThread* window_thread = NULL;
#endif
-static cv::Mutex& getWindowMutex()
+static int last_key = -1;
+
+static
+std::vector< std::shared_ptr<CvWindow> >& getGTKWindows()
{
- static cv::Mutex* g_window_mutex = new cv::Mutex();
- return *g_window_mutex;
+ static std::vector< std::shared_ptr<CvWindow> > g_windows;
+ return g_windows;
}
-static int last_key = -1;
-static std::vector< Ptr<CvWindow> > g_windows;
-
CV_IMPL int cvInitSystem( int argc, char** argv )
{
static int wasInitialized = 0;
#define CV_LOCK_MUTEX() cv::AutoLock lock(getWindowMutex())
-static CvWindow* icvFindWindowByName( const char* name )
+static
+std::shared_ptr<CvWindow> icvFindWindowByName(const std::string& name)
{
+ auto& g_windows = getGTKWindows();
for(size_t i = 0; i < g_windows.size(); ++i)
{
- CvWindow* window = g_windows[i].get();
+ auto window = g_windows[i];
+ if (!window)
+ continue;
if (window->name == name)
return window;
}
- return NULL;
+ return std::shared_ptr<CvWindow>();
}
+static inline
+std::shared_ptr<CvWindow> icvFindWindowByName(const char* name)
+{
+ CV_Assert(name);
+ return icvFindWindowByName(std::string(name));
+}
+
+
static CvWindow* icvWindowByWidget( GtkWidget* widget )
{
+ auto& g_windows = getGTKWindows();
for (size_t i = 0; i < g_windows.size(); ++i)
{
CvWindow* window = g_windows[i].get();
return NULL;
}
+static Rect getImageRect_(const std::shared_ptr<CvWindow>& window);
+
CvRect cvGetWindowRect_GTK(const char* name)
{
CV_Assert(name && "NULL name string");
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(name);
+ const auto window = icvFindWindowByName(name);
if (!window)
CV_Error( CV_StsNullPtr, "NULL window" );
+ return cvRect(getImageRect_(window));
+}
+
+static Rect getImageRect_(const std::shared_ptr<CvWindow>& window)
+{
+ CV_Assert(window);
+
gint wx, wy;
#ifdef HAVE_OPENGL
if (window->useGl) {
gtk_widget_translate_coordinates(window->widget, gtk_widget_get_toplevel(window->widget), 0, 0, &wx, &wy);
- return cvRect(wx, wy, window->widget->allocation.width, window->widget->allocation.height);
+ return Rect(wx, wy, window->widget->allocation.width, window->widget->allocation.height);
}
#endif
gtk_widget_translate_coordinates(&image_widget->widget, gtk_widget_get_toplevel(&image_widget->widget), 0, 0, &wx, &wy);
if (image_widget->scaled_image) {
#if defined (GTK_VERSION3)
- return cvRect(wx, wy, MIN(image_widget->scaled_image->cols, gtk_widget_get_allocated_width(window->widget)),
+ return Rect(wx, wy, MIN(image_widget->scaled_image->cols, gtk_widget_get_allocated_width(window->widget)),
MIN(image_widget->scaled_image->rows, gtk_widget_get_allocated_height(window->widget)));
#else
- return cvRect(wx, wy, MIN(image_widget->scaled_image->cols, window->widget->allocation.width),
+ return Rect(wx, wy, MIN(image_widget->scaled_image->cols, window->widget->allocation.width),
MIN(image_widget->scaled_image->rows, window->widget->allocation.height));
#endif //GTK_VERSION3
} else if (image_widget->original_image) {
#if defined (GTK_VERSION3)
- return cvRect(wx, wy, MIN(image_widget->original_image->cols, gtk_widget_get_allocated_width(window->widget)),
+ return Rect(wx, wy, MIN(image_widget->original_image->cols, gtk_widget_get_allocated_width(window->widget)),
MIN(image_widget->original_image->rows, gtk_widget_get_allocated_height(window->widget)));
#else
- return cvRect(wx, wy, MIN(image_widget->original_image->cols, window->widget->allocation.width),
+ return Rect(wx, wy, MIN(image_widget->original_image->cols, window->widget->allocation.width),
MIN(image_widget->original_image->rows, window->widget->allocation.height));
#endif //GTK_VERSION3
}
- return cvRect(-1, -1, -1, -1);
+ return Rect(-1, -1, -1, -1);
}
double cvGetModeWindow_GTK(const char* name)//YV
CV_Assert(name && "NULL name string");
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(name);
+ const auto window = icvFindWindowByName(name);
if (!window)
CV_Error( CV_StsNullPtr, "NULL window" );
return result;
}
-
+static bool setModeWindow_(const std::shared_ptr<CvWindow>& window, int mode);
void cvSetModeWindow_GTK( const char* name, double prop_value)//Yannick Verdie
{
CV_Assert(name && "NULL name string");
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(name);
- if( !window )
+ const auto window = icvFindWindowByName(name);
+ if (!window)
CV_Error( CV_StsNullPtr, "NULL window" );
- if(window->flags & CV_WINDOW_AUTOSIZE)//if the flag CV_WINDOW_AUTOSIZE is set
- return;
+ setModeWindow_(window, (int)prop_value);
+}
+
+static bool setModeWindow_(const std::shared_ptr<CvWindow>& window, int mode)
+{
+ if (window->flags & CV_WINDOW_AUTOSIZE) //if the flag CV_WINDOW_AUTOSIZE is set
+ return false;
//so easy to do fullscreen here, Linux rocks !
- if (window->status==CV_WINDOW_FULLSCREEN && prop_value==CV_WINDOW_NORMAL)
+ if (window->status == mode)
+ return true;
+
+ if (window->status==CV_WINDOW_FULLSCREEN && mode==CV_WINDOW_NORMAL)
{
gtk_window_unfullscreen(GTK_WINDOW(window->frame));
window->status=CV_WINDOW_NORMAL;
- return;
+ return true;
}
- if (window->status==CV_WINDOW_NORMAL && prop_value==CV_WINDOW_FULLSCREEN)
+ if (window->status==CV_WINDOW_NORMAL && mode==CV_WINDOW_FULLSCREEN)
{
gtk_window_fullscreen(GTK_WINDOW(window->frame));
window->status=CV_WINDOW_FULLSCREEN;
- return;
+ return true;
}
+
+ return false;
}
void cv::setWindowTitle(const String& winname, const String& title)
{
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(winname.c_str());
+ auto window = icvFindWindowByName(winname.c_str());
if (!window)
{
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(name);
+ const auto window = icvFindWindowByName(name);
if (!window)
return -1; // keep silence here
return result;
}
+static double getRatioWindow_(const std::shared_ptr<CvWindow>& window);
double cvGetRatioWindow_GTK(const char* name)
{
CV_Assert(name && "NULL name string");
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(name);
+ const auto window = icvFindWindowByName(name);
if (!window)
return -1; // keep silence here
+ return getRatioWindow_(window);
+}
+
+static double getRatioWindow_(const std::shared_ptr<CvWindow>& window)
+{
#if defined (GTK_VERSION3)
double result = static_cast<double>(
gtk_widget_get_allocated_width(window->widget)) / gtk_widget_get_allocated_height(window->widget);
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(name);
+ const auto window = icvFindWindowByName(name);
if (!window)
return -1; // keep silence here
}
#endif //GTK_VERSION3
+static std::shared_ptr<CvWindow> namedWindow_(const std::string& name, int flags);
CV_IMPL int cvNamedWindow( const char* name, int flags )
{
cvInitSystem(name ? 1 : 0,(char**)&name);
{
return 1;
}
+ auto window = namedWindow_(name, flags);
+ return window ? 1 : 0;
+}
- Ptr<CvWindow> window = makePtr<CvWindow>(name);
+static std::shared_ptr<CvWindow> namedWindow_(const std::string& name, int flags)
+{
+ cvInitSystem(0, NULL);
+
+ auto window_ptr = std::make_shared<CvWindow>(name);
+ CvWindow* window = window_ptr.get();
window->flags = flags;
window->status = CV_WINDOW_NORMAL;//YV
#endif //GTK_VERSION3_4
gtk_widget_show( window->frame );
- gtk_window_set_title( GTK_WINDOW(window->frame), name );
+ gtk_window_set_title(GTK_WINDOW(window->frame), name.c_str());
- g_windows.push_back(window);
+ {
+ AutoLock lock(getWindowMutex());
+ getGTKWindows().push_back(window_ptr);
+ }
bool b_nautosize = ((flags & CV_WINDOW_AUTOSIZE) == 0);
gtk_window_set_resizable( GTK_WINDOW(window->frame), b_nautosize );
cvSetOpenGlContext(name);
#endif
- return 1;
+ return window_ptr;
}
CvWindow::~CvWindow()
{
+ if (frame)
+ destroy();
+}
+
+inline void CvWindow::destroy()
+{
+ CV_LOG_INFO(NULL, "OpenCV/UI: destroying GTK window: " << name);
gtk_widget_destroy(frame);
+ frame = nullptr;
}
static void checkLastWindow()
{
// if last window...
- if (g_windows.empty())
+ if (getGTKWindows().empty())
{
#ifdef HAVE_GTHREAD
if( thread_started )
}
}
-static void icvDeleteWindow( CvWindow* window )
+static
+void icvDeleteWindow_( CvWindow* window )
{
+ AutoLock lock(getWindowMutex());
+ auto& g_windows = getGTKWindows();
bool found = false;
- for (std::vector< Ptr<CvWindow> >::iterator i = g_windows.begin();
- i != g_windows.end(); ++i)
+ for (auto i = g_windows.begin(); i != g_windows.end(); ++i)
{
if (i->get() == window)
{
break;
}
}
- CV_Assert(found && "Can't destroy non-registered window");
-
+ CV_LOG_IF_WARNING(NULL, !found, "OpenCV/GTK: Can't destroy non-registered window");
checkLastWindow();
}
CV_Assert(name && "NULL name string");
CV_LOCK_MUTEX();
+ auto& g_windows = getGTKWindows();
bool found = false;
- for (std::vector< Ptr<CvWindow> >::iterator i = g_windows.begin();
- i != g_windows.end(); ++i)
+ for (auto i = g_windows.begin(); i != g_windows.end(); ++i)
{
if (i->get()->name == name)
{
break;
}
}
- CV_Assert(found && "Can't destroy non-registered window");
+ CV_LOG_IF_ERROR(NULL, !found, "OpenCV/GTK: Can't destroy non-registered window: '" << name << "'");
checkLastWindow();
}
{
CV_LOCK_MUTEX();
- g_windows.clear();
+ getGTKWindows().clear();
checkLastWindow();
}
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(name);
+ auto window = icvFindWindowByName(name);
if(!window)
{
cvNamedWindow(name, 1);
}
}
+static void resizeWindow_(const std::shared_ptr<CvWindow>& window, int width, int height);
CV_IMPL void cvResizeWindow(const char* name, int width, int height )
{
CV_Assert(name && "NULL name string");
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(name);
+ auto window = icvFindWindowByName(name);
if(!window)
return;
+ return resizeWindow_(window, width, height);
+}
+
+static
+void resizeWindow_(const std::shared_ptr<CvWindow>& window, int width, int height)
+{
+ CV_Assert(window);
CvImageWidget* image_widget = CV_IMAGE_WIDGET( window->widget );
//if(image_widget->flags & CV_WINDOW_AUTOSIZE)
//EXIT;
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(name);
+ const auto window = icvFindWindowByName(name);
if(!window)
return;
gtk_window_move( GTK_WINDOW(window->frame), x, y );
}
-
-static CvTrackbar*
-icvFindTrackbarByName( const CvWindow* window, const char* name )
+static
+std::shared_ptr<CvTrackbar> icvFindTrackbarByName(const std::shared_ptr<CvWindow>& window, const std::string& name)
{
- for (size_t i = 0; i < window->trackbars.size(); ++i)
+ CV_Assert(window);
+ auto& trackbars = window->trackbars;
+ for(size_t i = 0; i < trackbars.size(); ++i)
{
- CvTrackbar* trackbar = window->trackbars[i].get();
+ auto trackbar = trackbars[i];
+ if (!trackbar)
+ continue;
if (trackbar->name == name)
return trackbar;
}
- return NULL;
+ return std::shared_ptr<CvTrackbar>();
}
+
static int
icvCreateTrackbar( const char* trackbar_name, const char* window_name,
int* val, int count, CvTrackbarCallback on_notify,
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(window_name);
+ const auto window = icvFindWindowByName(window_name);
if(!window)
return 0;
- CvTrackbar* trackbar = icvFindTrackbarByName(window, trackbar_name);
- if (!trackbar)
+ auto trackbar_ = icvFindTrackbarByName(window, trackbar_name);
+ if (!trackbar_)
{
- Ptr<CvTrackbar> trackbar_ = makePtr<CvTrackbar>(trackbar_name);
- trackbar = trackbar_.get();
- trackbar->parent = window;
+ trackbar_ = std::make_shared<CvTrackbar>(trackbar_name);
+ CvTrackbar* trackbar = trackbar_.get();
+ trackbar->parent = window.get();
window->trackbars.push_back(trackbar_);
GtkWidget* hscale_box = gtk_hbox_new( FALSE, 10 );
gtk_widget_show( hscale_box );
}
+ CvTrackbar* trackbar = trackbar_.get(); CV_DbgAssert(trackbar);
+
if( val )
{
int value = *val;
return 1;
}
-
CV_IMPL int
cvCreateTrackbar( const char* trackbar_name, const char* window_name,
int* val, int count, CvTrackbarCallback on_notify )
on_notify, 0, 0);
}
-
CV_IMPL int
cvCreateTrackbar2( const char* trackbar_name, const char* window_name,
int* val, int count, CvTrackbarCallback2 on_notify2,
0, on_notify2, userdata);
}
+static
+std::shared_ptr<CvTrackbar> createTrackbar_(
+ const std::shared_ptr<CvWindow>& window, const std::string& name,
+ int count,
+ TrackbarCallback onChange, void* userdata
+)
+{
+ CV_Assert(window);
+ CV_Assert(!name.empty());
+
+ if (count <= 0)
+ CV_Error(Error::StsOutOfRange, "Bad trackbar maximal value");
+
+ auto trackbar_ = std::make_shared<CvTrackbar>(name);
+ CvTrackbar* trackbar = trackbar_.get();
+ trackbar->parent = window.get();
+ window->trackbars.push_back(trackbar_);
+
+ GtkWidget* hscale_box = gtk_hbox_new( FALSE, 10 );
+ GtkWidget* hscale_label = gtk_label_new(name.c_str());
+ GtkWidget* hscale = gtk_hscale_new_with_range( 0, count, 1 );
+ gtk_scale_set_digits( GTK_SCALE(hscale), 0 );
+ //gtk_scale_set_value_pos( hscale, GTK_POS_TOP );
+ gtk_scale_set_draw_value( GTK_SCALE(hscale), TRUE );
+
+ trackbar->widget = hscale;
+ gtk_box_pack_start( GTK_BOX(hscale_box), hscale_label, FALSE, FALSE, 5 );
+ gtk_widget_show( hscale_label );
+ gtk_box_pack_start( GTK_BOX(hscale_box), hscale, TRUE, TRUE, 5 );
+ gtk_widget_show( hscale );
+ gtk_box_pack_start( GTK_BOX(window->paned), hscale_box, FALSE, FALSE, 5 );
+ gtk_widget_show( hscale_box );
+
+ trackbar->maxval = count;
+ trackbar->onChangeCallback = onChange;
+ trackbar->userdata = userdata;
+ g_signal_connect(trackbar->widget, "value-changed",
+ G_CALLBACK(icvOnTrackbar), trackbar);
+
+ // queue a widget resize to trigger a window resize to
+ // compensate for the addition of trackbars
+ gtk_widget_queue_resize(GTK_WIDGET(window->widget));
+
+ return trackbar_;
+}
+
CV_IMPL void
cvSetMouseCallback( const char* window_name, CvMouseCallback on_mouse, void* param )
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(window_name);
+ const auto window = icvFindWindowByName(window_name);
if (!window)
return;
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(window_name);
+ const auto window = icvFindWindowByName(window_name);
if (!window)
return -1;
- CvTrackbar* trackbar = icvFindTrackbarByName(window,trackbar_name);
+ const auto trackbar = icvFindTrackbarByName(window,trackbar_name);
if (!trackbar)
return -1;
return trackbar->pos;
}
-
+static void setTrackbarPos_(const std::shared_ptr<CvTrackbar>& trackbar, int pos);
CV_IMPL void cvSetTrackbarPos( const char* trackbar_name, const char* window_name, int pos )
{
CV_Assert(window_name && "NULL window name");
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(window_name);
+ const auto window = icvFindWindowByName(window_name);
if(!window)
return;
- CvTrackbar* trackbar = icvFindTrackbarByName(window,trackbar_name);
- if( trackbar )
- {
- if( pos < trackbar->minval )
- pos = trackbar->minval;
-
- if( pos > trackbar->maxval )
- pos = trackbar->maxval;
- }
- else
+ const auto trackbar = icvFindTrackbarByName(window, trackbar_name);
+ if (!trackbar)
{
CV_Error( CV_StsNullPtr, "No trackbar found" );
}
+ return setTrackbarPos_(trackbar, pos);
+}
+
+static void setTrackbarPos_(const std::shared_ptr<CvTrackbar>& trackbar, int pos)
+{
+ CV_Assert(trackbar);
+ CV_CheckLE(trackbar->minval, trackbar->maxval, "");
+
+ pos = std::max(pos, trackbar->minval);
+ pos = std::min(pos, trackbar->maxval);
+
gtk_range_set_value( GTK_RANGE(trackbar->widget), pos );
}
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(window_name);
+ const auto window = icvFindWindowByName(window_name);
if(!window)
return;
- CvTrackbar* trackbar = icvFindTrackbarByName(window,trackbar_name);
+ const auto trackbar = icvFindTrackbarByName(window,trackbar_name);
if(!trackbar)
return;
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(window_name);
+ const auto window = icvFindWindowByName(window_name);
if(!window)
return;
- CvTrackbar* trackbar = icvFindTrackbarByName(window,trackbar_name);
+ const auto trackbar = icvFindTrackbarByName(window,trackbar_name);
if(!trackbar)
return;
CV_LOCK_MUTEX();
- CvWindow* window = icvFindWindowByName(window_name);
+ const auto window = icvFindWindowByName(window_name);
if(!window)
return NULL;
trackbar->widget == widget )
{
trackbar->pos = pos;
+ if (trackbar->onChangeCallback)
+ trackbar->onChangeCallback(pos, trackbar->userdata);
+
+ // deprecated
if( trackbar->data )
*trackbar->data = pos;
if( trackbar->notify2 )
if( window->signature == CV_WINDOW_MAGIC_VAL &&
window->frame == widget )
{
- icvDeleteWindow(window);
+ try
+ {
+ icvDeleteWindow_(window);
+ }
+ catch (...)
+ {
+ CV_LOG_WARNING(NULL, "OpenCV/GTK: unexpected C++ exception in icvDeleteWindow_");
+ }
}
return TRUE;
}
expired = !g_cond_timed_wait(cond_have_key, last_key_mutex, &timer);
}
else{
- if (g_windows.empty())
+ if (getGTKWindows().empty())
{
CV_LOG_WARNING(NULL, "cv::waitKey() is called without timeout and missing active windows. Ignoring");
}
}
my_last_key = last_key;
g_mutex_unlock(last_key_mutex);
- if(expired || g_windows.empty()){
+ if (expired || getGTKWindows().empty())
+ {
return -1;
}
return my_last_key;
if( delay > 0 )
timer = g_timeout_add( delay, icvAlarm, &expired );
last_key = -1;
- while( gtk_main_iteration_do(TRUE) && last_key < 0 && !expired && (delay > 0 || !g_windows.empty()))
+ while( gtk_main_iteration_do(TRUE) && last_key < 0 && !expired && (delay > 0 || !getGTKWindows().empty()))
;
if( delay > 0 && !expired )
return last_key;
}
+namespace cv { namespace impl {
-#endif // HAVE_GTK
-#endif // _WIN32
+using namespace cv::highgui_backend;
+
+class GTKTrackbar;
+
+class GTKWindow
+ : public UIWindow
+ , public std::enable_shared_from_this<GTKWindow>
+{
+protected:
+ const std::string name_;
+ std::weak_ptr<CvWindow> window_;
+ std::map<std::string, std::shared_ptr<GTKTrackbar> > trackbars_;
+public:
+ GTKWindow(const std::string& name, const std::shared_ptr<CvWindow>& window)
+ : name_(name)
+ , window_(window)
+ {
+ // nothing
+ }
+
+ ~GTKWindow() CV_OVERRIDE
+ {
+ if (!window_.expired())
+ destroy();
+ CV_LOG_DEBUG(NULL, "OpenCV/UI/GTK: GTKWindow(" << name_ << ") is disposed");
+ }
+
+ const std::string& getID() const CV_OVERRIDE { return name_; }
+
+ bool isActive() const CV_OVERRIDE { return !window_.expired(); }
+
+ void destroy() CV_OVERRIDE
+ {
+ cv::AutoLock lock(getWindowMutex());
+ if (!window_.expired())
+ {
+ auto window = window_.lock();
+ if (window)
+ window->destroy();
+ window_.reset();
+ }
+ }
+
+ void imshow(InputArray image) CV_OVERRIDE
+ {
+ auto window = window_.lock();
+ CV_Assert(window);
+ CvImageWidget* image_widget = CV_IMAGE_WIDGET(window->widget);
+ CV_Assert(image_widget);
+ Mat img = image.getMat();
+ CvMat c_img = cvMat(img); // TODO Drop C-API
+ cvImageWidgetSetImage(image_widget, &c_img);
+ }
+
+ double getProperty(int prop) const CV_OVERRIDE
+ {
+ auto window = window_.lock();
+ CV_Assert(window);
+ // see cvGetWindowProperty
+ switch (prop)
+ {
+ case CV_WND_PROP_FULLSCREEN:
+ return (double)window->status;
-/* End of file. */
+ case CV_WND_PROP_AUTOSIZE:
+ return (window->flags & CV_WINDOW_AUTOSIZE) ? 1.0 : 0.0;
+
+ case CV_WND_PROP_ASPECTRATIO:
+ return getRatioWindow_(window);
+
+#ifdef HAVE_OPENGL
+ case CV_WND_PROP_OPENGL:
+ return window->useGl ? 1.0 : 0.0;
+#endif
+
+ default:
+ break;
+ }
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+
+ bool setProperty(int prop, double value) CV_OVERRIDE
+ {
+ auto window = window_.lock();
+ CV_Assert(window);
+ // see cvSetWindowProperty
+ switch (prop)
+ {
+ case CV_WND_PROP_FULLSCREEN:
+ if (value != CV_WINDOW_NORMAL && value != CV_WINDOW_FULLSCREEN) // bad arg
+ break;
+ setModeWindow_(window, value);
+ return true;
+
+ default:
+ break;
+ }
+ return false;
+ }
+
+ void resize(int width, int height) CV_OVERRIDE
+ {
+ auto window = window_.lock();
+ CV_Assert(window);
+ resizeWindow_(window, width, height);
+ }
+
+ void move(int x, int y) CV_OVERRIDE
+ {
+ auto window = window_.lock();
+ CV_Assert(window);
+ gtk_window_move(GTK_WINDOW(window->frame), x, y);
+ }
+
+ Rect getImageRect() const CV_OVERRIDE
+ {
+ auto window = window_.lock();
+ CV_Assert(window);
+ return getImageRect_(window);
+ }
+
+ void setTitle(const std::string& title) CV_OVERRIDE
+ {
+ auto window = window_.lock();
+ CV_Assert(window);
+ gtk_window_set_title(GTK_WINDOW(window->frame), title.c_str());
+ }
+
+ void setMouseCallback(MouseCallback onMouse, void* userdata /*= 0*/) CV_OVERRIDE
+ {
+ auto window = window_.lock();
+ CV_Assert(window);
+ window->on_mouse = onMouse;
+ window->on_mouse_param = userdata;
+ }
+
+ std::shared_ptr<UITrackbar> createTrackbar(
+ const std::string& name,
+ int count,
+ TrackbarCallback onChange /*= 0*/,
+ void* userdata /*= 0*/
+ ) CV_OVERRIDE
+ {
+ auto window = window_.lock();
+ CV_Assert(window);
+ CV_LOG_INFO(NULL, "OpenCV/UI: Creating GTK trackbar at '" << name_ << "': '" << name << "'");
+ auto trackbar = createTrackbar_(window, name, count, onChange, userdata);
+ auto ui_trackbar = std::make_shared<GTKTrackbar>(name, trackbar, shared_from_this());
+ {
+ cv::AutoLock lock(getWindowMutex());
+ trackbars_.emplace(name, ui_trackbar);
+ }
+ return std::static_pointer_cast<UITrackbar>(ui_trackbar);
+ }
+
+ std::shared_ptr<UITrackbar> findTrackbar(const std::string& name) CV_OVERRIDE
+ {
+ cv::AutoLock lock(getWindowMutex());
+ auto i = trackbars_.find(name);
+ if (i != trackbars_.end())
+ {
+ return std::static_pointer_cast<UITrackbar>(i->second);
+ }
+ return std::shared_ptr<UITrackbar>();
+ }
+}; // GTKWindow
+
+
+class GTKTrackbar : public UITrackbar
+{
+protected:
+ /*const*/ std::string name_;
+ std::weak_ptr<CvTrackbar> trackbar_;
+ std::weak_ptr<GTKWindow> parent_;
+ std::map<std::string, std::shared_ptr<GTKTrackbar> > trackbars_;
+public:
+ GTKTrackbar(const std::string& name, const std::shared_ptr<CvTrackbar>& trackbar, const std::shared_ptr<GTKWindow>& parent)
+ : trackbar_(trackbar)
+ , parent_(parent)
+ {
+ name_ = std::string("<") + name + ">@" + parent->getID();
+ }
+
+ ~GTKTrackbar() CV_OVERRIDE
+ {
+ if (!trackbar_.expired())
+ destroy();
+ CV_LOG_DEBUG(NULL, "OpenCV/UI/GTK: GTKTrackbar(" << name_ << ") is disposed");
+ }
+
+ const std::string& getID() const CV_OVERRIDE { return name_; }
+
+ bool isActive() const CV_OVERRIDE { return !trackbar_.expired(); }
+
+ void destroy() CV_OVERRIDE
+ {
+ // nothing (destroyed with parent window, dedicated trackbar removal is not supported)
+ }
+
+ int getPos() const CV_OVERRIDE
+ {
+ auto trackbar = trackbar_.lock();
+ CV_Assert(trackbar);
+ return trackbar->pos;
+ }
+ void setPos(int pos) CV_OVERRIDE
+ {
+ auto trackbar = trackbar_.lock();
+ CV_Assert(trackbar);
+ return setTrackbarPos_(trackbar, pos);
+ }
+
+ cv::Range getRange() const CV_OVERRIDE
+ {
+ auto trackbar = trackbar_.lock();
+ CV_Assert(trackbar);
+ return cv::Range(trackbar->minval, trackbar->maxval);
+ }
+
+ void setRange(const cv::Range& range) CV_OVERRIDE
+ {
+ auto trackbar = trackbar_.lock();
+ CV_Assert(trackbar);
+ CV_CheckLE(range.start, range.end, "Invalid trackbar range");
+ gtk_range_set_range(GTK_RANGE(trackbar->widget), range.start, range.end);
+ }
+}; // GTKTrackbar
+
+
+class GTKBackendUI : public UIBackend
+{
+public:
+ ~GTKBackendUI() CV_OVERRIDE
+ {
+ destroyAllWindows();
+ }
+
+ void destroyAllWindows() CV_OVERRIDE
+ {
+ cvDestroyAllWindows();
+ }
+
+ // namedWindow
+ virtual std::shared_ptr<UIWindow> createWindow(
+ const std::string& winname,
+ int flags
+ ) CV_OVERRIDE
+ {
+ CV_LOG_INFO(NULL, "OpenCV/UI: Creating GTK window: " << winname << " (" << flags << ")");
+ auto window = namedWindow_(winname, flags);
+ auto ui_window = std::make_shared<GTKWindow>(winname, window);
+ return ui_window;
+ }
+
+ int waitKeyEx(int delay) CV_OVERRIDE
+ {
+ return cvWaitKey(delay);
+ }
+ int pollKey() CV_OVERRIDE
+ {
+ return cvWaitKey(1); // TODO
+ }
+}; // GTKBackendUI
+
+static
+std::shared_ptr<GTKBackendUI>& getInstance()
+{
+ static std::shared_ptr<GTKBackendUI> g_instance = std::make_shared<GTKBackendUI>();
+ return g_instance;
+}
+
+} // namespace impl
+
+#ifndef BUILD_PLUGIN
+namespace highgui_backend {
+
+std::shared_ptr<UIBackend> createUIBackendGTK()
+{
+ return impl::getInstance();
+}
+
+} // namespace highgui_backend
+#endif
+
+} // namespace
+
+#ifdef BUILD_PLUGIN
+
+#define ABI_VERSION 0
+#define API_VERSION 0
+#include "plugin_api.hpp"
+
+static
+CvResult cv_getInstance(CV_OUT CvPluginUIBackend* handle) CV_NOEXCEPT
+{
+ try
+ {
+ if (!handle)
+ return CV_ERROR_FAIL;
+ *handle = cv::impl::getInstance().get();
+ return CV_ERROR_OK;
+ }
+ catch (...)
+ {
+ return CV_ERROR_FAIL;
+ }
+}
+
+static const OpenCV_UI_Plugin_API plugin_api =
+{
+ {
+ sizeof(OpenCV_UI_Plugin_API), ABI_VERSION, API_VERSION,
+ CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS,
+ "GTK" CVAUX_STR(GTK_MAJOR_VERSION) " OpenCV UI plugin"
+ },
+ {
+ /* 1*/cv_getInstance
+ }
+};
+
+const OpenCV_UI_Plugin_API* CV_API_CALL opencv_ui_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT
+{
+ if (requested_abi_version == ABI_VERSION && requested_api_version <= API_VERSION)
+ return &plugin_api;
+ return NULL;
+}
+
+#endif // BUILD_PLUGIN
+
+#endif // HAVE_GTK
inline void verify_size(const std::string &nm, const cv::Mat &img)
{
EXPECT_NO_THROW(imshow(nm, img));
- EXPECT_EQ(-1, waitKey(500));
+ EXPECT_EQ(-1, waitKey(200));
Rect rc;
EXPECT_NO_THROW(rc = getWindowImageRect(nm));
EXPECT_EQ(rc.size(), img.size());
}
-#if !defined HAVE_GTK && !defined HAVE_QT && !defined HAVE_WIN32UI && !defined HAVE_COCOA
+#if (!defined(ENABLE_PLUGINS) \
+ && !defined HAVE_GTK \
+ && !defined HAVE_QT \
+ && !defined HAVE_WIN32UI \
+ && !defined HAVE_COCOA \
+ )
TEST(Highgui_GUI, DISABLED_regression)
#else
TEST(Highgui_GUI, regression)
}
}
-#if !defined HAVE_GTK && !defined HAVE_QT && !defined HAVE_WIN32UI
-// && !defined HAVE_COCOA - TODO: fails on Mac?
-TEST(Highgui_GUI, DISABLED_trackbar)
+#if (!defined(ENABLE_PLUGINS) \
+ && !defined HAVE_GTK \
+ && !defined HAVE_QT \
+ && !defined HAVE_WIN32UI \
+ ) \
+ || defined(__APPLE__) // test fails on Mac (cocoa)
+TEST(Highgui_GUI, DISABLED_trackbar_unsafe)
#else
-TEST(Highgui_GUI, trackbar)
+TEST(Highgui_GUI, trackbar_unsafe)
#endif
{
int value = 50;
ASSERT_NO_THROW(namedWindow(window_name));
EXPECT_EQ((int)1, createTrackbar(trackbar_name, window_name, &value, 100, Foo, &callback_count));
EXPECT_EQ(value, getTrackbarPos(trackbar_name, window_name));
- EXPECT_EQ(0, callback_count);
+ EXPECT_GE(callback_count, 0);
+ EXPECT_LE(callback_count, 1);
+ int callback_count_base = callback_count;
+ EXPECT_NO_THROW(setTrackbarPos(trackbar_name, window_name, 90));
+ EXPECT_EQ(callback_count_base + 1, callback_count);
+ EXPECT_EQ(90, value);
+ EXPECT_EQ(90, getTrackbarPos(trackbar_name, window_name));
+ EXPECT_NO_THROW(destroyAllWindows());
+}
+
+static
+void testTrackbarCallback(int pos, void* param)
+{
+ CV_Assert(param);
+ int* status = (int*)param;
+ status[0] = pos;
+ status[1]++;
+}
+
+#if (!defined(ENABLE_PLUGINS) \
+ && !defined HAVE_GTK \
+ && !defined HAVE_QT \
+ && !defined HAVE_WIN32UI \
+ ) \
+ || defined(__APPLE__) // test fails on Mac (cocoa)
+TEST(Highgui_GUI, DISABLED_trackbar)
+#else
+TEST(Highgui_GUI, trackbar)
+#endif
+{
+ int status[2] = {-1, 0}; // pos, counter
+ const std::string window_name("trackbar_test_window");
+ const std::string trackbar_name("trackbar");
+
+ EXPECT_NO_THROW(destroyAllWindows());
+ ASSERT_NO_THROW(namedWindow(window_name));
+ EXPECT_EQ((int)1, createTrackbar(trackbar_name, window_name, NULL, 100, testTrackbarCallback, status));
+ EXPECT_EQ(0, getTrackbarPos(trackbar_name, window_name));
+ int callback_count = status[1];
+ EXPECT_GE(callback_count, 0);
+ EXPECT_LE(callback_count, 1);
+ int callback_count_base = callback_count;
EXPECT_NO_THROW(setTrackbarPos(trackbar_name, window_name, 90));
- EXPECT_EQ(1, callback_count);
+ callback_count = status[1];
+ EXPECT_EQ(callback_count_base + 1, callback_count);
+ int value = status[0];
EXPECT_EQ(90, value);
EXPECT_EQ(90, getTrackbarPos(trackbar_name, window_name));
EXPECT_NO_THROW(destroyAllWindows());