highgui: backends and plugins

This commit is contained in:
Alexander Alekhin 2021-05-01 13:13:58 +00:00 committed by Alexander Alekhin
parent 7d66f1e391
commit 70f69cb265
28 changed files with 2395 additions and 187 deletions

View File

@ -11,7 +11,7 @@ if(WITH_WIN32UI)
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)
@ -34,41 +34,6 @@ if(WITH_QT)
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)

View File

@ -6,4 +6,3 @@ set(MIN_VER_CUDNN 7.5)
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)

View File

@ -1183,6 +1183,9 @@ function(ocv_add_perf_tests)
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}")
@ -1278,6 +1281,9 @@ function(ocv_add_accuracy_tests)
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}")
@ -1368,6 +1374,9 @@ function(ocv_add_samples)
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)

View File

@ -28,9 +28,6 @@
/* Clp support */
#cmakedefine HAVE_CLP
/* Cocoa API */
#cmakedefine HAVE_COCOA
/* NVIDIA CUDA Runtime API*/
#cmakedefine HAVE_CUDA
@ -56,12 +53,6 @@
/* 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
@ -121,12 +112,6 @@
/* 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

View File

@ -1,3 +1,5 @@
ocv_cmake_dump_vars("" TOFILE "CMakeVars2.txt")
set(OCV_TEST_VAR 123)
add_definitions(-D__OPENCV_BUILD=1)
if(NOT OPENCV_MODULES_PATH)

View File

@ -80,7 +80,9 @@ LibHandle_t libraryLoad_(const FileSystemPath_t& filename)
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
}

View File

@ -1,10 +1,46 @@
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.
@ -24,15 +60,6 @@ if(HAVE_WEBP)
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"
@ -42,6 +69,8 @@ file(GLOB highgui_ext_hdrs
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)
@ -62,13 +91,16 @@ if(HAVE_QT5)
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})
@ -121,13 +153,64 @@ elseif(HAVE_WIN32UI)
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})
@ -139,6 +222,21 @@ if(TRUE)
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})
@ -162,5 +260,14 @@ if(NOT BUILD_opencv_world)
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})

View File

@ -0,0 +1,43 @@
# --- 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)

View File

@ -0,0 +1,25 @@
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)

View File

@ -0,0 +1,61 @@
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()

View File

@ -0,0 +1,69 @@
#!/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

View File

@ -0,0 +1,44 @@
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()

View File

@ -0,0 +1,21 @@
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

View File

@ -0,0 +1,21 @@
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

View File

@ -0,0 +1,13 @@
#!/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

View File

@ -0,0 +1,181 @@
// 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

View File

@ -0,0 +1,131 @@
// 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

View File

@ -0,0 +1,48 @@
// 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

View File

@ -0,0 +1,72 @@
// 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

View File

@ -0,0 +1,283 @@
// 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

View File

@ -42,10 +42,16 @@
#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"
@ -169,4 +175,11 @@ inline void convertToShow(const cv::Mat &src, const CvMat* arr, bool toRGB = tru
}
namespace cv {
CV_EXPORTS Mutex& getWindowMutex();
static inline Mutex& getInitializationMutex() { return getWindowMutex(); }
} // namespace
#endif /* __HIGHGUI_H_ */

View File

@ -0,0 +1,25 @@
// 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

View File

@ -0,0 +1,183 @@
// 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

View File

@ -40,14 +40,159 @@
//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.
@ -109,8 +254,19 @@ CV_IMPL void cvSetWindowProperty(const char* name, int prop_id, double prop_valu
/* 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)
{
@ -209,9 +365,18 @@ CV_IMPL double cvGetWindowProperty(const char* name, int 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)
@ -234,24 +399,90 @@ cv::Rect cv::getWindowImageRect(const String& winname)
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 );
}
@ -264,6 +495,15 @@ void cv::resizeWindow(const String& winname, const cv::Size& size)
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 );
}
@ -282,6 +522,16 @@ double cv::getWindowProperty(const String& winname, int prop_id)
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);
}
@ -308,6 +558,16 @@ int cv::waitKey(int 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);
}
@ -318,6 +578,44 @@ int cv::createTrackbar(const String& trackbarName, const String& winName,
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);
}
@ -325,30 +623,92 @@ int cv::createTrackbar(const String& trackbarName, const String& winName,
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);
}
@ -403,6 +763,39 @@ namespace
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);

View File

@ -1219,9 +1219,6 @@ void GuiReceiver::addSlider2(QString bar_name, QString window_name, void* value,
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" );
@ -1342,7 +1339,8 @@ void CvTrackbar::create(CvWindow* arg, QString name, int* value, int _count)
slider->setMinimum(0);
slider->setMaximum(_count);
slider->setPageStep(5);
slider->setValue(*value);
if (dataSlider)
slider->setValue(*dataSlider);
slider->setTickPosition(QSlider::TicksBelow);
@ -1409,7 +1407,8 @@ void CvTrackbar::update(int myvalue)
{
setLabel(myvalue);
*dataSlider = myvalue;
if (dataSlider)
*dataSlider = myvalue;
if (callback)
{
callback(myvalue);

View File

@ -256,7 +256,7 @@ private:
QPointer<QPushButton > label;
CvTrackbarCallback callback;
CvTrackbarCallback2 callback2;//look like it is use by python binding
int* dataSlider;
int* dataSlider; // deprecated
void* userdata;
};

File diff suppressed because it is too large Load Diff

View File

@ -47,13 +47,18 @@ namespace opencv_test { namespace {
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)
@ -126,11 +131,15 @@ static void Foo(int, void* counter)
}
}
#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;
@ -142,9 +151,52 @@ TEST(Highgui_GUI, trackbar)
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(1, callback_count);
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));
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());