Compare commits
2 Commits
version/3.
...
version/2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e60af7cc0 | ||
|
|
a7519c9e95 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,3 @@
|
||||
.*
|
||||
*~
|
||||
Makefile
|
||||
*.o
|
||||
lib*.a
|
||||
|
||||
4
3rdparty/CMakeLists.txt
vendored
4
3rdparty/CMakeLists.txt
vendored
@@ -1,4 +0,0 @@
|
||||
if (NOT SYSTEM_EXPAT)
|
||||
add_subdirectory(expat)
|
||||
endif()
|
||||
|
||||
33
3rdparty/expat/CMakeLists.txt
vendored
33
3rdparty/expat/CMakeLists.txt
vendored
@@ -1,33 +0,0 @@
|
||||
|
||||
configure_file (
|
||||
"${PROJECT_SOURCE_DIR}/3rdparty/expat/expat_config_cmake.in"
|
||||
"${PROJECT_BINARY_DIR}/3rdparty/expat/expat_config.h"
|
||||
)
|
||||
|
||||
set(expat_sources
|
||||
asciitab.h
|
||||
hashtable.h
|
||||
iasciitab.h
|
||||
latin1tab.h
|
||||
nametab.h
|
||||
utf8tab.h
|
||||
xmldef.h
|
||||
xmlparse.h
|
||||
xmlrole.h
|
||||
xmltok.h
|
||||
xmltok_impl.h
|
||||
hashtable.c
|
||||
xmlparse.c
|
||||
xmlrole.c
|
||||
xmltok.c
|
||||
internal.h
|
||||
ascii.h
|
||||
sg_expat.h
|
||||
sg_expat_external.h
|
||||
)
|
||||
|
||||
foreach(s ${expat_sources})
|
||||
set_property(GLOBAL
|
||||
APPEND PROPERTY LOCAL_EXPAT_SOURCES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${s}")
|
||||
endforeach()
|
||||
215
CMakeLists.txt
215
CMakeLists.txt
@@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 2.6.4)
|
||||
include (CheckFunctionExists)
|
||||
include (CheckIncludeFile)
|
||||
include (CheckCXXSourceCompiles)
|
||||
include (CheckCXXCompilerFlag)
|
||||
|
||||
|
||||
project(SimGear)
|
||||
|
||||
@@ -10,19 +10,9 @@ project(SimGear)
|
||||
file(READ version versionFile)
|
||||
string(STRIP ${versionFile} SIMGEAR_VERSION)
|
||||
|
||||
set(FIND_LIBRARY_USE_LIB64_PATHS ON)
|
||||
|
||||
# use simgear version also as the SO version (if building SOs)
|
||||
SET(SIMGEAR_SOVERSION ${SIMGEAR_VERSION})
|
||||
|
||||
# Warning when build is not an out-of-source build.
|
||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" InSourceBuild)
|
||||
if(InSourceBuild)
|
||||
message(WARNING "Avoid building inside the source tree!")
|
||||
message(WARNING "Create a separate build directory instead (i.e. 'sgbuild') and call CMake from there: ")
|
||||
message(WARNING " mkdir ../sgbuild && cd ../sgbuild && cmake ${CMAKE_SOURCE_DIR}")
|
||||
endif(InSourceBuild)
|
||||
|
||||
#packaging
|
||||
SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING")
|
||||
SET(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README")
|
||||
@@ -70,43 +60,15 @@ else(${CMAKE_VERSION} VERSION_GREATER 2.8.4)
|
||||
endif(${CMAKE_VERSION} VERSION_GREATER 2.8.4)
|
||||
message(STATUS "Library installation directory: ${CMAKE_INSTALL_LIBDIR}")
|
||||
|
||||
#####################################################################################
|
||||
# Configure library search paths
|
||||
#####################################################################################
|
||||
|
||||
if(NOT "${CMAKE_LIBRARY_ARCHITECTURE}" STREQUAL "")
|
||||
# Workaround for Ubuntu/Debian which introduced the "multiarch" library
|
||||
# directory structure, which is unsupported by CMake < 2.8.10, so we need to
|
||||
# add paths manually
|
||||
# see http://www.cmake.org/Bug/view.php?id=12049 and
|
||||
# http://www.cmake.org/Bug/view.php?id=12037
|
||||
list(APPEND ADDITIONAL_LIBRARY_PATHS
|
||||
/usr/local/lib/${CMAKE_LIBRARY_ARCHITECTURE}
|
||||
/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}
|
||||
/lib/${CMAKE_LIBRARY_ARCHITECTURE})
|
||||
message(STATUS "additional library directories: ${ADDITIONAL_LIBRARY_PATHS}")
|
||||
endif()
|
||||
|
||||
#####################################################################################
|
||||
|
||||
if (NOT MSVC)
|
||||
option(SIMGEAR_SHARED "Set to ON to build SimGear as a shared library/framework" OFF)
|
||||
option(SYSTEM_EXPAT "Set to ON to build SimGear using the system libExpat" OFF)
|
||||
else()
|
||||
# Building SimGear DLLs is currently not supported for MSVC.
|
||||
set(SIMGEAR_SHARED OFF)
|
||||
# Using a system expat is currently not supported for MSVC - it would require shared simgear (DLL).
|
||||
set(SYSTEM_EXPAT OFF)
|
||||
endif()
|
||||
|
||||
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
|
||||
option(JPEG_FACTORY "Enable JPEG-factory support" OFF)
|
||||
option(ENABLE_LIBSVN "Set to ON to build SimGear with libsvnclient support" ON)
|
||||
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
|
||||
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
|
||||
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
|
||||
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
|
||||
|
||||
if (MSVC)
|
||||
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
|
||||
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_SOURCE_DIR} PATH)
|
||||
if (CMAKE_CL_64)
|
||||
SET(TEST_3RDPARTY_DIR "${PARENT_DIR}/3rdparty.x64")
|
||||
else (CMAKE_CL_64)
|
||||
@@ -124,13 +86,11 @@ endif (MSVC)
|
||||
if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
message(STATUS "3rdparty files located in ${MSVC_3RDPARTY_ROOT}")
|
||||
set( OSG_MSVC "msvc" )
|
||||
if (${MSVC_VERSION} EQUAL 1700)
|
||||
set( OSG_MSVC ${OSG_MSVC}110 )
|
||||
elseif (${MSVC_VERSION} EQUAL 1600)
|
||||
if (${MSVC_VERSION} EQUAL 1600)
|
||||
set( OSG_MSVC ${OSG_MSVC}100 )
|
||||
else (${MSVC_VERSION} EQUAL 1700)
|
||||
else (${MSVC_VERSION} EQUAL 1600)
|
||||
set( OSG_MSVC ${OSG_MSVC}90 )
|
||||
endif (${MSVC_VERSION} EQUAL 1700)
|
||||
endif (${MSVC_VERSION} EQUAL 1600)
|
||||
if (CMAKE_CL_64)
|
||||
set( OSG_MSVC ${OSG_MSVC}-64 )
|
||||
set( MSVC_3RDPARTY_DIR 3rdParty.x64 )
|
||||
@@ -138,83 +98,61 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
set( MSVC_3RDPARTY_DIR 3rdParty )
|
||||
endif (CMAKE_CL_64)
|
||||
|
||||
set (CMAKE_LIBRARY_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/lib )
|
||||
set (CMAKE_INCLUDE_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/include)
|
||||
find_path(BOOST_ROOT boost/version.hpp
|
||||
${MSVC_3RDPARTY_ROOT}/boost
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_52_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_51_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_50_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_49_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_48_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_47_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_46_1
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_46_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_45_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_44_0
|
||||
)
|
||||
# set (BOOST_ROOT ${MSVC_3RDPARTY_ROOT}/boost_1_44_0)
|
||||
set (CMAKE_LIBRARY_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/lib )
|
||||
set (CMAKE_INCLUDE_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/include)
|
||||
set (BOOST_ROOT ${MSVC_3RDPARTY_ROOT}/boost_1_44_0)
|
||||
message(STATUS "BOOST_ROOT is ${BOOST_ROOT}")
|
||||
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
|
||||
set (ALUT_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
|
||||
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
|
||||
endif (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
|
||||
if(APPLE)
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
endif()
|
||||
|
||||
# Somehow this only works if included before searching for Boost...
|
||||
include(BoostTestTargets)
|
||||
|
||||
find_package(Boost REQUIRED)
|
||||
set (BOOST_CXX_FLAGS "-DBOOST_MULTI_INDEX_DISABLE_SERIALIZATION -DBOOST_BIMAP_DISABLE_SERIALIZATION")
|
||||
|
||||
if(SIMGEAR_HEADLESS)
|
||||
message(STATUS "SimGear mode: HEADLESS")
|
||||
set(ENABLE_SOUND 0)
|
||||
else()
|
||||
message(STATUS "SimGear mode: NORMAL")
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
if (ENABLE_SOUND)
|
||||
find_package(OpenAL REQUIRED)
|
||||
message(STATUS "Sound support: ENABLED")
|
||||
endif(ENABLE_SOUND)
|
||||
|
||||
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgUtil)
|
||||
find_package(OpenAL REQUIRED)
|
||||
find_package(ALUT REQUIRED)
|
||||
find_package(OpenSceneGraph 3.0.0 REQUIRED osgText osgSim osgDB osgParticle osgUtil)
|
||||
endif(SIMGEAR_HEADLESS)
|
||||
|
||||
if(JPEG_FACTORY)
|
||||
message(STATUS "JPEG-factory: ENABLED")
|
||||
find_package(JPEG REQUIRED)
|
||||
include_directories(${JPEG_INCLUDE_DIR})
|
||||
else()
|
||||
message(STATUS "JPEG-factory: DISABLED")
|
||||
endif(JPEG_FACTORY)
|
||||
|
||||
if(ENABLE_LIBSVN)
|
||||
find_package(SvnClient)
|
||||
|
||||
if(LIBSVN_FOUND)
|
||||
message(STATUS "Subversion client support: ENABLED")
|
||||
set(HAVE_SVN_CLIENT_H 1)
|
||||
set(HAVE_LIBSVN_CLIENT_1 1)
|
||||
else()
|
||||
# Oops. ENABLE_LIBSVN is ON, but svn is still missing.
|
||||
# Provide clearly visible warning/hint, so builders know what else they should install (or disable).
|
||||
message(WARNING "Failed to enable subversion client support. Unable to find required subversion client library. Some features may not be available (scenery download).")
|
||||
message(WARNING "Install 'libsvn' library/DLL (libsvn-devel/libsvnclient/...). Otherwise disable subversion support (set 'ENABLE_LIBSVN' to 'OFF').")
|
||||
endif(LIBSVN_FOUND)
|
||||
else()
|
||||
message(STATUS "Subversion client support: DISABLED")
|
||||
endif(ENABLE_LIBSVN)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
if (SYSTEM_EXPAT)
|
||||
message(STATUS "Requested to use system Expat library, forcing SIMGEAR_SHARED to true")
|
||||
set(SIMGEAR_SHARED ON)
|
||||
find_package(EXPAT REQUIRED)
|
||||
|
||||
else()
|
||||
message(STATUS "Using built-in expat code")
|
||||
# XML_STATIC is important to avoid sg_expat_external.h
|
||||
# declaring symbols as declspec(import)
|
||||
add_definitions(-DHAVE_EXPAT_CONFIG_H -DXML_STATIC)
|
||||
set(EXPAT_INCLUDE_DIRS
|
||||
${PROJECT_SOURCE_DIR}/3rdparty/expat
|
||||
${PROJECT_BINARY_DIR}/3rdparty/expat)
|
||||
endif(SYSTEM_EXPAT)
|
||||
|
||||
include_directories(${EXPAT_INCLUDE_DIRS})
|
||||
|
||||
check_include_file(inttypes.h HAVE_INTTYPES_H)
|
||||
check_include_file(sys/time.h HAVE_SYS_TIME_H)
|
||||
check_include_file(sys/timeb.h HAVE_SYS_TIMEB_H)
|
||||
check_include_file(unistd.h HAVE_UNISTD_H)
|
||||
check_include_file(windows.h HAVE_WINDOWS_H)
|
||||
|
||||
if(HAVE_INTTYPES_H)
|
||||
# ShivaVG needs inttypes.h
|
||||
add_definitions(-DHAVE_INTTYPES_H)
|
||||
endif()
|
||||
|
||||
if(ENABLE_RTI)
|
||||
# See if we have any rti library variant installed
|
||||
message(STATUS "RTI: ENABLED")
|
||||
@@ -252,22 +190,6 @@ if(HAVE_CLOCK_GETTIME)
|
||||
endif(HAVE_RT)
|
||||
endif(HAVE_CLOCK_GETTIME)
|
||||
|
||||
set(DL_LIBRARY "")
|
||||
check_cxx_source_compiles(
|
||||
"#include <dlfcn.h>
|
||||
int main(void) {
|
||||
return 0;
|
||||
}
|
||||
"
|
||||
HAVE_DLFCN_H)
|
||||
|
||||
if(HAVE_DLFCN_H)
|
||||
check_library_exists(dl dlerror "" HAVE_DL)
|
||||
if(HAVE_DL)
|
||||
set(DL_LIBRARY "dl")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually 'd' on windows")
|
||||
SET(CMAKE_RELEASE_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
||||
SET(CMAKE_RELWITHDEBINFO_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
||||
@@ -279,11 +201,6 @@ check_cxx_source_compiles(
|
||||
void f() { isnan(0.0);} "
|
||||
HAVE_ISNAN)
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"#include <cmath>
|
||||
void f() { std::isnan(0.0);} "
|
||||
HAVE_STD_ISNAN)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(WARNING_FLAGS_CXX "-Wall")
|
||||
set(WARNING_FLAGS_C "-Wall")
|
||||
@@ -301,14 +218,6 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
set(WARNING_FLAGS_C "-Wall")
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
# boost goes haywire wrt static asserts
|
||||
check_cxx_compiler_flag(-Wno-unused-local-typedefs HAS_NOWARN_UNUSED_TYPEDEFS)
|
||||
if(HAS_NOWARN_UNUSED_TYPEDEFS)
|
||||
set(WARNING_FLAGS_CXX " ${WARNING_FLAGS_CXX} -Wno-unused-local-typedefs")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
|
||||
if(MINGW)
|
||||
@@ -321,10 +230,7 @@ if(WIN32)
|
||||
# SET(WARNING_FLAGS "${WARNING_FLAGS} /wd${warning}")
|
||||
# endforeach(warning)
|
||||
|
||||
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /wd4996 /wd4250 -Dstrdup=_strdup")
|
||||
if (${MSVC_VERSION} GREATER 1599)
|
||||
set( MSVC_LD_FLAGS "/FORCE:MULTIPLE" )
|
||||
endif (${MSVC_VERSION} GREATER 1599)
|
||||
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /wd4996")
|
||||
endif(MSVC)
|
||||
|
||||
# assumed on Windows
|
||||
@@ -336,21 +242,17 @@ endif(WIN32)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS_C} ${MSVC_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS_CXX} ${MSVC_FLAGS} ${BOOST_CXX_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}")
|
||||
|
||||
# use BEFORE to ensure local directories are used first,
|
||||
# ahead of system-installed libs
|
||||
include_directories(BEFORE ${PROJECT_SOURCE_DIR})
|
||||
include_directories(BEFORE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_BINARY_DIR}/simgear)
|
||||
include_directories(${PROJECT_BINARY_DIR}/simgear/xml)
|
||||
|
||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIR}
|
||||
${OPENAL_INCLUDE_DIR}
|
||||
)
|
||||
${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR}
|
||||
${ALUT_INCLUDE_DIR} ${OPENAL_INCLUDE_DIR} )
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
add_definitions(-DHAVE_EXPAT_CONFIG_H)
|
||||
|
||||
# configure a header file to pass some of the CMake settings
|
||||
# to the source code
|
||||
@@ -359,33 +261,22 @@ configure_file (
|
||||
"${PROJECT_BINARY_DIR}/simgear/simgear_config.h"
|
||||
)
|
||||
|
||||
configure_file (
|
||||
"${PROJECT_SOURCE_DIR}/simgear/xml/expat_config_cmake.in"
|
||||
"${PROJECT_BINARY_DIR}/simgear/xml/expat_config.h"
|
||||
)
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
# enable CTest / make test target
|
||||
message(STATUS "Tests: ENABLED")
|
||||
|
||||
include (Dart)
|
||||
enable_testing()
|
||||
enable_testing()
|
||||
else()
|
||||
message(STATUS "Tests: DISABLED")
|
||||
endif(ENABLE_TESTS)
|
||||
|
||||
# always set TEST_LIBS as it is also used by other tools/applications
|
||||
set(TEST_LIBS_INTERNAL_CORE
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${ZLIB_LIBRARY}
|
||||
${WINSOCK_LIBRARY}
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${COCOA_LIBRARY})
|
||||
set(TEST_LIBS SimGearCore ${TEST_LIBS_INTERNAL_CORE})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
set(TEST_LIBS SimGearScene ${OPENGL_LIBRARIES} ${TEST_LIBS})
|
||||
endif()
|
||||
|
||||
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
|
||||
|
||||
add_subdirectory(3rdparty)
|
||||
add_subdirectory(simgear)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
@@ -397,5 +288,3 @@ CONFIGURE_FILE(
|
||||
IMMEDIATE @ONLY)
|
||||
ADD_CUSTOM_TARGET(uninstall
|
||||
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||
|
||||
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
# - Add tests using boost::test
|
||||
#
|
||||
# Add this line to your test files in place of including a basic boost test header:
|
||||
# #include <BoostTestTargetConfig.h>
|
||||
#
|
||||
# If you cannot do that and must use the included form for a given test,
|
||||
# include the line
|
||||
# // OVERRIDE_BOOST_TEST_INCLUDED_WARNING
|
||||
# in the same file with the boost test include.
|
||||
#
|
||||
# include(BoostTestTargets)
|
||||
# add_boost_test(<testdriver_name> SOURCES <source1> [<more sources...>]
|
||||
# [FAIL_REGULAR_EXPRESSION <additional fail regex>]
|
||||
# [LAUNCHER <generic launcher script>]
|
||||
# [LIBRARIES <library> [<library>...]]
|
||||
# [RESOURCES <resource> [<resource>...]]
|
||||
# [TESTS <testcasename> [<testcasename>...]])
|
||||
#
|
||||
# If for some reason you need access to the executable target created,
|
||||
# it can be found in ${${testdriver_name}_TARGET_NAME} as specified when
|
||||
# you called add_boost_test
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Requires:
|
||||
# GetForceIncludeDefinitions
|
||||
# CopyResourcesToBuildTree
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if(__add_boost_test)
|
||||
return()
|
||||
endif()
|
||||
set(__add_boost_test YES)
|
||||
|
||||
set(BOOST_TEST_TARGET_PREFIX "test")
|
||||
|
||||
if(NOT Boost_FOUND)
|
||||
find_package(Boost 1.34.0 QUIET)
|
||||
endif()
|
||||
if("${Boost_VERSION}0" LESS "1034000")
|
||||
set(_shared_msg
|
||||
"NOTE: boost::test-based targets and tests cannot "
|
||||
"be added: boost >= 1.34.0 required but not found. "
|
||||
"(found: '${Boost_VERSION}'; want >=103400) ")
|
||||
if(BUILD_TESTING)
|
||||
message(FATAL_ERROR
|
||||
${_shared_msg}
|
||||
"You may disable BUILD_TESTING to continue without the "
|
||||
"tests.")
|
||||
else()
|
||||
message(STATUS
|
||||
${_shared_msg}
|
||||
"BUILD_TESTING disabled, so continuing anyway.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(GetForceIncludeDefinitions)
|
||||
include(CopyResourcesToBuildTree)
|
||||
|
||||
if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
|
||||
set(_boosttesttargets_libs)
|
||||
set(_boostConfig "BoostTestTargetsIncluded.h")
|
||||
if(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
|
||||
find_package(Boost 1.34.0 QUIET COMPONENTS unit_test_framework)
|
||||
endif()
|
||||
if(Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
|
||||
set(_boosttesttargets_libs "${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}")
|
||||
if(Boost_USE_STATIC_LIBS)
|
||||
set(_boostConfig "BoostTestTargetsStatic.h")
|
||||
else()
|
||||
set(_boostConfig "BoostTestTargetsDynamic.h")
|
||||
endif()
|
||||
endif()
|
||||
get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
configure_file("${_moddir}/${_boostConfig}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/BoostTestTargetConfig.h"
|
||||
COPYONLY)
|
||||
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
|
||||
function(add_boost_test _name)
|
||||
if(NOT BUILD_TESTING)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# parse arguments
|
||||
set(_nowhere)
|
||||
set(_curdest _nowhere)
|
||||
set(_val_args
|
||||
SOURCES
|
||||
FAIL_REGULAR_EXPRESSION
|
||||
LAUNCHER
|
||||
LIBRARIES
|
||||
RESOURCES
|
||||
TESTS)
|
||||
set(_bool_args
|
||||
USE_COMPILED_LIBRARY)
|
||||
foreach(_arg ${_val_args} ${_bool_args})
|
||||
set(${_arg})
|
||||
endforeach()
|
||||
foreach(_element ${ARGN})
|
||||
list(FIND _val_args "${_element}" _val_arg_find)
|
||||
list(FIND _bool_args "${_element}" _bool_arg_find)
|
||||
if("${_val_arg_find}" GREATER "-1")
|
||||
set(_curdest "${_element}")
|
||||
elseif("${_bool_arg_find}" GREATER "-1")
|
||||
set("${_element}" ON)
|
||||
set(_curdest _nowhere)
|
||||
else()
|
||||
list(APPEND ${_curdest} "${_element}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(_nowhere)
|
||||
message(FATAL_ERROR "Syntax error in use of add_boost_test!")
|
||||
endif()
|
||||
|
||||
if(NOT SOURCES)
|
||||
message(FATAL_ERROR
|
||||
"Syntax error in use of add_boost_test: at least one source file required!")
|
||||
endif()
|
||||
|
||||
if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
|
||||
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
|
||||
set(includeType)
|
||||
foreach(src ${SOURCES})
|
||||
file(READ ${src} thefile)
|
||||
if("${thefile}" MATCHES ".*BoostTestTargetConfig.h.*")
|
||||
set(includeType CONFIGURED)
|
||||
set(includeFileLoc ${src})
|
||||
break()
|
||||
elseif("${thefile}" MATCHES ".*boost/test/included/unit_test.hpp.*")
|
||||
set(includeType INCLUDED)
|
||||
set(includeFileLoc ${src})
|
||||
set(_boosttesttargets_libs) # clear this out - linking would be a bad idea
|
||||
if(NOT
|
||||
"${thefile}"
|
||||
MATCHES
|
||||
".*OVERRIDE_BOOST_TEST_INCLUDED_WARNING.*")
|
||||
message("Please replace the include line in ${src} with this alternate include line instead:")
|
||||
message(" \#include <BoostTestTargetConfig.h>")
|
||||
message("Once you've saved your changes, re-run CMake. (See BoostTestTargets.cmake for more info)")
|
||||
endif()
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT _boostTestTargetsNagged${_name} STREQUAL "${includeType}")
|
||||
if("includeType" STREQUAL "CONFIGURED")
|
||||
message(STATUS
|
||||
"Test '${_name}' uses the CMake-configurable form of the boost test framework - congrats! (Including File: ${includeFileLoc})")
|
||||
elseif("${includeType}" STREQUAL "INCLUDED")
|
||||
message("In test '${_name}': ${includeFileLoc} uses the 'included' form of the boost unit test framework.")
|
||||
else()
|
||||
message("In test '${_name}': Didn't detect the CMake-configurable boost test include.")
|
||||
message("Please replace your existing boost test include in that test with the following:")
|
||||
message(" \#include <BoostTestTargetConfig.h>")
|
||||
message("Once you've saved your changes, re-run CMake. (See BoostTestTargets.cmake for more info)")
|
||||
endif()
|
||||
endif()
|
||||
set(_boostTestTargetsNagged${_name}
|
||||
"${includeType}"
|
||||
CACHE
|
||||
INTERNAL
|
||||
""
|
||||
FORCE)
|
||||
|
||||
|
||||
if(RESOURCES)
|
||||
list(APPEND SOURCES ${RESOURCES})
|
||||
endif()
|
||||
|
||||
# Generate a unique target name, using the relative binary dir
|
||||
# and provided name. (transform all / into _ and remove all other
|
||||
# non-alphabet characters)
|
||||
file(RELATIVE_PATH
|
||||
targetpath
|
||||
"${CMAKE_BINARY_DIR}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}")
|
||||
string(REGEX REPLACE "[^A-Za-z/_]" "" targetpath "${targetpath}")
|
||||
string(REPLACE "/" "_" targetpath "${targetpath}")
|
||||
|
||||
set(_target_name ${BOOST_TEST_TARGET_PREFIX}-${targetpath}-${_name})
|
||||
set(${_name}_TARGET_NAME "${_target_name}" PARENT_SCOPE)
|
||||
|
||||
# Build the test.
|
||||
add_executable(${_target_name} ${SOURCES})
|
||||
|
||||
list(APPEND LIBRARIES ${_boosttesttargets_libs})
|
||||
|
||||
if(LIBRARIES)
|
||||
target_link_libraries(${_target_name} ${LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(RESOURCES)
|
||||
set_property(TARGET ${_target_name} PROPERTY RESOURCE ${RESOURCES})
|
||||
copy_resources_to_build_tree(${_target_name})
|
||||
endif()
|
||||
|
||||
if(NOT Boost_TEST_FLAGS)
|
||||
# set(Boost_TEST_FLAGS --catch_system_error=yes --output_format=XML)
|
||||
set(Boost_TEST_FLAGS --catch_system_error=yes)
|
||||
endif()
|
||||
|
||||
# TODO: Figure out why only recent boost handles individual test running properly
|
||||
|
||||
if(LAUNCHER)
|
||||
set(_test_command ${LAUNCHER} "\$<TARGET_FILE:${_target_name}>")
|
||||
else()
|
||||
set(_test_command ${_target_name})
|
||||
endif()
|
||||
|
||||
if(TESTS AND ( "${Boost_VERSION}" VERSION_GREATER "103799" ))
|
||||
foreach(_test ${TESTS})
|
||||
add_test(
|
||||
${_name}-${_test}
|
||||
${_test_command} --run_test=${_test} ${Boost_TEST_FLAGS}
|
||||
)
|
||||
if(FAIL_REGULAR_EXPRESSION)
|
||||
set_tests_properties(${_name}-${_test}
|
||||
PROPERTIES
|
||||
FAIL_REGULAR_EXPRESSION
|
||||
"${FAIL_REGULAR_EXPRESSION}")
|
||||
endif()
|
||||
endforeach()
|
||||
else()
|
||||
add_test(
|
||||
${_name}-boost_test
|
||||
${_test_command} ${Boost_TEST_FLAGS}
|
||||
)
|
||||
if(FAIL_REGULAR_EXPRESSION)
|
||||
set_tests_properties(${_name}-boost_test
|
||||
PROPERTIES
|
||||
FAIL_REGULAR_EXPRESSION
|
||||
"${FAIL_REGULAR_EXPRESSION}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# CppCheck the test if we can.
|
||||
if(COMMAND add_cppcheck)
|
||||
add_cppcheck(${_target_name} STYLE UNUSED_FUNCTIONS)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -1,8 +0,0 @@
|
||||
// Small header computed by CMake to set up boost test.
|
||||
// include AFTER #define BOOST_TEST_MODULE whatever
|
||||
// but before any other boost test includes.
|
||||
|
||||
// Using the Boost UTF dynamic library
|
||||
|
||||
#define BOOST_TEST_DYN_LINK
|
||||
#include <boost/test/unit_test.hpp>
|
||||
@@ -1,7 +0,0 @@
|
||||
// Small header computed by CMake to set up boost test.
|
||||
// include AFTER #define BOOST_TEST_MODULE whatever
|
||||
// but before any other boost test includes.
|
||||
|
||||
// Using the Boost UTF included framework
|
||||
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
@@ -1,7 +0,0 @@
|
||||
// Small header computed by CMake to set up boost test.
|
||||
// include AFTER #define BOOST_TEST_MODULE whatever
|
||||
// but before any other boost test includes.
|
||||
|
||||
// Using the Boost UTF static library
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
@@ -1,83 +0,0 @@
|
||||
# - Copy the resources your app needs to the build tree.
|
||||
#
|
||||
# copy_resources_to_build_tree(<target_name>)
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if(__copy_resources_to_build_tree)
|
||||
return()
|
||||
endif()
|
||||
set(__copy_resources_to_build_tree YES)
|
||||
|
||||
function(copy_resources_to_build_tree _target)
|
||||
get_target_property(_resources ${_target} RESOURCE)
|
||||
if(NOT _resources)
|
||||
# Bail if no resources
|
||||
message(STATUS
|
||||
"Told to copy resources for target ${_target}, but "
|
||||
"no resources are set!")
|
||||
return()
|
||||
endif()
|
||||
|
||||
get_target_property(_path ${_target} LOCATION)
|
||||
get_filename_component(_path "${_path}" PATH)
|
||||
|
||||
if(NOT MSVC AND NOT "${CMAKE_GENERATOR}" MATCHES "Makefiles")
|
||||
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
|
||||
get_target_property(_path${_config} ${_target} LOCATION_${_config})
|
||||
get_filename_component(_path${_config} "${_path${_config}}" PATH)
|
||||
add_custom_command(TARGET ${_target}
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
ARGS -E make_directory "${_path${_config}}/"
|
||||
COMMENT "Creating directory ${_path${_config}}/")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
foreach(_res ${_resources})
|
||||
if(NOT IS_ABSOLUTE "${_res}")
|
||||
get_filename_component(_res "${_res}" ABSOLUTE)
|
||||
endif()
|
||||
get_filename_component(_name "${_res}" NAME)
|
||||
|
||||
if(MSVC)
|
||||
# Working dir is solution file dir, not exe file dir.
|
||||
add_custom_command(TARGET ${_target}
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
ARGS -E copy "${_res}" "${CMAKE_BINARY_DIR}/"
|
||||
COMMENT "Copying ${_name} to ${CMAKE_BINARY_DIR}/ for MSVC")
|
||||
else()
|
||||
if("${CMAKE_GENERATOR}" MATCHES "Makefiles")
|
||||
add_custom_command(TARGET ${_target}
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
ARGS -E copy "${_res}" "${_path}/"
|
||||
COMMENT "Copying ${_name} to ${_path}/")
|
||||
else()
|
||||
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
|
||||
add_custom_command(TARGET ${_target}
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
ARGS -E copy "${_res}" "${_path${_config}}"
|
||||
COMMENT "Copying ${_name} to ${_path${_config}}")
|
||||
endforeach()
|
||||
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
67
CMakeModules/FindALUT.cmake
Normal file
67
CMakeModules/FindALUT.cmake
Normal file
@@ -0,0 +1,67 @@
|
||||
# Locate ALUT
|
||||
# This module defines
|
||||
# ALUT_LIBRARY
|
||||
# ALUT_FOUND, if false, do not try to link to ALUT
|
||||
# ALUT_INCLUDE_DIR, where to find the headers
|
||||
#
|
||||
# $ALUTDIR is an environment variable that would
|
||||
# correspond to the ./configure --prefix=$ALUTDIR
|
||||
# used in building ALUT.
|
||||
#
|
||||
# Created by James Turner. This was influenced by the FindOpenAL.cmake module.
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2005-2009 Kitware, Inc.
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License (the "License");
|
||||
# see accompanying file Copyright.txt for details.
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
#=============================================================================
|
||||
# (To distributed this file outside of CMake, substitute the full
|
||||
# License text for the above reference.)
|
||||
|
||||
# Per my request, CMake should search for frameworks first in
|
||||
# the following order:
|
||||
# ~/Library/Frameworks/OpenAL.framework/Headers
|
||||
# /Library/Frameworks/OpenAL.framework/Headers
|
||||
# /System/Library/Frameworks/OpenAL.framework/Headers
|
||||
#
|
||||
# On OS X, this will prefer the Framework version (if found) over others.
|
||||
# People will have to manually change the cache values of
|
||||
# OPENAL_LIBRARY to override this selection or set the CMake environment
|
||||
# CMAKE_INCLUDE_PATH to modify the search paths.
|
||||
|
||||
FIND_LIBRARY(ALUT_LIBRARY
|
||||
NAMES ALUT alut
|
||||
HINTS
|
||||
$ENV{ALUTDIR}
|
||||
PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr
|
||||
/opt
|
||||
)
|
||||
|
||||
FIND_PATH(ALUT_INCLUDE_DIR
|
||||
NAMES ALUT/alut.h alut.h
|
||||
HINTS
|
||||
$ENV{ALUTDIR}
|
||||
PATH_SUFFIXES include/AL include
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr
|
||||
/opt
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ALUT DEFAULT_MSG ALUT_LIBRARY ALUT_INCLUDE_DIR)
|
||||
|
||||
MARK_AS_ADVANCED(ALUT_LIBRARY ALUT_INCLUDE_DIR)
|
||||
|
||||
81
CMakeModules/FindSvnClient.cmake
Normal file
81
CMakeModules/FindSvnClient.cmake
Normal file
@@ -0,0 +1,81 @@
|
||||
# Find Subversion client libraries, and dependencies
|
||||
# including APR (Apache Portable Runtime)
|
||||
|
||||
include (CheckFunctionExists)
|
||||
include (CheckIncludeFile)
|
||||
include (CheckLibraryExists)
|
||||
|
||||
macro(find_static_component comp libs)
|
||||
# account for alternative Windows svn distribution naming
|
||||
if(MSVC)
|
||||
set(compLib "lib${comp}")
|
||||
else(MSVC)
|
||||
set(compLib "${comp}")
|
||||
endif(MSVC)
|
||||
|
||||
string(TOUPPER "${comp}" compLibBase)
|
||||
set( compLibName ${compLibBase}_LIBRARY )
|
||||
|
||||
# NO_DEFAULT_PATH is important on Mac - we need to ensure subversion
|
||||
# libraires in dist/ or Macports are picked over the Apple version
|
||||
# in /usr, since that's what we will ship.
|
||||
FIND_LIBRARY(${compLibName}
|
||||
NO_DEFAULT_PATH
|
||||
NAMES ${compLib}
|
||||
HINTS $ENV{LIBSVN_DIR} ${CMAKE_INSTALL_PREFIX}
|
||||
PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64
|
||||
PATHS
|
||||
/opt/local
|
||||
/usr/local
|
||||
/usr
|
||||
)
|
||||
|
||||
list(APPEND ${libs} ${${compLibName}})
|
||||
endmacro()
|
||||
|
||||
find_program(HAVE_APR_CONFIG apr-1-config)
|
||||
if(HAVE_APR_CONFIG)
|
||||
|
||||
execute_process(COMMAND apr-1-config --cppflags --includes
|
||||
OUTPUT_VARIABLE APR_CFLAGS
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
execute_process(COMMAND apr-1-config --link-ld
|
||||
OUTPUT_VARIABLE RAW_APR_LIBS
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
# clean up some vars, or other CMake pieces complain
|
||||
string(STRIP "${RAW_APR_LIBS}" APR_LIBS)
|
||||
|
||||
else(HAVE_APR_CONFIG)
|
||||
message(STATUS "apr-1-config not found, implement manual search for APR")
|
||||
endif(HAVE_APR_CONFIG)
|
||||
|
||||
if(HAVE_APR_CONFIG OR MSVC)
|
||||
find_path(LIBSVN_INCLUDE_DIR svn_client.h
|
||||
NO_DEFAULT_PATH
|
||||
HINTS
|
||||
$ENV{LIBSVN_DIR} ${CMAKE_INSTALL_PREFIX}
|
||||
PATH_SUFFIXES include/subversion-1
|
||||
PATHS
|
||||
/opt/local
|
||||
/usr/local
|
||||
/usr
|
||||
)
|
||||
|
||||
set(LIBSVN_LIBRARIES "")
|
||||
if (MSVC)
|
||||
find_static_component("apr-1" LIBSVN_LIBRARIES)
|
||||
else (MSVC)
|
||||
list(APPEND LIBSVN_LIBRARIES ${APR_LIBS})
|
||||
endif (MSVC)
|
||||
find_static_component("svn_client-1" LIBSVN_LIBRARIES)
|
||||
find_static_component("svn_subr-1" LIBSVN_LIBRARIES)
|
||||
find_static_component("svn_ra-1" LIBSVN_LIBRARIES)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBSVN DEFAULT_MSG LIBSVN_LIBRARIES LIBSVN_INCLUDE_DIR)
|
||||
if(NOT LIBSVN_FOUND)
|
||||
set(LIBSVN_LIBRARIES "")
|
||||
endif(NOT LIBSVN_FOUND)
|
||||
endif(HAVE_APR_CONFIG OR MSVC)
|
||||
@@ -1,44 +0,0 @@
|
||||
# - Get the platform-appropriate flags to add to force inclusion of a file
|
||||
#
|
||||
# The most common use of this is to use a generated config.h-type file
|
||||
# placed out of the source tree in all files.
|
||||
#
|
||||
# get_force_include_definitions(var forcedincludefiles...) -
|
||||
# where var is the name of your desired output variable, and everything
|
||||
# else is a source file to forcibly include.
|
||||
# a list item to be filtered.
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if(__get_force_include_definitions)
|
||||
return()
|
||||
endif()
|
||||
set(__get_force_include_definitions YES)
|
||||
|
||||
function(get_force_include_definitions var)
|
||||
set(_flagprefix)
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(_flag "-include")
|
||||
elseif(MSVC)
|
||||
set(_flag "/FI")
|
||||
else()
|
||||
message(SEND_ERROR "You don't seem to be using MSVC or GCC, but")
|
||||
message(SEND_ERROR "the project called get_force_include_definitions.")
|
||||
message(SEND_ERROR "Contact this project with the name of your")
|
||||
message(FATAL_ERROR "compiler and preferably the flag to force includes")
|
||||
endif()
|
||||
|
||||
set(_out)
|
||||
foreach(_item ${ARGN})
|
||||
list(APPEND _out "${_flag} \"${_item}\"")
|
||||
endforeach()
|
||||
set(${var} "${_out}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -1,21 +1,23 @@
|
||||
|
||||
macro(simgear_component_common name includePath sourcesList sources headers)
|
||||
set(fc${sourcesList} ${name})
|
||||
set(fh${sourcesList} ${name})
|
||||
foreach(s ${sources})
|
||||
set_property(GLOBAL
|
||||
APPEND PROPERTY ${sourcesList} "${CMAKE_CURRENT_SOURCE_DIR}/${s}")
|
||||
set(fc${sourcesList} "${fc${sourcesList}}#${CMAKE_CURRENT_SOURCE_DIR}/${s}")
|
||||
endforeach()
|
||||
if (SIMGEAR_SHARED)
|
||||
|
||||
foreach(h ${headers})
|
||||
set_property(GLOBAL
|
||||
APPEND PROPERTY PUBLIC_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/${h}")
|
||||
set(fh${sourcesList} "${fh${sourcesList}}#${CMAKE_CURRENT_SOURCE_DIR}/${h}")
|
||||
endforeach()
|
||||
foreach(s ${sources})
|
||||
set_property(GLOBAL
|
||||
APPEND PROPERTY ${sourcesList} "${CMAKE_CURRENT_SOURCE_DIR}/${s}")
|
||||
endforeach()
|
||||
|
||||
set_property(GLOBAL APPEND PROPERTY FG_GROUPS_${sourcesList}_C "${fc${sourcesList}}@")
|
||||
set_property(GLOBAL APPEND PROPERTY FG_GROUPS_${sourcesList}_H "${fh${sourcesList}}@")
|
||||
foreach(h ${headers})
|
||||
set_property(GLOBAL
|
||||
APPEND PROPERTY PUBLIC_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/${h}")
|
||||
endforeach()
|
||||
|
||||
else()
|
||||
set(libName "sg${name}")
|
||||
add_library(${libName} STATIC ${sources} ${headers})
|
||||
|
||||
install (TARGETS ${libName} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif()
|
||||
|
||||
install (FILES ${headers} DESTINATION include/simgear/${includePath})
|
||||
endmacro()
|
||||
|
||||
1
INSTALL
1
INSTALL
@@ -31,6 +31,7 @@ SimGear depends on a number of 3rd party libraries, the most notable
|
||||
being:
|
||||
|
||||
* OpenSceneGraph (OSG) - see README.OSG
|
||||
* Portable Games Library (PLIB) - see README.plib
|
||||
* zlib compression library - see README.zlib
|
||||
* Open Audio Library (OpenAL) - see README.OpenAL
|
||||
* Subversion Client Library (optional dependency)
|
||||
|
||||
@@ -18,3 +18,12 @@ ccmake .
|
||||
then press 'g' to generate and exit ]
|
||||
|
||||
|
||||
The alut library is also required, but comes separately in the package
|
||||
freelut-1.1.0.tar.gz. This package can be downloaded from the same page
|
||||
(http://connect.creativelabs.com/openal/default.aspx). Download and run:
|
||||
tar xzvf freelut-1.1.0.tar.gz
|
||||
cd freealut-1.1.0
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ Probably you want to specify an install prefix:
|
||||
cmake ../simgear -DCMAKE_INSTALL_PREFIX=/usr
|
||||
|
||||
Note the install prefix is automatically searched for required libraries
|
||||
and header files, so if you install OpenSceneGraph to the same prefix,
|
||||
most configuration options are unnecessary.
|
||||
and header files, so if you install PLIB, OpenSceneGraph to the
|
||||
same prefix, most configuration options are unnecessary.
|
||||
|
||||
If for some reason you have a dependency (or several) at a different prefix,
|
||||
you can specify one or more via CMAKE_PREFIX_PATH:
|
||||
@@ -33,7 +33,7 @@ you can specify one or more via CMAKE_PREFIX_PATH:
|
||||
Standard prefixes are searched automatically (/usr, /usr/local, /opt/local)
|
||||
|
||||
Most dependencies also expose an environment variable to specify their
|
||||
installation directory explicitly eg OSG_DIR. Any of the methods
|
||||
installation directory explicitly eg OSG_DIR or PLIBDIR. Any of the methods
|
||||
described above will work, but specifying an INSTALL_PREFIX or PREFIX_PATH is
|
||||
usually simpler.
|
||||
|
||||
|
||||
32
README.plib
Normal file
32
README.plib
Normal file
@@ -0,0 +1,32 @@
|
||||
[This file is mirrored in both the FlightGear and SimGear packages.]
|
||||
|
||||
You *must* have plib version 1.8.5 or later installed on your system
|
||||
to build FlightGear!" Flight Gear is no longer compatible with the
|
||||
earlier versions of the library.
|
||||
|
||||
You can get the latest version of plib from:
|
||||
|
||||
http://plib.sourceforge.net
|
||||
|
||||
Build notes:
|
||||
|
||||
You should be able to just run "./configure" to configure the package
|
||||
and use all of plib's defaults. Then run "make" followed by "make
|
||||
install". By default, plib installs itself into /usr so if you don't
|
||||
like this, be sure to specify an alternate prefix such as --prefix=/usr/local
|
||||
|
||||
As of this writing (2007-11-18), many linux distributions are shipped with a
|
||||
working version of plib, so chances are that this library is already
|
||||
installed. It should be noted, that currently plib version no longer compiles
|
||||
using recent versions of gcc (confirmed on version gcc 4.1.2, as shipped with
|
||||
SuSe 10.2). As a workaround, it is possible to either use plib SVN. Run the
|
||||
following commands:
|
||||
|
||||
|
||||
svn co https://plib.svn.sourceforge.net/svnroot/plib/trunk plib
|
||||
cd plib
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
|
||||
@@ -9,7 +9,9 @@ Source: http://mirrors.ibiblio.org/pub/mirrors/flightgear/ftp/Source/simgear
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
||||
|
||||
BuildRequires: gcc, gcc-c++, cmake
|
||||
BuildRequires: freealut, freealut-devel
|
||||
BuildRequires: libopenal1-soft, openal-soft
|
||||
BuildRequires: plib-devel >= 1.8.5
|
||||
BuildRequires: libOpenSceneGraph-devel >= 3.0
|
||||
BuildRequires: zlib, zlib-devel
|
||||
BuildRequires: libjpeg62, libjpeg62-devel
|
||||
@@ -25,6 +27,7 @@ simulation and visualization applications such as FlightGear or TerraGear.
|
||||
Group: Development/Libraries/Other
|
||||
Summary: Development header files for SimGear
|
||||
Requires: SimGear = %{version}
|
||||
Requires: plib-devel
|
||||
|
||||
%description devel
|
||||
Development headers and libraries for building applications against SimGear.
|
||||
|
||||
@@ -3,7 +3,6 @@ file(WRITE ${PROJECT_BINARY_DIR}/simgear/version.h "#define SIMGEAR_VERSION ${SI
|
||||
|
||||
foreach( mylibfolder
|
||||
bucket
|
||||
bvh
|
||||
debug
|
||||
ephemeris
|
||||
io
|
||||
@@ -11,14 +10,12 @@ foreach( mylibfolder
|
||||
math
|
||||
misc
|
||||
nasal
|
||||
nasal/cppbind
|
||||
props
|
||||
serial
|
||||
structure
|
||||
threads
|
||||
timing
|
||||
xml
|
||||
package
|
||||
)
|
||||
|
||||
add_subdirectory(${mylibfolder})
|
||||
@@ -26,7 +23,6 @@ foreach( mylibfolder
|
||||
endforeach( mylibfolder )
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
add_subdirectory(canvas)
|
||||
add_subdirectory(environment)
|
||||
add_subdirectory(screen)
|
||||
add_subdirectory(scene)
|
||||
@@ -42,102 +38,51 @@ endif(ENABLE_RTI)
|
||||
set(HEADERS compiler.h constants.h sg_inlines.h ${PROJECT_BINARY_DIR}/simgear/version.h)
|
||||
install (FILES ${HEADERS} DESTINATION include/simgear/)
|
||||
|
||||
get_property(coreSources GLOBAL PROPERTY CORE_SOURCES)
|
||||
get_property(sceneSources GLOBAL PROPERTY SCENE_SOURCES)
|
||||
get_property(publicHeaders GLOBAL PROPERTY PUBLIC_HEADERS)
|
||||
get_property(localExpatSources GLOBAL PROPERTY LOCAL_EXPAT_SOURCES)
|
||||
|
||||
if(SIMGEAR_SHARED)
|
||||
message(STATUS "Library building mode: SHARED LIBRARIES")
|
||||
add_library(SimGearCore SHARED ${coreSources} ${localExpatSources})
|
||||
get_property(coreSources GLOBAL PROPERTY CORE_SOURCES)
|
||||
get_property(sceneSources GLOBAL PROPERTY SCENE_SOURCES)
|
||||
get_property(publicHeaders GLOBAL PROPERTY PUBLIC_HEADERS)
|
||||
|
||||
add_library(SimGearCore SHARED ${coreSources})
|
||||
|
||||
# set_property(TARGET SimGearCore PROPERTY FRAMEWORK 1)
|
||||
# message(STATUS "public header: ${publicHeaders}")
|
||||
# set_property(TARGET SimGearCore PROPERTY PUBLIC_HEADER "${publicHeaders}")
|
||||
set_property(TARGET SimGearCore PROPERTY LINKER_LANGUAGE CXX)
|
||||
|
||||
set_property(TARGET SimGearCore PROPERTY VERSION ${SIMGEAR_VERSION})
|
||||
set_property(TARGET SimGearCore PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||
install(TARGETS SimGearCore EXPORT SimGearCoreConfig LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(EXPORT SimGearCoreConfig DESTINATION share/SimGearCore)
|
||||
|
||||
|
||||
target_link_libraries(SimGearCore ${ZLIB_LIBRARY} ${RT_LIBRARY})
|
||||
install(TARGETS SimGearCore LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
if(LIBSVN_FOUND)
|
||||
add_definitions(${APR_CFLAGS})
|
||||
include_directories(${LIBSVN_INCLUDE_DIR})
|
||||
target_link_libraries(SimGearCore ${LIBSVN_LIBRARIES})
|
||||
endif(LIBSVN_FOUND)
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
add_library(SimGearScene SHARED ${sceneSources})
|
||||
# set_property(TARGET SimGearScene PROPERTY FRAMEWORK 1)
|
||||
# set_property(TARGET SimGearScene PROPERTY PUBLIC_HEADER "${publicHeaders}")
|
||||
set_property(TARGET SimGearScene PROPERTY LINKER_LANGUAGE CXX)
|
||||
set_property(TARGET SimGearScene PROPERTY VERSION ${SIMGEAR_VERSION})
|
||||
set_property(TARGET SimGearScene PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||
|
||||
# EXPORT SimGearSceneConfig
|
||||
install(TARGETS SimGearScene LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} )
|
||||
# install(EXPORT SimGearSceneConfig DESTINATION share/SimGearScene)
|
||||
endif()
|
||||
target_link_libraries(SimGearScene
|
||||
SimGearCore
|
||||
${ZLIB_LIBRARY}
|
||||
${OPENSCENEGRAPH_LIBRARIES}
|
||||
${OPENAL_LIBRARY} ${ALUT_LIBRARY}
|
||||
${OPENGL_LIBRARY}
|
||||
${JPEG_LIBRARY})
|
||||
|
||||
install(TARGETS SimGearScene LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(STATUS "Library building mode: STATIC LIBRARIES")
|
||||
|
||||
get_property(FG_GROUPS_CORE_SOURCES_C GLOBAL PROPERTY FG_GROUPS_CORE_SOURCES_C)
|
||||
string(REPLACE "@" ";" groups ${FG_GROUPS_CORE_SOURCES_C} )
|
||||
foreach(g ${groups})
|
||||
string(REPLACE "#" ";" g2 ${g})
|
||||
list(GET g2 0 name)
|
||||
list(REMOVE_AT g2 0)
|
||||
source_group("${name}\\Sources" FILES ${g2})
|
||||
endforeach()
|
||||
|
||||
get_property(FG_GROUPS_CORE_SOURCES_H GLOBAL PROPERTY FG_GROUPS_CORE_SOURCES_H)
|
||||
string(REPLACE "@" ";" groups ${FG_GROUPS_CORE_SOURCES_H} )
|
||||
foreach(g ${groups})
|
||||
string(REPLACE "#" ";" g2 ${g})
|
||||
list(GET g2 0 name)
|
||||
list(REMOVE_AT g2 0)
|
||||
source_group("${name}\\Headers" FILES ${g2})
|
||||
endforeach()
|
||||
|
||||
add_library(SimGearCore STATIC ${coreSources} ${localExpatSources})
|
||||
install(TARGETS SimGearCore ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
get_property(FG_GROUPS_SCENE_SOURCES_C GLOBAL PROPERTY FG_GROUPS_SCENE_SOURCES_C)
|
||||
string(REPLACE "@" ";" groups ${FG_GROUPS_SCENE_SOURCES_C} )
|
||||
foreach(g ${groups})
|
||||
string(REPLACE "#" ";" g2 ${g})
|
||||
list(GET g2 0 name)
|
||||
list(REMOVE_AT g2 0)
|
||||
source_group("${name}\\Sources" FILES ${g2})
|
||||
endforeach()
|
||||
|
||||
get_property(FG_GROUPS_SCENE_SOURCES_H GLOBAL PROPERTY FG_GROUPS_SCENE_SOURCES_H)
|
||||
string(REPLACE "@" ";" groups ${FG_GROUPS_SCENE_SOURCES_H} )
|
||||
foreach(g ${groups})
|
||||
string(REPLACE "#" ";" g2 ${g})
|
||||
list(GET g2 0 name)
|
||||
list(REMOVE_AT g2 0)
|
||||
source_group("${name}\\Headers" FILES ${g2})
|
||||
endforeach()
|
||||
|
||||
add_library(SimGearScene STATIC ${sceneSources})
|
||||
install(TARGETS SimGearScene ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif(NOT SIMGEAR_HEADLESS)
|
||||
endif(SIMGEAR_SHARED)
|
||||
|
||||
target_link_libraries(SimGearCore
|
||||
${ZLIB_LIBRARY}
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${EXPAT_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${COCOA_LIBRARY})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
target_link_libraries(SimGearScene
|
||||
SimGearCore
|
||||
${ZLIB_LIBRARY}
|
||||
${OPENSCENEGRAPH_LIBRARIES}
|
||||
${OPENAL_LIBRARY}
|
||||
${OPENGL_LIBRARY}
|
||||
${JPEG_LIBRARY})
|
||||
endif()
|
||||
|
||||
if(ENABLE_RTI)
|
||||
# Ugly first aid to make hla compile agian
|
||||
set_property(SOURCE hla/RTI13InteractionClass.cxx hla/RTI13ObjectClass.cxx
|
||||
hla/RTI13ObjectInstance.cxx hla/RTI13Federate.cxx
|
||||
hla/RTI13FederateFactory.cxx
|
||||
APPEND PROPERTY COMPILE_FLAGS "-I${RTI_INCLUDE_DIR}")
|
||||
endif(ENABLE_RTI)
|
||||
|
||||
@@ -4,13 +4,4 @@ include (SimGearComponent)
|
||||
set(HEADERS newbucket.hxx)
|
||||
set(SOURCES newbucket.cxx)
|
||||
|
||||
simgear_component(bucket bucket "${SOURCES}" "${HEADERS}")
|
||||
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
|
||||
add_executable(test_bucket test_bucket.cxx)
|
||||
add_test(test_bucket ${EXECUTABLE_OUTPUT_PATH}/test_bucket)
|
||||
target_link_libraries(test_bucket ${TEST_LIBS})
|
||||
|
||||
endif(ENABLE_TESTS)
|
||||
simgear_component(bucket bucket "${SOURCES}" "${HEADERS}")
|
||||
@@ -27,56 +27,36 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio> // some platforms need this for ::snprintf
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include "newbucket.hxx"
|
||||
|
||||
|
||||
// default constructor
|
||||
SGBucket::SGBucket() :
|
||||
lon(-1000),
|
||||
lat(-1000),
|
||||
x(0),
|
||||
y(0)
|
||||
{
|
||||
SGBucket::SGBucket() {
|
||||
}
|
||||
|
||||
bool SGBucket::isValid() const
|
||||
{
|
||||
// The most northerly valid latitude is 89, not 90. There is no tile
|
||||
// whose *bottom* latitude is 90. Similar there is no tile whose left egde
|
||||
// is 180 longitude.
|
||||
return (lon >= -180) &&
|
||||
(lon < 180) &&
|
||||
(lat >= -90) &&
|
||||
(lat < 90) &&
|
||||
(x < 8) && (y < 8);
|
||||
}
|
||||
|
||||
void SGBucket::make_bad()
|
||||
{
|
||||
lon = -1000;
|
||||
lat = -1000;
|
||||
}
|
||||
|
||||
#ifndef NO_DEPRECATED_API
|
||||
|
||||
// constructor for specified location
|
||||
SGBucket::SGBucket(const double dlon, const double dlat) {
|
||||
set_bucket(dlon, dlat);
|
||||
}
|
||||
#endif
|
||||
|
||||
SGBucket::SGBucket(const SGGeod& geod) {
|
||||
innerSet(geod.getLongitudeDeg(),
|
||||
geod.getLatitudeDeg());
|
||||
set_bucket(geod);
|
||||
}
|
||||
|
||||
// create an impossible bucket if false
|
||||
SGBucket::SGBucket(const bool is_good) {
|
||||
set_bucket(0.0, 0.0);
|
||||
if ( !is_good ) {
|
||||
lon = -1000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Parse a unique scenery tile index and find the lon, lat, x, and y
|
||||
SGBucket::SGBucket(const long int bindex) {
|
||||
long int index = bindex;
|
||||
@@ -95,59 +75,48 @@ SGBucket::SGBucket(const long int bindex) {
|
||||
x = index;
|
||||
}
|
||||
|
||||
/* Calculate the greatest integral value less than
|
||||
* or equal to the given value (floor(x)),
|
||||
* but attribute coordinates close to the boundary to the next
|
||||
* (increasing) integral
|
||||
*/
|
||||
static int floorWithEpsilon(double x)
|
||||
{
|
||||
return static_cast<int>(floor(x + SG_EPSILON));
|
||||
}
|
||||
|
||||
#ifndef NO_DEPRECATED_API
|
||||
|
||||
void SGBucket::set_bucket(double dlon, double dlat)
|
||||
{
|
||||
innerSet(dlon, dlat);
|
||||
}
|
||||
|
||||
|
||||
void SGBucket::set_bucket(const SGGeod& geod)
|
||||
{
|
||||
innerSet(geod.getLongitudeDeg(), geod.getLatitudeDeg());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Set the bucket params for the specified lat and lon
|
||||
void SGBucket::innerSet( double dlon, double dlat )
|
||||
{
|
||||
if ((dlon < -180.0) || (dlon >= 180.0)) {
|
||||
SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed longitude:" << dlon);
|
||||
dlon = SGMiscd::normalizePeriodic(-180.0, 180.0, dlon);
|
||||
}
|
||||
|
||||
if ((dlat < -90.0) || (dlat > 90.0)) {
|
||||
SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed latitude" << dlat);
|
||||
dlat = SGMiscd::clip(dlat, -90.0, 90.0);
|
||||
}
|
||||
|
||||
void SGBucket::set_bucket( double *lonlat ) {
|
||||
set_bucket( lonlat[0], lonlat[1] );
|
||||
}
|
||||
|
||||
|
||||
// Set the bucket params for the specified lat and lon
|
||||
void SGBucket::set_bucket( double dlon, double dlat ) {
|
||||
//
|
||||
// longitude first
|
||||
// latitude first
|
||||
//
|
||||
double span = sg_bucket_span( dlat );
|
||||
// we do NOT need to special case lon=180 here, since
|
||||
// normalizePeriodic will never return 180; it will
|
||||
// return -180, which is what we want.
|
||||
lon = floorWithEpsilon(dlon);
|
||||
|
||||
double diff = dlon - (double)(int)dlon;
|
||||
|
||||
// cout << "diff = " << diff << " span = " << span << endl;
|
||||
|
||||
/* Calculate the greatest integral longitude less than
|
||||
* or equal to the given longitude (floor(dlon)),
|
||||
* but attribute coordinates near the east border
|
||||
* to the next tile.
|
||||
*/
|
||||
if ( (dlon >= 0) || (fabs(diff) < SG_EPSILON) ) {
|
||||
lon = (int)dlon;
|
||||
} else {
|
||||
lon = (int)dlon - 1;
|
||||
}
|
||||
|
||||
// find subdivision or super lon if needed
|
||||
if ( span <= 1.0 ) {
|
||||
if ( span < SG_EPSILON ) {
|
||||
/* sg_bucket_span() never returns 0.0
|
||||
* or anything near it, so this really
|
||||
* should not occur at any time.
|
||||
*/
|
||||
// polar cap
|
||||
lon = 0;
|
||||
x = 0;
|
||||
} else if ( span <= 1.0 ) {
|
||||
/* We have more than one tile per degree of
|
||||
* longitude, so we need an x offset.
|
||||
*/
|
||||
x = floorWithEpsilon((dlon - lon) / span);
|
||||
x = (int)((dlon - lon) / span);
|
||||
} else {
|
||||
/* We have one or more degrees per tile,
|
||||
* so we need to find the base longitude
|
||||
@@ -160,28 +129,48 @@ void SGBucket::innerSet( double dlon, double dlat )
|
||||
*
|
||||
* That way, the Greenwich Meridian is always
|
||||
* a tile border.
|
||||
*
|
||||
* This gets us into trouble with the polar caps,
|
||||
* which have width 360 and thus either span
|
||||
* the range from 0 to 360 or from -360 to 0
|
||||
* degrees, depending on whether lon is positive
|
||||
* or negative!
|
||||
*
|
||||
* We also get into trouble with the 8 degree tiles
|
||||
* north of 88N and south of 88S, because the west-
|
||||
* and east-most tiles in that range will cover 184W
|
||||
* to 176W and 176E to 184E respectively, with their
|
||||
* center at 180E/W!
|
||||
*/
|
||||
lon=static_cast<int>(floor(lon / span) * span);
|
||||
x = 0;
|
||||
lon=(int)floor(floor((lon+SG_EPSILON)/span)*span);
|
||||
/* Correct the polar cap issue */
|
||||
if ( lon < -180 ) {
|
||||
lon = -180;
|
||||
}
|
||||
x = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// then latitude
|
||||
//
|
||||
lat = floorWithEpsilon(dlat);
|
||||
|
||||
// special case when passing in the north pole point (possibly due to
|
||||
// clipping latitude above). Ensures we generate a valid bucket in this
|
||||
// scenario
|
||||
if (lat == 90) {
|
||||
lat = 89;
|
||||
y = 7;
|
||||
diff = dlat - (double)(int)dlat;
|
||||
|
||||
/* Again, a modified floor() function (see longitude) */
|
||||
if ( (dlat >= 0) || (fabs(diff) < SG_EPSILON) ) {
|
||||
lat = (int)dlat;
|
||||
} else {
|
||||
/* Latitude base and offset are easier, as
|
||||
* tiles always are 1/8 degree of latitude wide.
|
||||
*/
|
||||
y = floorWithEpsilon((dlat - lat) * 8);
|
||||
lat = (int)dlat - 1;
|
||||
}
|
||||
/* Latitude base and offset are easier, as
|
||||
* tiles always are 1/8 degree of latitude wide.
|
||||
*/
|
||||
y = (int)((dlat - lat) * 8);
|
||||
}
|
||||
|
||||
|
||||
void SGBucket::set_bucket(const SGGeod& geod)
|
||||
{
|
||||
set_bucket(geod.getLongitudeDeg(), geod.getLatitudeDeg());
|
||||
}
|
||||
|
||||
// Build the path name for this bucket
|
||||
@@ -223,7 +212,7 @@ std::string SGBucket::gen_base_path() const {
|
||||
main_lat *= -1;
|
||||
}
|
||||
|
||||
::snprintf(raw_path, 256, "%c%03d%c%02d/%c%03d%c%02d",
|
||||
sprintf(raw_path, "%c%03d%c%02d/%c%03d%c%02d",
|
||||
hem, top_lon, pole, top_lat,
|
||||
hem, main_lon, pole, main_lat);
|
||||
|
||||
@@ -235,6 +224,17 @@ std::string SGBucket::gen_base_path() const {
|
||||
|
||||
// return width of the tile in degrees
|
||||
double SGBucket::get_width() const {
|
||||
if (lon==-180 && (lat==-89 || lat==88) ) {
|
||||
/* Normally the tile at 180W in 88N and 89S
|
||||
* would cover 184W to 176W and the next
|
||||
* on the east side starts at 176W.
|
||||
* To correct, make this a special tile
|
||||
* from 180W to 176W with 4 degrees width
|
||||
* instead of the normal 8 degrees at
|
||||
* that latitude.
|
||||
*/
|
||||
return 4.0;
|
||||
}
|
||||
return sg_bucket_span( get_center_lat() );
|
||||
}
|
||||
|
||||
@@ -244,32 +244,17 @@ double SGBucket::get_height() const {
|
||||
return SG_BUCKET_SPAN;
|
||||
}
|
||||
|
||||
double SGBucket::get_highest_lat() const
|
||||
{
|
||||
unsigned char adjustedY = y;
|
||||
if (lat >= 0) {
|
||||
// tile is north of the equator, so we want the top edge. Add one
|
||||
// to y to achieve this.
|
||||
++adjustedY;
|
||||
|
||||
// return width of the tile in meters
|
||||
double SGBucket::get_width_m() const {
|
||||
double clat = (int)get_center_lat();
|
||||
if ( clat > 0 ) {
|
||||
clat = (int)clat + 0.5;
|
||||
} else {
|
||||
clat = (int)clat - 0.5;
|
||||
}
|
||||
|
||||
return lat + (adjustedY / 8.0);
|
||||
}
|
||||
|
||||
|
||||
// return width of the tile in meters. This function is used by the
|
||||
// tile-manager to estimate how many tiles are in the view distance, so
|
||||
// we care about the smallest width, which occurs at the highest latitude.
|
||||
double SGBucket::get_width_m() const
|
||||
{
|
||||
double clat_rad = get_highest_lat() * SGD_DEGREES_TO_RADIANS;
|
||||
double clat_rad = clat * SGD_DEGREES_TO_RADIANS;
|
||||
double cos_lat = cos( clat_rad );
|
||||
if (fabs(cos_lat) < SG_EPSILON) {
|
||||
// happens for polar tiles, since we pass in a latitude of 90
|
||||
// return an arbitrary small value so all tiles are loaded
|
||||
return 10.0;
|
||||
}
|
||||
|
||||
double local_radius = cos_lat * SG_EQUATORIAL_RADIUS_M;
|
||||
double local_perimeter = local_radius * SGD_2PI;
|
||||
double degree_width = local_perimeter / 360.0;
|
||||
@@ -286,41 +271,7 @@ double SGBucket::get_height_m() const {
|
||||
return SG_BUCKET_SPAN * degree_height;
|
||||
}
|
||||
|
||||
SGBucket SGBucket::sibling(int dx, int dy) const
|
||||
{
|
||||
if (!isValid()) {
|
||||
SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::sibling: requesting sibling of invalid bucket");
|
||||
return SGBucket();
|
||||
}
|
||||
|
||||
double clat = get_center_lat() + dy * SG_BUCKET_SPAN;
|
||||
// return invalid here instead of clipping, so callers can discard
|
||||
// invalid buckets without having to check if it's an existing one
|
||||
if ((clat < -90.0) || (clat > 90.0)) {
|
||||
return SGBucket();
|
||||
}
|
||||
|
||||
// find the lon span for the new latitude
|
||||
double span = sg_bucket_span( clat );
|
||||
|
||||
double tmp = get_center_lon() + dx * span;
|
||||
tmp = SGMiscd::normalizePeriodic(-180.0, 180.0, tmp);
|
||||
|
||||
SGBucket b;
|
||||
b.innerSet(tmp, clat);
|
||||
return b;
|
||||
}
|
||||
|
||||
std::string SGBucket::gen_index_str() const
|
||||
{
|
||||
char tmp[20];
|
||||
::snprintf(tmp, 20, "%ld",
|
||||
(((long)lon + 180) << 14) + ((lat + 90) << 6)
|
||||
+ (y << 3) + x);
|
||||
return (std::string)tmp;
|
||||
}
|
||||
|
||||
#ifndef NO_DEPRECATED_API
|
||||
// find the bucket which is offset by the specified tile units in the
|
||||
// X & Y direction. We need the current lon and lat to resolve
|
||||
// ambiguities when going from a wider tile to a narrower one above or
|
||||
@@ -347,7 +298,7 @@ SGBucket sgBucketOffset( double dlon, double dlat, int dx, int dy ) {
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// calculate the offset between two buckets
|
||||
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy ) {
|
||||
@@ -405,25 +356,4 @@ void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy ) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void sgGetBuckets( const SGGeod& min, const SGGeod& max, std::vector<SGBucket>& list ) {
|
||||
double lon, lat, span;
|
||||
|
||||
for (lat = min.getLatitudeDeg(); lat < max.getLatitudeDeg()+SG_BUCKET_SPAN; lat += SG_BUCKET_SPAN) {
|
||||
span = sg_bucket_span( lat );
|
||||
for (lon = min.getLongitudeDeg(); lon <= max.getLongitudeDeg(); lon += span)
|
||||
{
|
||||
SGBucket b(SGGeod::fromDeg(lon, lat));
|
||||
if (!b.isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list.push_back(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<< ( std::ostream& out, const SGBucket& b )
|
||||
{
|
||||
return out << b.lon << ":" << (int)b.x << ", " << b.lat << ":" << (int)b.y;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,11 +39,9 @@
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio> // sprintf()
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
|
||||
// #define NO_DEPRECATED_API
|
||||
|
||||
/**
|
||||
* standard size of a bucket in degrees (1/8 of a degree)
|
||||
@@ -59,7 +57,9 @@
|
||||
// return the horizontal tile span factor based on latitude
|
||||
static double sg_bucket_span( double l ) {
|
||||
if ( l >= 89.0 ) {
|
||||
return 12.0;
|
||||
return 360.0;
|
||||
} else if ( l >= 88.0 ) {
|
||||
return 8.0;
|
||||
} else if ( l >= 86.0 ) {
|
||||
return 4.0;
|
||||
} else if ( l >= 83.0 ) {
|
||||
@@ -80,10 +80,12 @@ static double sg_bucket_span( double l ) {
|
||||
return 1.0;
|
||||
} else if ( l >= -86.0 ) {
|
||||
return 2.0;
|
||||
} else if ( l >= -89.0 ) {
|
||||
} else if ( l >= -88.0 ) {
|
||||
return 4.0;
|
||||
} else if ( l >= -89.0 ) {
|
||||
return 8.0;
|
||||
} else {
|
||||
return 12.0;
|
||||
return 360.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,31 +102,23 @@ class SGBucket {
|
||||
private:
|
||||
short lon; // longitude index (-180 to 179)
|
||||
short lat; // latitude index (-90 to 89)
|
||||
unsigned char x; // x subdivision (0 to 7)
|
||||
unsigned char y; // y subdivision (0 to 7)
|
||||
char x; // x subdivision (0 to 7)
|
||||
char y; // y subdivision (0 to 7)
|
||||
|
||||
void innerSet( double dlon, double dlat );
|
||||
public:
|
||||
|
||||
/**
|
||||
* Default constructor, creates an invalid SGBucket
|
||||
* Default constructor.
|
||||
*/
|
||||
SGBucket();
|
||||
|
||||
/**
|
||||
* Check if this bucket refers to a valid tile, or not.
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
#ifndef NO_DEPRECATED_API
|
||||
/**
|
||||
* Construct a bucket given a specific location.
|
||||
* @param dlon longitude specified in degrees
|
||||
* @param dlat latitude specified in degrees
|
||||
*/
|
||||
SGBucket(const double dlon, const double dlat);
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Construct a bucket given a specific location.
|
||||
* @param dlon longitude specified in degrees
|
||||
@@ -132,36 +126,51 @@ public:
|
||||
*/
|
||||
SGBucket(const SGGeod& geod);
|
||||
|
||||
/** Construct a bucket.
|
||||
* @param is_good if false, create an invalid bucket. This is
|
||||
* useful * if you are comparing cur_bucket to last_bucket and
|
||||
* you want to * make sure last_bucket starts out as something
|
||||
* impossible.
|
||||
*/
|
||||
SGBucket(const bool is_good);
|
||||
|
||||
/** Construct a bucket given a unique bucket index number.
|
||||
* @param bindex unique bucket index
|
||||
*/
|
||||
SGBucket(const long int bindex);
|
||||
|
||||
#ifndef NO_DEPRECATED_API
|
||||
/**
|
||||
* Reset a bucket to represent a new lat and lon
|
||||
* @param dlon longitude specified in degrees
|
||||
* @param dlat latitude specified in degrees
|
||||
*/
|
||||
void set_bucket(const SGGeod& geod);
|
||||
|
||||
|
||||
/**
|
||||
* Reset a bucket to represent a new lat and lon
|
||||
* @param dlon longitude specified in degrees
|
||||
* @param dlat latitude specified in degrees
|
||||
*/
|
||||
void set_bucket( double dlon, double dlat );
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Reset a bucket to represent a new lat and lon
|
||||
* @param lonlat an array of double[2] holding lon and lat
|
||||
* (specified) in degrees
|
||||
*/
|
||||
void set_bucket( double *lonlat );
|
||||
|
||||
/**
|
||||
* Reset a bucket to represent a new lat and lon
|
||||
* @param dlon longitude specified in degrees
|
||||
* @param dlat latitude specified in degrees
|
||||
*/
|
||||
void set_bucket(const SGGeod& geod);
|
||||
|
||||
/**
|
||||
* Create an impossible bucket.
|
||||
* This is useful if you are comparing cur_bucket to last_bucket
|
||||
* and you want to make sure last_bucket starts out as something
|
||||
* impossible.
|
||||
*/
|
||||
void make_bad();
|
||||
|
||||
inline void make_bad() {
|
||||
set_bucket(0.0, 0.0);
|
||||
lon = -1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the unique scenery tile index for this bucket
|
||||
*
|
||||
@@ -186,8 +195,14 @@ public:
|
||||
* string form.
|
||||
* @return tile index in string form
|
||||
*/
|
||||
std::string gen_index_str() const;
|
||||
|
||||
inline std::string gen_index_str() const {
|
||||
char tmp[20];
|
||||
std::sprintf(tmp, "%ld",
|
||||
(((long)lon + 180) << 14) + ((lat + 90) << 6)
|
||||
+ (y << 3) + x);
|
||||
return (std::string)tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the base path name for this bucket.
|
||||
* @return base path in string form
|
||||
@@ -214,13 +229,6 @@ public:
|
||||
return lat + y / 8.0 + SG_HALF_BUCKET_SPAN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the highest (furthest from the equator) latitude of this
|
||||
* tile. This is the top edge for tiles north of the equator, and
|
||||
* the bottom edge for tiles south
|
||||
*/
|
||||
double get_highest_lat() const;
|
||||
|
||||
/**
|
||||
* @return the width of the tile in degrees.
|
||||
*/
|
||||
@@ -282,11 +290,6 @@ public:
|
||||
*/
|
||||
inline int get_y() const { return y; }
|
||||
|
||||
/**
|
||||
* @return bucket offset from this by dx,dy
|
||||
*/
|
||||
SGBucket sibling(int dx, int dy) const;
|
||||
|
||||
// friends
|
||||
|
||||
friend std::ostream& operator<< ( std::ostream&, const SGBucket& );
|
||||
@@ -298,7 +301,7 @@ inline bool operator!= (const SGBucket& lhs, const SGBucket& rhs)
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
#ifndef NO_DEPRECATED_API
|
||||
|
||||
/**
|
||||
* \relates SGBucket
|
||||
* Return the bucket which is offset from the specified dlon, dlat by
|
||||
@@ -310,7 +313,6 @@ inline bool operator!= (const SGBucket& lhs, const SGBucket& rhs)
|
||||
* @return offset bucket
|
||||
*/
|
||||
SGBucket sgBucketOffset( double dlon, double dlat, int x, int y );
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
@@ -324,21 +326,17 @@ SGBucket sgBucketOffset( double dlon, double dlat, int x, int y );
|
||||
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy );
|
||||
|
||||
|
||||
/**
|
||||
* \relates SGBucket
|
||||
* retrieve a list of buckets in the given bounding box
|
||||
* @param min min lon,lat of bounding box in degrees
|
||||
* @param max max lon,lat of bounding box in degrees
|
||||
* @param list standard vector of buckets within the bounding box
|
||||
*/
|
||||
void sgGetBuckets( const SGGeod& min, const SGGeod& max, std::vector<SGBucket>& list );
|
||||
|
||||
/**
|
||||
* Write the bucket lon, lat, x, and y to the output stream.
|
||||
* @param out output stream
|
||||
* @param b bucket
|
||||
*/
|
||||
std::ostream& operator<< ( std::ostream& out, const SGBucket& b );
|
||||
inline std::ostream&
|
||||
operator<< ( std::ostream& out, const SGBucket& b )
|
||||
{
|
||||
return out << b.lon << ":" << (int)b.x << ", " << b.lat << ":" << (int)b.y;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compare two bucket structures for equality.
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
/**************************************************************************
|
||||
* test_bucket.cxx -- unit-tests for SGBucket class
|
||||
*
|
||||
* Copyright (C) 2014 James Turner - <zakalawe@mac.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
#include <simgear/bucket/newbucket.hxx>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
void testBucketSpans()
|
||||
{
|
||||
COMPARE(sg_bucket_span(0.0), 0.125);
|
||||
COMPARE(sg_bucket_span(-20), 0.125);
|
||||
COMPARE(sg_bucket_span(-40), 0.25);
|
||||
COMPARE(sg_bucket_span(89.9), 12.0);
|
||||
COMPARE(sg_bucket_span(88.1), 4.0);
|
||||
COMPARE(sg_bucket_span(-89.9), 12.0);
|
||||
}
|
||||
|
||||
void testBasic()
|
||||
{
|
||||
SGBucket b1(5.1, 55.05);
|
||||
COMPARE(b1.get_chunk_lon(), 5);
|
||||
COMPARE(b1.get_chunk_lat(), 55);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 0);
|
||||
COMPARE(b1.gen_index(), 3040320);
|
||||
COMPARE(b1.gen_base_path(), "e000n50/e005n55");
|
||||
VERIFY(b1.isValid());
|
||||
|
||||
SGBucket b2(-10.1, -43.8);
|
||||
COMPARE(b2.get_chunk_lon(), -11);
|
||||
COMPARE(b2.get_chunk_lat(), -44);
|
||||
COMPARE(b2.get_x(), 3);
|
||||
COMPARE(b2.get_y(), 1); // latitude chunks numbered bottom to top, it seems
|
||||
COMPARE(b2.gen_base_path(), "w020s50/w011s44");
|
||||
VERIFY(b2.isValid());
|
||||
|
||||
SGBucket b3(123.48, 9.01);
|
||||
COMPARE(b3.get_chunk_lon(), 123);
|
||||
COMPARE(b3.get_chunk_lat(), 9);
|
||||
COMPARE(b3.get_x(), 3);
|
||||
COMPARE(b3.get_y(), 0);
|
||||
COMPARE(b3.gen_base_path(), "e120n00/e123n09");
|
||||
VERIFY(b3.isValid());
|
||||
|
||||
SGBucket defBuck;
|
||||
VERIFY(!defBuck.isValid());
|
||||
|
||||
b3.make_bad();
|
||||
VERIFY(!b3.isValid());
|
||||
|
||||
SGBucket atAntiMeridian(180.0, 12.3);
|
||||
VERIFY(atAntiMeridian.isValid());
|
||||
COMPARE(atAntiMeridian.get_chunk_lon(), -180);
|
||||
COMPARE(atAntiMeridian.get_x(), 0);
|
||||
|
||||
SGBucket atAntiMeridian2(-180.0, -78.1);
|
||||
VERIFY(atAntiMeridian2.isValid());
|
||||
COMPARE(atAntiMeridian2.get_chunk_lon(), -180);
|
||||
COMPARE(atAntiMeridian2.get_x(), 0);
|
||||
|
||||
// check comparisom operator overload
|
||||
SGBucket b4(5.11, 55.1);
|
||||
VERIFY(b1 == b4); // should be equal
|
||||
VERIFY(b1 == b1);
|
||||
VERIFY(b1 != defBuck);
|
||||
VERIFY(b1 != b2);
|
||||
|
||||
// check wrapping/clipping of inputs
|
||||
SGBucket wrapMeridian(-200.0, 45.0);
|
||||
COMPARE(wrapMeridian.get_chunk_lon(), 160);
|
||||
|
||||
SGBucket clipPole(48.9, 91);
|
||||
COMPARE(clipPole.get_chunk_lat(), 89);
|
||||
}
|
||||
|
||||
void testPolar()
|
||||
{
|
||||
SGBucket b1(0.0, 89.92);
|
||||
SGBucket b2(10.0, 89.96);
|
||||
COMPARE(b1.get_chunk_lat(), 89);
|
||||
COMPARE(b1.get_chunk_lon(), 0);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 7);
|
||||
|
||||
COMPARE_EP(b1.get_highest_lat(), 90.0);
|
||||
COMPARE_EP(b1.get_width_m(), 10.0);
|
||||
|
||||
COMPARE(b2.get_chunk_lat(), 89);
|
||||
COMPARE(b2.get_chunk_lon(), 0);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 7);
|
||||
|
||||
COMPARE(b1.gen_index(), b2.gen_index());
|
||||
|
||||
SGGeod actualNorthPole1 = b1.get_corner(2);
|
||||
SGGeod actualNorthPole2 = b1.get_corner(3);
|
||||
COMPARE_EP(actualNorthPole1.getLatitudeDeg(), 90.0);
|
||||
COMPARE_EP(actualNorthPole1.getLongitudeDeg(), 12.0);
|
||||
COMPARE_EP(actualNorthPole2.getLatitudeDeg(), 90.0);
|
||||
COMPARE_EP(actualNorthPole2.getLongitudeDeg(), 0.0);
|
||||
|
||||
SGBucket b3(-2, 89.88);
|
||||
SGBucket b4(-7, 89.88);
|
||||
COMPARE(b3.gen_index(), b4.gen_index());
|
||||
|
||||
// south pole
|
||||
SGBucket b5(-170, -89.88);
|
||||
SGBucket b6(-179, -89.88);
|
||||
|
||||
COMPARE(b5.get_chunk_lat(), -90);
|
||||
COMPARE(b5.get_chunk_lon(), -180);
|
||||
COMPARE(b5.get_x(), 0);
|
||||
COMPARE(b5.get_y(), 0);
|
||||
COMPARE(b5.gen_index(), b6.gen_index());
|
||||
COMPARE_EP(b5.get_highest_lat(), -90.0);
|
||||
COMPARE_EP(b5.get_width_m(), 10.0);
|
||||
|
||||
SGGeod actualSouthPole1 = b5.get_corner(0);
|
||||
SGGeod actualSouthPole2 = b5.get_corner(1);
|
||||
COMPARE_EP(actualSouthPole1.getLatitudeDeg(), -90.0);
|
||||
COMPARE_EP(actualSouthPole1.getLongitudeDeg(), -180);
|
||||
COMPARE_EP(actualSouthPole2.getLatitudeDeg(), -90.0);
|
||||
COMPARE_EP(actualSouthPole2.getLongitudeDeg(), -168);
|
||||
|
||||
SGBucket b7(200, 89.88);
|
||||
COMPARE(b7.get_chunk_lon(), -168);
|
||||
|
||||
}
|
||||
|
||||
// test the tiles just below the pole (between 86 & 89 degrees N/S)
|
||||
void testNearPolar()
|
||||
{
|
||||
SGBucket b1(1, 88.5);
|
||||
SGBucket b2(-1, 88.8);
|
||||
COMPARE(b1.get_chunk_lon(), 0);
|
||||
COMPARE(b1.get_chunk_lat(), 88);
|
||||
VERIFY(b1.gen_index() != b2.gen_index());
|
||||
|
||||
SGBucket b3(176.1, 88.5);
|
||||
COMPARE(b3.get_chunk_lon(), 176);
|
||||
|
||||
SGBucket b4(-178, 88.5);
|
||||
COMPARE(b4.get_chunk_lon(), -180);
|
||||
}
|
||||
|
||||
void testOffset()
|
||||
{
|
||||
// bucket just below the 22 degree cutoff, so the next tile north
|
||||
// is twice the width
|
||||
SGBucket b1(-59.8, 21.9);
|
||||
COMPARE(b1.get_chunk_lat(), 21);
|
||||
COMPARE(b1.get_chunk_lon(), -60);
|
||||
COMPARE(b1.get_x(), 1);
|
||||
COMPARE(b1.get_y(), 7);
|
||||
|
||||
// offset vertically
|
||||
SGBucket b2(b1.sibling(0, 1));
|
||||
COMPARE(b2.get_chunk_lat(), 22);
|
||||
COMPARE(b2.get_chunk_lon(), -60);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 0);
|
||||
|
||||
COMPARE(b2.gen_index(), sgBucketOffset(-59.8, 21.9, 0, 1));
|
||||
|
||||
// offset vertically and horizontally. We compute horizontal (x)
|
||||
// movement at the target latitude, so this should move 0.25 * -3 degrees,
|
||||
// NOT 0.125 * -3 degrees.
|
||||
SGBucket b3(b1.sibling(-3, 1));
|
||||
COMPARE(b3.get_chunk_lat(), 22);
|
||||
COMPARE(b3.get_chunk_lon(), -61);
|
||||
COMPARE(b3.get_x(), 1);
|
||||
COMPARE(b3.get_y(), 0);
|
||||
|
||||
COMPARE(b3.gen_index(), sgBucketOffset(-59.8, 21.9, -3, 1));
|
||||
}
|
||||
|
||||
void testPolarOffset()
|
||||
{
|
||||
SGBucket b1(-11.7, -89.6);
|
||||
COMPARE(b1.get_chunk_lat(), -90);
|
||||
COMPARE(b1.get_chunk_lon(), -12);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 3);
|
||||
|
||||
// offset horizontally
|
||||
SGBucket b2(b1.sibling(-2, 0));
|
||||
COMPARE(b2.get_chunk_lat(), -90);
|
||||
COMPARE(b2.get_chunk_lon(), -36);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 3);
|
||||
|
||||
COMPARE(b2.gen_index(), sgBucketOffset(-11.7, -89.6, -2, 0));
|
||||
|
||||
// offset and wrap
|
||||
SGBucket b3(-170, 89.1);
|
||||
SGBucket b4(b3.sibling(-1, 0));
|
||||
COMPARE(b4.get_chunk_lat(), 89);
|
||||
COMPARE(b4.get_chunk_lon(), 168);
|
||||
COMPARE(b4.get_x(), 0);
|
||||
COMPARE(b4.get_y(), 0);
|
||||
|
||||
COMPARE(b4.gen_index(), sgBucketOffset(-170, 89.1, -1, 0));
|
||||
|
||||
|
||||
SGBucket b5(177, 87.3);
|
||||
SGBucket b6(b5.sibling(1, 1));
|
||||
COMPARE(b6.get_chunk_lat(), 87);
|
||||
COMPARE(b6.get_chunk_lon(), -180);
|
||||
COMPARE(b6.get_x(), 0);
|
||||
COMPARE(b6.get_y(), 3);
|
||||
|
||||
COMPARE(b6.gen_index(), sgBucketOffset(177, 87.3, 1, 1));
|
||||
|
||||
// offset vertically towards the pole
|
||||
SGBucket b7(b1.sibling(0, -5));
|
||||
VERIFY(!b7.isValid());
|
||||
|
||||
VERIFY(!SGBucket(0, 90).sibling(0, 1).isValid());
|
||||
}
|
||||
|
||||
// test behaviour of bucket-offset near the anti-meridian (180-meridian)
|
||||
void testOffsetWrap()
|
||||
{
|
||||
// near the equator
|
||||
SGBucket b1(-179.8, 16.8);
|
||||
COMPARE(b1.get_chunk_lat(), 16);
|
||||
COMPARE(b1.get_chunk_lon(), -180);
|
||||
COMPARE(b1.get_x(), 1);
|
||||
COMPARE(b1.get_y(), 6);
|
||||
|
||||
SGBucket b2(b1.sibling(-2, 0));
|
||||
COMPARE(b2.get_chunk_lat(), 16);
|
||||
COMPARE(b2.get_chunk_lon(), 179);
|
||||
COMPARE(b2.get_x(), 7);
|
||||
COMPARE(b2.get_y(), 6);
|
||||
COMPARE(b2.gen_index(), sgBucketOffset(-179.8, 16.8, -2, 0));
|
||||
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
testBucketSpans();
|
||||
|
||||
testBasic();
|
||||
testPolar();
|
||||
testNearPolar();
|
||||
testOffset();
|
||||
testOffsetWrap();
|
||||
testPolarOffset();
|
||||
|
||||
cout << "all tests passed OK" << endl;
|
||||
return 0; // passed
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright (C) 2008 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include "BVHMaterial.hxx"
|
||||
|
||||
namespace simgear {
|
||||
|
||||
BVHMaterial::BVHMaterial() :
|
||||
_solid(true),
|
||||
_friction_factor(1),
|
||||
_rolling_friction(0.02),
|
||||
_bumpiness(0),
|
||||
_load_resistance(1e30)
|
||||
{
|
||||
}
|
||||
|
||||
BVHMaterial::~BVHMaterial()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
// Copyright (C) 2008 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef BVHMaterial_hxx
|
||||
#define BVHMaterial_hxx
|
||||
|
||||
#include <simgear/structure/SGReferenced.hxx>
|
||||
|
||||
namespace simgear {
|
||||
|
||||
class BVHMaterial : public SGReferenced {
|
||||
public:
|
||||
BVHMaterial();
|
||||
virtual ~BVHMaterial();
|
||||
|
||||
/**
|
||||
* Return if the surface material is solid, if it is not solid, a fluid
|
||||
* can be assumed, that is usually water.
|
||||
*/
|
||||
bool get_solid () const { return _solid; }
|
||||
|
||||
/**
|
||||
* Get the friction factor for that material
|
||||
*/
|
||||
double get_friction_factor () const { return _friction_factor; }
|
||||
|
||||
/**
|
||||
* Get the rolling friction for that material
|
||||
*/
|
||||
double get_rolling_friction () const { return _rolling_friction; }
|
||||
|
||||
/**
|
||||
* Get the bumpines for that material
|
||||
*/
|
||||
double get_bumpiness () const { return _bumpiness; }
|
||||
|
||||
/**
|
||||
* Get the load resistance
|
||||
*/
|
||||
double get_load_resistance () const { return _load_resistance; }
|
||||
|
||||
protected:
|
||||
// True if the material is solid, false if it is a fluid
|
||||
bool _solid;
|
||||
|
||||
// the friction factor of that surface material
|
||||
double _friction_factor;
|
||||
|
||||
// the rolling friction of that surface material
|
||||
double _rolling_friction;
|
||||
|
||||
// the bumpiness of that surface material
|
||||
double _bumpiness;
|
||||
|
||||
// the load resistance of that surface material
|
||||
double _load_resistance;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (C) 2008 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef BVHPageNode_hxx
|
||||
#define BVHPageNode_hxx
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
#include "BVHGroup.hxx"
|
||||
#include "BVHVisitor.hxx"
|
||||
|
||||
namespace simgear {
|
||||
|
||||
class BVHPager;
|
||||
class BVHPageRequest;
|
||||
|
||||
class BVHPageNode : public BVHGroup {
|
||||
public:
|
||||
BVHPageNode();
|
||||
virtual ~BVHPageNode();
|
||||
|
||||
virtual void accept(BVHVisitor& visitor);
|
||||
|
||||
/// Return the usage stamp of the last access
|
||||
unsigned getUseStamp() const
|
||||
{ return _useStamp; }
|
||||
|
||||
virtual SGSphered computeBoundingSphere() const = 0;
|
||||
|
||||
virtual BVHPageRequest* newRequest() = 0;
|
||||
|
||||
protected:
|
||||
virtual void invalidateBound() = 0;
|
||||
|
||||
bool getRequested() const
|
||||
{ return _requested; }
|
||||
void setRequested(bool requested)
|
||||
{ _requested = requested; }
|
||||
|
||||
private:
|
||||
friend class BVHPager;
|
||||
|
||||
std::list<SGSharedPtr<BVHPageNode> >::iterator _iterator;
|
||||
unsigned _useStamp;
|
||||
bool _requested;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright (C) 2008 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef BVHPageRequest_hxx
|
||||
#define BVHPageRequest_hxx
|
||||
|
||||
#include <simgear/structure/SGReferenced.hxx>
|
||||
|
||||
namespace simgear {
|
||||
|
||||
class BVHPageNode;
|
||||
|
||||
class BVHPageRequest : public SGReferenced {
|
||||
public:
|
||||
virtual ~BVHPageRequest();
|
||||
|
||||
/// Happens in the pager thread, do not modify the calling bvh tree
|
||||
virtual void load() = 0;
|
||||
/// Happens in the bvh main thread where the bvh is actually used.
|
||||
/// So inside here it is safe to modify the paged node
|
||||
virtual void insert() = 0;
|
||||
/// The page node this request is for
|
||||
virtual BVHPageNode* getPageNode() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,236 +0,0 @@
|
||||
// Copyright (C) 2008 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include "BVHPager.hxx"
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <simgear/threads/SGThread.hxx>
|
||||
#include <simgear/threads/SGGuard.hxx>
|
||||
|
||||
#include "BVHPageNode.hxx"
|
||||
#include "BVHPageRequest.hxx"
|
||||
|
||||
namespace simgear {
|
||||
|
||||
struct BVHPager::_PrivateData : protected SGThread {
|
||||
typedef SGSharedPtr<BVHPageRequest> _Request;
|
||||
typedef std::list<_Request> _RequestList;
|
||||
typedef std::list<SGSharedPtr<BVHPageNode> > _PageNodeList;
|
||||
|
||||
struct _LockedQueue {
|
||||
void _push(const _Request& request)
|
||||
{
|
||||
SGGuard<SGMutex> scopeLock(_mutex);
|
||||
_requestList.push_back(request);
|
||||
}
|
||||
_Request _pop()
|
||||
{
|
||||
SGGuard<SGMutex> scopeLock(_mutex);
|
||||
if (_requestList.empty())
|
||||
return _Request();
|
||||
_Request request;
|
||||
request.swap(_requestList.front());
|
||||
_requestList.pop_front();
|
||||
return request;
|
||||
}
|
||||
private:
|
||||
SGMutex _mutex;
|
||||
_RequestList _requestList;
|
||||
};
|
||||
|
||||
struct _WorkQueue {
|
||||
void _stop()
|
||||
{
|
||||
_push(_Request());
|
||||
}
|
||||
void _push(const _Request& request)
|
||||
{
|
||||
SGGuard<SGMutex> scopeLock(_mutex);
|
||||
bool needSignal = _requestList.empty();
|
||||
_requestList.push_back(request);
|
||||
if (needSignal)
|
||||
_waitCondition.signal();
|
||||
}
|
||||
_Request _pop()
|
||||
{
|
||||
SGGuard<SGMutex> scopeLock(_mutex);
|
||||
while (_requestList.empty())
|
||||
_waitCondition.wait(_mutex);
|
||||
_Request request;
|
||||
request.swap(_requestList.front());
|
||||
_requestList.pop_front();
|
||||
return request;
|
||||
}
|
||||
private:
|
||||
SGMutex _mutex;
|
||||
SGWaitCondition _waitCondition;
|
||||
_RequestList _requestList;
|
||||
};
|
||||
|
||||
_PrivateData() :
|
||||
_started(false),
|
||||
_useStamp(0)
|
||||
{
|
||||
}
|
||||
virtual ~_PrivateData()
|
||||
{
|
||||
_stop();
|
||||
}
|
||||
|
||||
virtual void run()
|
||||
{
|
||||
for (;;) {
|
||||
_Request request = _pendingRequests._pop();
|
||||
// This means stop working
|
||||
if (!request.valid())
|
||||
return;
|
||||
request->load();
|
||||
_processedRequests._push(request);
|
||||
}
|
||||
}
|
||||
|
||||
bool _start()
|
||||
{
|
||||
if (_started)
|
||||
return true;
|
||||
if (!start())
|
||||
return false;
|
||||
_started = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void _stop()
|
||||
{
|
||||
if (!_started)
|
||||
return;
|
||||
// send a stop request ...
|
||||
_pendingRequests._stop();
|
||||
// ... and wait for the thread to finish
|
||||
join();
|
||||
_started = false;
|
||||
}
|
||||
|
||||
void _use(BVHPageNode& pageNode)
|
||||
{
|
||||
if (pageNode._requested) {
|
||||
// move it forward in the lru list
|
||||
_pageNodeList.splice(_pageNodeList.end(), _pageNodeList,
|
||||
pageNode._iterator);
|
||||
} else {
|
||||
_Request request = pageNode.newRequest();
|
||||
if (!request.valid())
|
||||
return;
|
||||
|
||||
pageNode._iterator = _pageNodeList.insert(_pageNodeList.end(),
|
||||
&pageNode);
|
||||
pageNode._requested = true;
|
||||
|
||||
if (_started) {
|
||||
_pendingRequests._push(request);
|
||||
} else {
|
||||
request->load();
|
||||
request->insert();
|
||||
}
|
||||
}
|
||||
pageNode._useStamp = _useStamp;
|
||||
}
|
||||
|
||||
void _update(unsigned expiry)
|
||||
{
|
||||
// Insert all processed requests
|
||||
for (;;) {
|
||||
SGSharedPtr<BVHPageRequest> request;
|
||||
request = _processedRequests._pop();
|
||||
if (!request.valid())
|
||||
break;
|
||||
request->insert();
|
||||
}
|
||||
|
||||
// ... and throw away stuff that is not used for a long time
|
||||
unsigned useStamp = _useStamp - expiry;
|
||||
_PageNodeList::iterator i = _pageNodeList.begin();
|
||||
while (i != _pageNodeList.end()) {
|
||||
// Ok, this means if the highest bit in the below difference
|
||||
// is set which is aequivalent to having a negative difference
|
||||
// but being wraparound save.
|
||||
unsigned diff = (*i)->_useStamp - useStamp;
|
||||
// test the sign bit of the difference
|
||||
if (!(diff & (~((~0u) >> 1))))
|
||||
break;
|
||||
(*i)->clear();
|
||||
(*i)->_requested = false;
|
||||
i = _pageNodeList.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool _started;
|
||||
unsigned _useStamp;
|
||||
_WorkQueue _pendingRequests;
|
||||
_LockedQueue _processedRequests;
|
||||
// Store the rcu list of loaded nodes so that they can expire
|
||||
_PageNodeList _pageNodeList;
|
||||
};
|
||||
|
||||
BVHPager::BVHPager() :
|
||||
_privateData(new _PrivateData)
|
||||
{
|
||||
}
|
||||
|
||||
BVHPager::~BVHPager()
|
||||
{
|
||||
delete _privateData;
|
||||
_privateData = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
BVHPager::start()
|
||||
{
|
||||
return _privateData->_start();
|
||||
}
|
||||
|
||||
void
|
||||
BVHPager::stop()
|
||||
{
|
||||
_privateData->_stop();
|
||||
}
|
||||
|
||||
void
|
||||
BVHPager::use(BVHPageNode& pageNode)
|
||||
{
|
||||
_privateData->_use(pageNode);
|
||||
}
|
||||
|
||||
void
|
||||
BVHPager::update(unsigned expiry)
|
||||
{
|
||||
_privateData->_update(expiry);
|
||||
}
|
||||
|
||||
void
|
||||
BVHPager::setUseStamp(unsigned stamp)
|
||||
{
|
||||
_privateData->_useStamp = stamp;
|
||||
}
|
||||
|
||||
unsigned
|
||||
BVHPager::getUseStamp() const
|
||||
{
|
||||
return _privateData->_useStamp;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright (C) 2008 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef BVHPager_hxx
|
||||
#define BVHPager_hxx
|
||||
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
namespace simgear {
|
||||
|
||||
class BVHPageNode;
|
||||
class BVHPageRequest;
|
||||
|
||||
class BVHPager {
|
||||
public:
|
||||
BVHPager();
|
||||
~BVHPager();
|
||||
|
||||
/// Starts the pager thread
|
||||
bool start();
|
||||
|
||||
/// Stops the pager thread
|
||||
void stop();
|
||||
|
||||
/// Use this page node, if loaded make it as used, if not loaded schedule
|
||||
void use(BVHPageNode& pageNode);
|
||||
|
||||
/// Call this from the main thread to incorporate the processed page
|
||||
/// requests into the bounding volume tree
|
||||
void update(unsigned expiry);
|
||||
|
||||
/// The usage stamp to mark usage of BVHPageNodes
|
||||
void setUseStamp(unsigned stamp);
|
||||
unsigned getUseStamp() const;
|
||||
|
||||
private:
|
||||
BVHPager(const BVHPager&);
|
||||
BVHPager& operator=(const BVHPager&);
|
||||
|
||||
struct _PrivateData;
|
||||
_PrivateData* _privateData;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,37 +0,0 @@
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS
|
||||
canvas_fwd.hxx
|
||||
Canvas.hxx
|
||||
CanvasEvent.hxx
|
||||
CanvasEventManager.hxx
|
||||
CanvasEventTypes.hxx
|
||||
CanvasEventVisitor.hxx
|
||||
CanvasMgr.hxx
|
||||
CanvasObjectPlacement.hxx
|
||||
CanvasPlacement.hxx
|
||||
CanvasSystemAdapter.hxx
|
||||
CanvasWindow.hxx
|
||||
ODGauge.hxx
|
||||
VGInitOperation.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
Canvas.cxx
|
||||
CanvasEvent.cxx
|
||||
CanvasEventManager.cxx
|
||||
CanvasEventVisitor.cxx
|
||||
CanvasMgr.cxx
|
||||
CanvasObjectPlacement.cxx
|
||||
CanvasPlacement.cxx
|
||||
CanvasWindow.cxx
|
||||
ODGauge.cxx
|
||||
VGInitOperation.cxx
|
||||
)
|
||||
|
||||
add_subdirectory(ShivaVG/src)
|
||||
add_subdirectory(elements)
|
||||
add_subdirectory(events)
|
||||
add_subdirectory(layout)
|
||||
|
||||
simgear_scene_component(canvas canvas "${SOURCES}" "${HEADERS}")
|
||||
@@ -1,697 +0,0 @@
|
||||
// The canvas for rendering with the 2d API
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasEventManager.hxx"
|
||||
#include "CanvasEventVisitor.hxx"
|
||||
#include "CanvasPlacement.hxx"
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/scene/util/parse_color.hxx>
|
||||
#include <simgear/scene/util/RenderConstants.hxx>
|
||||
|
||||
#include <osg/Camera>
|
||||
#include <osg/Geode>
|
||||
#include <osgText/Text>
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Canvas::CullCallback::CullCallback(const CanvasWeakPtr& canvas):
|
||||
_canvas( canvas )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::CullCallback::operator()( osg::Node* node,
|
||||
osg::NodeVisitor* nv )
|
||||
{
|
||||
if( (nv->getTraversalMask() & simgear::MODEL_BIT) )
|
||||
{
|
||||
CanvasPtr canvas = _canvas.lock();
|
||||
if( canvas )
|
||||
canvas->enableRendering();
|
||||
}
|
||||
|
||||
traverse(node, nv);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Canvas::Canvas(SGPropertyNode* node):
|
||||
PropertyBasedElement(node),
|
||||
_canvas_mgr(0),
|
||||
_event_manager(new EventManager),
|
||||
_size_x(-1),
|
||||
_size_y(-1),
|
||||
_view_width(-1),
|
||||
_view_height(-1),
|
||||
_status(node, "status"),
|
||||
_status_msg(node, "status-msg"),
|
||||
_sampling_dirty(false),
|
||||
_render_dirty(true),
|
||||
_visible(true),
|
||||
_render_always(false)
|
||||
{
|
||||
_status = 0;
|
||||
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
|
||||
|
||||
_root_group.reset( new Group(this, _node) );
|
||||
|
||||
// Remove automatically created property listener as we forward them on our
|
||||
// own
|
||||
_root_group->removeListener();
|
||||
_cull_callback = new CullCallback(this);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Canvas::~Canvas()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::onDestroy()
|
||||
{
|
||||
if( _root_group )
|
||||
{
|
||||
_root_group->clearEventListener();
|
||||
_root_group->onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::setCanvasMgr(CanvasMgr* canvas_mgr)
|
||||
{
|
||||
_canvas_mgr = canvas_mgr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasMgr* Canvas::getCanvasMgr() const
|
||||
{
|
||||
return _canvas_mgr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Canvas::isInit() const
|
||||
{
|
||||
return _texture.serviceable();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::addParentCanvas(const CanvasWeakPtr& canvas)
|
||||
{
|
||||
if( canvas.expired() )
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_WARN,
|
||||
"Canvas::addParentCanvas(" << _node->getPath(true) << "): "
|
||||
"got an expired parent!"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
_parent_canvases.insert(canvas);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::addChildCanvas(const CanvasWeakPtr& canvas)
|
||||
{
|
||||
if( canvas.expired() )
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_WARN,
|
||||
"Canvas::addChildCanvas(" << _node->getPath(true) << "): "
|
||||
" got an expired child!"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
_child_canvases.insert(canvas);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::removeParentCanvas(const CanvasWeakPtr& canvas)
|
||||
{
|
||||
_parent_canvases.erase(canvas);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::removeChildCanvas(const CanvasWeakPtr& canvas)
|
||||
{
|
||||
_child_canvases.erase(canvas);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
GroupPtr Canvas::createGroup(const std::string& name)
|
||||
{
|
||||
return _root_group->createChild<Group>(name);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
GroupPtr Canvas::getGroup(const std::string& name)
|
||||
{
|
||||
return _root_group->getChild<Group>(name);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
GroupPtr Canvas::getOrCreateGroup(const std::string& name)
|
||||
{
|
||||
return _root_group->getOrCreateChild<Group>(name);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
GroupPtr Canvas::getRootGroup()
|
||||
{
|
||||
return _root_group;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::setLayout(const LayoutRef& layout)
|
||||
{
|
||||
_layout = layout;
|
||||
_layout->setCanvas(this);
|
||||
_status |= LAYOUT_DIRTY;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::enableRendering(bool force)
|
||||
{
|
||||
_visible = true;
|
||||
if( force )
|
||||
_render_dirty = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::update(double delta_time_sec)
|
||||
{
|
||||
if( _status & (CREATE_FAILED | MISSING_SIZE) )
|
||||
return;
|
||||
|
||||
if( _status & STATUS_DIRTY )
|
||||
{
|
||||
_texture.setSize(_size_x, _size_y);
|
||||
|
||||
if( !_texture.serviceable() )
|
||||
{
|
||||
_texture.useImageCoords(true);
|
||||
_texture.useStencil(true);
|
||||
_texture.allocRT(/*_camera_callback*/);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resizing causes a new texture to be created so we need to reapply all
|
||||
// existing placements
|
||||
reloadPlacements();
|
||||
}
|
||||
|
||||
osg::Camera* camera = _texture.getCamera();
|
||||
|
||||
// TODO Allow custom render order? For now just keep in order with
|
||||
// property tree.
|
||||
camera->setRenderOrder(osg::Camera::PRE_RENDER, _node->getIndex());
|
||||
|
||||
osg::Vec4 clear_color(0.0f, 0.0f, 0.0f , 1.0f);
|
||||
parseColor(_node->getStringValue("background"), clear_color);
|
||||
camera->setClearColor(clear_color);
|
||||
|
||||
camera->addChild(_root_group->getMatrixTransform());
|
||||
|
||||
if( _texture.serviceable() )
|
||||
{
|
||||
setStatusFlags(STATUS_OK);
|
||||
setStatusFlags(STATUS_DIRTY, false);
|
||||
_render_dirty = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
setStatusFlags(CREATE_FAILED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( _layout )
|
||||
{
|
||||
if( (_status & LAYOUT_DIRTY) )
|
||||
{
|
||||
_layout->setGeometry(SGRecti(0, 0, _view_width, _view_height));
|
||||
_status &= ~LAYOUT_DIRTY;
|
||||
}
|
||||
else
|
||||
_layout->update();
|
||||
}
|
||||
|
||||
if( _visible || _render_always )
|
||||
{
|
||||
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _child_canvases)
|
||||
{
|
||||
// TODO should we check if the image the child canvas is displayed
|
||||
// within is really visible?
|
||||
CanvasPtr canvas = canvas_weak.lock();
|
||||
if( canvas )
|
||||
canvas->_visible = true;
|
||||
}
|
||||
|
||||
if( _render_dirty )
|
||||
{
|
||||
// Also mark all canvases this canvas is displayed within as dirty
|
||||
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _parent_canvases)
|
||||
{
|
||||
CanvasPtr canvas = canvas_weak.lock();
|
||||
if( canvas )
|
||||
canvas->_render_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
_texture.setRender(_render_dirty);
|
||||
|
||||
_render_dirty = false;
|
||||
_visible = false;
|
||||
}
|
||||
else
|
||||
_texture.setRender(false);
|
||||
|
||||
_root_group->update(delta_time_sec);
|
||||
|
||||
if( _sampling_dirty )
|
||||
{
|
||||
_texture.setSampling(
|
||||
_node->getBoolValue("mipmapping"),
|
||||
_node->getIntValue("coverage-samples"),
|
||||
_node->getIntValue("color-samples")
|
||||
);
|
||||
_sampling_dirty = false;
|
||||
_render_dirty = true;
|
||||
}
|
||||
|
||||
while( !_dirty_placements.empty() )
|
||||
{
|
||||
SGPropertyNode *node = _dirty_placements.back();
|
||||
_dirty_placements.pop_back();
|
||||
|
||||
if( node->getIndex() >= static_cast<int>(_placements.size()) )
|
||||
// New placement
|
||||
_placements.resize(node->getIndex() + 1);
|
||||
else
|
||||
// Remove possibly existing placements
|
||||
_placements[ node->getIndex() ].clear();
|
||||
|
||||
// Get new placements
|
||||
PlacementFactoryMap::const_iterator placement_factory =
|
||||
_placement_factories.find( node->getStringValue("type", "object") );
|
||||
if( placement_factory != _placement_factories.end() )
|
||||
{
|
||||
Placements& placements = _placements[ node->getIndex() ] =
|
||||
placement_factory->second(node, this);
|
||||
node->setStringValue
|
||||
(
|
||||
"status-msg",
|
||||
placements.empty() ? "No match" : "Ok"
|
||||
);
|
||||
}
|
||||
else
|
||||
node->setStringValue("status-msg", "Unknown placement type");
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Canvas::addEventListener( const std::string& type,
|
||||
const EventListener& cb )
|
||||
{
|
||||
if( !_root_group.get() )
|
||||
throw std::runtime_error("Canvas::addEventListener: no root group!");
|
||||
|
||||
return _root_group->addEventListener(type, cb);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Canvas::dispatchEvent(const EventPtr& event)
|
||||
{
|
||||
if( !_root_group.get() )
|
||||
throw std::runtime_error("Canvas::dispatchEvent: no root group!");
|
||||
|
||||
return _root_group->dispatchEvent(event);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::setSizeX(int sx)
|
||||
{
|
||||
if( _size_x == sx )
|
||||
return;
|
||||
_size_x = sx;
|
||||
setStatusFlags(STATUS_DIRTY);
|
||||
|
||||
if( _size_x <= 0 )
|
||||
setStatusFlags(MISSING_SIZE_X);
|
||||
else
|
||||
setStatusFlags(MISSING_SIZE_X, false);
|
||||
|
||||
// reset flag to allow creation with new size
|
||||
setStatusFlags(CREATE_FAILED, false);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::setSizeY(int sy)
|
||||
{
|
||||
if( _size_y == sy )
|
||||
return;
|
||||
_size_y = sy;
|
||||
setStatusFlags(STATUS_DIRTY);
|
||||
|
||||
if( _size_y <= 0 )
|
||||
setStatusFlags(MISSING_SIZE_Y);
|
||||
else
|
||||
setStatusFlags(MISSING_SIZE_Y, false);
|
||||
|
||||
// reset flag to allow creation with new size
|
||||
setStatusFlags(CREATE_FAILED, false);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Canvas::getSizeX() const
|
||||
{
|
||||
return _size_x;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Canvas::getSizeY() const
|
||||
{
|
||||
return _size_y;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::setViewWidth(int w)
|
||||
{
|
||||
if( _view_width == w )
|
||||
return;
|
||||
_view_width = w;
|
||||
_status |= LAYOUT_DIRTY;
|
||||
|
||||
_texture.setViewSize(_view_width, _view_height);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::setViewHeight(int h)
|
||||
{
|
||||
if( _view_height == h )
|
||||
return;
|
||||
_view_height = h;
|
||||
_status |= LAYOUT_DIRTY;
|
||||
|
||||
_texture.setViewSize(_view_width, _view_height);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Canvas::getViewWidth() const
|
||||
{
|
||||
return _texture.getViewSize().x();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Canvas::getViewHeight() const
|
||||
{
|
||||
return _texture.getViewSize().y();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGRect<int> Canvas::getViewport() const
|
||||
{
|
||||
return SGRect<int>(0, 0, getViewWidth(), getViewHeight());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Canvas::handleMouseEvent(const MouseEventPtr& event)
|
||||
{
|
||||
if( !_root_group )
|
||||
return false;
|
||||
|
||||
EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
|
||||
event->getClientPos(),
|
||||
_root_group );
|
||||
if( !_root_group->accept(visitor) )
|
||||
return false;
|
||||
|
||||
return _event_manager->handleEvent(event, visitor.getPropagationPath());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Canvas::propagateEvent( EventPtr const& event,
|
||||
EventPropagationPath const& path )
|
||||
{
|
||||
return _event_manager->propagateEvent(event, path);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::childAdded( SGPropertyNode * parent,
|
||||
SGPropertyNode * child )
|
||||
{
|
||||
if( parent != _node )
|
||||
return;
|
||||
|
||||
if( child->getNameString() == "placement" )
|
||||
_dirty_placements.push_back(child);
|
||||
else if( _root_group.get() )
|
||||
static_cast<Element*>(_root_group.get())->childAdded(parent, child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::childRemoved( SGPropertyNode * parent,
|
||||
SGPropertyNode * child )
|
||||
{
|
||||
_render_dirty = true;
|
||||
|
||||
if( parent != _node )
|
||||
return;
|
||||
|
||||
if( child->getNameString() == "placement" )
|
||||
_placements[ child->getIndex() ].clear();
|
||||
else if( _root_group.get() )
|
||||
static_cast<Element*>(_root_group.get())->childRemoved(parent, child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::valueChanged(SGPropertyNode* node)
|
||||
{
|
||||
const std::string& name = node->getNameString();
|
||||
|
||||
if( boost::starts_with(name, "status")
|
||||
|| boost::starts_with(name, "data-") )
|
||||
return;
|
||||
_render_dirty = true;
|
||||
|
||||
bool handled = true;
|
||||
if( node->getParent()->getParent() == _node
|
||||
&& node->getParent()->getNameString() == "placement" )
|
||||
{
|
||||
size_t index = node->getIndex();
|
||||
if( index < _placements.size() )
|
||||
{
|
||||
Placements& placements = _placements[index];
|
||||
if( !placements.empty() )
|
||||
{
|
||||
bool placement_dirty = false;
|
||||
BOOST_FOREACH(PlacementPtr& placement, placements)
|
||||
{
|
||||
// check if change can be directly handled by placement
|
||||
if( placement->getProps() == node->getParent()
|
||||
&& !placement->childChanged(node) )
|
||||
placement_dirty = true;
|
||||
}
|
||||
|
||||
if( !placement_dirty )
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// prevent double updates...
|
||||
for( size_t i = 0; i < _dirty_placements.size(); ++i )
|
||||
{
|
||||
if( node->getParent() == _dirty_placements[i] )
|
||||
return;
|
||||
}
|
||||
|
||||
_dirty_placements.push_back(node->getParent());
|
||||
}
|
||||
else if( node->getParent() == _node )
|
||||
{
|
||||
if( name == "background" )
|
||||
{
|
||||
osg::Vec4 color;
|
||||
if( _texture.getCamera() && parseColor(node->getStringValue(), color) )
|
||||
{
|
||||
_texture.getCamera()->setClearColor(color);
|
||||
_render_dirty = true;
|
||||
}
|
||||
}
|
||||
else if( name == "mipmapping"
|
||||
|| name == "coverage-samples"
|
||||
|| name == "color-samples" )
|
||||
{
|
||||
_sampling_dirty = true;
|
||||
}
|
||||
else if( name == "additive-blend" )
|
||||
{
|
||||
_texture.useAdditiveBlend( node->getBoolValue() );
|
||||
}
|
||||
else if( name == "render-always" )
|
||||
{
|
||||
_render_always = node->getBoolValue();
|
||||
}
|
||||
else if( name == "size" )
|
||||
{
|
||||
if( node->getIndex() == 0 )
|
||||
setSizeX( node->getIntValue() );
|
||||
else if( node->getIndex() == 1 )
|
||||
setSizeY( node->getIntValue() );
|
||||
}
|
||||
else if( name == "update" )
|
||||
{
|
||||
if( _root_group )
|
||||
_root_group->update(0);
|
||||
return update(0);
|
||||
}
|
||||
else if( name == "view" )
|
||||
{
|
||||
if( node->getIndex() == 0 )
|
||||
setViewWidth( node->getIntValue() );
|
||||
else if( node->getIndex() == 1 )
|
||||
setViewHeight( node->getIntValue() );
|
||||
}
|
||||
else if( name == "freeze" )
|
||||
_texture.setRender( node->getBoolValue() );
|
||||
else
|
||||
handled = false;
|
||||
}
|
||||
else
|
||||
handled = false;
|
||||
|
||||
if( !handled && _root_group.get() )
|
||||
_root_group->valueChanged(node);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Texture2D* Canvas::getTexture() const
|
||||
{
|
||||
return _texture.getTexture();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Canvas::CullCallbackPtr Canvas::getCullCallback() const
|
||||
{
|
||||
return _cull_callback;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::reloadPlacements(const std::string& type)
|
||||
{
|
||||
for(size_t i = 0; i < _placements.size(); ++i)
|
||||
{
|
||||
if( _placements[i].empty() )
|
||||
continue;
|
||||
|
||||
SGPropertyNode* child = _placements[i].front()->getProps();
|
||||
if( type.empty()
|
||||
// reload if type matches or no type specified
|
||||
|| child->getStringValue("type", type.c_str()) == type )
|
||||
{
|
||||
_dirty_placements.push_back(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::addPlacementFactory( const std::string& type,
|
||||
PlacementFactory factory )
|
||||
{
|
||||
if( _placement_factories.find(type) != _placement_factories.end() )
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_WARN,
|
||||
"Canvas::addPlacementFactory: replace existing factory '" << type << "'"
|
||||
);
|
||||
|
||||
_placement_factories[type] = factory;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::removePlacementFactory(const std::string& type)
|
||||
{
|
||||
PlacementFactoryMap::iterator it = _placement_factories.find(type);
|
||||
if( it == _placement_factories.end() )
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_WARN,
|
||||
"Canvas::removePlacementFactory: no such factory '" << type << "'"
|
||||
);
|
||||
else
|
||||
_placement_factories.erase(it);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::setSystemAdapter(const SystemAdapterPtr& system_adapter)
|
||||
{
|
||||
_system_adapter = system_adapter;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SystemAdapterPtr Canvas::getSystemAdapter()
|
||||
{
|
||||
return _system_adapter;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::setStatusFlags(unsigned int flags, bool set)
|
||||
{
|
||||
if( set )
|
||||
_status |= flags;
|
||||
else
|
||||
_status &= ~flags;
|
||||
|
||||
if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) )
|
||||
_status_msg = "Missing size";
|
||||
else if( _status & MISSING_SIZE_X )
|
||||
_status_msg = "Missing size-x";
|
||||
else if( _status & MISSING_SIZE_Y )
|
||||
_status_msg = "Missing size-y";
|
||||
else if( _status & CREATE_FAILED )
|
||||
_status_msg = "Creating render target failed";
|
||||
else if( _status & STATUS_DIRTY )
|
||||
_status_msg = "Creation pending...";
|
||||
else
|
||||
_status_msg = "Ok";
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Canvas::PlacementFactoryMap Canvas::_placement_factories;
|
||||
SystemAdapterPtr Canvas::_system_adapter;
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,239 +0,0 @@
|
||||
///@file The canvas for rendering with the 2d API
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_HXX_
|
||||
#define CANVAS_HXX_
|
||||
|
||||
#include "canvas_fwd.hxx"
|
||||
#include "ODGauge.hxx"
|
||||
|
||||
#include <simgear/canvas/elements/CanvasGroup.hxx>
|
||||
#include <simgear/canvas/layout/Layout.hxx>
|
||||
#include <simgear/math/SGRect.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalObject.hxx>
|
||||
#include <simgear/props/PropertyBasedElement.hxx>
|
||||
#include <simgear/props/propertyObject.hxx>
|
||||
|
||||
#include <osg/NodeCallback>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
/// Canvas 2D drawing API
|
||||
namespace canvas
|
||||
{
|
||||
class CanvasMgr;
|
||||
class MouseEvent;
|
||||
|
||||
class Canvas:
|
||||
public PropertyBasedElement,
|
||||
public nasal::Object
|
||||
{
|
||||
public:
|
||||
|
||||
enum StatusFlags
|
||||
{
|
||||
STATUS_OK,
|
||||
STATUS_DIRTY = 1,
|
||||
LAYOUT_DIRTY = STATUS_DIRTY << 1,
|
||||
MISSING_SIZE_X = LAYOUT_DIRTY << 1,
|
||||
MISSING_SIZE_Y = MISSING_SIZE_X << 1,
|
||||
MISSING_SIZE = MISSING_SIZE_X | MISSING_SIZE_Y,
|
||||
CREATE_FAILED = MISSING_SIZE_Y << 1
|
||||
};
|
||||
|
||||
/**
|
||||
* This callback is installed on every placement of the canvas in the
|
||||
* scene to only render the canvas if at least one placement is visible
|
||||
*/
|
||||
class CullCallback:
|
||||
public osg::NodeCallback
|
||||
{
|
||||
public:
|
||||
CullCallback(const CanvasWeakPtr& canvas);
|
||||
|
||||
private:
|
||||
CanvasWeakPtr _canvas;
|
||||
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
|
||||
};
|
||||
typedef osg::ref_ptr<CullCallback> CullCallbackPtr;
|
||||
|
||||
Canvas(SGPropertyNode* node);
|
||||
virtual ~Canvas();
|
||||
virtual void onDestroy();
|
||||
|
||||
void setCanvasMgr(CanvasMgr* canvas_mgr);
|
||||
CanvasMgr* getCanvasMgr() const;
|
||||
|
||||
bool isInit() const;
|
||||
|
||||
/**
|
||||
* Add a canvas which should be marked as dirty upon any change to this
|
||||
* canvas.
|
||||
*
|
||||
* This mechanism is used to eg. redraw a canvas if it's displaying
|
||||
* another canvas (recursive canvases)
|
||||
*/
|
||||
void addParentCanvas(const CanvasWeakPtr& canvas);
|
||||
|
||||
/**
|
||||
* Add a canvas which should be marked visible if this canvas is visible.
|
||||
*/
|
||||
void addChildCanvas(const CanvasWeakPtr& canvas);
|
||||
|
||||
/**
|
||||
* Stop notifying the given canvas upon changes
|
||||
*/
|
||||
void removeParentCanvas(const CanvasWeakPtr& canvas);
|
||||
void removeChildCanvas(const CanvasWeakPtr& canvas);
|
||||
|
||||
/**
|
||||
* Create a new group
|
||||
*/
|
||||
GroupPtr createGroup(const std::string& name = "");
|
||||
|
||||
/**
|
||||
* Get an existing group with the given name
|
||||
*/
|
||||
GroupPtr getGroup(const std::string& name);
|
||||
|
||||
/**
|
||||
* Get an existing group with the given name or otherwise create a new
|
||||
* group
|
||||
*/
|
||||
GroupPtr getOrCreateGroup(const std::string& name);
|
||||
|
||||
/**
|
||||
* Get the root group of the canvas
|
||||
*/
|
||||
GroupPtr getRootGroup();
|
||||
|
||||
/**
|
||||
* Set the layout of the canvas (the layout will automatically update with
|
||||
* the viewport size of the canvas)
|
||||
*/
|
||||
void setLayout(const LayoutRef& layout);
|
||||
|
||||
/**
|
||||
* Enable rendering for the next frame
|
||||
*
|
||||
* @param force Force redraw even if nothing has changed (if dirty flag
|
||||
* is not set)
|
||||
*/
|
||||
void enableRendering(bool force = false);
|
||||
|
||||
void update(double delta_time_sec);
|
||||
|
||||
bool addEventListener(const std::string& type, const EventListener& cb);
|
||||
bool dispatchEvent(const EventPtr& event);
|
||||
|
||||
void setSizeX(int sx);
|
||||
void setSizeY(int sy);
|
||||
|
||||
int getSizeX() const;
|
||||
int getSizeY() const;
|
||||
|
||||
void setViewWidth(int w);
|
||||
void setViewHeight(int h);
|
||||
|
||||
int getViewWidth() const;
|
||||
int getViewHeight() const;
|
||||
SGRect<int> getViewport() const;
|
||||
|
||||
bool handleMouseEvent(const MouseEventPtr& event);
|
||||
bool propagateEvent( EventPtr const& event,
|
||||
EventPropagationPath const& path );
|
||||
|
||||
virtual void childAdded( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void childRemoved( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void valueChanged (SGPropertyNode * node);
|
||||
|
||||
osg::Texture2D* getTexture() const;
|
||||
|
||||
CullCallbackPtr getCullCallback() const;
|
||||
|
||||
void reloadPlacements( const std::string& type = std::string() );
|
||||
static void addPlacementFactory( const std::string& type,
|
||||
PlacementFactory factory );
|
||||
static void removePlacementFactory(const std::string& type);
|
||||
|
||||
/**
|
||||
* Set global SystemAdapter for all Canvas/ODGauge instances.
|
||||
*
|
||||
* The SystemAdapter is responsible for application specific operations
|
||||
* like loading images/fonts and adding/removing cameras to the scene
|
||||
* graph.
|
||||
*/
|
||||
static void setSystemAdapter(const SystemAdapterPtr& system_adapter);
|
||||
static SystemAdapterPtr getSystemAdapter();
|
||||
|
||||
protected:
|
||||
|
||||
CanvasMgr *_canvas_mgr;
|
||||
|
||||
boost::scoped_ptr<EventManager> _event_manager;
|
||||
|
||||
int _size_x,
|
||||
_size_y,
|
||||
_view_width,
|
||||
_view_height;
|
||||
|
||||
PropertyObject<int> _status;
|
||||
PropertyObject<std::string> _status_msg;
|
||||
|
||||
bool _sampling_dirty,
|
||||
_render_dirty,
|
||||
_visible;
|
||||
|
||||
ODGauge _texture;
|
||||
|
||||
GroupPtr _root_group;
|
||||
LayoutRef _layout;
|
||||
|
||||
CullCallbackPtr _cull_callback;
|
||||
bool _render_always; //<! Used to disable automatic lazy rendering (culling)
|
||||
|
||||
std::vector<SGPropertyNode*> _dirty_placements;
|
||||
std::vector<Placements> _placements;
|
||||
std::set<CanvasWeakPtr> _parent_canvases, //<! Canvases showing this canvas
|
||||
_child_canvases; //<! Canvases displayed within
|
||||
// this canvas
|
||||
|
||||
typedef std::map<std::string, PlacementFactory> PlacementFactoryMap;
|
||||
static PlacementFactoryMap _placement_factories;
|
||||
|
||||
void setStatusFlags(unsigned int flags, bool set = true);
|
||||
|
||||
private:
|
||||
|
||||
static SystemAdapterPtr _system_adapter;
|
||||
|
||||
Canvas(const Canvas&); // = delete;
|
||||
Canvas& operator=(const Canvas&); // = delete;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_HXX_ */
|
||||
@@ -1,138 +0,0 @@
|
||||
// Canvas Event for event model similar to DOM Level 3 Event Model
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasEvent.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Event::Event():
|
||||
type(UNKNOWN),
|
||||
time(-1),
|
||||
propagation_stopped(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Event::~Event()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Event::canBubble() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Event::getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
std::string Event::getTypeString() const
|
||||
{
|
||||
return typeToStr(type);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementWeakPtr Event::getTarget() const
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementWeakPtr Event::getCurrentTarget() const
|
||||
{
|
||||
return current_target;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
double Event::getTime() const
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Event::stopPropagation()
|
||||
{
|
||||
propagation_stopped = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Event::getOrRegisterType(const std::string& type_str)
|
||||
{
|
||||
int type = strToType(type_str);
|
||||
|
||||
if( type == UNKNOWN )
|
||||
{
|
||||
// Register new type
|
||||
TypeMap& type_map = getTypeMap();
|
||||
type = type_map.size() + 1; // ids start with 1 (after UNKNOWN)
|
||||
type_map.insert(TypeMap::value_type(type_str, type));
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Event::strToType(const std::string& str)
|
||||
{
|
||||
TypeMap const& type_map = getTypeMap();
|
||||
|
||||
TypeMap::map_by<name>::const_iterator it = type_map.by<name>().find(str);
|
||||
if( it == type_map.by<name>().end() )
|
||||
return UNKNOWN;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
std::string Event::typeToStr(int type)
|
||||
{
|
||||
TypeMap const& type_map = getTypeMap();
|
||||
|
||||
TypeMap::map_by<id>::const_iterator it = type_map.by<id>().find(type);
|
||||
if( it == type_map.by<id>().end() )
|
||||
return "unknown";
|
||||
return it->second;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Event::TypeMap& Event::getTypeMap()
|
||||
{
|
||||
static TypeMap type_map;
|
||||
|
||||
if( type_map.empty() )
|
||||
{
|
||||
# define ENUM_MAPPING(type, str)\
|
||||
type_map.insert(TypeMap::value_type(str, type));
|
||||
# include "CanvasEventTypes.hxx"
|
||||
# undef ENUM_MAPPING
|
||||
}
|
||||
|
||||
return type_map;
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,98 +0,0 @@
|
||||
// Canvas Event for event model similar to DOM Level 3 Event Model
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_EVENT_HXX_
|
||||
#define CANVAS_EVENT_HXX_
|
||||
|
||||
#include "canvas_fwd.hxx"
|
||||
#include <boost/bimap.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class Event:
|
||||
public SGReferenced
|
||||
{
|
||||
public:
|
||||
|
||||
enum Type
|
||||
{
|
||||
UNKNOWN,
|
||||
# define ENUM_MAPPING(name, str) name,
|
||||
# include "CanvasEventTypes.hxx"
|
||||
# undef ENUM_MAPPING
|
||||
CUSTOM_EVENT ///< all user defined event types share the same id. They
|
||||
/// are just differentiated by using the type string.
|
||||
};
|
||||
|
||||
int type;
|
||||
ElementWeakPtr target,
|
||||
current_target;
|
||||
double time;
|
||||
bool propagation_stopped;
|
||||
|
||||
Event();
|
||||
|
||||
// We need a vtable to allow nasal::Ghost to determine the dynamic type
|
||||
// of the actual event instances.
|
||||
virtual ~Event();
|
||||
|
||||
/**
|
||||
* Get whether this events support bubbling
|
||||
*/
|
||||
virtual bool canBubble() const;
|
||||
|
||||
/**
|
||||
* Set type of event.
|
||||
*
|
||||
* If no such type exists it is registered.
|
||||
*/
|
||||
void setType(const std::string& type);
|
||||
|
||||
int getType() const;
|
||||
std::string getTypeString() const;
|
||||
|
||||
ElementWeakPtr getTarget() const;
|
||||
ElementWeakPtr getCurrentTarget() const;
|
||||
|
||||
double getTime() const;
|
||||
|
||||
void stopPropagation();
|
||||
|
||||
static int getOrRegisterType(const std::string& type);
|
||||
static int strToType(const std::string& type);
|
||||
static std::string typeToStr(int type);
|
||||
|
||||
protected:
|
||||
struct name {};
|
||||
struct id {};
|
||||
typedef boost::bimaps::bimap<
|
||||
boost::bimaps::tagged<std::string, name>,
|
||||
boost::bimaps::tagged<int, id>
|
||||
> TypeMap;
|
||||
|
||||
static TypeMap& getTypeMap();
|
||||
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_EVENT_HXX_ */
|
||||
@@ -1,352 +0,0 @@
|
||||
// Manage event handling inside a Canvas similar to the DOM Level 3 Event Model
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasEventManager.hxx"
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||
#include <cmath>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
const unsigned int drag_threshold = 8;
|
||||
const double multi_click_timeout = 0.4;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
EventManager::StampedPropagationPath::StampedPropagationPath():
|
||||
time(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
EventManager::StampedPropagationPath::StampedPropagationPath(
|
||||
const EventPropagationPath& path,
|
||||
double time
|
||||
):
|
||||
path(path),
|
||||
time(time)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void EventManager::StampedPropagationPath::clear()
|
||||
{
|
||||
path.clear();
|
||||
time = 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool EventManager::StampedPropagationPath::valid() const
|
||||
{
|
||||
return !path.empty() && time > 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void EventManager::MouseEventInfo::set( const MouseEventPtr& event,
|
||||
const EventPropagationPath& p )
|
||||
{
|
||||
path = p;
|
||||
time = event->time;
|
||||
button = event->button;
|
||||
pos = event->screen_pos;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
EventManager::EventManager():
|
||||
_current_click_count(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool EventManager::handleEvent( const MouseEventPtr& event,
|
||||
const EventPropagationPath& path )
|
||||
{
|
||||
bool handled = false;
|
||||
switch( event->type )
|
||||
{
|
||||
case Event::MOUSE_DOWN:
|
||||
_last_mouse_down.set(event, path);
|
||||
break;
|
||||
case Event::MOUSE_UP:
|
||||
{
|
||||
// If the mouse has moved while a button was down (aka. dragging) we
|
||||
// need to notify the original element that the mouse has left it, and
|
||||
// the new element that it has been entered
|
||||
if( _last_mouse_down.path != path )
|
||||
handled |= handleMove(event, path);
|
||||
|
||||
// normal mouseup
|
||||
handled |= propagateEvent(event, path);
|
||||
|
||||
if( !_last_mouse_down.valid() )
|
||||
// Ignore mouse up without any previous mouse down
|
||||
return handled;
|
||||
|
||||
// now handle click/dblclick
|
||||
if( checkClickDistance(event->screen_pos, _last_mouse_down.pos) )
|
||||
handled |=
|
||||
handleClick(event, getCommonAncestor(_last_mouse_down.path, path));
|
||||
|
||||
_last_mouse_down.clear();
|
||||
|
||||
return handled;
|
||||
}
|
||||
case Event::DRAG:
|
||||
if( !_last_mouse_down.valid() )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
// OSG does not set button for drag events.
|
||||
event->button = _last_mouse_down.button;
|
||||
return propagateEvent(event, _last_mouse_down.path);
|
||||
}
|
||||
case Event::MOUSE_MOVE:
|
||||
handled |= handleMove(event, path);
|
||||
break;
|
||||
case Event::MOUSE_LEAVE:
|
||||
// Mouse leaves window and therefore also current mouseover element
|
||||
handleMove(event, EventPropagationPath());
|
||||
|
||||
// Event is only send if mouse is moved outside the window or dragging
|
||||
// has ended somewhere outside the window. In both cases a mouse button
|
||||
// has been released, so no more mouse down or click...
|
||||
_last_mouse_down.clear();
|
||||
_last_click.clear();
|
||||
|
||||
return true;
|
||||
case Event::WHEEL:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return handled | propagateEvent(event, path);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool EventManager::propagateEvent( const EventPtr& event,
|
||||
const EventPropagationPath& path )
|
||||
{
|
||||
event->target = path.back().element;
|
||||
MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get());
|
||||
|
||||
// Event propagation similar to DOM Level 3 event flow:
|
||||
// http://www.w3.org/TR/DOM-Level-3-Events/#event-flow
|
||||
|
||||
// Position update only needed for drag event (as event needs to be
|
||||
// delivered to element of initial mousedown, but with update positions)
|
||||
if( mouse_event && mouse_event->type == MouseEvent::DRAG )
|
||||
{
|
||||
osg::Vec2f local_pos = mouse_event->client_pos;
|
||||
|
||||
// Capturing phase (currently just update position)
|
||||
for( EventPropagationPath::const_iterator it = path.begin();
|
||||
it != path.end();
|
||||
++it )
|
||||
{
|
||||
ElementPtr el = it->element.lock();
|
||||
if( !el )
|
||||
continue;
|
||||
|
||||
it->local_pos = local_pos = el->posToLocal(local_pos);
|
||||
}
|
||||
}
|
||||
|
||||
bool const do_bubble = event->canBubble();
|
||||
|
||||
// Bubbling phase
|
||||
for( EventPropagationPath::const_reverse_iterator
|
||||
it = path.rbegin();
|
||||
it != path.rend();
|
||||
++it )
|
||||
{
|
||||
ElementPtr el = it->element.lock();
|
||||
|
||||
if( !el )
|
||||
{
|
||||
// Ignore element if it has been destroyed while traversing the event
|
||||
// (eg. removed by another event handler)
|
||||
if( do_bubble )
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO provide functions to convert delta to local coordinates on demand.
|
||||
// Maybe also provide a clone method for events as local coordinates
|
||||
// might differ between different elements receiving the same event.
|
||||
if( mouse_event )
|
||||
mouse_event->local_pos = it->local_pos;
|
||||
|
||||
event->current_target = el;
|
||||
el->handleEvent(event);
|
||||
|
||||
if( event->propagation_stopped || !do_bubble )
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool EventManager::handleClick( const MouseEventPtr& event,
|
||||
const EventPropagationPath& path )
|
||||
{
|
||||
MouseEventPtr click(new MouseEvent(*event));
|
||||
click->type = Event::CLICK;
|
||||
|
||||
if( event->getTime() > _last_click.time + multi_click_timeout )
|
||||
_current_click_count = 1;
|
||||
else
|
||||
{
|
||||
// Maximum current click count is 3
|
||||
_current_click_count = (_current_click_count % 3) + 1;
|
||||
|
||||
if( _current_click_count > 1 )
|
||||
{
|
||||
// Reset current click count if moved too far or different button has
|
||||
// been clicked
|
||||
if( !checkClickDistance(event->screen_pos, _last_click.pos)
|
||||
|| _last_click.button != event->button )
|
||||
_current_click_count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
click->click_count = _current_click_count;
|
||||
|
||||
MouseEventPtr dbl_click;
|
||||
if( _current_click_count == 2 )
|
||||
{
|
||||
dbl_click.reset(new MouseEvent(*click));
|
||||
dbl_click->type = Event::DBL_CLICK;
|
||||
}
|
||||
|
||||
bool handled = propagateEvent(click, path);
|
||||
|
||||
if( dbl_click )
|
||||
handled |= propagateEvent( dbl_click,
|
||||
getCommonAncestor(_last_click.path, path) );
|
||||
|
||||
_last_click.set(event, path);
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool EventManager::handleMove( const MouseEventPtr& event,
|
||||
const EventPropagationPath& path )
|
||||
{
|
||||
EventPropagationPath& last_path = _last_mouse_over.path;
|
||||
if( last_path == path )
|
||||
return false;
|
||||
|
||||
bool handled = false;
|
||||
|
||||
// Leave old element
|
||||
if( !last_path.empty() )
|
||||
{
|
||||
MouseEventPtr mouseout(new MouseEvent(*event));
|
||||
mouseout->type = Event::MOUSE_OUT;
|
||||
handled |= propagateEvent(mouseout, last_path);
|
||||
|
||||
// Send a mouseleave event to all ancestors of the currently left element
|
||||
// which are not ancestor of the new element currently entered
|
||||
EventPropagationPath path_leave = last_path;
|
||||
for(size_t i = path_leave.size() - 1; i > 0; --i)
|
||||
{
|
||||
if( i < path.size() && path[i] == path_leave[i] )
|
||||
break;
|
||||
|
||||
MouseEventPtr mouseleave(new MouseEvent(*event));
|
||||
mouseleave->type = Event::MOUSE_LEAVE;
|
||||
handled |= propagateEvent(mouseleave, path_leave);
|
||||
|
||||
path_leave.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// Enter new element
|
||||
if( !path.empty() )
|
||||
{
|
||||
MouseEventPtr mouseover(new MouseEvent(*event));
|
||||
mouseover->type = Event::MOUSE_OVER;
|
||||
handled |= propagateEvent(mouseover, path);
|
||||
|
||||
// Send a mouseenter event to all ancestors of the currently entered
|
||||
// element which are not ancestor of the old element currently being
|
||||
// left
|
||||
EventPropagationPath path_enter;
|
||||
for(size_t i = 0; i < path.size(); ++i)
|
||||
{
|
||||
path_enter.push_back(path[i]);
|
||||
|
||||
if( i < last_path.size() && path[i] == last_path[i] )
|
||||
continue;
|
||||
|
||||
MouseEventPtr mouseenter(new MouseEvent(*event));
|
||||
mouseenter->type = Event::MOUSE_ENTER;
|
||||
handled |= propagateEvent(mouseenter, path_enter);
|
||||
}
|
||||
}
|
||||
|
||||
_last_mouse_over.path = path;
|
||||
return handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
EventManager::checkClickDistance( const osg::Vec2f& pos1,
|
||||
const osg::Vec2f& pos2 ) const
|
||||
{
|
||||
osg::Vec2 delta = pos1 - pos2;
|
||||
return std::fabs(delta.x()) < drag_threshold
|
||||
&& std::fabs(delta.y()) < drag_threshold;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
EventPropagationPath
|
||||
EventManager::getCommonAncestor( const EventPropagationPath& path1,
|
||||
const EventPropagationPath& path2 ) const
|
||||
{
|
||||
if( path1.empty() || path2.empty() )
|
||||
return EventPropagationPath();
|
||||
|
||||
if( path1.back().element.lock() == path2.back().element.lock() )
|
||||
return path2;
|
||||
|
||||
EventPropagationPath path;
|
||||
|
||||
for( size_t i = 0; i < path1.size() && i < path2.size(); ++i )
|
||||
{
|
||||
if( path1[i].element.lock() != path2[i].element.lock() )
|
||||
break;
|
||||
|
||||
path.push_back(path2[i]);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,116 +0,0 @@
|
||||
// Manage event handling inside a Canvas similar to the DOM Level 3 Event Model
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_EVENT_MANAGER_HXX_
|
||||
#define CANVAS_EVENT_MANAGER_HXX_
|
||||
|
||||
#include "canvas_fwd.hxx"
|
||||
#include <deque>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
struct EventTarget
|
||||
{
|
||||
ElementWeakPtr element;
|
||||
|
||||
// Used as storage by EventManager during event propagation
|
||||
mutable osg::Vec2f local_pos;
|
||||
|
||||
EventTarget( Element* el,
|
||||
const osg::Vec2f pos = osg::Vec2f() ):
|
||||
element(el),
|
||||
local_pos(pos)
|
||||
{}
|
||||
};
|
||||
|
||||
inline bool operator==(const EventTarget& t1, const EventTarget& t2)
|
||||
{
|
||||
return t1.element.lock() == t2.element.lock();
|
||||
}
|
||||
|
||||
class EventManager
|
||||
{
|
||||
public:
|
||||
EventManager();
|
||||
|
||||
bool handleEvent( const MouseEventPtr& event,
|
||||
const EventPropagationPath& path );
|
||||
|
||||
bool propagateEvent( const EventPtr& event,
|
||||
const EventPropagationPath& path );
|
||||
|
||||
protected:
|
||||
struct StampedPropagationPath
|
||||
{
|
||||
StampedPropagationPath();
|
||||
StampedPropagationPath(const EventPropagationPath& path, double time);
|
||||
|
||||
void clear();
|
||||
bool valid() const;
|
||||
|
||||
EventPropagationPath path;
|
||||
double time;
|
||||
};
|
||||
|
||||
// TODO if we really need the paths modify to not copy around the paths
|
||||
// that much.
|
||||
StampedPropagationPath _last_mouse_over;
|
||||
size_t _current_click_count;
|
||||
|
||||
struct MouseEventInfo:
|
||||
public StampedPropagationPath
|
||||
{
|
||||
int button;
|
||||
osg::Vec2f pos;
|
||||
|
||||
void set( const MouseEventPtr& event,
|
||||
const EventPropagationPath& path );
|
||||
} _last_mouse_down,
|
||||
_last_click;
|
||||
|
||||
/**
|
||||
* Propagate click event and handle multi-click (eg. create dblclick)
|
||||
*/
|
||||
bool handleClick( const MouseEventPtr& event,
|
||||
const EventPropagationPath& path );
|
||||
|
||||
/**
|
||||
* Handle mouseover/enter/out/leave
|
||||
*/
|
||||
bool handleMove( const MouseEventPtr& event,
|
||||
const EventPropagationPath& path );
|
||||
|
||||
/**
|
||||
* Check if two click events (either mousedown/up or two consecutive
|
||||
* clicks) are inside a maximum distance to still create a click or
|
||||
* dblclick event respectively.
|
||||
*/
|
||||
bool checkClickDistance( const osg::Vec2f& pos1,
|
||||
const osg::Vec2f& pos2 ) const;
|
||||
EventPropagationPath
|
||||
getCommonAncestor( const EventPropagationPath& path1,
|
||||
const EventPropagationPath& path2 ) const;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_EVENT_MANAGER_HXX_ */
|
||||
@@ -1,33 +0,0 @@
|
||||
// Mapping between canvas gui Event types and their names
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef ENUM_MAPPING
|
||||
# error "Don't include this file directly!"
|
||||
#endif
|
||||
|
||||
ENUM_MAPPING(MOUSE_DOWN, "mousedown")
|
||||
ENUM_MAPPING(MOUSE_UP, "mouseup")
|
||||
ENUM_MAPPING(CLICK, "click")
|
||||
ENUM_MAPPING(DBL_CLICK, "dblclick")
|
||||
ENUM_MAPPING(DRAG, "drag")
|
||||
ENUM_MAPPING(WHEEL, "wheel")
|
||||
ENUM_MAPPING(MOUSE_MOVE, "mousemove")
|
||||
ENUM_MAPPING(MOUSE_OVER, "mouseover")
|
||||
ENUM_MAPPING(MOUSE_OUT, "mouseout")
|
||||
ENUM_MAPPING(MOUSE_ENTER, "mouseenter")
|
||||
ENUM_MAPPING(MOUSE_LEAVE, "mouseleave")
|
||||
@@ -1,91 +0,0 @@
|
||||
// Visitor for traversing a canvas element hierarchy similar to the traversal
|
||||
// of the DOM Level 3 Event Model
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasEvent.hxx"
|
||||
#include "CanvasEventVisitor.hxx"
|
||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
EventVisitor::EventVisitor( TraverseMode mode,
|
||||
const osg::Vec2f& pos,
|
||||
const ElementPtr& root ):
|
||||
_traverse_mode( mode ),
|
||||
_root(root)
|
||||
{
|
||||
if( mode == TRAVERSE_DOWN )
|
||||
_target_path.push_back( EventTarget(NULL, pos) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
EventVisitor::~EventVisitor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool EventVisitor::traverse(Element& el)
|
||||
{
|
||||
if( _traverse_mode == TRAVERSE_UP )
|
||||
return el.ascend(*this);
|
||||
else
|
||||
return el.traverse(*this);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool EventVisitor::apply(Element& el)
|
||||
{
|
||||
// We only need to check for hits while traversing down
|
||||
if( _traverse_mode == TRAVERSE_DOWN )
|
||||
{
|
||||
const osg::Vec2f& pos = _target_path.back().local_pos;
|
||||
const osg::Vec2f local_pos = el.posToLocal(pos);
|
||||
|
||||
// Don't check specified root element for collision, as its purpose is to
|
||||
// catch all events which have no target. This allows for example calling
|
||||
// event listeners attached to the canvas itself (its root group) even if
|
||||
// no element has been hit.
|
||||
if( _root.get() != &el
|
||||
&& !el.hitBound(_target_path.front().local_pos, pos, local_pos) )
|
||||
return false;
|
||||
|
||||
_target_path.push_back( EventTarget(&el, local_pos) );
|
||||
|
||||
if( el.traverse(*this) || &el == _root.get() )
|
||||
return true;
|
||||
|
||||
_target_path.pop_back();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return el.ascend(*this);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const EventPropagationPath& EventVisitor::getPropagationPath() const
|
||||
{
|
||||
return _target_path;
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,68 +0,0 @@
|
||||
// Visitor for traversing a canvas element hierarchy similar to the traversal
|
||||
// of the DOM Level 3 Event Model
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_EVENT_VISITOR_HXX_
|
||||
#define CANVAS_EVENT_VISITOR_HXX_
|
||||
|
||||
#include "canvas_fwd.hxx"
|
||||
#include "CanvasEventManager.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class EventVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
enum TraverseMode
|
||||
{
|
||||
TRAVERSE_UP,
|
||||
TRAVERSE_DOWN
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mode
|
||||
* @param pos Mouse position
|
||||
* @param root Element to dispatch events to if no element is hit
|
||||
*/
|
||||
EventVisitor( TraverseMode mode,
|
||||
const osg::Vec2f& pos,
|
||||
const ElementPtr& root = ElementPtr() );
|
||||
virtual ~EventVisitor();
|
||||
virtual bool traverse(Element& el);
|
||||
virtual bool apply(Element& el);
|
||||
|
||||
const EventPropagationPath& getPropagationPath() const;
|
||||
|
||||
protected:
|
||||
|
||||
TraverseMode _traverse_mode;
|
||||
EventPropagationPath _target_path;
|
||||
ElementPtr _root;
|
||||
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
|
||||
#endif /* CANVAS_EVENTVISITOR_HXX_ */
|
||||
@@ -1,71 +0,0 @@
|
||||
// Canvas with 2D rendering API
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasMgr.hxx"
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasEventManager.hxx"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
/**
|
||||
* Canvas factory
|
||||
*/
|
||||
CanvasPtr canvasFactory(SGPropertyNode* node)
|
||||
{
|
||||
return CanvasPtr(new Canvas(node));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasMgr::CanvasMgr(SGPropertyNode_ptr node):
|
||||
PropertyBasedMgr(node, "texture", &canvasFactory)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasPtr CanvasMgr::createCanvas(const std::string& name)
|
||||
{
|
||||
return static_cast<Canvas*>( createElement(name).get() );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasPtr CanvasMgr::getCanvas(size_t index) const
|
||||
{
|
||||
return static_cast<Canvas*>( getElement(index).get() );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasPtr CanvasMgr::getCanvas(const std::string& name) const
|
||||
{
|
||||
return static_cast<Canvas*>( getElement(name).get() );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void CanvasMgr::elementCreated(PropertyBasedElementPtr element)
|
||||
{
|
||||
CanvasPtr canvas = static_cast<Canvas*>(element.get());
|
||||
canvas->setCanvasMgr(this);
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,70 +0,0 @@
|
||||
// Canvas with 2D rendering API
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef SG_CANVAS_MGR_H_
|
||||
#define SG_CANVAS_MGR_H_
|
||||
|
||||
#include "canvas_fwd.hxx"
|
||||
#include <simgear/props/PropertyBasedMgr.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class CanvasMgr:
|
||||
public PropertyBasedMgr
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @param node Root node of branch used to control canvasses
|
||||
*/
|
||||
CanvasMgr(SGPropertyNode_ptr node);
|
||||
|
||||
/**
|
||||
* Create a new canvas
|
||||
*
|
||||
* @param name Name of the new canvas
|
||||
*/
|
||||
CanvasPtr createCanvas(const std::string& name = "");
|
||||
|
||||
/**
|
||||
* Get ::Canvas by index
|
||||
*
|
||||
* @param index Index of texture node in /canvas/by-index/
|
||||
*/
|
||||
CanvasPtr getCanvas(size_t index) const;
|
||||
|
||||
/**
|
||||
* Get ::Canvas by name
|
||||
*
|
||||
* @param name Value of child node "name" in
|
||||
* /canvas/by-index/texture[i]/name
|
||||
*/
|
||||
CanvasPtr getCanvas(const std::string& name) const;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void elementCreated(PropertyBasedElementPtr element);
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* SG_CANVAS_MGR_H_ */
|
||||
@@ -1,244 +0,0 @@
|
||||
// Canvas placement for placing a canvas texture onto osg objects.
|
||||
//
|
||||
// It also provides a SGPickCallback for passing mouse events to the canvas and
|
||||
// manages emissive lighting of the placed canvas.
|
||||
//
|
||||
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasObjectPlacement.hxx"
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/scene/util/SGPickCallback.hxx>
|
||||
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
/**
|
||||
* Handle picking events on object with a canvas placed onto
|
||||
*/
|
||||
class ObjectPickCallback:
|
||||
public SGPickCallback
|
||||
{
|
||||
public:
|
||||
|
||||
ObjectPickCallback(const CanvasWeakPtr& canvas):
|
||||
_canvas(canvas)
|
||||
{}
|
||||
|
||||
virtual bool needsUV() const { return true; }
|
||||
|
||||
virtual bool buttonPressed( int,
|
||||
const osgGA::GUIEventAdapter& ea,
|
||||
const Info& info )
|
||||
{
|
||||
MouseEventPtr event(new MouseEvent(ea));
|
||||
updatePosFromUV(event, info.uv);
|
||||
|
||||
if( ea.getEventType() == osgGA::GUIEventAdapter::SCROLL )
|
||||
{
|
||||
event->type = Event::WHEEL;
|
||||
event->delta.set(0,0);
|
||||
switch( ea.getScrollingMotion() )
|
||||
{
|
||||
case osgGA::GUIEventAdapter::SCROLL_UP:
|
||||
event->delta.y() = 1;
|
||||
break;
|
||||
case osgGA::GUIEventAdapter::SCROLL_DOWN:
|
||||
event->delta.y() = -1;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
event->type = Event::MOUSE_DOWN;
|
||||
}
|
||||
|
||||
return handleEvent(event);
|
||||
}
|
||||
|
||||
virtual void buttonReleased( int,
|
||||
const osgGA::GUIEventAdapter& ea,
|
||||
const Info* info )
|
||||
{
|
||||
if( ea.getEventType() != osgGA::GUIEventAdapter::RELEASE )
|
||||
return;
|
||||
|
||||
MouseEventPtr event(new MouseEvent(ea));
|
||||
event->type = Event::MOUSE_UP;
|
||||
updatePosFromUV(event, info ? info->uv : SGVec2d(-1,-1));
|
||||
|
||||
handleEvent(event);
|
||||
}
|
||||
|
||||
virtual void mouseMoved( const osgGA::GUIEventAdapter& ea,
|
||||
const Info* info )
|
||||
{
|
||||
// drag (currently only with LMB)
|
||||
if( ea.getEventType() != osgGA::GUIEventAdapter::DRAG )
|
||||
return;
|
||||
|
||||
MouseEventPtr event(new MouseEvent(ea));
|
||||
event->type = Event::DRAG;
|
||||
updatePosFromUV(event, info ? info->uv : SGVec2d(-1,-1));
|
||||
|
||||
handleEvent(event);
|
||||
}
|
||||
|
||||
virtual bool hover( const osg::Vec2d& windowPos,
|
||||
const Info& info )
|
||||
{
|
||||
// TODO somehow get more info about event (time, modifiers, pressed
|
||||
// buttons, ...)
|
||||
MouseEventPtr event(new MouseEvent);
|
||||
event->type = Event::MOUSE_MOVE;
|
||||
event->screen_pos = windowPos;
|
||||
updatePosFromUV(event, info.uv);
|
||||
|
||||
return handleEvent(event);
|
||||
}
|
||||
|
||||
virtual void mouseLeave( const osg::Vec2d& windowPos )
|
||||
{
|
||||
MouseEventPtr event(new MouseEvent);
|
||||
event->type = Event::MOUSE_LEAVE;
|
||||
event->screen_pos = windowPos;
|
||||
|
||||
handleEvent(event);
|
||||
}
|
||||
|
||||
protected:
|
||||
CanvasWeakPtr _canvas;
|
||||
osg::Vec2f _last_pos,
|
||||
_last_delta;
|
||||
|
||||
void updatePosFromUV(const MouseEventPtr& event, const SGVec2d& uv)
|
||||
{
|
||||
CanvasPtr canvas = _canvas.lock();
|
||||
if( !canvas )
|
||||
return;
|
||||
|
||||
osg::Vec2d pos( uv.x() * canvas->getViewWidth(),
|
||||
(1 - uv.y()) * canvas->getViewHeight() );
|
||||
|
||||
_last_delta = pos - _last_pos;
|
||||
_last_pos = pos;
|
||||
|
||||
event->client_pos = pos;
|
||||
event->delta = _last_delta;
|
||||
}
|
||||
|
||||
bool handleEvent(const MouseEventPtr& event)
|
||||
{
|
||||
CanvasPtr canvas = _canvas.lock();
|
||||
if( !canvas )
|
||||
return false;
|
||||
|
||||
return canvas->handleMouseEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ObjectPlacement::ObjectPlacement( SGPropertyNode* node,
|
||||
const GroupPtr& group,
|
||||
const CanvasWeakPtr& canvas ):
|
||||
Placement(node),
|
||||
_group(group),
|
||||
_canvas(canvas)
|
||||
{
|
||||
// TODO make more generic and extendable for more properties
|
||||
if( node->hasValue("emission") )
|
||||
setEmission( node->getFloatValue("emission") );
|
||||
if( node->hasValue("capture-events") )
|
||||
setCaptureEvents( node->getBoolValue("capture-events") );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ObjectPlacement::~ObjectPlacement()
|
||||
{
|
||||
assert( _group->getNumChildren() == 1 );
|
||||
osg::Node *child = _group->getChild(0);
|
||||
|
||||
if( _group->getNumParents() )
|
||||
{
|
||||
osg::Group *parent = _group->getParent(0);
|
||||
parent->addChild(child);
|
||||
parent->removeChild(_group);
|
||||
}
|
||||
|
||||
_group->removeChild(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ObjectPlacement::setEmission(float emit)
|
||||
{
|
||||
emit = SGMiscf::clip(emit, 0, 1);
|
||||
|
||||
if( !_material )
|
||||
{
|
||||
_material = new osg::Material;
|
||||
_material->setColorMode(osg::Material::OFF);
|
||||
_material->setDataVariance(osg::Object::DYNAMIC);
|
||||
_group->getOrCreateStateSet()
|
||||
->setAttribute(_material, ( osg::StateAttribute::ON
|
||||
| osg::StateAttribute::OVERRIDE ) );
|
||||
}
|
||||
|
||||
_material->setEmission(
|
||||
osg::Material::FRONT_AND_BACK,
|
||||
osg::Vec4(emit, emit, emit, emit)
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ObjectPlacement::setCaptureEvents(bool enable)
|
||||
{
|
||||
if( !enable && _scene_user_data )
|
||||
return;
|
||||
|
||||
if( enable && !_pick_cb )
|
||||
_pick_cb = new ObjectPickCallback(_canvas);
|
||||
|
||||
_scene_user_data = SGSceneUserData::getOrCreateSceneUserData(_group);
|
||||
_scene_user_data->setPickCallback(enable ? _pick_cb.get() : 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool ObjectPlacement::childChanged(SGPropertyNode* node)
|
||||
{
|
||||
if( node->getParent() != _node )
|
||||
return false;
|
||||
|
||||
if( node->getNameString() == "emission" )
|
||||
setEmission( node->getFloatValue() );
|
||||
else if( node->getNameString() == "capture-events" )
|
||||
setCaptureEvents( node->getBoolValue() );
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,78 +0,0 @@
|
||||
///@file Placement for putting a canvas texture onto OpenSceneGraph objects.
|
||||
//
|
||||
// It also provides a SGPickCallback for passing mouse events to the canvas and
|
||||
// manages emissive lighting of the placed canvas.
|
||||
//
|
||||
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_OBJECT_PLACEMENT_HXX_
|
||||
#define CANVAS_OBJECT_PLACEMENT_HXX_
|
||||
|
||||
#include "CanvasPlacement.hxx"
|
||||
#include "canvas_fwd.hxx"
|
||||
|
||||
#include <simgear/scene/util/SGSceneUserData.hxx>
|
||||
#include <osg/Material>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
/**
|
||||
* Place a Canvas onto an osg object (as texture).
|
||||
*/
|
||||
class ObjectPlacement:
|
||||
public Placement
|
||||
{
|
||||
public:
|
||||
|
||||
typedef osg::ref_ptr<osg::Group> GroupPtr;
|
||||
typedef osg::ref_ptr<osg::Material> MaterialPtr;
|
||||
|
||||
ObjectPlacement( SGPropertyNode* node,
|
||||
const GroupPtr& group,
|
||||
const CanvasWeakPtr& canvas );
|
||||
virtual ~ObjectPlacement();
|
||||
|
||||
/**
|
||||
* Set emissive lighting of the object the canvas is placed on.
|
||||
*/
|
||||
void setEmission(float emit);
|
||||
|
||||
/**
|
||||
* Set whether pick events should be captured.
|
||||
*/
|
||||
void setCaptureEvents(bool enable);
|
||||
|
||||
virtual bool childChanged(SGPropertyNode* child);
|
||||
|
||||
protected:
|
||||
typedef SGSharedPtr<SGPickCallback> PickCallbackPtr;
|
||||
typedef osg::ref_ptr<SGSceneUserData> SGSceneUserDataPtr;
|
||||
|
||||
GroupPtr _group;
|
||||
MaterialPtr _material;
|
||||
CanvasWeakPtr _canvas;
|
||||
PickCallbackPtr _pick_cb;
|
||||
SGSceneUserDataPtr _scene_user_data;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_OBJECT_PLACEMENT_HXX_ */
|
||||
@@ -1,59 +0,0 @@
|
||||
// Base class for canvas placements
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasPlacement.hxx"
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Placement::Placement(SGPropertyNode* node):
|
||||
_node(node)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Placement::~Placement()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGConstPropertyNode_ptr Placement::getProps() const
|
||||
{
|
||||
return _node;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGPropertyNode_ptr Placement::getProps()
|
||||
{
|
||||
return _node;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Placement::childChanged(SGPropertyNode* child)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,51 +0,0 @@
|
||||
// Base class for canvas placements
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_PLACEMENT_HXX_
|
||||
#define CANVAS_PLACEMENT_HXX_
|
||||
|
||||
#include <simgear/props/propsfwd.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class Placement
|
||||
{
|
||||
public:
|
||||
Placement(SGPropertyNode* node);
|
||||
virtual ~Placement() = 0;
|
||||
|
||||
SGConstPropertyNode_ptr getProps() const;
|
||||
SGPropertyNode_ptr getProps();
|
||||
|
||||
virtual bool childChanged(SGPropertyNode* child);
|
||||
|
||||
protected:
|
||||
SGPropertyNode_ptr _node;
|
||||
|
||||
private:
|
||||
Placement(const Placement&) /* = delete */;
|
||||
Placement& operator=(const Placement&) /* = delete */;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_PLACEMENT_HXX_ */
|
||||
@@ -1,48 +0,0 @@
|
||||
// Adapter for using the canvas with different applications
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef SG_CANVAS_SYSTEM_ADAPTER_HXX_
|
||||
#define SG_CANVAS_SYSTEM_ADAPTER_HXX_
|
||||
|
||||
#include "canvas_fwd.hxx"
|
||||
|
||||
class SGSubsystem;
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace HTTP { class Client; }
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class SystemAdapter
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~SystemAdapter() {}
|
||||
virtual FontPtr getFont(const std::string& name) const = 0;
|
||||
virtual void addCamera(osg::Camera* camera) const = 0;
|
||||
virtual void removeCamera(osg::Camera* camera) const = 0;
|
||||
virtual osg::Image* getImage(const std::string& path) const = 0;
|
||||
virtual SGSubsystem* getSubsystem(const std::string& name) const = 0;
|
||||
virtual HTTP::Client* getHTTPClient() const = 0;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* SG_CANVAS_SYSTEM_ADAPTER_HXX_ */
|
||||
@@ -1,330 +0,0 @@
|
||||
// Window for placing a Canvas onto it (for dialogs, menus, etc.)
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasMgr.hxx"
|
||||
#include "CanvasSystemAdapter.hxx"
|
||||
#include "CanvasWindow.hxx"
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
|
||||
#include <osgGA/GUIEventHandler>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const std::string Window::TYPE_NAME = "window";
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Window::Window( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
Image(canvas, node, parent_style, parent),
|
||||
_attributes_dirty(0),
|
||||
_resizable(false),
|
||||
_capture_events(true),
|
||||
_resize_top(node, "resize-top"),
|
||||
_resize_right(node, "resize-right"),
|
||||
_resize_bottom(node, "resize-bottom"),
|
||||
_resize_left(node, "resize-left"),
|
||||
_resize_status(node, "resize-status")
|
||||
{
|
||||
node->setFloatValue("source/right", 1);
|
||||
node->setFloatValue("source/bottom", 1);
|
||||
node->setBoolValue("source/normalized", true);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Window::~Window()
|
||||
{
|
||||
if( _canvas_decoration )
|
||||
_canvas_decoration->destroy();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::update(double delta_time_sec)
|
||||
{
|
||||
if( _attributes_dirty & DECORATION )
|
||||
{
|
||||
updateDecoration();
|
||||
_attributes_dirty &= ~DECORATION;
|
||||
}
|
||||
|
||||
Image::update(delta_time_sec);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::valueChanged(SGPropertyNode * node)
|
||||
{
|
||||
bool handled = false;
|
||||
if( node->getParent() == _node )
|
||||
{
|
||||
handled = true;
|
||||
const std::string& name = node->getNameString();
|
||||
if( name == "resize" )
|
||||
_resizable = node->getBoolValue();
|
||||
else if( name == "update" )
|
||||
update(0);
|
||||
else if( name == "capture-events" )
|
||||
_capture_events = node->getBoolValue();
|
||||
else if( name == "decoration-border" )
|
||||
parseDecorationBorder(node->getStringValue());
|
||||
else if( boost::starts_with(name, "shadow-")
|
||||
|| name == "content-size" )
|
||||
_attributes_dirty |= DECORATION;
|
||||
else
|
||||
handled = false;
|
||||
}
|
||||
|
||||
if( !handled )
|
||||
Image::valueChanged(node);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Group* Window::getGroup()
|
||||
{
|
||||
return getMatrixTransform();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const SGVec2<float> Window::getPosition() const
|
||||
{
|
||||
const osg::Matrix& m = getMatrixTransform()->getMatrix();
|
||||
return SGVec2<float>( m(3, 0), m(3, 1) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const SGRect<float> Window::getScreenRegion() const
|
||||
{
|
||||
return getPosition() + getRegion();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::setCanvasContent(CanvasPtr canvas)
|
||||
{
|
||||
_canvas_content = canvas;
|
||||
if( _layout )
|
||||
{
|
||||
canvas->setLayout(_layout);
|
||||
_layout.clear();
|
||||
}
|
||||
|
||||
if( _image_content )
|
||||
// Placement within decoration canvas
|
||||
_image_content->setSrcCanvas(canvas);
|
||||
else
|
||||
setSrcCanvas(canvas);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasWeakPtr Window::getCanvasContent() const
|
||||
{
|
||||
return _canvas_content;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::setLayout(const LayoutRef& layout)
|
||||
{
|
||||
CanvasPtr canvas = _canvas_content.lock();
|
||||
if( canvas )
|
||||
canvas->setLayout(layout);
|
||||
else
|
||||
_layout = layout; // keep layout until content canvas is set
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasPtr Window::getCanvasDecoration() const
|
||||
{
|
||||
return _canvas_decoration;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Window::isResizable() const
|
||||
{
|
||||
return _resizable;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Window::isCapturingEvents() const
|
||||
{
|
||||
return _capture_events;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::raise()
|
||||
{
|
||||
// on writing the z-index the window always is moved to the top of all other
|
||||
// windows with the same z-index.
|
||||
set<int>("z-index", get<int>("z-index", 0));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::handleResize( uint8_t mode,
|
||||
const osg::Vec2f& offset )
|
||||
{
|
||||
if( mode == NONE )
|
||||
{
|
||||
_resize_status = 0;
|
||||
return;
|
||||
}
|
||||
else if( mode & INIT )
|
||||
{
|
||||
_resize_top = getRegion().t();
|
||||
_resize_right = getRegion().r();
|
||||
_resize_bottom = getRegion().b();
|
||||
_resize_left = getRegion().l();
|
||||
_resize_status = 1;
|
||||
}
|
||||
|
||||
if( mode & BOTTOM )
|
||||
_resize_bottom = getRegion().b() + offset.y();
|
||||
else if( mode & TOP )
|
||||
_resize_top = getRegion().t() + offset.y();
|
||||
|
||||
if( mode & canvas::Window::RIGHT )
|
||||
_resize_right = getRegion().r() + offset.x();
|
||||
else if( mode & canvas::Window::LEFT )
|
||||
_resize_left = getRegion().l() + offset.x();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::parseDecorationBorder(const std::string& str)
|
||||
{
|
||||
_decoration_border = simgear::CSSBorder::parse(str);
|
||||
_attributes_dirty |= DECORATION;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::updateDecoration()
|
||||
{
|
||||
int shadow_radius = get<float>("shadow-radius") + 0.5;
|
||||
if( shadow_radius < 2 )
|
||||
shadow_radius = 0;
|
||||
|
||||
CanvasPtr content = _canvas_content.lock();
|
||||
SGRect<int> content_view
|
||||
(
|
||||
0,
|
||||
0,
|
||||
get<int>("content-size[0]", content ? content->getViewWidth() : 400),
|
||||
get<int>("content-size[1]", content ? content->getViewHeight() : 300)
|
||||
);
|
||||
|
||||
if( _decoration_border.isNone() && !shadow_radius )
|
||||
{
|
||||
setSrcCanvas(content);
|
||||
set<int>("size[0]", content_view.width());
|
||||
set<int>("size[1]", content_view.height());
|
||||
|
||||
_image_content.reset();
|
||||
_image_shadow.reset();
|
||||
if( _canvas_decoration )
|
||||
_canvas_decoration->destroy();
|
||||
_canvas_decoration.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if( !_canvas_decoration )
|
||||
{
|
||||
CanvasMgr* mgr = dynamic_cast<CanvasMgr*>(
|
||||
Canvas::getSystemAdapter()->getSubsystem("Canvas")
|
||||
);
|
||||
|
||||
if( !mgr )
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "canvas::Window: no canvas manager!");
|
||||
return;
|
||||
}
|
||||
|
||||
_canvas_decoration = mgr->createCanvas("window-decoration");
|
||||
_canvas_decoration->getProps()
|
||||
->setStringValue("background", "rgba(0,0,0,0)");
|
||||
setSrcCanvas(_canvas_decoration);
|
||||
|
||||
_image_content = _canvas_decoration->getRootGroup()
|
||||
->createChild<Image>("content");
|
||||
_image_content->setSrcCanvas(content);
|
||||
|
||||
// Draw content on top of decoration
|
||||
_image_content->set<int>("z-index", 1);
|
||||
}
|
||||
|
||||
GroupPtr group_decoration =
|
||||
_canvas_decoration->getOrCreateGroup("decoration");
|
||||
group_decoration->set<int>("tf/t[0]", shadow_radius);
|
||||
group_decoration->set<int>("tf/t[1]", shadow_radius);
|
||||
// TODO do we need clipping or shall we trust the decorator not to draw over
|
||||
// the shadow?
|
||||
|
||||
CSSBorder::Offsets const border =
|
||||
_decoration_border.getAbsOffsets(content_view);
|
||||
|
||||
int shad2 = 2 * shadow_radius,
|
||||
outer_width = border.l + content_view.width() + border.r + shad2,
|
||||
outer_height = border.t + content_view.height() + border.b + shad2;
|
||||
|
||||
_canvas_decoration->setSizeX( outer_width );
|
||||
_canvas_decoration->setSizeY( outer_height );
|
||||
_canvas_decoration->setViewWidth( outer_width );
|
||||
_canvas_decoration->setViewHeight( outer_height );
|
||||
|
||||
set<int>("size[0]", outer_width - shad2);
|
||||
set<int>("size[1]", outer_height - shad2);
|
||||
set<int>("outset", shadow_radius);
|
||||
|
||||
assert(_image_content);
|
||||
_image_content->set<int>("x", shadow_radius + border.l);
|
||||
_image_content->set<int>("y", shadow_radius + border.t);
|
||||
_image_content->set<int>("size[0]", content_view.width());
|
||||
_image_content->set<int>("size[1]", content_view.height());
|
||||
|
||||
if( !shadow_radius )
|
||||
{
|
||||
if( _image_shadow )
|
||||
{
|
||||
_image_shadow->destroy();
|
||||
_image_shadow.reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int shadow_inset = std::max<int>(get<float>("shadow-inset") + 0.5, 0),
|
||||
slice_width = shadow_radius + shadow_inset;
|
||||
|
||||
_image_shadow = _canvas_decoration->getRootGroup()
|
||||
->getOrCreateChild<Image>("shadow");
|
||||
_image_shadow->set<std::string>("src", "gui/images/shadow.png");
|
||||
_image_shadow->set<float>("slice", 7);
|
||||
_image_shadow->set<std::string>("fill", "#000000");
|
||||
_image_shadow->set<float>("slice-width", slice_width);
|
||||
_image_shadow->set<int>("size[0]", outer_width);
|
||||
_image_shadow->set<int>("size[1]", outer_height);
|
||||
|
||||
// Draw shadow below decoration
|
||||
_image_shadow->set<int>("z-index", -1);
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,131 +0,0 @@
|
||||
// Window for placing a Canvas onto it (for dialogs, menus, etc.)
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_WINDOW_HXX_
|
||||
#define CANVAS_WINDOW_HXX_
|
||||
|
||||
#include <simgear/canvas/elements/CanvasImage.hxx>
|
||||
#include <simgear/canvas/layout/Layout.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/props/PropertyBasedElement.hxx>
|
||||
#include <simgear/props/propertyObject.hxx>
|
||||
#include <simgear/misc/CSSBorder.hxx>
|
||||
|
||||
#include <osg/Geode>
|
||||
#include <osg/Geometry>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class Window:
|
||||
public Image,
|
||||
public LayoutItem
|
||||
{
|
||||
public:
|
||||
static const std::string TYPE_NAME;
|
||||
|
||||
enum Resize
|
||||
{
|
||||
NONE = 0,
|
||||
LEFT = 1,
|
||||
RIGHT = LEFT << 1,
|
||||
TOP = RIGHT << 1,
|
||||
BOTTOM = TOP << 1,
|
||||
INIT = BOTTOM << 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @param node Property node containing settings for this window:
|
||||
* capture-events Disable/Enable event capturing
|
||||
* content-size[0-1] Size of content area (excluding
|
||||
* decoration border)
|
||||
* decoration-border Size of decoration border
|
||||
* resize Enable resize cursor and properties
|
||||
* shadow-inset Inset of shadow image
|
||||
* shadow-radius Radius/outset of shadow image
|
||||
*/
|
||||
Window( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style = Style(),
|
||||
Element* parent = 0 );
|
||||
virtual ~Window();
|
||||
|
||||
virtual void update(double delta_time_sec);
|
||||
virtual void valueChanged(SGPropertyNode* node);
|
||||
|
||||
osg::Group* getGroup();
|
||||
const SGVec2<float> getPosition() const;
|
||||
const SGRect<float> getScreenRegion() const;
|
||||
|
||||
void setCanvasContent(CanvasPtr canvas);
|
||||
simgear::canvas::CanvasWeakPtr getCanvasContent() const;
|
||||
|
||||
void setLayout(const LayoutRef& layout);
|
||||
|
||||
CanvasPtr getCanvasDecoration() const;
|
||||
|
||||
bool isResizable() const;
|
||||
bool isCapturingEvents() const;
|
||||
|
||||
/**
|
||||
* Moves window on top of all other windows with the same z-index.
|
||||
*
|
||||
* @note If no z-index is set it defaults to 0.
|
||||
*/
|
||||
void raise();
|
||||
|
||||
void handleResize( uint8_t mode,
|
||||
const osg::Vec2f& offset = osg::Vec2f() );
|
||||
|
||||
protected:
|
||||
|
||||
enum Attributes
|
||||
{
|
||||
DECORATION = 1
|
||||
};
|
||||
|
||||
uint32_t _attributes_dirty;
|
||||
|
||||
CanvasPtr _canvas_decoration;
|
||||
CanvasWeakPtr _canvas_content;
|
||||
LayoutRef _layout;
|
||||
|
||||
ImagePtr _image_content,
|
||||
_image_shadow;
|
||||
|
||||
bool _resizable,
|
||||
_capture_events;
|
||||
|
||||
PropertyObject<int> _resize_top,
|
||||
_resize_right,
|
||||
_resize_bottom,
|
||||
_resize_left,
|
||||
_resize_status;
|
||||
|
||||
CSSBorder _decoration_border;
|
||||
|
||||
void parseDecorationBorder(const std::string& str);
|
||||
void updateDecoration();
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_WINDOW_HXX_ */
|
||||
@@ -1,379 +0,0 @@
|
||||
// Owner Drawn Gauge helper class
|
||||
//
|
||||
// Written by Harald JOHNSEN, started May 2005.
|
||||
//
|
||||
// Copyright (C) 2005 Harald JOHNSEN
|
||||
//
|
||||
// Ported to OSG by Tim Moore - Jun 2007
|
||||
//
|
||||
// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
|
||||
// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
|
||||
// the texture in the scene by certain filter criteria
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include "ODGauge.hxx"
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasSystemAdapter.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/scene/util/RenderConstants.hxx>
|
||||
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/AlphaFunc>
|
||||
#include <osg/BlendFunc>
|
||||
#include <osg/Camera>
|
||||
#include <osg/Matrix>
|
||||
#include <osg/PolygonMode>
|
||||
#include <osg/ShadeModel>
|
||||
#include <osg/StateSet>
|
||||
#include <osg/FrameBufferObject> // for GL_DEPTH_STENCIL_EXT on Windows
|
||||
#include <osgUtil/RenderBin>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class PreOrderBin:
|
||||
public osgUtil::RenderBin
|
||||
{
|
||||
public:
|
||||
|
||||
PreOrderBin()
|
||||
{}
|
||||
PreOrderBin( const RenderBin& rhs,
|
||||
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY ):
|
||||
RenderBin(rhs, copyop)
|
||||
{}
|
||||
|
||||
virtual osg::Object* cloneType() const
|
||||
{
|
||||
return new PreOrderBin();
|
||||
}
|
||||
virtual osg::Object* clone(const osg::CopyOp& copyop) const
|
||||
{
|
||||
return new PreOrderBin(*this,copyop);
|
||||
}
|
||||
virtual bool isSameKindAs(const osg::Object* obj) const
|
||||
{
|
||||
return dynamic_cast<const PreOrderBin*>(obj) != 0L;
|
||||
}
|
||||
virtual const char* className() const
|
||||
{
|
||||
return "PreOrderBin";
|
||||
}
|
||||
|
||||
virtual void sort()
|
||||
{
|
||||
// Do not sort to keep traversal order...
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef OSG_INIT_SINGLETON_PROXY
|
||||
/**
|
||||
* http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk/include/osg/Object
|
||||
*
|
||||
* Helper macro that creates a static proxy object to call singleton function
|
||||
* on it's construction, ensuring that the singleton gets initialized at
|
||||
* startup.
|
||||
*/
|
||||
# define OSG_INIT_SINGLETON_PROXY(ProxyName, Func)\
|
||||
static struct ProxyName{ ProxyName() { Func; } } s_##ProxyName;
|
||||
#endif
|
||||
|
||||
OSG_INIT_SINGLETON_PROXY(
|
||||
PreOrderBinProxy,
|
||||
(osgUtil::RenderBin::addRenderBinPrototype("PreOrderBin", new PreOrderBin))
|
||||
);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ODGauge::ODGauge():
|
||||
_size_x( -1 ),
|
||||
_size_y( -1 ),
|
||||
_view_width( -1 ),
|
||||
_view_height( -1 ),
|
||||
_flags(0),
|
||||
_coverage_samples( 0 ),
|
||||
_color_samples( 0 )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ODGauge::~ODGauge()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::setSize(int size_x, int size_y)
|
||||
{
|
||||
_size_x = size_x;
|
||||
_size_y = size_y < 0 ? size_x : size_y;
|
||||
|
||||
if( serviceable() )
|
||||
reinit();
|
||||
else if( texture )
|
||||
texture->setTextureSize(_size_x, _size_y);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::setViewSize(int width, int height)
|
||||
{
|
||||
_view_width = width;
|
||||
_view_height = height < 0 ? width : height;
|
||||
|
||||
if( camera )
|
||||
updateCoordinateFrame();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Vec2s ODGauge::getViewSize() const
|
||||
{
|
||||
return osg::Vec2s(_view_width, _view_height);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::useImageCoords(bool use)
|
||||
{
|
||||
if( updateFlag(USE_IMAGE_COORDS, use) && texture )
|
||||
updateCoordinateFrame();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::useStencil(bool use)
|
||||
{
|
||||
if( updateFlag(USE_STENCIL, use) && texture )
|
||||
updateStencil();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::useAdditiveBlend(bool use)
|
||||
{
|
||||
if( updateFlag(USE_ADDITIVE_BLEND, use) && camera )
|
||||
updateBlendMode();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::setSampling( bool mipmapping,
|
||||
int coverage_samples,
|
||||
int color_samples )
|
||||
{
|
||||
if( !updateFlag(USE_MIPMAPPING, mipmapping)
|
||||
&& _coverage_samples == coverage_samples
|
||||
&& _color_samples == color_samples )
|
||||
return;
|
||||
|
||||
if( color_samples > coverage_samples )
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_GL,
|
||||
SG_WARN,
|
||||
"ODGauge::setSampling: color_samples > coverage_samples not allowed!"
|
||||
);
|
||||
color_samples = coverage_samples;
|
||||
}
|
||||
|
||||
_coverage_samples = coverage_samples;
|
||||
_color_samples = color_samples;
|
||||
|
||||
updateSampling();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::setRender(bool render)
|
||||
{
|
||||
// Only the far camera should trigger this texture to be rendered.
|
||||
camera->setNodeMask(render ? simgear::BACKGROUND_BIT : 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool ODGauge::serviceable() const
|
||||
{
|
||||
return _flags & AVAILABLE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::allocRT(osg::NodeCallback* camera_cull_callback)
|
||||
{
|
||||
camera = new osg::Camera;
|
||||
camera->setDataVariance(osg::Object::DYNAMIC);
|
||||
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
||||
camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
||||
camera->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 0.0f));
|
||||
camera->setClearStencil(0);
|
||||
camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT,
|
||||
osg::Camera::FRAME_BUFFER );
|
||||
|
||||
if( camera_cull_callback )
|
||||
camera->setCullCallback(camera_cull_callback);
|
||||
|
||||
setRender(true);
|
||||
updateCoordinateFrame();
|
||||
updateStencil();
|
||||
|
||||
osg::StateSet* stateSet = camera->getOrCreateStateSet();
|
||||
stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||
stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
|
||||
stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
|
||||
stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
|
||||
stateSet->setAttributeAndModes(
|
||||
new osg::PolygonMode( osg::PolygonMode::FRONT_AND_BACK,
|
||||
osg::PolygonMode::FILL ),
|
||||
osg::StateAttribute::ON );
|
||||
stateSet->setAttributeAndModes(
|
||||
new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.001f),
|
||||
osg::StateAttribute::ON );
|
||||
stateSet->setAttribute(new osg::ShadeModel(osg::ShadeModel::FLAT));
|
||||
|
||||
if( !texture )
|
||||
{
|
||||
texture = new osg::Texture2D;
|
||||
texture->setResizeNonPowerOfTwoHint(false);
|
||||
texture->setTextureSize(_size_x, _size_y);
|
||||
texture->setInternalFormat(GL_RGBA);
|
||||
}
|
||||
|
||||
updateSampling();
|
||||
updateBlendMode();
|
||||
|
||||
if( Canvas::getSystemAdapter() )
|
||||
Canvas::getSystemAdapter()->addCamera(camera.get());
|
||||
|
||||
_flags |= AVAILABLE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::reinit()
|
||||
{
|
||||
osg::NodeCallback* cull_callback = camera ? camera->getCullCallback() : 0;
|
||||
clear();
|
||||
allocRT(cull_callback);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::clear()
|
||||
{
|
||||
if( camera.valid() && Canvas::getSystemAdapter() )
|
||||
Canvas::getSystemAdapter()->removeCamera(camera.get());
|
||||
camera.release();
|
||||
texture.release();
|
||||
|
||||
_flags &= ~AVAILABLE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool ODGauge::updateFlag(Flags flag, bool enable)
|
||||
{
|
||||
if( bool(_flags & flag) == enable )
|
||||
return false;
|
||||
|
||||
_flags ^= flag;
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::updateCoordinateFrame()
|
||||
{
|
||||
assert( camera );
|
||||
|
||||
if( _view_width < 0 )
|
||||
_view_width = _size_x;
|
||||
if( _view_height < 0 )
|
||||
_view_height = _size_y;
|
||||
|
||||
camera->setViewport(0, 0, _size_x, _size_y);
|
||||
|
||||
if( _flags & USE_IMAGE_COORDS )
|
||||
camera->setProjectionMatrix(
|
||||
osg::Matrix::ortho2D(0, _view_width, _view_height, 0)
|
||||
);
|
||||
else
|
||||
camera->setProjectionMatrix(
|
||||
osg::Matrix::ortho2D( -_view_width/2., _view_width/2.,
|
||||
-_view_height/2., _view_height/2. )
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::updateStencil()
|
||||
{
|
||||
assert( camera );
|
||||
|
||||
GLbitfield mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
|
||||
|
||||
if( _flags & USE_STENCIL )
|
||||
{
|
||||
camera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER,
|
||||
GL_DEPTH_STENCIL_EXT );
|
||||
mask |= GL_STENCIL_BUFFER_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
camera->detach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER);
|
||||
}
|
||||
|
||||
camera->setClearMask(mask);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::updateSampling()
|
||||
{
|
||||
assert( camera );
|
||||
assert( texture );
|
||||
|
||||
texture->setFilter(
|
||||
osg::Texture2D::MIN_FILTER,
|
||||
(_flags & USE_MIPMAPPING) ? osg::Texture2D::LINEAR_MIPMAP_LINEAR
|
||||
: osg::Texture2D::LINEAR
|
||||
);
|
||||
camera->attach(
|
||||
osg::Camera::COLOR_BUFFER,
|
||||
texture.get(),
|
||||
0, 0,
|
||||
_flags & USE_MIPMAPPING,
|
||||
_coverage_samples,
|
||||
_color_samples
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ODGauge::updateBlendMode()
|
||||
{
|
||||
assert( camera );
|
||||
|
||||
camera->getOrCreateStateSet()
|
||||
->setAttributeAndModes
|
||||
(
|
||||
(_flags & USE_ADDITIVE_BLEND)
|
||||
? new osg::BlendFunc( osg::BlendFunc::SRC_ALPHA,
|
||||
osg::BlendFunc::ONE_MINUS_SRC_ALPHA,
|
||||
osg::BlendFunc::ONE,
|
||||
osg::BlendFunc::ONE )
|
||||
: new osg::BlendFunc( osg::BlendFunc::SRC_ALPHA,
|
||||
osg::BlendFunc::ONE_MINUS_SRC_ALPHA )
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,172 +0,0 @@
|
||||
// Owner Drawn Gauge helper class
|
||||
//
|
||||
// Written by Harald JOHNSEN, started May 2005.
|
||||
//
|
||||
// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
|
||||
//
|
||||
// Ported to OSG by Tim Moore - Jun 2007
|
||||
//
|
||||
// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
|
||||
// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
|
||||
// the texture in the scene by certain filter criteria
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef _SG_OD_GAUGE_HXX
|
||||
#define _SG_OD_GAUGE_HXX
|
||||
|
||||
#include "canvas_fwd.hxx"
|
||||
|
||||
#include <osg/NodeCallback>
|
||||
#include <osg/Group>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Camera;
|
||||
class Texture2D;
|
||||
}
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
/**
|
||||
* Owner Drawn Gauge (aka render-to-texture) helper class
|
||||
*/
|
||||
class ODGauge
|
||||
{
|
||||
public:
|
||||
|
||||
ODGauge();
|
||||
virtual ~ODGauge();
|
||||
|
||||
/**
|
||||
* Set the size of the render target.
|
||||
*
|
||||
* @param size_x X size
|
||||
* @param size_y Y size. Defaults to size_x if not specified
|
||||
*/
|
||||
void setSize(int size_x, int size_y = -1);
|
||||
|
||||
/**
|
||||
* Set the size of the viewport
|
||||
*
|
||||
* @param width
|
||||
* @param height Defaults to width if not specified
|
||||
*/
|
||||
void setViewSize(int width, int height = -1);
|
||||
|
||||
osg::Vec2s getViewSize() const;
|
||||
|
||||
/**
|
||||
* DEPRECATED
|
||||
*
|
||||
* Get size of squared texture
|
||||
*/
|
||||
int size() const { return _size_x; }
|
||||
|
||||
/**
|
||||
* Set whether to use image coordinates or not.
|
||||
*
|
||||
* Default: origin == center of texture
|
||||
* Image Coords: origin == top left corner
|
||||
*/
|
||||
void useImageCoords(bool use = true);
|
||||
|
||||
/**
|
||||
* Enable/Disable using a stencil buffer
|
||||
*/
|
||||
void useStencil(bool use = true);
|
||||
|
||||
/**
|
||||
* Enable/Disable additive alpha blending (Can improve results with
|
||||
* transparent background)
|
||||
*/
|
||||
void useAdditiveBlend(bool use = true);
|
||||
|
||||
/**
|
||||
* Set sampling parameters for mipmapping and coverage sampling
|
||||
* antialiasing.
|
||||
*
|
||||
* @note color_samples is not allowed to be higher than coverage_samples
|
||||
*
|
||||
*/
|
||||
void setSampling( bool mipmapping,
|
||||
int coverage_samples = 0,
|
||||
int color_samples = 0 );
|
||||
|
||||
/**
|
||||
* Enable/Disable updating the texture (If disabled the contents of the
|
||||
* texture remains with the outcome of the last rendering pass)
|
||||
*/
|
||||
void setRender(bool render);
|
||||
|
||||
/**
|
||||
* Say if we can render to a texture.
|
||||
* @return true if rtt is available
|
||||
*/
|
||||
bool serviceable() const;
|
||||
|
||||
/**
|
||||
* Get the OSG camera for drawing this gauge.
|
||||
*/
|
||||
osg::Camera* getCamera() const { return camera.get(); }
|
||||
|
||||
osg::Texture2D* getTexture() const { return texture.get(); }
|
||||
|
||||
// Real initialization function. Bad name.
|
||||
void allocRT(osg::NodeCallback* camera_cull_callback = 0);
|
||||
void reinit();
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
|
||||
int _size_x,
|
||||
_size_y,
|
||||
_view_width,
|
||||
_view_height;
|
||||
|
||||
enum Flags
|
||||
{
|
||||
AVAILABLE = 1,
|
||||
USE_IMAGE_COORDS = AVAILABLE << 1,
|
||||
USE_STENCIL = USE_IMAGE_COORDS << 1,
|
||||
USE_MIPMAPPING = USE_STENCIL << 1,
|
||||
USE_ADDITIVE_BLEND = USE_MIPMAPPING << 1
|
||||
};
|
||||
|
||||
uint32_t _flags;
|
||||
|
||||
// Multisampling parameters
|
||||
int _coverage_samples,
|
||||
_color_samples;
|
||||
|
||||
osg::ref_ptr<osg::Camera> camera;
|
||||
osg::ref_ptr<osg::Texture2D> texture;
|
||||
|
||||
bool updateFlag(Flags flag, bool enable);
|
||||
|
||||
void updateCoordinateFrame();
|
||||
void updateStencil();
|
||||
void updateSampling();
|
||||
void updateBlendMode();
|
||||
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif // _SG_OD_GAUGE_HXX
|
||||
@@ -1,12 +0,0 @@
|
||||
===============================================================================
|
||||
ShivaVG - an open-source LGPL ANSI C implementation of the OpenVG specification
|
||||
===============================================================================
|
||||
|
||||
Lead developer:
|
||||
|
||||
Ivan Leben <ivan.leben@gmail.com>
|
||||
|
||||
Occasional patches:
|
||||
|
||||
Daniel Turing
|
||||
Vincenzo Pupillo
|
||||
@@ -1,504 +0,0 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
04 Feb 2008 Ivan Leben <ivan.leben@gmail.com>
|
||||
|
||||
* shImage.c: shCopyPixels uses memcpy when formats are
|
||||
equal.
|
||||
|
||||
04 Feb 2008 Ivan Leben <ivan.leben@gmail.com>
|
||||
|
||||
* openvg.h: changed typedef of VGHandle to void*. Code
|
||||
compiles with no warnings on 64-bit systems now and
|
||||
it's much safer anyway.
|
||||
|
||||
03 Feb 2008 Ivan Leben <ivan.leben@gmail.com>
|
||||
|
||||
* shContest.c: fixed a bug in SHContext destructor where
|
||||
resource deletion loop was iterating image array while
|
||||
freeing paint objects
|
||||
|
||||
03 Feb 2008 Ivan Leben <ivan.leben@gmail.com>
|
||||
|
||||
* a patch by Vincenzo Pupillo fixes array constructor behavior
|
||||
when it runs out of memory plus makes its destructor a bit
|
||||
more robust
|
||||
|
||||
03 Feb 2008 Ivan Leben <ivan.leben@gmail.com>
|
||||
|
||||
* autogen.sh: added --force to autoreconf call
|
||||
|
||||
03 Feb 2008 Ivan Leben <ivan.leben@gmail.com>
|
||||
|
||||
* Removed automake/autoconf generated files. Added autogen.sh
|
||||
to generate them.
|
||||
|
||||
03 Feb 2008 Ivan Leben <ivan.leben@gmail.com>
|
||||
|
||||
* configure.in: changed order of checks for glut library and
|
||||
glXGetProcAddress in -lGL so that latter doesn't fail when
|
||||
glut is not installed.
|
||||
|
||||
30 Jan 2008 Ivan Leben <ivan.leben@gmail.com>
|
||||
|
||||
* shGeometry.c: shStrokePath() fixed to handle zero-length line
|
||||
segments gracefully
|
||||
|
||||
* fixed the image color component ordering to be valid against the
|
||||
spec
|
||||
|
||||
* shImage.c, shImage.h: completely overhauled to support multiple
|
||||
image formats. Everything but _PRE and _BW_1 now supported.
|
||||
|
||||
24 Jan 2008 Ivan Leben <ivan.leben@gmail.com>
|
||||
|
||||
* Moved release-related info from ChangeLog to NEWS. ChangeLog will
|
||||
now include info regarding each commit to repository
|
||||
|
||||
* Rearranged the path tesselletaion and user-to-surface transformation
|
||||
steps in the pipeline to produce higher curve resolution when path is
|
||||
scaled up
|
||||
|
||||
* Using new pipeline design, transformed bound-box query can now return
|
||||
really tight bounds
|
||||
|
||||
* Inserted empty/nop definitions for all non-implemented function so
|
||||
that linking against the library doesn't fail.
|
||||
|
||||
22 Jan 2008 Ivan Leben <ivan.leben@gmail.com>
|
||||
|
||||
* Fixed a bug where vgGetError would always return VG_NO_ERROR
|
||||
@@ -1,80 +0,0 @@
|
||||
shIsValid* functions do a search through array!!!
|
||||
That is required because user handles are actually pointers and need to be found in the valid path array. Solution would be to use indices into an array of pointers for internal handle-to-pointer conversion. When a path is deleted, an empty space would be left in the array and used when the next path is created.
|
||||
|
||||
How to speed up image upload / manipulation
|
||||
=============================================
|
||||
|
||||
1) shCopyPixels uses memcpy
|
||||
|
||||
First, manipulation can be speeded up by modifying shCopyPixels
|
||||
to copy lines using memcpy directly when source and target
|
||||
formats are equal. If stride is same too, than we can memcpy
|
||||
the whole block of memory.
|
||||
|
||||
2) What about mapping image manipulation directly to OpenGL
|
||||
texture manipulation calls? Which formats could support this?
|
||||
|
||||
PROBLEM: if NOPS textures are not supported, then writing
|
||||
and reading the image data back results in a precision loss!
|
||||
Even if PBO available we'd need to gluScaleImage into it.
|
||||
|
||||
--> means: no NOPS, need intermediate buffer anyway
|
||||
|
||||
=== Solution1: PBO are available ====
|
||||
|
||||
Extension required: EXT_pixel_buffer_object (ARB_pixel_buffer_object ?)
|
||||
|
||||
Complexity of implementation: really easy - PBO simply
|
||||
replaces the buffer that would be used if NOPS were not there
|
||||
|
||||
Cannot just glBindBuffer(GL_PIXEL_UNPACK_BUFFER) and then
|
||||
glReadPixels into client memory, because glPixelStore
|
||||
doesn't allow for random row byte size ("stride" must be
|
||||
a multiple of pixel byte size).
|
||||
|
||||
We can safely glMapBuffer and copy from it whatever we want
|
||||
however we want, and do any kind of conversion inbetween.
|
||||
Is glMapBuffer + memcpy into user memory faster than just
|
||||
glGetTexImage? Probably yes, since glGetTexImage probably
|
||||
first downloads the data from GPU anyway.
|
||||
|
||||
glMapBuffer better anyway, because we can directly do the format
|
||||
conversions unsupported by OpenGL (premultiplied to unpremultiplied,
|
||||
grayscale conversion with different per component coefficients
|
||||
instead of simple averaging etc.). We use all the exact same code
|
||||
as when NOPS not supported.
|
||||
|
||||
|
||||
=== Solution2: no PBOs ===
|
||||
|
||||
- vgImageSubData => glTexSubImage2D
|
||||
- vgGetImageSubData => glGetTexImage
|
||||
- vgCopyImage => glGetTexImage, glTexSubImage2D
|
||||
- vgSetPixels => glGetTexImage, glDrawPixels
|
||||
|
||||
(PROBLEM: for glGetTexImage, row length in glPixelStore must
|
||||
be a multiple of pixel byte size!)
|
||||
|
||||
- when copying pixels to/from the texture, we still need to
|
||||
manually clip the working pixel region to the intersection
|
||||
of the source and destination rectangles, since opengl spec
|
||||
says INVALID_VALUE error is generated for invalid regions
|
||||
(e.g. dstX + copyW > dstW)
|
||||
|
||||
|
||||
How to solve great slow-down when scaled up?
|
||||
=============================================
|
||||
|
||||
Reasons:
|
||||
- cpu is subdividing a loooong path
|
||||
- fill-rate is a bad thing
|
||||
|
||||
1. By writing gradient shaders, there would be no need to
|
||||
draw into stencil first and then fill the whole area where stencil
|
||||
odd - at least not when drawing stroke (optimizes half of the pipeline)
|
||||
|
||||
2. Real tesselation would reduce fill rate for filled paths, but does
|
||||
the CPU bottleneck outweight the gain?
|
||||
|
||||
3. Early path discarding (transformed bounds outside surface? maybe
|
||||
early convex-hull rule removal?)
|
||||
@@ -1,38 +0,0 @@
|
||||
version 0.2.0 to 0.2.1 (20 Mar 2009):
|
||||
* Implemented scissoring for 1 scissor rectangle
|
||||
|
||||
version 0.2.0 (20 Jan 2008):
|
||||
* Fixed a bug where rotation in the paint-to-user matrix broke radial
|
||||
gradient fill.
|
||||
|
||||
* vgCreateContextSH interface changed to allow for specification of
|
||||
surface width and height
|
||||
|
||||
* new extension: vgResizeSurfaceSH to be called whenever the surface
|
||||
needs to be resized to match the new window size
|
||||
|
||||
* #define GL_GLEXT_LEGACY in shExtensions.h prior to including gl.h
|
||||
so it doesn't include glext.h automatically, which enables us to
|
||||
check for OpenGL version and define OpenGL extension functions
|
||||
manually.
|
||||
|
||||
* Optimized image uploading by replacing the call to gluBuild2DMipmaps
|
||||
with glTexImage2D and changing the filter used to GL_LINEAR
|
||||
|
||||
* Added detection of ARB_texture_non_power_of_two extension to upload
|
||||
rectangular images directly instead of scaling them and loosing
|
||||
precision
|
||||
|
||||
* Pattern paint implemented
|
||||
|
||||
* Include folder changed to "vg" (lowercase)
|
||||
|
||||
* Added a message to test_blend program to notify the user that it
|
||||
has not been implemented yet (black screen is therefore fine)
|
||||
|
||||
* All the calls to glLoadMatrix replaced with glPushMatrix,
|
||||
glMultMatrix, glPopMatrix.
|
||||
|
||||
* Replaced images used in example programs for royalty-free
|
||||
|
||||
* README file written
|
||||
@@ -1,252 +0,0 @@
|
||||
ShivaVG
|
||||
=============================
|
||||
|
||||
See AUTHORS for the list of contributors
|
||||
|
||||
ShivaVG is an open-source LGPL ANSI C implementation of the Khronos
|
||||
Group OpenVG specification.
|
||||
|
||||
I. BUILD
|
||||
II. TESTING
|
||||
III. IMPLEMENTATION STATUS
|
||||
IV. EXTENSIONS
|
||||
|
||||
|
||||
I. BUILD
|
||||
=============================
|
||||
|
||||
* Prerequisites:
|
||||
|
||||
OpenGL development libraries and headers should be installed.
|
||||
Othe than that, since it's ANSI C should compile with any modern
|
||||
C compiler. jpeglib needs to be installed for example programs
|
||||
that use images.
|
||||
|
||||
* Compiling under UNIX systems:
|
||||
|
||||
Read the INSTALL file for more detailed (though generic) directions.
|
||||
This library uses the standard ./configure ; make. The example
|
||||
programs are automatically compiled. However, compilation of each
|
||||
example program can be toggled by ./configure --with-example-xxx
|
||||
command where xxx denotes the name of the example. Run ./configure
|
||||
--help for a list of such options.
|
||||
|
||||
* Compiling on Mac:
|
||||
|
||||
No XCode project files provided yet. The easiest way is by just
|
||||
using gcc, in which case look under UNIX compiling section.
|
||||
|
||||
* Compiling natively on Windows platform:
|
||||
|
||||
Solution files are provided for Visual C++ version 7 and 8. For
|
||||
the example programs using images to compile, you will need the
|
||||
appropriate build of jpeglib to match your Visual C++ version.
|
||||
|
||||
* Compiling in mingw / cygwin environment:
|
||||
|
||||
Might work just as fine as any UNIX-flavored system, but hasn't
|
||||
been tested yet.
|
||||
|
||||
|
||||
II. TESTING
|
||||
=============================
|
||||
|
||||
There is no real testing suite yet. The example programs are there
|
||||
just to play with what the implementation can currently do, but
|
||||
can hardly provide any proper validation, since no reference images
|
||||
are provided. Here is a description of each example program and
|
||||
what features it highlights:
|
||||
|
||||
* test_vgu
|
||||
|
||||
Constructs some path primitives using the VGU API.
|
||||
|
||||
* test_tiger
|
||||
|
||||
The most simple performance test. It draws the well known svg
|
||||
tiger using just simple stroke and fill of solid colors. It
|
||||
consists of 240 paths.
|
||||
|
||||
* test_dash
|
||||
|
||||
Shows different stroke dashing modes.
|
||||
|
||||
* test_linear
|
||||
|
||||
A rectangle drawn using 3-color linear gradient fill paint
|
||||
|
||||
* test_radial
|
||||
|
||||
A rectangle drawn using 3-color radial gradient fill paint
|
||||
|
||||
* test_interpolate
|
||||
|
||||
Interpolates between two paths - an apple and a pear.
|
||||
|
||||
* test_image
|
||||
|
||||
Images are drawn using VG_DRAW_IMAGE_MULTIPLY image mode to be
|
||||
multiplied with radial gradient fill paint.
|
||||
|
||||
* test_pattern
|
||||
|
||||
An image is drawn in multiply mode with an image pattern fill
|
||||
paint.
|
||||
|
||||
|
||||
III. IMPLEMENTATION STATUS
|
||||
=============================
|
||||
|
||||
Khronos states in the OpenVG specification, that the contexts for all
|
||||
their client APIs are expected to be created via the EGL API. Since
|
||||
EGL to use with ShivaVG has not been implemented yet, there is a set
|
||||
of extension functions provided for the task of creating, maintaining
|
||||
and destroying the OpenVG context. (See next section EXTENSIONS for
|
||||
details.)
|
||||
|
||||
What follows is a description of which functions or to what extent
|
||||
a certain function has been implemented. When a function is marked
|
||||
as PARTIALLY implemented, the TODO file or the comments in the code
|
||||
itself would provide further clues.
|
||||
|
||||
|
||||
* General:
|
||||
|
||||
vgGetError ............................ FULLY implemented
|
||||
vgFlush ............................... FULLY implemented
|
||||
vgFinish .............................. FULLY implemented
|
||||
|
||||
* Getters and setters:
|
||||
|
||||
vgSet ................................. FULLY implemented
|
||||
vgSeti ................................ FULLY implemented
|
||||
vgSetfv ............................... FULLY implemented
|
||||
vgSetiv ............................... FULLY implemented
|
||||
vgGetf ................................ FULLY implemented
|
||||
vgGeti ................................ FULLY implemented
|
||||
vgGetVectorSize ....................... FULLY implemented
|
||||
vgGetfv ............................... FULLY implemented
|
||||
vgGetiv ............................... FULLY implemented
|
||||
vgSetParameterf ....................... FULLY implemented
|
||||
vgSetParameteri ....................... FULLY implemented
|
||||
vgSetParameterfv ...................... FULLY implemented
|
||||
vgSetParameteriv ...................... FULLY implemented
|
||||
vgGetParameterf ....................... FULLY implemented
|
||||
vgGetParameteri ....................... FULLY implemented
|
||||
vgGetParameterVectorSize............... FULLY implemented
|
||||
vgGetParameterfv ...................... FULLY implemented
|
||||
vgGetParameteriv ...................... FULLY implemented
|
||||
|
||||
* Matrix Manipulation:
|
||||
|
||||
vgLoadIdentity ........................ FULLY implemented
|
||||
vgLoadMatrix .......................... FULLY implemented
|
||||
vgGetMatrix ........................... FULLY implemented
|
||||
vgMultMatrix .......................... FULLY implemented
|
||||
vgTranslate ........................... FULLY implemented
|
||||
vgScale ............................... FULLY implemented
|
||||
vgShear ............................... FULLY implemented
|
||||
vgRotate .............................. FULLY implemented
|
||||
|
||||
* Masking and Clearing:
|
||||
|
||||
vgMask ................................ NOT implemented
|
||||
vgClear ............................... FULLY implemented
|
||||
|
||||
* Paths:
|
||||
|
||||
vgCreatePath .......................... FULLY implemented
|
||||
vgClearPath ........................... FULLY implemented
|
||||
vgDestroyPath ......................... FULLY implemented
|
||||
vgRemovePathCapabilities .............. FULLY implemented
|
||||
vgGetPathCapabilities ................. FULLY implemented
|
||||
vgAppendPath .......................... FULLY implemented
|
||||
vgAppendPathData ...................... FULLY implemented
|
||||
vgModifyPathCoords .................... FULLY implemented
|
||||
vgTransformPath ....................... FULLY implemented
|
||||
vgInterpolatePath ..................... FULLY implemented
|
||||
vgPathLength .......................... NOT implemented
|
||||
vgPointAlongPath ...................... NOT implemented
|
||||
vgPathBounds .......................... FULLY implemented
|
||||
vgPathTransformedBounds ............... FULLY implemented
|
||||
vgDrawPath ............................ PARTIALLY implemented
|
||||
|
||||
* Paint:
|
||||
|
||||
vgCreatePaint ......................... FULLY implemented
|
||||
vgDestroyPaint ........................ FULLY implemented
|
||||
vgSetPaint ............................ FULLY implemented
|
||||
vgGetPaint ............................ FULLY implemented
|
||||
vgSetColor ............................ FULLY implemented
|
||||
vgGetColor ............................ FULLY implemented
|
||||
vgPaintPattern ........................ FULLY implemented
|
||||
|
||||
* Images:
|
||||
|
||||
vgCreateImage ......................... PARTIALLY implemented
|
||||
vgDestroyImage ........................ FULLY implemented
|
||||
vgClearImage .......................... FULLY implemented
|
||||
vgImageSubData ........................ PARTIALLY implemented
|
||||
vgGetImageSubData ..................... PARTIALLY implemented
|
||||
vgChildImage .......................... NOT implemented
|
||||
vgGetParent ........................... NOT implemented
|
||||
vgCopyImage ........................... FULLY implemented
|
||||
vgDrawImage ........................... PARTIALLY implemented
|
||||
vgSetPixels ........................... FULLY implemented
|
||||
vgWritePixels ......................... FULLY implemented
|
||||
vgGetPixels ........................... FULLY implemented
|
||||
vgReadPixels .......................... FULLY implemented
|
||||
vgCopyPixels .......................... FULLY implemented
|
||||
|
||||
* Image Filters:
|
||||
|
||||
vgColorMatrix ......................... NOT implemented
|
||||
vgConvolve ............................ NOT implemented
|
||||
vgSeparableConvolve ................... NOT implemented
|
||||
vgGaussianBlur ........................ NOT implemented
|
||||
vgLookup .............................. NOT implemented
|
||||
vgLookupSingle ........................ NOT implemented
|
||||
|
||||
* Hardware Queries:
|
||||
|
||||
vgHardwareQuery ....................... NOT implemented
|
||||
|
||||
* Renderer and Extension Information:
|
||||
|
||||
vgGetString ........................... FULLY implemented
|
||||
|
||||
* VGU
|
||||
|
||||
vguLine ............................... FULLY implemented
|
||||
vguPolygon ............................ FULLY implemented
|
||||
vguRect ............................... FULLY implemented
|
||||
vguRoundRect .......................... FULLY implemented
|
||||
vguEllipse ............................ FULLY implemented
|
||||
vguArc ................................ FULLY implemented
|
||||
vguComputeWarpQuadToSquare ............ NOT implemented
|
||||
vguComputeWarpSquareToQuad ............ NOT implemented
|
||||
vguComputeWarpQuadToQuad .............. NOT implemented
|
||||
|
||||
|
||||
IV. EXTENSIONS
|
||||
=============================
|
||||
|
||||
There are three extensions to the API that manipulate the OpenVG
|
||||
context as a temporary replacement for EGL:
|
||||
|
||||
VGboolean vgCreateContextSH(VGint width, VGint height)
|
||||
|
||||
Creates an OpenVG context on top of an existing OpenGL context
|
||||
that should have been manually initialized by the user of the
|
||||
library. Width and height specify the size of the rendering
|
||||
surface. No multi-threading support has been implemented yet.
|
||||
The context is created once per process.
|
||||
|
||||
void vgResizeSurfaceSH(VGint width, VGint height)
|
||||
|
||||
Should be called whenever the size of the surface changes (e.g.
|
||||
the owner window of the OpenGL context is resized).
|
||||
|
||||
void vgDestroyContextSH()
|
||||
|
||||
Destroys the OpenVG context associated with the calling process.
|
||||
@@ -1,42 +0,0 @@
|
||||
----------
|
||||
Known bugs
|
||||
----------
|
||||
|
||||
- outofmemory in arrays silently ignored
|
||||
- blending doesn't use premultiplied colors (not porter-duff model)
|
||||
- gradient color ramp is not interpolated in linear space
|
||||
- linear and non-linear image formats behave both linearly
|
||||
- radial gradient circle has got a fixed step instead of
|
||||
proportional to its radius
|
||||
|
||||
-------------------------
|
||||
Incomplete functionality:
|
||||
-------------------------
|
||||
|
||||
- non-zero fill mode
|
||||
- proper porter-duff blending
|
||||
- masking
|
||||
- scissoring
|
||||
- child images
|
||||
- VG_DRAW_IMAGE_STENCIL
|
||||
- _PRE and BW_1 image formats
|
||||
- VGU warp matrices
|
||||
- EGL (image drawing targets)
|
||||
- boundbox / point-on-path / tangent-on-path queries
|
||||
- RENDERING_QUALITY_FASTER == RENDERING_QUALITY_BETTER
|
||||
|
||||
---------------------------
|
||||
Possible portability issues
|
||||
---------------------------
|
||||
|
||||
- GL_UNSIGNED_INT_8_8_8_8 and other for images
|
||||
- GL_MIRRORED_REPEAT for gradients
|
||||
- GL_CLAMP_TO_EDGE for gradients and pattern paints
|
||||
- GL_CLAMP_TO_BORDER for pattern paints
|
||||
- multitexturing for VG_DRAW_IMAGE_MULTIPLY, VG_DRAW_IMAGE_STENCIL
|
||||
|
||||
------
|
||||
Bonus:
|
||||
------
|
||||
|
||||
- gradient shaders? antialiasing shader?
|
||||
@@ -1,638 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _OPENVG_H
|
||||
#define _OPENVG_H
|
||||
|
||||
#define OPENVG_VERSION_1_0_1 1
|
||||
|
||||
/* Type definitions */
|
||||
/* TODO: we can't use config.h in this header so there
|
||||
must be some other technique to assert the proper
|
||||
size of the basic types */
|
||||
|
||||
typedef float VGfloat;
|
||||
typedef char VGbyte;
|
||||
typedef unsigned char VGubyte;
|
||||
typedef short VGshort;
|
||||
typedef int VGint;
|
||||
typedef unsigned int VGuint;
|
||||
typedef unsigned int VGbitfield;
|
||||
|
||||
typedef enum {
|
||||
VG_FALSE = 0,
|
||||
VG_TRUE = 1
|
||||
} VGboolean;
|
||||
|
||||
#define VG_MAXSHORT ((VGshort)((~((unsigned)0)) >> 1))
|
||||
#define VG_MAXINT ((VGint)((~((unsigned)0)) >> 1))
|
||||
|
||||
/* Define handle to be of same length as any pointer */
|
||||
|
||||
typedef void * VGHandle;
|
||||
#define VG_INVALID_HANDLE ((VGHandle)NULL)
|
||||
|
||||
/* Enumerations */
|
||||
|
||||
typedef enum {
|
||||
VG_NO_ERROR = 0,
|
||||
VG_BAD_HANDLE_ERROR = 0x1000,
|
||||
VG_ILLEGAL_ARGUMENT_ERROR = 0x1001,
|
||||
VG_OUT_OF_MEMORY_ERROR = 0x1002,
|
||||
VG_PATH_CAPABILITY_ERROR = 0x1003,
|
||||
VG_UNSUPPORTED_IMAGE_FORMAT_ERROR = 0x1004,
|
||||
VG_UNSUPPORTED_PATH_FORMAT_ERROR = 0x1005,
|
||||
VG_IMAGE_IN_USE_ERROR = 0x1006,
|
||||
VG_NO_CONTEXT_ERROR = 0x1007
|
||||
} VGErrorCode;
|
||||
|
||||
typedef enum {
|
||||
/* Mode settings */
|
||||
VG_MATRIX_MODE = 0x1100,
|
||||
VG_FILL_RULE = 0x1101,
|
||||
VG_IMAGE_QUALITY = 0x1102,
|
||||
VG_RENDERING_QUALITY = 0x1103,
|
||||
VG_BLEND_MODE = 0x1104,
|
||||
VG_IMAGE_MODE = 0x1105,
|
||||
|
||||
/* Scissoring rectangles */
|
||||
VG_SCISSOR_RECTS = 0x1106,
|
||||
|
||||
/* Stroke parameters */
|
||||
VG_STROKE_LINE_WIDTH = 0x1110,
|
||||
VG_STROKE_CAP_STYLE = 0x1111,
|
||||
VG_STROKE_JOIN_STYLE = 0x1112,
|
||||
VG_STROKE_MITER_LIMIT = 0x1113,
|
||||
VG_STROKE_DASH_PATTERN = 0x1114,
|
||||
VG_STROKE_DASH_PHASE = 0x1115,
|
||||
VG_STROKE_DASH_PHASE_RESET = 0x1116,
|
||||
|
||||
/* Edge fill color for VG_TILE_FILL tiling mode */
|
||||
VG_TILE_FILL_COLOR = 0x1120,
|
||||
|
||||
/* Color for vgClear */
|
||||
VG_CLEAR_COLOR = 0x1121,
|
||||
|
||||
/* Enable/disable alpha masking and scissoring */
|
||||
VG_MASKING = 0x1130,
|
||||
VG_SCISSORING = 0x1131,
|
||||
|
||||
/* Pixel layout information */
|
||||
VG_PIXEL_LAYOUT = 0x1140,
|
||||
VG_SCREEN_LAYOUT = 0x1141,
|
||||
|
||||
/* Source format selection for image filters */
|
||||
VG_FILTER_FORMAT_LINEAR = 0x1150,
|
||||
VG_FILTER_FORMAT_PREMULTIPLIED = 0x1151,
|
||||
|
||||
/* Destination write enable mask for image filters */
|
||||
VG_FILTER_CHANNEL_MASK = 0x1152,
|
||||
|
||||
/* Implementation limits (read-only) */
|
||||
VG_MAX_SCISSOR_RECTS = 0x1160,
|
||||
VG_MAX_DASH_COUNT = 0x1161,
|
||||
VG_MAX_KERNEL_SIZE = 0x1162,
|
||||
VG_MAX_SEPARABLE_KERNEL_SIZE = 0x1163,
|
||||
VG_MAX_COLOR_RAMP_STOPS = 0x1164,
|
||||
VG_MAX_IMAGE_WIDTH = 0x1165,
|
||||
VG_MAX_IMAGE_HEIGHT = 0x1166,
|
||||
VG_MAX_IMAGE_PIXELS = 0x1167,
|
||||
VG_MAX_IMAGE_BYTES = 0x1168,
|
||||
VG_MAX_FLOAT = 0x1169,
|
||||
VG_MAX_GAUSSIAN_STD_DEVIATION = 0x116A
|
||||
} VGParamType;
|
||||
|
||||
typedef enum {
|
||||
VG_RENDERING_QUALITY_NONANTIALIASED = 0x1200,
|
||||
VG_RENDERING_QUALITY_FASTER = 0x1201,
|
||||
VG_RENDERING_QUALITY_BETTER = 0x1202 /* Default */
|
||||
} VGRenderingQuality;
|
||||
|
||||
typedef enum {
|
||||
VG_PIXEL_LAYOUT_UNKNOWN = 0x1300,
|
||||
VG_PIXEL_LAYOUT_RGB_VERTICAL = 0x1301,
|
||||
VG_PIXEL_LAYOUT_BGR_VERTICAL = 0x1302,
|
||||
VG_PIXEL_LAYOUT_RGB_HORIZONTAL = 0x1303,
|
||||
VG_PIXEL_LAYOUT_BGR_HORIZONTAL = 0x1304
|
||||
} VGPixelLayout;
|
||||
|
||||
typedef enum {
|
||||
VG_MATRIX_PATH_USER_TO_SURFACE = 0x1400,
|
||||
VG_MATRIX_IMAGE_USER_TO_SURFACE = 0x1401,
|
||||
VG_MATRIX_FILL_PAINT_TO_USER = 0x1402,
|
||||
VG_MATRIX_STROKE_PAINT_TO_USER = 0x1403
|
||||
} VGMatrixMode;
|
||||
|
||||
typedef enum {
|
||||
VG_CLEAR_MASK = 0x1500,
|
||||
VG_FILL_MASK = 0x1501,
|
||||
VG_SET_MASK = 0x1502,
|
||||
VG_UNION_MASK = 0x1503,
|
||||
VG_INTERSECT_MASK = 0x1504,
|
||||
VG_SUBTRACT_MASK = 0x1505
|
||||
} VGMaskOperation;
|
||||
|
||||
#define VG_PATH_FORMAT_STANDARD 0
|
||||
|
||||
typedef enum {
|
||||
VG_PATH_DATATYPE_S_8 = 0,
|
||||
VG_PATH_DATATYPE_S_16 = 1,
|
||||
VG_PATH_DATATYPE_S_32 = 2,
|
||||
VG_PATH_DATATYPE_F = 3
|
||||
} VGPathDatatype;
|
||||
|
||||
typedef enum {
|
||||
VG_ABSOLUTE = 0,
|
||||
VG_RELATIVE = 1
|
||||
} VGPathAbsRel;
|
||||
|
||||
typedef enum {
|
||||
VG_CLOSE_PATH = ( 0 << 1),
|
||||
VG_MOVE_TO = ( 1 << 1),
|
||||
VG_LINE_TO = ( 2 << 1),
|
||||
VG_HLINE_TO = ( 3 << 1),
|
||||
VG_VLINE_TO = ( 4 << 1),
|
||||
VG_QUAD_TO = ( 5 << 1),
|
||||
VG_CUBIC_TO = ( 6 << 1),
|
||||
VG_SQUAD_TO = ( 7 << 1),
|
||||
VG_SCUBIC_TO = ( 8 << 1),
|
||||
VG_SCCWARC_TO = ( 9 << 1),
|
||||
VG_SCWARC_TO = (10 << 1),
|
||||
VG_LCCWARC_TO = (11 << 1),
|
||||
VG_LCWARC_TO = (12 << 1)
|
||||
} VGPathSegment;
|
||||
|
||||
typedef enum {
|
||||
VG_MOVE_TO_ABS = VG_MOVE_TO | VG_ABSOLUTE,
|
||||
VG_MOVE_TO_REL = VG_MOVE_TO | VG_RELATIVE,
|
||||
VG_LINE_TO_ABS = VG_LINE_TO | VG_ABSOLUTE,
|
||||
VG_LINE_TO_REL = VG_LINE_TO | VG_RELATIVE,
|
||||
VG_HLINE_TO_ABS = VG_HLINE_TO | VG_ABSOLUTE,
|
||||
VG_HLINE_TO_REL = VG_HLINE_TO | VG_RELATIVE,
|
||||
VG_VLINE_TO_ABS = VG_VLINE_TO | VG_ABSOLUTE,
|
||||
VG_VLINE_TO_REL = VG_VLINE_TO | VG_RELATIVE,
|
||||
VG_QUAD_TO_ABS = VG_QUAD_TO | VG_ABSOLUTE,
|
||||
VG_QUAD_TO_REL = VG_QUAD_TO | VG_RELATIVE,
|
||||
VG_CUBIC_TO_ABS = VG_CUBIC_TO | VG_ABSOLUTE,
|
||||
VG_CUBIC_TO_REL = VG_CUBIC_TO | VG_RELATIVE,
|
||||
VG_SQUAD_TO_ABS = VG_SQUAD_TO | VG_ABSOLUTE,
|
||||
VG_SQUAD_TO_REL = VG_SQUAD_TO | VG_RELATIVE,
|
||||
VG_SCUBIC_TO_ABS = VG_SCUBIC_TO | VG_ABSOLUTE,
|
||||
VG_SCUBIC_TO_REL = VG_SCUBIC_TO | VG_RELATIVE,
|
||||
VG_SCCWARC_TO_ABS = VG_SCCWARC_TO | VG_ABSOLUTE,
|
||||
VG_SCCWARC_TO_REL = VG_SCCWARC_TO | VG_RELATIVE,
|
||||
VG_SCWARC_TO_ABS = VG_SCWARC_TO | VG_ABSOLUTE,
|
||||
VG_SCWARC_TO_REL = VG_SCWARC_TO | VG_RELATIVE,
|
||||
VG_LCCWARC_TO_ABS = VG_LCCWARC_TO | VG_ABSOLUTE,
|
||||
VG_LCCWARC_TO_REL = VG_LCCWARC_TO | VG_RELATIVE,
|
||||
VG_LCWARC_TO_ABS = VG_LCWARC_TO | VG_ABSOLUTE,
|
||||
VG_LCWARC_TO_REL = VG_LCWARC_TO | VG_RELATIVE
|
||||
} VGPathCommand;
|
||||
|
||||
typedef VGHandle VGPath;
|
||||
|
||||
typedef enum {
|
||||
VG_PATH_CAPABILITY_APPEND_FROM = (1 << 0),
|
||||
VG_PATH_CAPABILITY_APPEND_TO = (1 << 1),
|
||||
VG_PATH_CAPABILITY_MODIFY = (1 << 2),
|
||||
VG_PATH_CAPABILITY_TRANSFORM_FROM = (1 << 3),
|
||||
VG_PATH_CAPABILITY_TRANSFORM_TO = (1 << 4),
|
||||
VG_PATH_CAPABILITY_INTERPOLATE_FROM = (1 << 5),
|
||||
VG_PATH_CAPABILITY_INTERPOLATE_TO = (1 << 6),
|
||||
VG_PATH_CAPABILITY_PATH_LENGTH = (1 << 7),
|
||||
VG_PATH_CAPABILITY_POINT_ALONG_PATH = (1 << 8),
|
||||
VG_PATH_CAPABILITY_TANGENT_ALONG_PATH = (1 << 9),
|
||||
VG_PATH_CAPABILITY_PATH_BOUNDS = (1 << 10),
|
||||
VG_PATH_CAPABILITY_PATH_TRANSFORMED_BOUNDS = (1 << 11),
|
||||
VG_PATH_CAPABILITY_ALL = (1 << 12) - 1
|
||||
} VGPathCapabilities;
|
||||
|
||||
typedef enum {
|
||||
VG_PATH_FORMAT = 0x1600,
|
||||
VG_PATH_DATATYPE = 0x1601,
|
||||
VG_PATH_SCALE = 0x1602,
|
||||
VG_PATH_BIAS = 0x1603,
|
||||
VG_PATH_NUM_SEGMENTS = 0x1604,
|
||||
VG_PATH_NUM_COORDS = 0x1605
|
||||
} VGPathParamType;
|
||||
|
||||
typedef enum {
|
||||
VG_CAP_BUTT = 0x1700,
|
||||
VG_CAP_ROUND = 0x1701,
|
||||
VG_CAP_SQUARE = 0x1702
|
||||
} VGCapStyle;
|
||||
|
||||
typedef enum {
|
||||
VG_JOIN_MITER = 0x1800,
|
||||
VG_JOIN_ROUND = 0x1801,
|
||||
VG_JOIN_BEVEL = 0x1802
|
||||
} VGJoinStyle;
|
||||
|
||||
typedef enum {
|
||||
VG_EVEN_ODD = 0x1900,
|
||||
VG_NON_ZERO = 0x1901
|
||||
} VGFillRule;
|
||||
|
||||
typedef enum {
|
||||
VG_STROKE_PATH = (1 << 0),
|
||||
VG_FILL_PATH = (1 << 1)
|
||||
} VGPaintMode;
|
||||
|
||||
typedef VGHandle VGPaint;
|
||||
|
||||
typedef enum {
|
||||
/* Color paint parameters */
|
||||
VG_PAINT_TYPE = 0x1A00,
|
||||
VG_PAINT_COLOR = 0x1A01,
|
||||
VG_PAINT_COLOR_RAMP_SPREAD_MODE = 0x1A02,
|
||||
VG_PAINT_COLOR_RAMP_PREMULTIPLIED = 0x1A07,
|
||||
VG_PAINT_COLOR_RAMP_STOPS = 0x1A03,
|
||||
|
||||
/* Linear gradient paint parameters */
|
||||
VG_PAINT_LINEAR_GRADIENT = 0x1A04,
|
||||
|
||||
/* Radial gradient paint parameters */
|
||||
VG_PAINT_RADIAL_GRADIENT = 0x1A05,
|
||||
|
||||
/* Pattern paint parameters */
|
||||
VG_PAINT_PATTERN_TILING_MODE = 0x1A06
|
||||
} VGPaintParamType;
|
||||
|
||||
typedef enum {
|
||||
VG_PAINT_TYPE_COLOR = 0x1B00,
|
||||
VG_PAINT_TYPE_LINEAR_GRADIENT = 0x1B01,
|
||||
VG_PAINT_TYPE_RADIAL_GRADIENT = 0x1B02,
|
||||
VG_PAINT_TYPE_PATTERN = 0x1B03
|
||||
} VGPaintType;
|
||||
|
||||
typedef enum {
|
||||
VG_COLOR_RAMP_SPREAD_PAD = 0x1C00,
|
||||
VG_COLOR_RAMP_SPREAD_REPEAT = 0x1C01,
|
||||
VG_COLOR_RAMP_SPREAD_REFLECT = 0x1C02
|
||||
} VGColorRampSpreadMode;
|
||||
|
||||
typedef enum {
|
||||
VG_TILE_FILL = 0x1D00,
|
||||
VG_TILE_PAD = 0x1D01,
|
||||
VG_TILE_REPEAT = 0x1D02,
|
||||
VG_TILE_REFLECT = 0x1D03
|
||||
} VGTilingMode;
|
||||
|
||||
typedef enum {
|
||||
/* RGB{A,X} channel ordering */
|
||||
VG_sRGBX_8888 = 0,
|
||||
VG_sRGBA_8888 = 1,
|
||||
VG_sRGBA_8888_PRE = 2,
|
||||
VG_sRGB_565 = 3,
|
||||
VG_sRGBA_5551 = 4,
|
||||
VG_sRGBA_4444 = 5,
|
||||
VG_sL_8 = 6,
|
||||
VG_lRGBX_8888 = 7,
|
||||
VG_lRGBA_8888 = 8,
|
||||
VG_lRGBA_8888_PRE = 9,
|
||||
VG_lL_8 = 10,
|
||||
VG_A_8 = 11,
|
||||
VG_BW_1 = 12,
|
||||
|
||||
/* {A,X}RGB channel ordering */
|
||||
VG_sXRGB_8888 = 0 | (1 << 6),
|
||||
VG_sARGB_8888 = 1 | (1 << 6),
|
||||
VG_sARGB_8888_PRE = 2 | (1 << 6),
|
||||
VG_sARGB_1555 = 4 | (1 << 6),
|
||||
VG_sARGB_4444 = 5 | (1 << 6),
|
||||
VG_lXRGB_8888 = 7 | (1 << 6),
|
||||
VG_lARGB_8888 = 8 | (1 << 6),
|
||||
VG_lARGB_8888_PRE = 9 | (1 << 6),
|
||||
|
||||
/* BGR{A,X} channel ordering */
|
||||
VG_sBGRX_8888 = 0 | (1 << 7),
|
||||
VG_sBGRA_8888 = 1 | (1 << 7),
|
||||
VG_sBGRA_8888_PRE = 2 | (1 << 7),
|
||||
VG_sBGR_565 = 3 | (1 << 7),
|
||||
VG_sBGRA_5551 = 4 | (1 << 7),
|
||||
VG_sBGRA_4444 = 5 | (1 << 7),
|
||||
VG_lBGRX_8888 = 7 | (1 << 7),
|
||||
VG_lBGRA_8888 = 8 | (1 << 7),
|
||||
VG_lBGRA_8888_PRE = 9 | (1 << 7),
|
||||
|
||||
/* {A,X}BGR channel ordering */
|
||||
VG_sXBGR_8888 = 0 | (1 << 6) | (1 << 7),
|
||||
VG_sABGR_8888 = 1 | (1 << 6) | (1 << 7),
|
||||
VG_sABGR_8888_PRE = 2 | (1 << 6) | (1 << 7),
|
||||
VG_sABGR_1555 = 4 | (1 << 6) | (1 << 7),
|
||||
VG_sABGR_4444 = 5 | (1 << 6) | (1 << 7),
|
||||
VG_lXBGR_8888 = 7 | (1 << 6) | (1 << 7),
|
||||
VG_lABGR_8888 = 8 | (1 << 6) | (1 << 7),
|
||||
VG_lABGR_8888_PRE = 9 | (1 << 6) | (1 << 7)
|
||||
} VGImageFormat;
|
||||
|
||||
typedef VGHandle VGImage;
|
||||
|
||||
typedef enum {
|
||||
VG_IMAGE_QUALITY_NONANTIALIASED = (1 << 0),
|
||||
VG_IMAGE_QUALITY_FASTER = (1 << 1),
|
||||
VG_IMAGE_QUALITY_BETTER = (1 << 2)
|
||||
} VGImageQuality;
|
||||
|
||||
typedef enum {
|
||||
VG_IMAGE_FORMAT = 0x1E00,
|
||||
VG_IMAGE_WIDTH = 0x1E01,
|
||||
VG_IMAGE_HEIGHT = 0x1E02
|
||||
} VGImageParamType;
|
||||
|
||||
typedef enum {
|
||||
VG_DRAW_IMAGE_NORMAL = 0x1F00,
|
||||
VG_DRAW_IMAGE_MULTIPLY = 0x1F01,
|
||||
VG_DRAW_IMAGE_STENCIL = 0x1F02
|
||||
} VGImageMode;
|
||||
|
||||
typedef enum {
|
||||
VG_RED = (1 << 3),
|
||||
VG_GREEN = (1 << 2),
|
||||
VG_BLUE = (1 << 1),
|
||||
VG_ALPHA = (1 << 0)
|
||||
} VGImageChannel;
|
||||
|
||||
typedef enum {
|
||||
VG_BLEND_SRC = 0x2000,
|
||||
VG_BLEND_SRC_OVER = 0x2001,
|
||||
VG_BLEND_DST_OVER = 0x2002,
|
||||
VG_BLEND_SRC_IN = 0x2003,
|
||||
VG_BLEND_DST_IN = 0x2004,
|
||||
VG_BLEND_MULTIPLY = 0x2005,
|
||||
VG_BLEND_SCREEN = 0x2006,
|
||||
VG_BLEND_DARKEN = 0x2007,
|
||||
VG_BLEND_LIGHTEN = 0x2008,
|
||||
VG_BLEND_ADDITIVE = 0x2009,
|
||||
VG_BLEND_SRC_OUT_SH = 0x200A,
|
||||
VG_BLEND_DST_OUT_SH = 0x200B,
|
||||
VG_BLEND_SRC_ATOP_SH = 0x200C,
|
||||
VG_BLEND_DST_ATOP_SH = 0x200D
|
||||
} VGBlendMode;
|
||||
|
||||
typedef enum {
|
||||
VG_IMAGE_FORMAT_QUERY = 0x2100,
|
||||
VG_PATH_DATATYPE_QUERY = 0x2101
|
||||
} VGHardwareQueryType;
|
||||
|
||||
typedef enum {
|
||||
VG_HARDWARE_ACCELERATED = 0x2200,
|
||||
VG_HARDWARE_UNACCELERATED = 0x2201
|
||||
} VGHardwareQueryResult;
|
||||
|
||||
typedef enum {
|
||||
VG_VENDOR = 0x2300,
|
||||
VG_RENDERER = 0x2301,
|
||||
VG_VERSION = 0x2302,
|
||||
VG_EXTENSIONS = 0x2303
|
||||
} VGStringID;
|
||||
|
||||
/* Function Prototypes */
|
||||
|
||||
#if 0 // We are including ShivaVG directly into libSimGearScene and only using
|
||||
// inside the library so there is no need for any dll import/export stuff
|
||||
#if defined(_WIN32) || defined(__VC32__)
|
||||
# if defined(VG_API_EXPORT)
|
||||
# define VG_API_CALL __declspec(dllexport)
|
||||
# else
|
||||
# define VG_API_CALL __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define VG_API_CALL extern
|
||||
#endif
|
||||
#else
|
||||
# define VG_API_CALL
|
||||
#endif
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
VG_API_CALL VGErrorCode vgGetError(void);
|
||||
|
||||
VG_API_CALL void vgFlush(void);
|
||||
VG_API_CALL void vgFinish(void);
|
||||
|
||||
/* Getters and Setters */
|
||||
VG_API_CALL void vgSetf (VGParamType type, VGfloat value);
|
||||
VG_API_CALL void vgSeti (VGParamType type, VGint value);
|
||||
VG_API_CALL void vgSetfv(VGParamType type, VGint count,
|
||||
const VGfloat * values);
|
||||
VG_API_CALL void vgSetiv(VGParamType type, VGint count,
|
||||
const VGint * values);
|
||||
|
||||
VG_API_CALL VGfloat vgGetf(VGParamType type);
|
||||
VG_API_CALL VGint vgGeti(VGParamType type);
|
||||
VG_API_CALL VGint vgGetVectorSize(VGParamType type);
|
||||
VG_API_CALL void vgGetfv(VGParamType type, VGint count, VGfloat * values);
|
||||
VG_API_CALL void vgGetiv(VGParamType type, VGint count, VGint * values);
|
||||
|
||||
VG_API_CALL void vgSetParameterf(VGHandle object,
|
||||
VGint paramType,
|
||||
VGfloat value);
|
||||
VG_API_CALL void vgSetParameteri(VGHandle object,
|
||||
VGint paramType,
|
||||
VGint value);
|
||||
VG_API_CALL void vgSetParameterfv(VGHandle object,
|
||||
VGint paramType,
|
||||
VGint count, const VGfloat * values);
|
||||
VG_API_CALL void vgSetParameteriv(VGHandle object,
|
||||
VGint paramType,
|
||||
VGint count, const VGint * values);
|
||||
|
||||
VG_API_CALL VGfloat vgGetParameterf(VGHandle object,
|
||||
VGint paramType);
|
||||
VG_API_CALL VGint vgGetParameteri(VGHandle object,
|
||||
VGint paramType);
|
||||
VG_API_CALL VGint vgGetParameterVectorSize(VGHandle object,
|
||||
VGint paramType);
|
||||
VG_API_CALL void vgGetParameterfv(VGHandle object,
|
||||
VGint paramType,
|
||||
VGint count, VGfloat * values);
|
||||
VG_API_CALL void vgGetParameteriv(VGHandle object,
|
||||
VGint paramType,
|
||||
VGint count, VGint * values);
|
||||
|
||||
/* Matrix Manipulation */
|
||||
VG_API_CALL void vgLoadIdentity(void);
|
||||
VG_API_CALL void vgLoadMatrix(const VGfloat * m);
|
||||
VG_API_CALL void vgGetMatrix(VGfloat * m);
|
||||
VG_API_CALL void vgMultMatrix(const VGfloat * m);
|
||||
VG_API_CALL void vgTranslate(VGfloat tx, VGfloat ty);
|
||||
VG_API_CALL void vgScale(VGfloat sx, VGfloat sy);
|
||||
VG_API_CALL void vgShear(VGfloat shx, VGfloat shy);
|
||||
VG_API_CALL void vgRotate(VGfloat angle);
|
||||
|
||||
/* Masking and Clearing */
|
||||
VG_API_CALL void vgMask(VGImage mask, VGMaskOperation operation,
|
||||
VGint x, VGint y, VGint width, VGint height);
|
||||
VG_API_CALL void vgClear(VGint x, VGint y, VGint width, VGint height);
|
||||
|
||||
/* Paths */
|
||||
VG_API_CALL VGPath vgCreatePath(VGint pathFormat,
|
||||
VGPathDatatype datatype,
|
||||
VGfloat scale, VGfloat bias,
|
||||
VGint segmentCapacityHint,
|
||||
VGint coordCapacityHint,
|
||||
VGbitfield capabilities);
|
||||
VG_API_CALL void vgClearPath(VGPath path, VGbitfield capabilities);
|
||||
VG_API_CALL void vgDestroyPath(VGPath path);
|
||||
VG_API_CALL void vgRemovePathCapabilities(VGPath path,
|
||||
VGbitfield capabilities);
|
||||
VG_API_CALL VGbitfield vgGetPathCapabilities(VGPath path);
|
||||
VG_API_CALL void vgAppendPath(VGPath dstPath, VGPath srcPath);
|
||||
VG_API_CALL void vgAppendPathData(VGPath dstPath,
|
||||
VGint numSegments,
|
||||
const VGubyte * pathSegments,
|
||||
const void * pathData);
|
||||
VG_API_CALL void vgModifyPathCoords(VGPath dstPath, VGint startIndex,
|
||||
VGint numSegments,
|
||||
const void * pathData);
|
||||
VG_API_CALL void vgTransformPath(VGPath dstPath, VGPath srcPath);
|
||||
VG_API_CALL VGboolean vgInterpolatePath(VGPath dstPath,
|
||||
VGPath startPath,
|
||||
VGPath endPath,
|
||||
VGfloat amount);
|
||||
VG_API_CALL VGfloat vgPathLength(VGPath path,
|
||||
VGint startSegment, VGint numSegments);
|
||||
VG_API_CALL void vgPointAlongPath(VGPath path,
|
||||
VGint startSegment, VGint numSegments,
|
||||
VGfloat distance,
|
||||
VGfloat * x, VGfloat * y,
|
||||
VGfloat * tangentX, VGfloat * tangentY);
|
||||
VG_API_CALL void vgPathBounds(VGPath path,
|
||||
VGfloat * minX, VGfloat * minY,
|
||||
VGfloat * width, VGfloat * height);
|
||||
VG_API_CALL void vgPathTransformedBounds(VGPath path,
|
||||
VGfloat * minX, VGfloat * minY,
|
||||
VGfloat * width, VGfloat * height);
|
||||
VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes);
|
||||
|
||||
/* Paint */
|
||||
VG_API_CALL VGPaint vgCreatePaint(void);
|
||||
VG_API_CALL void vgDestroyPaint(VGPaint paint);
|
||||
VG_API_CALL void vgSetPaint(VGPaint paint, VGbitfield paintModes);
|
||||
VG_API_CALL VGPaint vgGetPaint(VGPaintMode paintMode);
|
||||
VG_API_CALL void vgSetColor(VGPaint paint, VGuint rgba);
|
||||
VG_API_CALL VGuint vgGetColor(VGPaint paint);
|
||||
VG_API_CALL void vgPaintPattern(VGPaint paint, VGImage pattern);
|
||||
|
||||
/* Images */
|
||||
VG_API_CALL VGImage vgCreateImage(VGImageFormat format,
|
||||
VGint width, VGint height,
|
||||
VGbitfield allowedQuality);
|
||||
VG_API_CALL void vgDestroyImage(VGImage image);
|
||||
VG_API_CALL void vgClearImage(VGImage image,
|
||||
VGint x, VGint y, VGint width, VGint height);
|
||||
VG_API_CALL void vgImageSubData(VGImage image,
|
||||
const void * data, VGint dataStride,
|
||||
VGImageFormat dataFormat,
|
||||
VGint x, VGint y, VGint width, VGint height);
|
||||
VG_API_CALL void vgGetImageSubData(VGImage image,
|
||||
void * data, VGint dataStride,
|
||||
VGImageFormat dataFormat,
|
||||
VGint x, VGint y,
|
||||
VGint width, VGint height);
|
||||
VG_API_CALL VGImage vgChildImage(VGImage parent,
|
||||
VGint x, VGint y, VGint width, VGint height);
|
||||
VG_API_CALL VGImage vgGetParent(VGImage image);
|
||||
VG_API_CALL void vgCopyImage(VGImage dst, VGint dx, VGint dy,
|
||||
VGImage src, VGint sx, VGint sy,
|
||||
VGint width, VGint height,
|
||||
VGboolean dither);
|
||||
VG_API_CALL void vgDrawImage(VGImage image);
|
||||
VG_API_CALL void vgSetPixels(VGint dx, VGint dy,
|
||||
VGImage src, VGint sx, VGint sy,
|
||||
VGint width, VGint height);
|
||||
VG_API_CALL void vgWritePixels(const void * data, VGint dataStride,
|
||||
VGImageFormat dataFormat,
|
||||
VGint dx, VGint dy,
|
||||
VGint width, VGint height);
|
||||
VG_API_CALL void vgGetPixels(VGImage dst, VGint dx, VGint dy,
|
||||
VGint sx, VGint sy,
|
||||
VGint width, VGint height);
|
||||
VG_API_CALL void vgReadPixels(void * data, VGint dataStride,
|
||||
VGImageFormat dataFormat,
|
||||
VGint sx, VGint sy,
|
||||
VGint width, VGint height);
|
||||
VG_API_CALL void vgCopyPixels(VGint dx, VGint dy,
|
||||
VGint sx, VGint sy,
|
||||
VGint width, VGint height);
|
||||
|
||||
/* Image Filters */
|
||||
VG_API_CALL void vgColorMatrix(VGImage dst, VGImage src,
|
||||
const VGfloat * matrix);
|
||||
VG_API_CALL void vgConvolve(VGImage dst, VGImage src,
|
||||
VGint kernelWidth, VGint kernelHeight,
|
||||
VGint shiftX, VGint shiftY,
|
||||
const VGshort * kernel,
|
||||
VGfloat scale,
|
||||
VGfloat bias,
|
||||
VGTilingMode tilingMode);
|
||||
VG_API_CALL void vgSeparableConvolve(VGImage dst, VGImage src,
|
||||
VGint kernelWidth,
|
||||
VGint kernelHeight,
|
||||
VGint shiftX, VGint shiftY,
|
||||
const VGshort * kernelX,
|
||||
const VGshort * kernelY,
|
||||
VGfloat scale,
|
||||
VGfloat bias,
|
||||
VGTilingMode tilingMode);
|
||||
VG_API_CALL void vgGaussianBlur(VGImage dst, VGImage src,
|
||||
VGfloat stdDeviationX,
|
||||
VGfloat stdDeviationY,
|
||||
VGTilingMode tilingMode);
|
||||
VG_API_CALL void vgLookup(VGImage dst, VGImage src,
|
||||
const VGubyte * redLUT,
|
||||
const VGubyte * greenLUT,
|
||||
const VGubyte * blueLUT,
|
||||
const VGubyte * alphaLUT,
|
||||
VGboolean outputLinear,
|
||||
VGboolean outputPremultiplied);
|
||||
VG_API_CALL void vgLookupSingle(VGImage dst, VGImage src,
|
||||
const VGuint * lookupTable,
|
||||
VGImageChannel sourceChannel,
|
||||
VGboolean outputLinear,
|
||||
VGboolean outputPremultiplied);
|
||||
|
||||
/* Hardware Queries */
|
||||
VG_API_CALL VGHardwareQueryResult vgHardwareQuery(VGHardwareQueryType key,
|
||||
VGint setting);
|
||||
|
||||
/* Renderer and Extension Information */
|
||||
VG_API_CALL const VGubyte * vgGetString(VGStringID name);
|
||||
|
||||
/* Extensions */
|
||||
|
||||
#define OVG_SH_blend_src_out 1
|
||||
#define OVG_SH_blend_dst_out 1
|
||||
#define OVG_SH_blend_src_atop 1
|
||||
#define OVG_SH_blend_dst_atop 1
|
||||
|
||||
VG_API_CALL VGboolean vgCreateContextSH(VGint width, VGint height);
|
||||
VG_API_CALL VGboolean vgHasContextSH();
|
||||
VG_API_CALL void vgResizeSurfaceSH(VGint width, VGint height);
|
||||
VG_API_CALL void vgDestroyContextSH(void);
|
||||
|
||||
|
||||
#if defined (__cplusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif /* _OPENVG_H */
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _VGU_H
|
||||
#define _VGU_H
|
||||
|
||||
#include "openvg.h"
|
||||
|
||||
#define VGU_VERSION_1_0 1
|
||||
#define VGU_VERSION_1_0_1 1
|
||||
|
||||
#ifndef VGU_API_CALL
|
||||
#define VGU_API_CALL VG_API_CALL
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
VGU_NO_ERROR = 0,
|
||||
VGU_BAD_HANDLE_ERROR = 0xF000,
|
||||
VGU_ILLEGAL_ARGUMENT_ERROR = 0xF001,
|
||||
VGU_OUT_OF_MEMORY_ERROR = 0xF002,
|
||||
VGU_PATH_CAPABILITY_ERROR = 0xF003,
|
||||
VGU_BAD_WARP_ERROR = 0xF004
|
||||
} VGUErrorCode;
|
||||
|
||||
typedef enum {
|
||||
VGU_ARC_OPEN = 0xF100,
|
||||
VGU_ARC_CHORD = 0xF101,
|
||||
VGU_ARC_PIE = 0xF102
|
||||
} VGUArcType;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguLine(VGPath path,
|
||||
VGfloat x0, VGfloat y0,
|
||||
VGfloat x1, VGfloat y1);
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguPolygon(VGPath path,
|
||||
const VGfloat * points, VGint count,
|
||||
VGboolean closed);
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguRect(VGPath path,
|
||||
VGfloat x, VGfloat y,
|
||||
VGfloat width, VGfloat height);
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguRoundRect(VGPath path,
|
||||
VGfloat x, VGfloat y,
|
||||
VGfloat width, VGfloat height,
|
||||
VGfloat arcWidth, VGfloat arcHeight);
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguEllipse(VGPath path,
|
||||
VGfloat cx, VGfloat cy,
|
||||
VGfloat width, VGfloat height);
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguArc(VGPath path,
|
||||
VGfloat x, VGfloat y,
|
||||
VGfloat width, VGfloat height,
|
||||
VGfloat startAngle, VGfloat angleExtent,
|
||||
VGUArcType arcType);
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguComputeWarpQuadToSquare(VGfloat sx0, VGfloat sy0,
|
||||
VGfloat sx1, VGfloat sy1,
|
||||
VGfloat sx2, VGfloat sy2,
|
||||
VGfloat sx3, VGfloat sy3,
|
||||
VGfloat * matrix);
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguComputeWarpSquareToQuad(VGfloat dx0, VGfloat dy0,
|
||||
VGfloat dx1, VGfloat dy1,
|
||||
VGfloat dx2, VGfloat dy2,
|
||||
VGfloat dx3, VGfloat dy3,
|
||||
VGfloat * matrix);
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguComputeWarpQuadToQuad(VGfloat dx0, VGfloat dy0,
|
||||
VGfloat dx1, VGfloat dy1,
|
||||
VGfloat dx2, VGfloat dy2,
|
||||
VGfloat dx3, VGfloat dy3,
|
||||
VGfloat sx0, VGfloat sy0,
|
||||
VGfloat sx1, VGfloat sy1,
|
||||
VGfloat sx2, VGfloat sy2,
|
||||
VGfloat sx3, VGfloat sy3,
|
||||
VGfloat * matrix);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* #ifndef _VGU_H */
|
||||
@@ -1,30 +0,0 @@
|
||||
include(SimGearComponent)
|
||||
|
||||
set(HEADERS
|
||||
../include/vg/openvg.h
|
||||
../include/vg/vgu.h
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
shArrays.c
|
||||
shArrays.h
|
||||
shContext.c
|
||||
shContext.h
|
||||
shExtensions.c
|
||||
shExtensions.h
|
||||
shGeometry.c
|
||||
shGeometry.h
|
||||
shImage.c
|
||||
shImage.h
|
||||
shPaint.c
|
||||
shPaint.h
|
||||
shParams.c
|
||||
shPath.c
|
||||
shPath.h
|
||||
shPipeline.c
|
||||
shVectors.c
|
||||
shVectors.h
|
||||
shVgu.c
|
||||
)
|
||||
|
||||
simgear_scene_component(ShivaVG canvas/ShivaVG "${SOURCES}" "${HEADERS}")
|
||||
@@ -1,370 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/*------------------------------------------------------------
|
||||
* The base for any type of array. According to appropriate
|
||||
* macro definitions, specific array types will be generated
|
||||
* and their manipulation functions respectively.
|
||||
*
|
||||
* This code assumes the following are defined:
|
||||
* _ITEM_T: the type of the items in the array
|
||||
* _ARRAY_T: the name of the structure
|
||||
* _FUNC_T: the prefix to prepend to each function
|
||||
*
|
||||
* And at least one of these:
|
||||
* _ARRAY_DECLARE: generate structure declaration
|
||||
* _ARRAY_DEFINE: generate function definitions
|
||||
*-----------------------------------------------------------*/
|
||||
|
||||
#ifndef __SHARRAYBASE_H
|
||||
#define __SHARRAYBASE_H
|
||||
|
||||
#include "shDefs.h"
|
||||
|
||||
#define VAL(x,y) x ## y
|
||||
#define JN(x,y) VAL(x,y)
|
||||
|
||||
#endif
|
||||
|
||||
/*--------------------------------------------
|
||||
* The rest is not #ifndef protected to allow
|
||||
* for various array type definitions.
|
||||
*--------------------------------------------*/
|
||||
|
||||
|
||||
#ifdef _ARRAY_DECLARE
|
||||
typedef struct
|
||||
{
|
||||
_ITEM_T *items;
|
||||
SHint32 capacity;
|
||||
SHint32 size;
|
||||
SHint outofmemory;
|
||||
|
||||
} _ARRAY_T;
|
||||
#endif
|
||||
|
||||
|
||||
void JN(_ARRAY_T,_ctor) (_ARRAY_T *a)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
a->items = (_ITEM_T*)malloc(sizeof(_ITEM_T));
|
||||
|
||||
if (!a->items) {
|
||||
a->outofmemory = 1;
|
||||
a->capacity = 0;
|
||||
a->size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
a->outofmemory = 0;
|
||||
a->capacity = 1;
|
||||
a->size = 0;
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
void JN(_ARRAY_T,_dtor) (_ARRAY_T *a)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
if (a->items) {
|
||||
free(a->items);
|
||||
a->items = NULL;
|
||||
}
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
void JN(_FUNC_T,Clear) (_ARRAY_T *a)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
a->size = 0;
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
/*--------------------------------------------------------
|
||||
* Set the capacity of the array. In case of reallocation
|
||||
* the items are not preserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
int JN(_FUNC_T,Realloc) (_ARRAY_T *a, SHint newsize)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
_ITEM_T *newitems = 0;
|
||||
|
||||
SH_ASSERT(newsize > 0);
|
||||
if (newsize == a->capacity)
|
||||
return 1;
|
||||
|
||||
newitems = (_ITEM_T*)malloc(newsize * sizeof(_ITEM_T));
|
||||
|
||||
if (!newitems) {
|
||||
a->outofmemory = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->items)
|
||||
free(a->items);
|
||||
|
||||
a->outofmemory = 0;
|
||||
a->items = newitems;
|
||||
a->capacity = newsize;
|
||||
a->size = 0;
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
/*------------------------------------------------------
|
||||
* Asserts the capacity is at least [newsize]. In case
|
||||
* of reallocation items are not preserved.
|
||||
*------------------------------------------------------*/
|
||||
|
||||
int JN(_FUNC_T,Reserve) (_ARRAY_T *a, SHint newsize)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
_ITEM_T *newitems = 0;
|
||||
|
||||
SH_ASSERT(newsize >= 0);
|
||||
if (newsize <= a->capacity)
|
||||
return 1;
|
||||
|
||||
newitems = (_ITEM_T*)malloc(newsize * sizeof(_ITEM_T));
|
||||
|
||||
if (!newitems) {
|
||||
a->outofmemory = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->items)
|
||||
free(a->items);
|
||||
|
||||
a->outofmemory = 0;
|
||||
a->items = newitems;
|
||||
a->capacity = newsize;
|
||||
a->size = 0;
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
/*------------------------------------------------------
|
||||
* Asserts the capacity is at least [newsize]. In case
|
||||
* of reallocation items are copied.
|
||||
*------------------------------------------------------*/
|
||||
|
||||
int JN(_FUNC_T,ReserveAndCopy) (_ARRAY_T *a, SHint newsize)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
_ITEM_T *newitems = 0;
|
||||
|
||||
SH_ASSERT(newsize >= 0);
|
||||
if (newsize <= a->capacity)
|
||||
return 1;
|
||||
|
||||
newitems = (_ITEM_T*)realloc(a->items, newsize * sizeof(_ITEM_T));
|
||||
|
||||
if (!newitems) {
|
||||
a->outofmemory = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
a->outofmemory = 0;
|
||||
a->items = newitems;
|
||||
a->capacity = newsize;
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
int JN(_FUNC_T,PushBack) (_ARRAY_T *a, _ITEM_T item)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
if (a->capacity == 0) {
|
||||
JN(_FUNC_T,Realloc)(a, 1);
|
||||
if (a->outofmemory)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->size + 1 > a->capacity)
|
||||
JN(_FUNC_T,ReserveAndCopy)(a, a->capacity*2);
|
||||
|
||||
if (a->outofmemory)
|
||||
return 0;
|
||||
|
||||
a->items[a->size++] = item;
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
int JN(_FUNC_T,PushBackP) (_ARRAY_T *a, _ITEM_T *item)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
if (a->capacity == 0) {
|
||||
JN(_FUNC_T,Realloc)(a, 1);
|
||||
if (a->outofmemory)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->size + 1 > a->capacity)
|
||||
JN(_FUNC_T,ReserveAndCopy)(a, a->capacity*2);
|
||||
|
||||
if (a->outofmemory)
|
||||
return 0;
|
||||
|
||||
a->items[a->size++] = *item;
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
void JN(_FUNC_T,PopBack) (_ARRAY_T *a)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
SH_ASSERT(a->size);
|
||||
--a->size;
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
_ITEM_T JN(_FUNC_T,Front) (_ARRAY_T *a)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
SH_ASSERT(a->size);
|
||||
return a->items[0];
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
_ITEM_T* JN(_FUNC_T,FrontP) (_ARRAY_T *a)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
SH_ASSERT(a->size);
|
||||
return &a->items[0];
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
_ITEM_T JN(_FUNC_T,Back) (_ARRAY_T *a)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
SH_ASSERT(a->size);
|
||||
return a->items[a->size - 1];
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
_ITEM_T* JN(_FUNC_T,BackP) (_ARRAY_T *a)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
SH_ASSERT(a->size);
|
||||
return &a->items[a->size - 1];
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
_ITEM_T JN(_FUNC_T,At) (_ARRAY_T *a, SHint index)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
SH_ASSERT(index >= 0);
|
||||
SH_ASSERT(index < a->size);
|
||||
return a->items[index];
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
_ITEM_T* JN(_FUNC_T,AtP) (_ARRAY_T *a, SHint index)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
SH_ASSERT(index >= 0);
|
||||
SH_ASSERT(index < a->size);
|
||||
return &a->items[index];
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
SHint JN(_FUNC_T,Find) (_ARRAY_T *a, _ITEM_T item)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<a->size; ++i) {
|
||||
#ifdef _COMPARE_T
|
||||
if (_COMPARE_T(a->items[i], item))
|
||||
return i;
|
||||
#else
|
||||
if (a->items[i] == item)
|
||||
return i;
|
||||
#endif
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
void JN(_FUNC_T,RemoveAt) (_ARRAY_T *a, SHint index)
|
||||
#ifdef _ARRAY_DEFINE
|
||||
{
|
||||
int i;
|
||||
SH_ASSERT(index >= 0);
|
||||
SH_ASSERT(index < a->size);
|
||||
for (i=index; i<a->size-1; ++i)
|
||||
a->items[i] = a->items[i+1];
|
||||
a->size--;
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
#undef _ITEM_T
|
||||
#undef _ARRAY_T
|
||||
#undef _FUNC_T
|
||||
#undef _COMPARE_T
|
||||
#undef _ARRAY_DEFINE
|
||||
#undef _ARRAY_DECLARE
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/*--------------------------------------------
|
||||
* Definitions of all the arrays used
|
||||
*--------------------------------------------*/
|
||||
|
||||
#include "shArrays.h"
|
||||
|
||||
#define _ITEM_T SHint
|
||||
#define _ARRAY_T SHIntArray
|
||||
#define _FUNC_T shIntArray
|
||||
#define _ARRAY_DEFINE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
#define _ITEM_T SHuint8
|
||||
#define _ARRAY_T SHUint8Array
|
||||
#define _FUNC_T shUint8Array
|
||||
#define _ARRAY_DEFINE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
#define _ITEM_T SHfloat
|
||||
#define _ARRAY_T SHFloatArray
|
||||
#define _FUNC_T shFloatArray
|
||||
#define _ARRAY_DEFINE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
#define _ITEM_T SHRectangle
|
||||
#define _ARRAY_T SHRectArray
|
||||
#define _FUNC_T shRectArray
|
||||
#define _COMPARE_T(x,y) 1
|
||||
#define _ARRAY_DEFINE
|
||||
#include "shArrayBase.h"
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/*--------------------------------------------
|
||||
* Declarations of all the arrays used
|
||||
*--------------------------------------------*/
|
||||
|
||||
#ifndef __SHARRAYS_H
|
||||
#define __SHARRAYS_H
|
||||
|
||||
#include "shVectors.h"
|
||||
|
||||
|
||||
#define _ITEM_T SHint
|
||||
#define _ARRAY_T SHIntArray
|
||||
#define _FUNC_T shIntArray
|
||||
#define _ARRAY_DECLARE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
#define _ITEM_T SHuint8
|
||||
#define _ARRAY_T SHUint8Array
|
||||
#define _FUNC_T shUint8Array
|
||||
#define _ARRAY_DECLARE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
#define _ITEM_T SHfloat
|
||||
#define _ARRAY_T SHFloatArray
|
||||
#define _FUNC_T shFloatArray
|
||||
#define _ARRAY_DECLARE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
#define _ITEM_T SHRectangle
|
||||
#define _ARRAY_T SHRectArray
|
||||
#define _FUNC_T shRectArray
|
||||
#define _ARRAY_DECLARE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
#endif
|
||||
@@ -1,46 +0,0 @@
|
||||
|
||||
#ifndef __SHCONFIG_H
|
||||
#define __SHCONFIG_H
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Identify the operating system
|
||||
////////////////////////////////////////////////////////////
|
||||
#if defined(_WIN32) || defined(__WIN32__)
|
||||
|
||||
// Windows
|
||||
#define VG_API_WINDOWS
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#elif defined(linux) || defined(__linux)
|
||||
|
||||
// Linux
|
||||
#define VG_API_LINUX
|
||||
|
||||
#elif defined(__APPLE__) || defined(MACOSX) || defined(macintosh) || defined(Macintosh)
|
||||
|
||||
// MacOS
|
||||
#define VG_API_MACOSX
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
|
||||
// FreeBSD
|
||||
#define VG_API_FREEBSD
|
||||
|
||||
#else
|
||||
|
||||
// Unsupported system
|
||||
#error This operating system is not supported by SFML library
|
||||
|
||||
#endif
|
||||
|
||||
// We currently do not support using images (inside paths). If we were going to
|
||||
// use it loading and unloading needs to happen within OpenSceneGraph to handle
|
||||
// synchronization correctly in multithreading mode.
|
||||
#define SH_NO_IMAGE
|
||||
|
||||
#endif // __SHCONFIG_H
|
||||
@@ -1,500 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <vg/openvg.h>
|
||||
#include "shContext.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*-----------------------------------------------------
|
||||
* Simple functions to create a VG context instance
|
||||
* on top of an existing OpenGL context.
|
||||
* TODO: There is no mechanics yet to asure the OpenGL
|
||||
* context exists and to choose which context / window
|
||||
* to bind to.
|
||||
*-----------------------------------------------------*/
|
||||
|
||||
static VGContext *g_context = NULL;
|
||||
|
||||
VG_API_CALL VGboolean vgCreateContextSH(VGint width, VGint height)
|
||||
{
|
||||
/* return if already created */
|
||||
if (g_context) return VG_TRUE;
|
||||
|
||||
/* create new context */
|
||||
SH_NEWOBJ(VGContext, g_context);
|
||||
if (!g_context) return VG_FALSE;
|
||||
|
||||
/* init surface info */
|
||||
g_context->surfaceWidth = width;
|
||||
g_context->surfaceHeight = height;
|
||||
|
||||
/* setup GL projection */
|
||||
/* We handle viewport and projection ourselves...
|
||||
|
||||
glViewport(0,0,width,height);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, width, 0, height, -1, 1);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();*/
|
||||
|
||||
return VG_TRUE;
|
||||
}
|
||||
|
||||
VG_API_CALL VGboolean vgHasContextSH()
|
||||
{
|
||||
return g_context != NULL;
|
||||
}
|
||||
|
||||
VG_API_CALL void vgResizeSurfaceSH(VGint width, VGint height)
|
||||
{
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
/* update surface info */
|
||||
context->surfaceWidth = width;
|
||||
context->surfaceHeight = height;
|
||||
|
||||
/* setup GL projection */
|
||||
glViewport(0,0,width,height);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, width, 0, height, -1, 1);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
VG_API_CALL void vgDestroyContextSH()
|
||||
{
|
||||
/* return if already released */
|
||||
if (!g_context) return;
|
||||
|
||||
/* delete context object */
|
||||
SH_DELETEOBJ(VGContext, g_context);
|
||||
g_context = NULL;
|
||||
}
|
||||
|
||||
VGContext* shGetContext()
|
||||
{
|
||||
SH_ASSERT(g_context);
|
||||
return g_context;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------
|
||||
* VGContext constructor
|
||||
*-----------------------------------------------------*/
|
||||
|
||||
void shLoadExtensions(VGContext *c);
|
||||
|
||||
void VGContext_ctor(VGContext *c)
|
||||
{
|
||||
/* Surface info */
|
||||
c->surfaceWidth = 0;
|
||||
c->surfaceHeight = 0;
|
||||
|
||||
/* GetString info */
|
||||
strncpy(c->vendor, "Ivan Leben", sizeof(c->vendor));
|
||||
strncpy(c->renderer, "ShivaVG 0.1.0", sizeof(c->renderer));
|
||||
strncpy(c->version, "1.0", sizeof(c->version));
|
||||
strncpy(c->extensions, "", sizeof(c->extensions));
|
||||
|
||||
/* Mode settings */
|
||||
c->matrixMode = VG_MATRIX_PATH_USER_TO_SURFACE;
|
||||
c->fillRule = VG_EVEN_ODD;
|
||||
c->imageQuality = VG_IMAGE_QUALITY_FASTER;
|
||||
c->renderingQuality = VG_RENDERING_QUALITY_BETTER;
|
||||
c->blendMode = VG_BLEND_SRC_OVER;
|
||||
c->imageMode = VG_DRAW_IMAGE_NORMAL;
|
||||
|
||||
/* Scissor rectangles */
|
||||
SH_INITOBJ(SHRectArray, c->scissor);
|
||||
c->scissoring = VG_FALSE;
|
||||
c->masking = VG_FALSE;
|
||||
|
||||
/* Stroke parameters */
|
||||
c->strokeLineWidth = 1.0f;
|
||||
c->strokeCapStyle = VG_CAP_BUTT;
|
||||
c->strokeJoinStyle = VG_JOIN_MITER;
|
||||
c->strokeMiterLimit = 4.0f;
|
||||
c->strokeDashPhase = 0.0f;
|
||||
c->strokeDashPhaseReset = VG_FALSE;
|
||||
SH_INITOBJ(SHFloatArray, c->strokeDashPattern);
|
||||
|
||||
/* Edge fill color for vgConvolve and pattern paint */
|
||||
CSET(c->tileFillColor, 0,0,0,0);
|
||||
|
||||
/* Color for vgClear */
|
||||
CSET(c->clearColor, 0,0,0,0);
|
||||
|
||||
/* Color components layout inside pixel */
|
||||
c->pixelLayout = VG_PIXEL_LAYOUT_UNKNOWN;
|
||||
|
||||
/* Source format for image filters */
|
||||
c->filterFormatLinear = VG_FALSE;
|
||||
c->filterFormatPremultiplied = VG_FALSE;
|
||||
c->filterChannelMask = VG_RED|VG_GREEN|VG_BLUE|VG_ALPHA;
|
||||
|
||||
/* Matrices */
|
||||
SH_INITOBJ(SHMatrix3x3, c->pathTransform);
|
||||
SH_INITOBJ(SHMatrix3x3, c->imageTransform);
|
||||
SH_INITOBJ(SHMatrix3x3, c->fillTransform);
|
||||
SH_INITOBJ(SHMatrix3x3, c->strokeTransform);
|
||||
|
||||
/* Paints */
|
||||
c->fillPaint = NULL;
|
||||
c->strokePaint = NULL;
|
||||
SH_INITOBJ(SHPaint, c->defaultPaint);
|
||||
|
||||
/* Error */
|
||||
c->error = VG_NO_ERROR;
|
||||
|
||||
/* Resources */
|
||||
SH_INITOBJ(SHPathArray, c->paths);
|
||||
SH_INITOBJ(SHPaintArray, c->paints);
|
||||
SH_INITOBJ(SHImageArray, c->images);
|
||||
|
||||
shLoadExtensions(c);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------
|
||||
* VGContext constructor
|
||||
*-----------------------------------------------------*/
|
||||
|
||||
void VGContext_dtor(VGContext *c)
|
||||
{
|
||||
int i;
|
||||
|
||||
SH_DEINITOBJ(SHRectArray, c->scissor);
|
||||
SH_DEINITOBJ(SHFloatArray, c->strokeDashPattern);
|
||||
|
||||
/* Destroy resources */
|
||||
for (i=0; i<c->paths.size; ++i)
|
||||
SH_DELETEOBJ(SHPath, c->paths.items[i]);
|
||||
|
||||
for (i=0; i<c->paints.size; ++i)
|
||||
SH_DELETEOBJ(SHPaint, c->paints.items[i]);
|
||||
|
||||
for (i=0; i<c->images.size; ++i)
|
||||
SH_DELETEOBJ(SHImage, c->images.items[i]);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
* Tries to find resources in this context
|
||||
*--------------------------------------------------*/
|
||||
|
||||
SHint shIsValidPath(VGContext *c, VGHandle h)
|
||||
{
|
||||
int index = shPathArrayFind(&c->paths, (SHPath*)h);
|
||||
return (index == -1) ? 0 : 1;
|
||||
}
|
||||
|
||||
SHint shIsValidPaint(VGContext *c, VGHandle h)
|
||||
{
|
||||
int index = shPaintArrayFind(&c->paints, (SHPaint*)h);
|
||||
return (index == -1) ? 0 : 1;
|
||||
}
|
||||
|
||||
SHint shIsValidImage(VGContext *c, VGHandle h)
|
||||
{
|
||||
int index = shImageArrayFind(&c->images, (SHImage*)h);
|
||||
return (index == -1) ? 0 : 1;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
* Tries to find a resources in this context and
|
||||
* return its type or invalid flag.
|
||||
*--------------------------------------------------*/
|
||||
|
||||
SHResourceType shGetResourceType(VGContext *c, VGHandle h)
|
||||
{
|
||||
if (shIsValidPath(c, h))
|
||||
return SH_RESOURCE_PATH;
|
||||
|
||||
else if (shIsValidPaint(c, h))
|
||||
return SH_RESOURCE_PAINT;
|
||||
|
||||
else if (shIsValidImage(c, h))
|
||||
return SH_RESOURCE_IMAGE;
|
||||
|
||||
else
|
||||
return SH_RESOURCE_INVALID;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------
|
||||
* Sets the specified error on the given context if
|
||||
* there is no pending error yet
|
||||
*-----------------------------------------------------*/
|
||||
|
||||
void shSetError(VGContext *c, VGErrorCode e)
|
||||
{
|
||||
if (c->error == VG_NO_ERROR)
|
||||
c->error = e;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
* Returns the oldest error pending on the current
|
||||
* context and clears its error code
|
||||
*--------------------------------------------------*/
|
||||
|
||||
VG_API_CALL VGErrorCode vgGetError(void)
|
||||
{
|
||||
VGErrorCode error;
|
||||
VG_GETCONTEXT(VG_NO_CONTEXT_ERROR);
|
||||
error = context->error;
|
||||
context->error = VG_NO_ERROR;
|
||||
VG_RETURN(error);
|
||||
}
|
||||
|
||||
VG_API_CALL void vgFlush(void)
|
||||
{
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
glFlush();
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
VG_API_CALL void vgFinish(void)
|
||||
{
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
glFinish();
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
VG_API_CALL void vgMask(VGImage mask, VGMaskOperation operation,
|
||||
VGint x, VGint y, VGint width, VGint height)
|
||||
{
|
||||
}
|
||||
|
||||
VG_API_CALL void vgClear(VGint x, VGint y, VGint width, VGint height)
|
||||
{
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
/* Clip to window */
|
||||
if (x < 0) x = 0;
|
||||
if (y < 0) y = 0;
|
||||
if (width > context->surfaceWidth) width = context->surfaceWidth;
|
||||
if (height > context->surfaceHeight) height = context->surfaceHeight;
|
||||
|
||||
/* Check if scissoring needed */
|
||||
if (x > 0 || y > 0 ||
|
||||
width < context->surfaceWidth ||
|
||||
height < context->surfaceHeight) {
|
||||
|
||||
glScissor(x, y, width, height);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
/* Clear GL color buffer */
|
||||
/* TODO: what about stencil and depth? when do we clear that?
|
||||
we would need some kind of special "begin" function at
|
||||
beginning of each drawing or clear the planes prior to each
|
||||
drawing where it takes places */
|
||||
glClearColor(context->clearColor.r,
|
||||
context->clearColor.g,
|
||||
context->clearColor.b,
|
||||
context->clearColor.a);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT |
|
||||
GL_STENCIL_BUFFER_BIT |
|
||||
GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Returns the matrix currently selected via VG_MATRIX_MODE
|
||||
*-----------------------------------------------------------*/
|
||||
|
||||
SHMatrix3x3* shCurrentMatrix(VGContext *c)
|
||||
{
|
||||
switch(c->matrixMode) {
|
||||
case VG_MATRIX_PATH_USER_TO_SURFACE:
|
||||
return &c->pathTransform;
|
||||
case VG_MATRIX_IMAGE_USER_TO_SURFACE:
|
||||
return &c->imageTransform;
|
||||
case VG_MATRIX_FILL_PAINT_TO_USER:
|
||||
return &c->fillTransform;
|
||||
default:
|
||||
return &c->strokeTransform;
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------
|
||||
* Sets the current matrix to identity
|
||||
*--------------------------------------*/
|
||||
|
||||
VG_API_CALL void vgLoadIdentity(void)
|
||||
{
|
||||
SHMatrix3x3 *m;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
m = shCurrentMatrix(context);
|
||||
IDMAT((*m));
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------
|
||||
* Loads values into the current matrix from the given array.
|
||||
* Matrix affinity is preserved if an affine matrix is loaded.
|
||||
*-------------------------------------------------------------*/
|
||||
|
||||
VG_API_CALL void vgLoadMatrix(const VGfloat * mm)
|
||||
{
|
||||
SHMatrix3x3 *m;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
VG_RETURN_ERR_IF(!mm, VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
|
||||
/* TODO: check matrix array alignment */
|
||||
|
||||
m = shCurrentMatrix(context);
|
||||
|
||||
if (context->matrixMode == VG_MATRIX_IMAGE_USER_TO_SURFACE) {
|
||||
|
||||
SETMAT((*m),
|
||||
mm[0], mm[3], mm[6],
|
||||
mm[1], mm[4], mm[7],
|
||||
mm[2], mm[5], mm[8]);
|
||||
}else{
|
||||
|
||||
SETMAT((*m),
|
||||
mm[0], mm[3], mm[6],
|
||||
mm[1], mm[4], mm[7],
|
||||
0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
* Outputs the values of the current matrix into the given array
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
VG_API_CALL void vgGetMatrix(VGfloat * mm)
|
||||
{
|
||||
SHMatrix3x3 *m; int i,j,k=0;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
VG_RETURN_ERR_IF(!mm, VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
|
||||
/* TODO: check matrix array alignment */
|
||||
|
||||
m = shCurrentMatrix(context);
|
||||
|
||||
for (i=0; i<3; ++i)
|
||||
for (j=0; j<3; ++j)
|
||||
mm[k++] = m->m[j][i];
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------
|
||||
* Right-multiplies the current matrix with the one specified
|
||||
* in the given array. Matrix affinity is preserved if an
|
||||
* affine matrix is begin multiplied.
|
||||
*-------------------------------------------------------------*/
|
||||
|
||||
VG_API_CALL void vgMultMatrix(const VGfloat * mm)
|
||||
{
|
||||
SHMatrix3x3 *m, mul, temp;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
VG_RETURN_ERR_IF(!mm, VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
|
||||
/* TODO: check matrix array alignment */
|
||||
|
||||
m = shCurrentMatrix(context);
|
||||
|
||||
if (context->matrixMode == VG_MATRIX_IMAGE_USER_TO_SURFACE) {
|
||||
|
||||
SETMAT(mul,
|
||||
mm[0], mm[3], mm[6],
|
||||
mm[1], mm[4], mm[7],
|
||||
mm[2], mm[5], mm[8]);
|
||||
}else{
|
||||
|
||||
SETMAT(mul,
|
||||
mm[0], mm[3], mm[6],
|
||||
mm[1], mm[4], mm[7],
|
||||
0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
MULMATMAT((*m), mul, temp);
|
||||
SETMATMAT((*m), temp);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
VG_API_CALL void vgTranslate(VGfloat tx, VGfloat ty)
|
||||
{
|
||||
SHMatrix3x3 *m;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
m = shCurrentMatrix(context);
|
||||
TRANSLATEMATR((*m), tx, ty);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
VG_API_CALL void vgScale(VGfloat sx, VGfloat sy)
|
||||
{
|
||||
SHMatrix3x3 *m;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
m = shCurrentMatrix(context);
|
||||
SCALEMATR((*m), sx, sy);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
VG_API_CALL void vgShear(VGfloat shx, VGfloat shy)
|
||||
{
|
||||
SHMatrix3x3 *m;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
m = shCurrentMatrix(context);
|
||||
SHEARMATR((*m), shx, shy);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
VG_API_CALL void vgRotate(VGfloat angle)
|
||||
{
|
||||
SHfloat a;
|
||||
SHMatrix3x3 *m;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
a = SH_DEG2RAD(angle);
|
||||
m = shCurrentMatrix(context);
|
||||
ROTATEMATR((*m), a);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
VG_API_CALL VGHardwareQueryResult vgHardwareQuery(VGHardwareQueryType key,
|
||||
VGint setting)
|
||||
{
|
||||
return VG_HARDWARE_UNACCELERATED;
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SHCONTEXT_H
|
||||
#define __SHCONTEXT_H
|
||||
|
||||
#include "shDefs.h"
|
||||
#include "shVectors.h"
|
||||
#include "shArrays.h"
|
||||
#include "shPath.h"
|
||||
#include "shPaint.h"
|
||||
#include "shImage.h"
|
||||
|
||||
/*------------------------------------------------
|
||||
* VGContext object
|
||||
*------------------------------------------------*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SH_RESOURCE_INVALID = 0,
|
||||
SH_RESOURCE_PATH = 1,
|
||||
SH_RESOURCE_PAINT = 2,
|
||||
SH_RESOURCE_IMAGE = 3
|
||||
} SHResourceType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* Surface info (since no EGL yet) */
|
||||
SHint surfaceWidth;
|
||||
SHint surfaceHeight;
|
||||
|
||||
/* GetString info */
|
||||
char vendor[256];
|
||||
char renderer[256];
|
||||
char version[256];
|
||||
char extensions[256];
|
||||
|
||||
/* Mode settings */
|
||||
VGMatrixMode matrixMode;
|
||||
VGFillRule fillRule;
|
||||
VGImageQuality imageQuality;
|
||||
VGRenderingQuality renderingQuality;
|
||||
VGBlendMode blendMode;
|
||||
VGImageMode imageMode;
|
||||
|
||||
/* Scissor rectangles */
|
||||
SHRectArray scissor;
|
||||
VGboolean scissoring;
|
||||
VGboolean masking;
|
||||
|
||||
/* Stroke parameters */
|
||||
SHfloat strokeLineWidth;
|
||||
VGCapStyle strokeCapStyle;
|
||||
VGJoinStyle strokeJoinStyle;
|
||||
SHfloat strokeMiterLimit;
|
||||
SHFloatArray strokeDashPattern;
|
||||
SHfloat strokeDashPhase;
|
||||
VGboolean strokeDashPhaseReset;
|
||||
|
||||
/* Edge fill color for vgConvolve and pattern paint */
|
||||
SHColor tileFillColor;
|
||||
|
||||
/* Color for vgClear */
|
||||
SHColor clearColor;
|
||||
|
||||
/* Color components layout inside pixel */
|
||||
VGPixelLayout pixelLayout;
|
||||
|
||||
/* Source format for image filters */
|
||||
VGboolean filterFormatLinear;
|
||||
VGboolean filterFormatPremultiplied;
|
||||
VGbitfield filterChannelMask;
|
||||
|
||||
/* Matrices */
|
||||
SHMatrix3x3 pathTransform;
|
||||
SHMatrix3x3 imageTransform;
|
||||
SHMatrix3x3 fillTransform;
|
||||
SHMatrix3x3 strokeTransform;
|
||||
|
||||
/* Paints */
|
||||
SHPaint* fillPaint;
|
||||
SHPaint* strokePaint;
|
||||
SHPaint defaultPaint;
|
||||
|
||||
VGErrorCode error;
|
||||
|
||||
/* Resources */
|
||||
SHPathArray paths;
|
||||
SHPaintArray paints;
|
||||
SHImageArray images;
|
||||
|
||||
/* Pointers to extensions */
|
||||
SHint isGLAvailable_ClampToEdge;
|
||||
SHint isGLAvailable_MirroredRepeat;
|
||||
SHint isGLAvailable_Multitexture;
|
||||
SHint isGLAvailable_TextureNonPowerOfTwo;
|
||||
SH_PGLACTIVETEXTURE pglActiveTexture;
|
||||
SH_PGLMULTITEXCOORD1F pglMultiTexCoord1f;
|
||||
SH_PGLMULTITEXCOORD2F pglMultiTexCoord2f;
|
||||
|
||||
} VGContext;
|
||||
|
||||
void VGContext_ctor(VGContext *c);
|
||||
void VGContext_dtor(VGContext *c);
|
||||
void shSetError(VGContext *c, VGErrorCode e);
|
||||
SHint shIsValidPath(VGContext *c, VGHandle h);
|
||||
SHint shIsValidPaint(VGContext *c, VGHandle h);
|
||||
SHint shIsValidImage(VGContext *c, VGHandle h);
|
||||
SHResourceType shGetResourceType(VGContext *c, VGHandle h);
|
||||
VGContext* shGetContext();
|
||||
|
||||
/*----------------------------------------------------
|
||||
* TODO: Add mutex locking/unlocking to these macros
|
||||
* to assure sequentiallity in multithreading.
|
||||
*----------------------------------------------------*/
|
||||
|
||||
#define VG_NO_RETVAL
|
||||
|
||||
#define VG_GETCONTEXT(RETVAL) \
|
||||
VGContext *context = shGetContext(); \
|
||||
if (!context) return RETVAL;
|
||||
|
||||
#define VG_RETURN(RETVAL) \
|
||||
{ return RETVAL; }
|
||||
|
||||
#define VG_RETURN_ERR(ERRORCODE, RETVAL) \
|
||||
{ shSetError(context,ERRORCODE); return RETVAL; }
|
||||
|
||||
#define VG_RETURN_ERR_IF(COND, ERRORCODE, RETVAL) \
|
||||
{ if (COND) {shSetError(context,ERRORCODE); return RETVAL;} }
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Same macros but no mutex handling - used by sub-functions
|
||||
*-----------------------------------------------------------*/
|
||||
|
||||
#define SH_NO_RETVAL
|
||||
|
||||
#define SH_GETCONTEXT(RETVAL) \
|
||||
VGContext *context = shGetContext(); \
|
||||
if (!context) return RETVAL;
|
||||
|
||||
#define SH_RETURN(RETVAL) \
|
||||
{ return RETVAL; }
|
||||
|
||||
#define SH_RETURN_ERR(ERRORCODE, RETVAL) \
|
||||
{ shSetError(context,ERRORCODE); return RETVAL; }
|
||||
|
||||
#define SH_RETURN_ERR_IF(COND, ERRORCODE, RETVAL) \
|
||||
{ if (COND) {shSetError(context,ERRORCODE); return RETVAL;} }
|
||||
|
||||
|
||||
#endif /* __SHCONTEXT_H */
|
||||
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SHDEFS_H
|
||||
#define __SHDEFS_H
|
||||
|
||||
/* Standard headers */
|
||||
|
||||
#include "shConfig.h"
|
||||
|
||||
#ifdef VG_API_WINDOWS
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
#if !defined(VG_API_MACOSX) && !defined(__FreeBSD__)
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
|
||||
/* Disable VGHandle-pointer conversion warnings since we
|
||||
do deal with it by defining VGHandle properly */
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable:4311)
|
||||
# pragma warning(disable:4312)
|
||||
#endif
|
||||
|
||||
/* Type definitions */
|
||||
|
||||
#if HAVE_INTTYPES_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#else
|
||||
|
||||
#define int8_t char
|
||||
#define uint8_t unsigned char
|
||||
#define int16_t short
|
||||
#define uint16_t unsigned short
|
||||
#define int32_t int
|
||||
#define uint32_t unsigned int
|
||||
#define int64_t long long
|
||||
#define uint64_t unsigned long long
|
||||
|
||||
#endif
|
||||
|
||||
typedef int8_t SHint8;
|
||||
typedef uint8_t SHuint8;
|
||||
typedef int16_t SHint16;
|
||||
typedef uint16_t SHuint16;
|
||||
typedef int32_t SHint32;
|
||||
typedef uint32_t SHuint32;
|
||||
typedef float SHfloat32;
|
||||
|
||||
#define SHint SHint32
|
||||
#define SHuint SHuint32
|
||||
#define SHfloat SHfloat32
|
||||
|
||||
/* Maximum / minimum values */
|
||||
|
||||
#define SH_MAX_INT (0x7fffffff)
|
||||
#define SH_MIN_INT (-0x7fffffff-1)
|
||||
|
||||
#define SH_MANTISSA_BITS 23
|
||||
#define SH_EXPONENT_BITS 8
|
||||
|
||||
/* all 1s in exponent yields NaN in IEEE 754 so we take
|
||||
1 less then maximum representable with exponent bits */
|
||||
#define SH_MAX_EXPONENT ((1 << SH_EXPONENT_BITS) - 2)
|
||||
/* maximum representable with mantissa bits */
|
||||
#define SH_MAX_MANTISSA ((1 << SH_MANTISSA_BITS) - 1)
|
||||
/* compose into IEEE754 floating point bit value */
|
||||
#define SH_MAX_FLOAT_BITS (SH_MAX_EXPONENT << SH_MANTISSA_BITS) | SH_MAX_MANTISSA
|
||||
|
||||
typedef union {
|
||||
float f;
|
||||
unsigned int i;
|
||||
} SHfloatint;
|
||||
|
||||
SHfloat getMaxFloat();
|
||||
|
||||
/* Portable function definitions */
|
||||
|
||||
#define SH_SQRT (float)sqrt
|
||||
#define SH_COS (float)cos
|
||||
#define SH_SIN (float)sin
|
||||
#define SH_ACOS (float)acos
|
||||
#define SH_ASIN (float)asin
|
||||
#define SH_ATAN (float)atan
|
||||
#define SH_FLOOR (float)floor
|
||||
#define SH_CEIL (float)ceil
|
||||
#define SH_ASSERT assert
|
||||
|
||||
#if defined(__isnan) || (defined(__APPLE__) && (__GNUC__ == 3))
|
||||
# define SH_ISNAN __isnan
|
||||
#elif defined(_isnan) || defined(WIN32)
|
||||
# define SH_ISNAN _isnan
|
||||
#else
|
||||
# define SH_ISNAN isnan
|
||||
#endif
|
||||
|
||||
|
||||
/* Helper macros */
|
||||
|
||||
#define PI 3.141592654f
|
||||
#define SH_DEG2RAD(a) (a * PI / 180.0f)
|
||||
#define SH_RAD2DEG(a) (a * 180.0f / PI)
|
||||
#define SH_ABS(a) ((a < 0.0f) ? -a : a)
|
||||
#define SH_MAX(a,b) ((a > b) ? a : b)
|
||||
#define SH_MIN(a,b) ((a < b) ? a : b)
|
||||
#define SH_NEARZERO(a) (a >= -0.0001 && a < 0.0001)
|
||||
#define SH_SWAP(a,b) {SHfloat t=a; a=b; b=t;}
|
||||
#define SH_CLAMP(a,min,max) {if (a<min) a=min; if (a>max) a=max; }
|
||||
|
||||
#define SH_NEWOBJ(type,obj) { obj = (type*)malloc(sizeof(type)); if(obj) type ## _ctor(obj); }
|
||||
#define SH_INITOBJ(type,obj){ type ## _ctor(&obj); }
|
||||
#define SH_DEINITOBJ(type,obj) { type ## _dtor(&obj); }
|
||||
#define SH_DELETEOBJ(type,obj) { if(obj) type ## _dtor(obj); free(obj); }
|
||||
|
||||
/* Implementation limits */
|
||||
|
||||
#define SH_MAX_SCISSOR_RECTS 1
|
||||
#define SH_MAX_DASH_COUNT VG_MAXINT
|
||||
#define SH_MAX_IMAGE_WIDTH VG_MAXINT
|
||||
#define SH_MAX_IMAGE_HEIGHT VG_MAXINT
|
||||
#define SH_MAX_IMAGE_PIXELS VG_MAXINT
|
||||
#define SH_MAX_IMAGE_BYTES VG_MAXINT
|
||||
#define SH_MAX_COLOR_RAMP_STOPS 256
|
||||
|
||||
#define SH_MAX_VERTICES 999999999
|
||||
#define SH_MAX_RECURSE_DEPTH 16
|
||||
|
||||
#define SH_GRADIENT_TEX_SIZE 1024
|
||||
#define SH_GRADIENT_TEX_COORDSIZE 4096 /* 1024 * RGBA */
|
||||
|
||||
/* OpenGL headers */
|
||||
|
||||
#if defined(VG_API_LINUX)
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glx.h>
|
||||
#elif defined(VG_API_MACOSX)
|
||||
#include <OpenGL/gl.h>
|
||||
#elif defined(VG_API_WINDOWS)
|
||||
#include <GL/gl.h>
|
||||
#else
|
||||
#define GL_GLEXT_LEGACY /* don't include glext.h */
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glx.h>
|
||||
#endif
|
||||
|
||||
#include "shExtensions.h"
|
||||
|
||||
|
||||
#endif /* __SHDEFS_H */
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <vg/openvg.h>
|
||||
#include "shDefs.h"
|
||||
#include "shExtensions.h"
|
||||
#include "shContext.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/*-----------------------------------------------------
|
||||
* Extensions check
|
||||
*-----------------------------------------------------*/
|
||||
|
||||
void fallbackActiveTexture(GLenum texture) {
|
||||
}
|
||||
|
||||
void fallbackMultiTexCoord1f(GLenum target, GLfloat x) {
|
||||
glTexCoord1f(x);
|
||||
}
|
||||
|
||||
void fallbackMultiTexCoord2f(GLenum target, GLfloat x, GLfloat y) {
|
||||
glTexCoord2f(x, y);
|
||||
}
|
||||
|
||||
static int checkExtension(const char *extensions, const char *name)
|
||||
{
|
||||
int nlen = (int)strlen(name);
|
||||
int elen = (int)strlen(extensions);
|
||||
const char *e = extensions;
|
||||
SH_ASSERT(nlen > 0);
|
||||
|
||||
while (1) {
|
||||
|
||||
/* Try to find sub-string */
|
||||
e = strstr(e, name);
|
||||
if (e == NULL) return 0;
|
||||
/* Check if last */
|
||||
if (e == extensions + elen - nlen)
|
||||
return 1;
|
||||
/* Check if space follows (avoid same names with a suffix) */
|
||||
if (*(e + nlen) == ' ')
|
||||
return 1;
|
||||
|
||||
e += nlen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef void (*PFVOID)();
|
||||
|
||||
PFVOID shGetProcAddress(const char *name)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return (PFVOID)wglGetProcAddress(name);
|
||||
#elif defined(__APPLE__)
|
||||
/* TODO: Mac OS glGetProcAddress implementation */
|
||||
return (PFVOID)NULL;
|
||||
#else
|
||||
return (PFVOID)glXGetProcAddress((const unsigned char *)name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void shLoadExtensions(VGContext *c)
|
||||
{
|
||||
const char *ext = (const char*)glGetString(GL_EXTENSIONS);
|
||||
|
||||
/* GL_TEXTURE_CLAMP_TO_EDGE */
|
||||
if (checkExtension(ext, "GL_EXT_texture_edge_clamp"))
|
||||
c->isGLAvailable_ClampToEdge = 1;
|
||||
else if (checkExtension(ext, "GL_SGIS_texture_edge_clamp"))
|
||||
c->isGLAvailable_ClampToEdge = 1;
|
||||
else /* Unavailable */
|
||||
c->isGLAvailable_ClampToEdge = 0;
|
||||
|
||||
|
||||
/* GL_TEXTURE_MIRRORED_REPEAT */
|
||||
if (checkExtension(ext, "GL_ARB_texture_mirrored_repeat"))
|
||||
c->isGLAvailable_MirroredRepeat = 1;
|
||||
else if(checkExtension(ext, "GL_IBM_texture_mirrored_repeat"))
|
||||
c->isGLAvailable_MirroredRepeat = 1;
|
||||
else /* Unavailable */
|
||||
c->isGLAvailable_MirroredRepeat = 0;
|
||||
|
||||
|
||||
/* glActiveTexture, glMultiTexCoord1f */
|
||||
if (checkExtension(ext, "GL_ARB_multitexture")) {
|
||||
c->isGLAvailable_Multitexture = 1;
|
||||
|
||||
c->pglActiveTexture = (SH_PGLACTIVETEXTURE)
|
||||
shGetProcAddress("glActiveTextureARB");
|
||||
c->pglMultiTexCoord1f = (SH_PGLMULTITEXCOORD1F)
|
||||
shGetProcAddress("glMultiTexCoord1fARB");
|
||||
c->pglMultiTexCoord2f = (SH_PGLMULTITEXCOORD2F)
|
||||
shGetProcAddress("glMultiTexCoord2fARB");
|
||||
|
||||
if (c->pglActiveTexture == NULL || c->pglMultiTexCoord1f == NULL ||
|
||||
c->pglMultiTexCoord2f == NULL)
|
||||
c->isGLAvailable_Multitexture = 0;
|
||||
|
||||
}else{ /* Unavailable */
|
||||
c->isGLAvailable_Multitexture = 0;
|
||||
c->pglActiveTexture = (SH_PGLACTIVETEXTURE)fallbackActiveTexture;
|
||||
c->pglMultiTexCoord1f = (SH_PGLMULTITEXCOORD1F)fallbackMultiTexCoord1f;
|
||||
c->pglMultiTexCoord2f = (SH_PGLMULTITEXCOORD2F)fallbackMultiTexCoord2f;
|
||||
}
|
||||
|
||||
/* Non-power-of-two textures */
|
||||
if (checkExtension(ext, "GL_ARB_texture_non_power_of_two"))
|
||||
c->isGLAvailable_TextureNonPowerOfTwo = 1;
|
||||
else /* Unavailable */
|
||||
c->isGLAvailable_TextureNonPowerOfTwo = 0;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SHEXTENSIONS_H
|
||||
#define __SHEXTENSIONS_H
|
||||
|
||||
/* Define missing constants and route missing
|
||||
functions to extension pointers */
|
||||
|
||||
#ifndef APIENTRY
|
||||
#define APIENTRY
|
||||
#endif
|
||||
#ifndef APIENTRYP
|
||||
#define APIENTRYP APIENTRY *
|
||||
#endif
|
||||
|
||||
#ifndef GL_VERSION_1_2
|
||||
# define GL_BGRA 0x80E1
|
||||
# define GL_UNSIGNED_SHORT_5_6_5 0x8363
|
||||
# define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
|
||||
# define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
|
||||
# define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
|
||||
# define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
|
||||
# define GL_UNSIGNED_INT_8_8_8_8 0x8035
|
||||
# define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
|
||||
# define GL_CLAMP_TO_EDGE 0x812F
|
||||
#endif
|
||||
|
||||
#ifndef GL_VERSION_1_3
|
||||
# define GL_MULTISAMPLE 0x809D
|
||||
# define GL_TEXTURE0 0x84C0
|
||||
# define GL_TEXTURE1 0x84C1
|
||||
# define GL_CLAMP_TO_BORDER 0x812D
|
||||
# define glActiveTexture context->pglActiveTexture
|
||||
# define glMultiTexCoord1f context->pglMultiTexCoord1f
|
||||
# define glMultiTexCoord2f context->pglMultiTexCoord2f
|
||||
#endif
|
||||
|
||||
#ifndef GL_VERSION_1_4
|
||||
# define GL_MIRRORED_REPEAT 0x8370
|
||||
#endif
|
||||
|
||||
typedef void (APIENTRYP SH_PGLACTIVETEXTURE) (GLenum);
|
||||
typedef void (APIENTRYP SH_PGLMULTITEXCOORD1F) (GLenum, GLfloat);
|
||||
typedef void (APIENTRYP SH_PGLMULTITEXCOORD2F) (GLenum, GLfloat, GLfloat);
|
||||
|
||||
#endif
|
||||
@@ -1,936 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <vg/openvg.h>
|
||||
#include "shContext.h"
|
||||
#include "shGeometry.h"
|
||||
|
||||
|
||||
static int shAddVertex(SHPath *p, SHVertex *v, SHint *contourStart)
|
||||
{
|
||||
/* Assert contour was open */
|
||||
SH_ASSERT((*contourStart) >= 0);
|
||||
|
||||
/* Check vertex limit */
|
||||
if (p->vertices.size >= SH_MAX_VERTICES) return 0;
|
||||
|
||||
/* Add vertex to subdivision */
|
||||
shVertexArrayPushBackP(&p->vertices, v);
|
||||
|
||||
/* Increment contour size. Its stored in
|
||||
the flags of first contour vertex */
|
||||
p->vertices.items[*contourStart].flags++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void shSubrecurseQuad(SHPath *p, SHQuad *quad, SHint *contourStart)
|
||||
{
|
||||
SHVertex v;
|
||||
SHVector2 mid, dif, c1, c2, c3;
|
||||
SHQuad quads[SH_MAX_RECURSE_DEPTH];
|
||||
SHQuad *q, *qleft, *qright;
|
||||
SHint qindex=0;
|
||||
quads[0] = *quad;
|
||||
|
||||
while (qindex >= 0) {
|
||||
|
||||
q = &quads[qindex];
|
||||
|
||||
/* Calculate distance of control point from its
|
||||
counterpart on the line between end points */
|
||||
SET2V(mid, q->p1); ADD2V(mid, q->p3); DIV2(mid, 2);
|
||||
SET2V(dif, q->p2); SUB2V(dif, mid); ABS2(dif);
|
||||
|
||||
/* Cancel if the curve is flat enough */
|
||||
if (dif.x + dif.y <= 1.0f || qindex == SH_MAX_RECURSE_DEPTH-1) {
|
||||
|
||||
/* Add subdivision point */
|
||||
v.point = q->p3; v.flags = 0;
|
||||
if (qindex == 0) return; /* Skip last point */
|
||||
if (!shAddVertex(p, &v, contourStart)) return;
|
||||
--qindex;
|
||||
|
||||
}else{
|
||||
|
||||
/* Left recursion goes on top of stack! */
|
||||
qright = q; qleft = &quads[++qindex];
|
||||
|
||||
/* Subdivide into 2 sub-curves */
|
||||
SET2V(c1, q->p1); ADD2V(c1, q->p2); DIV2(c1, 2);
|
||||
SET2V(c3, q->p2); ADD2V(c3, q->p3); DIV2(c3, 2);
|
||||
SET2V(c2, c1); ADD2V(c2, c3); DIV2(c2, 2);
|
||||
|
||||
/* Add left recursion onto stack */
|
||||
qleft->p1 = q->p1;
|
||||
qleft->p2 = c1;
|
||||
qleft->p3 = c2;
|
||||
|
||||
/* Add right recursion onto stack */
|
||||
qright->p1 = c2;
|
||||
qright->p2 = c3;
|
||||
qright->p3 = q->p3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shSubrecurseCubic(SHPath *p, SHCubic *cubic, SHint *contourStart)
|
||||
{
|
||||
SHVertex v;
|
||||
SHfloat dx1, dy1, dx2, dy2;
|
||||
SHVector2 mm, c1, c2, c3, c4, c5;
|
||||
SHCubic cubics[SH_MAX_RECURSE_DEPTH];
|
||||
SHCubic *c, *cleft, *cright;
|
||||
SHint cindex = 0;
|
||||
cubics[0] = *cubic;
|
||||
|
||||
while (cindex >= 0) {
|
||||
|
||||
c = &cubics[cindex];
|
||||
|
||||
/* Calculate distance of control points from their
|
||||
counterparts on the line between end points */
|
||||
dx1 = 3.0f*c->p2.x - 2.0f*c->p1.x - c->p4.x; dx1 *= dx1;
|
||||
dy1 = 3.0f*c->p2.y - 2.0f*c->p1.y - c->p4.y; dy1 *= dy1;
|
||||
dx2 = 3.0f*c->p3.x - 2.0f*c->p4.x - c->p1.x; dx2 *= dx2;
|
||||
dy2 = 3.0f*c->p3.y - 2.0f*c->p4.y - c->p1.y; dy2 *= dy2;
|
||||
if (dx1 < dx2) dx1 = dx2;
|
||||
if (dy1 < dy2) dy1 = dy2;
|
||||
|
||||
/* Cancel if the curve is flat enough */
|
||||
if (dx1+dy1 <= 1.0 || cindex == SH_MAX_RECURSE_DEPTH-1) {
|
||||
|
||||
/* Add subdivision point */
|
||||
v.point = c->p4; v.flags = 0;
|
||||
if (cindex == 0) return; /* Skip last point */
|
||||
if (!shAddVertex(p, &v, contourStart)) return;
|
||||
--cindex;
|
||||
|
||||
}else{
|
||||
|
||||
/* Left recursion goes on top of stack! */
|
||||
cright = c; cleft = &cubics[++cindex];
|
||||
|
||||
/* Subdivide into 2 sub-curves */
|
||||
SET2V(c1, c->p1); ADD2V(c1, c->p2); DIV2(c1, 2);
|
||||
SET2V(mm, c->p2); ADD2V(mm, c->p3); DIV2(mm, 2);
|
||||
SET2V(c5, c->p3); ADD2V(c5, c->p4); DIV2(c5, 2);
|
||||
|
||||
SET2V(c2, c1); ADD2V(c2, mm); DIV2(c2, 2);
|
||||
SET2V(c4, mm); ADD2V(c4, c5); DIV2(c4, 2);
|
||||
|
||||
SET2V(c3, c2); ADD2V(c3, c4); DIV2(c3, 2);
|
||||
|
||||
/* Add left recursion to stack */
|
||||
cleft->p1 = c->p1;
|
||||
cleft->p2 = c1;
|
||||
cleft->p3 = c2;
|
||||
cleft->p4 = c3;
|
||||
|
||||
/* Add right recursion to stack */
|
||||
cright->p1 = c3;
|
||||
cright->p2 = c4;
|
||||
cright->p3 = c5;
|
||||
cright->p4 = c->p4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shSubrecurseArc(SHPath *p, SHArc *arc,
|
||||
SHVector2 *c,SHVector2 *ux, SHVector2 *uy,
|
||||
SHint *contourStart)
|
||||
{
|
||||
SHVertex v;
|
||||
SHfloat am, cosa, sina, dx, dy;
|
||||
SHVector2 uux, uuy, c1, m;
|
||||
SHArc arcs[SH_MAX_RECURSE_DEPTH];
|
||||
SHArc *a, *aleft, *aright;
|
||||
SHint aindex=0;
|
||||
arcs[0] = *arc;
|
||||
|
||||
while (aindex >= 0) {
|
||||
|
||||
a = &arcs[aindex];
|
||||
|
||||
/* Middle angle and its cos/sin */
|
||||
am = (a->a1 + a->a2)/2;
|
||||
cosa = SH_COS(am);
|
||||
sina = SH_SIN(am);
|
||||
|
||||
/* New point */
|
||||
SET2V(uux, (*ux)); MUL2(uux, cosa);
|
||||
SET2V(uuy, (*uy)); MUL2(uuy, sina);
|
||||
SET2V(c1, (*c)); ADD2V(c1, uux); ADD2V(c1, uuy);
|
||||
|
||||
/* Check distance from linear midpoint */
|
||||
SET2V(m, a->p1); ADD2V(m, a->p2); DIV2(m, 2);
|
||||
dx = c1.x - m.x; dy = c1.y - m.y;
|
||||
if (dx < 0.0f) dx = -dx;
|
||||
if (dy < 0.0f) dy = -dy;
|
||||
|
||||
/* Stop if flat enough */
|
||||
if (dx+dy <= 1.0f || aindex == SH_MAX_RECURSE_DEPTH-1) {
|
||||
|
||||
/* Add middle subdivision point */
|
||||
v.point = c1; v.flags = 0;
|
||||
if (!shAddVertex(p, &v, contourStart)) return;
|
||||
if (aindex == 0) return; /* Skip very last point */
|
||||
|
||||
/* Add end subdivision point */
|
||||
v.point = a->p2; v.flags = 0;
|
||||
if (!shAddVertex(p, &v, contourStart)) return;
|
||||
--aindex;
|
||||
|
||||
}else{
|
||||
|
||||
/* Left subdivision goes on top of stack! */
|
||||
aright = a; aleft = &arcs[++aindex];
|
||||
|
||||
/* Add left recursion to stack */
|
||||
aleft->p1 = a->p1;
|
||||
aleft->a1 = a->a1;
|
||||
aleft->p2 = c1;
|
||||
aleft->a2 = am;
|
||||
|
||||
/* Add right recursion to stack */
|
||||
aright->p1 = c1;
|
||||
aright->a1 = am;
|
||||
aright->p2 = a->p2;
|
||||
aright->a2 = a->a2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shSubdivideSegment(SHPath *p, VGPathSegment segment,
|
||||
VGPathCommand originalCommand,
|
||||
SHfloat *data, void *userData)
|
||||
{
|
||||
SHVertex v;
|
||||
SHint *contourStart = ((SHint**)userData)[0];
|
||||
SHint *surfaceSpace = ((SHint**)userData)[1];
|
||||
SHQuad quad; SHCubic cubic; SHArc arc;
|
||||
SHVector2 c, ux, uy;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
switch (segment)
|
||||
{
|
||||
case VG_MOVE_TO:
|
||||
|
||||
/* Set contour start here */
|
||||
(*contourStart) = p->vertices.size;
|
||||
|
||||
/* First contour vertex */
|
||||
v.point.x = data[2];
|
||||
v.point.y = data[3];
|
||||
v.flags = 0;
|
||||
if (*surfaceSpace)
|
||||
TRANSFORM2(v.point, context->pathTransform);
|
||||
break;
|
||||
|
||||
case VG_CLOSE_PATH:
|
||||
|
||||
/* Last contour vertex */
|
||||
v.point.x = data[2];
|
||||
v.point.y = data[3];
|
||||
v.flags = SH_VERTEX_FLAG_SEGEND | SH_VERTEX_FLAG_CLOSE;
|
||||
if (*surfaceSpace)
|
||||
TRANSFORM2(v.point, context->pathTransform);
|
||||
break;
|
||||
|
||||
case VG_LINE_TO:
|
||||
|
||||
/* Last segment vertex */
|
||||
v.point.x = data[2];
|
||||
v.point.y = data[3];
|
||||
v.flags = SH_VERTEX_FLAG_SEGEND;
|
||||
if (*surfaceSpace)
|
||||
TRANSFORM2(v.point, context->pathTransform);
|
||||
break;
|
||||
|
||||
case VG_QUAD_TO:
|
||||
|
||||
/* Recurse subdivision */
|
||||
SET2(quad.p1, data[0], data[1]);
|
||||
SET2(quad.p2, data[2], data[3]);
|
||||
SET2(quad.p3, data[4], data[5]);
|
||||
if (*surfaceSpace) {
|
||||
TRANSFORM2(quad.p1, context->pathTransform);
|
||||
TRANSFORM2(quad.p2, context->pathTransform);
|
||||
TRANSFORM2(quad.p3, context->pathTransform); }
|
||||
shSubrecurseQuad(p, &quad, contourStart);
|
||||
|
||||
/* Last segment vertex */
|
||||
v.point.x = data[4];
|
||||
v.point.y = data[5];
|
||||
v.flags = SH_VERTEX_FLAG_SEGEND;
|
||||
if (*surfaceSpace)
|
||||
TRANSFORM2(v.point, context->pathTransform);
|
||||
break;
|
||||
|
||||
case VG_CUBIC_TO:
|
||||
|
||||
/* Recurse subdivision */
|
||||
SET2(cubic.p1, data[0], data[1]);
|
||||
SET2(cubic.p2, data[2], data[3]);
|
||||
SET2(cubic.p3, data[4], data[5]);
|
||||
SET2(cubic.p4, data[6], data[7]);
|
||||
if (*surfaceSpace) {
|
||||
TRANSFORM2(cubic.p1, context->pathTransform);
|
||||
TRANSFORM2(cubic.p2, context->pathTransform);
|
||||
TRANSFORM2(cubic.p3, context->pathTransform);
|
||||
TRANSFORM2(cubic.p4, context->pathTransform); }
|
||||
shSubrecurseCubic(p, &cubic, contourStart);
|
||||
|
||||
/* Last segment vertex */
|
||||
v.point.x = data[6];
|
||||
v.point.y = data[7];
|
||||
v.flags = SH_VERTEX_FLAG_SEGEND;
|
||||
if (*surfaceSpace)
|
||||
TRANSFORM2(v.point, context->pathTransform);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
SH_ASSERT(segment==VG_SCWARC_TO || segment==VG_SCCWARC_TO ||
|
||||
segment==VG_LCWARC_TO || segment==VG_LCCWARC_TO);
|
||||
|
||||
/* Recurse subdivision */
|
||||
SET2(arc.p1, data[0], data[1]);
|
||||
SET2(arc.p2, data[10], data[11]);
|
||||
arc.a1 = data[8]; arc.a2 = data[9];
|
||||
SET2(c, data[2], data[3]);
|
||||
SET2(ux, data[4], data[5]);
|
||||
SET2(uy, data[6], data[7]);
|
||||
if (*surfaceSpace) {
|
||||
TRANSFORM2(arc.p1, context->pathTransform);
|
||||
TRANSFORM2(arc.p2, context->pathTransform);
|
||||
TRANSFORM2(c, context->pathTransform);
|
||||
TRANSFORM2DIR(ux, context->pathTransform);
|
||||
TRANSFORM2DIR(uy, context->pathTransform); }
|
||||
shSubrecurseArc(p, &arc, &c, &ux, &uy, contourStart);
|
||||
|
||||
/* Last segment vertex */
|
||||
v.point.x = data[10];
|
||||
v.point.y = data[11];
|
||||
v.flags = SH_VERTEX_FLAG_SEGEND;
|
||||
if (*surfaceSpace) {
|
||||
TRANSFORM2(v.point, context->pathTransform); }
|
||||
break;
|
||||
}
|
||||
|
||||
/* Add subdivision vertex */
|
||||
shAddVertex(p, &v, contourStart);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
* Processes path data by simplfying it and sending
|
||||
* each segment to subdivision callback function
|
||||
*--------------------------------------------------*/
|
||||
|
||||
void shFlattenPath(SHPath *p, SHint surfaceSpace)
|
||||
{
|
||||
SHint contourStart = -1;
|
||||
// SHint surfSpace = surfaceSpace;
|
||||
SHint *userData[2];
|
||||
SHint processFlags =
|
||||
SH_PROCESS_SIMPLIFY_LINES |
|
||||
SH_PROCESS_SIMPLIFY_CURVES |
|
||||
SH_PROCESS_CENTRALIZE_ARCS |
|
||||
SH_PROCESS_REPAIR_ENDS;
|
||||
|
||||
userData[0] = &contourStart;
|
||||
userData[1] = &surfaceSpace;
|
||||
|
||||
shVertexArrayClear(&p->vertices);
|
||||
shProcessPathData(p, processFlags, shSubdivideSegment, userData);
|
||||
}
|
||||
|
||||
/*-------------------------------------------
|
||||
* Adds a rectangle to the path's stroke.
|
||||
*-------------------------------------------*/
|
||||
|
||||
static void shPushStrokeQuad(SHPath *p, SHVector2 *p1, SHVector2 *p2,
|
||||
SHVector2 *p3, SHVector2 *p4)
|
||||
{
|
||||
shVector2ArrayPushBackP(&p->stroke, p1);
|
||||
shVector2ArrayPushBackP(&p->stroke, p2);
|
||||
shVector2ArrayPushBackP(&p->stroke, p3);
|
||||
shVector2ArrayPushBackP(&p->stroke, p3);
|
||||
shVector2ArrayPushBackP(&p->stroke, p4);
|
||||
shVector2ArrayPushBackP(&p->stroke, p1);
|
||||
}
|
||||
|
||||
/*-------------------------------------------
|
||||
* Adds a triangle to the path's stroke.
|
||||
*-------------------------------------------*/
|
||||
|
||||
static void shPushStrokeTri(SHPath *p, SHVector2 *p1,
|
||||
SHVector2 *p2, SHVector2 *p3)
|
||||
{
|
||||
shVector2ArrayPushBackP(&p->stroke, p1);
|
||||
shVector2ArrayPushBackP(&p->stroke, p2);
|
||||
shVector2ArrayPushBackP(&p->stroke, p3);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Adds a miter join to the path's stroke at the given
|
||||
* turn point [c], with the end of the previous segment
|
||||
* outset [o1] and the beginning of the next segment
|
||||
* outset [o2], transiting from tangent [d1] to [d2].
|
||||
*-----------------------------------------------------------*/
|
||||
|
||||
static void shStrokeJoinMiter(SHPath *p, SHVector2 *c,
|
||||
SHVector2 *o1, SHVector2 *d1,
|
||||
SHVector2 *o2, SHVector2 *d2)
|
||||
{
|
||||
/* Init miter top to first point in case lines are colinear */
|
||||
SHVector2 x; SET2V(x,(*o1));
|
||||
|
||||
/* Find intersection of two outer turn edges
|
||||
(lines defined by origin and direction) */
|
||||
shLineLineXsection(o1, d1, o2, d2, &x);
|
||||
|
||||
/* Add a "diamond" quad with top on intersected point
|
||||
and bottom on center of turn (on the line) */
|
||||
shPushStrokeQuad(p, &x, o1, c, o2);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Adds a round join to the path's stroke at the given
|
||||
* turn point [c], with the end of the previous segment
|
||||
* outset [pstart] and the beginning of the next segment
|
||||
* outset [pend], transiting from perpendicular vector
|
||||
* [tstart] to [tend].
|
||||
*-----------------------------------------------------------*/
|
||||
|
||||
static void shStrokeJoinRound(SHPath *p, SHVector2 *c,
|
||||
SHVector2 *pstart, SHVector2 *tstart,
|
||||
SHVector2 *pend, SHVector2 *tend)
|
||||
{
|
||||
SHVector2 p1, p2;
|
||||
SHfloat a, ang, cosa, sina;
|
||||
|
||||
/* Find angle between lines */
|
||||
ang = ANGLE2((*tstart),(*tend));
|
||||
|
||||
/* Begin with start point */
|
||||
SET2V(p1,(*pstart));
|
||||
for (a=0.0f; a<ang; a+=PI/12) {
|
||||
|
||||
/* Rotate perpendicular vector around and
|
||||
find next offset point from center */
|
||||
cosa = SH_COS(-a);
|
||||
sina = SH_SIN(-a);
|
||||
SET2(p2, tstart->x*cosa - tstart->y*sina,
|
||||
tstart->x*sina + tstart->y*cosa);
|
||||
ADD2V(p2, (*c));
|
||||
|
||||
/* Add triangle, save previous */
|
||||
shPushStrokeTri(p, &p1, &p2, c);
|
||||
SET2V(p1, p2);
|
||||
}
|
||||
|
||||
/* Add last triangle */
|
||||
shPushStrokeTri(p, &p1, pend, c);
|
||||
}
|
||||
|
||||
static void shStrokeCapRound(SHPath *p, SHVector2 *c, SHVector2 *t, SHint start)
|
||||
{
|
||||
SHint a;
|
||||
SHfloat ang, cosa, sina;
|
||||
SHVector2 p1, p2;
|
||||
SHint steps = 12;
|
||||
SHVector2 tt;
|
||||
|
||||
/* Revert perpendicular vector if start cap */
|
||||
SET2V(tt, (*t));
|
||||
if (start) MUL2(tt, -1);
|
||||
|
||||
/* Find start point */
|
||||
SET2V(p1, (*c));
|
||||
ADD2V(p1, tt);
|
||||
|
||||
for (a = 1; a<=steps; ++a) {
|
||||
|
||||
/* Rotate perpendicular vector around and
|
||||
find next offset point from center */
|
||||
ang = (SHfloat)a * PI / steps;
|
||||
cosa = SH_COS(-ang);
|
||||
sina = SH_SIN(-ang);
|
||||
SET2(p2, tt.x*cosa - tt.y*sina,
|
||||
tt.x*sina + tt.y*cosa);
|
||||
ADD2V(p2, (*c));
|
||||
|
||||
/* Add triangle, save previous */
|
||||
shPushStrokeTri(p, &p1, &p2, c);
|
||||
SET2V(p1, p2);
|
||||
}
|
||||
}
|
||||
|
||||
static void shStrokeCapSquare(SHPath *p, SHVector2 *c, SHVector2 *t, SHint start)
|
||||
{
|
||||
SHVector2 tt, p1, p2, p3, p4;
|
||||
|
||||
/* Revert perpendicular vector if start cap */
|
||||
SET2V(tt, (*t));
|
||||
if (start) MUL2(tt, -1);
|
||||
|
||||
/* Find four corners of the quad */
|
||||
SET2V(p1, (*c));
|
||||
ADD2V(p1, tt);
|
||||
|
||||
SET2V(p2, p1);
|
||||
ADD2(p2, tt.y, -tt.x);
|
||||
|
||||
SET2V(p3, p2);
|
||||
ADD2(p3, -2*tt.x, -2*tt.y);
|
||||
|
||||
SET2V(p4, p3);
|
||||
ADD2(p4, -tt.y, tt.x);
|
||||
|
||||
shPushStrokeQuad(p, &p1, &p2, &p3, &p4);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Generates stroke of a path according to VGContext state.
|
||||
* Produces quads for every linear subdivision segment or
|
||||
* dash "on" segment, handles line caps and joins.
|
||||
*-----------------------------------------------------------*/
|
||||
|
||||
void shStrokePath(VGContext* c, SHPath *p)
|
||||
{
|
||||
/* Line width and vertex count */
|
||||
SHfloat w = c->strokeLineWidth / 2;
|
||||
SHfloat mlimit = c->strokeMiterLimit;
|
||||
SHint vertsize = p->vertices.size;
|
||||
|
||||
/* Contour state */
|
||||
SHint contourStart = 0;
|
||||
SHint contourLength = 0;
|
||||
SHint start = 0;
|
||||
SHint end = 0;
|
||||
SHint loop = 0;
|
||||
SHint close = 0;
|
||||
SHint segend = 0;
|
||||
|
||||
/* Current vertices */
|
||||
SHint i1=0, i2=0;
|
||||
SHVertex *v1, *v2;
|
||||
SHVector2 *p1, *p2;
|
||||
SHVector2 d, t, dprev, tprev;
|
||||
SHfloat norm, cross, mlength;
|
||||
|
||||
/* Stroke edge points */
|
||||
SHVector2 l1, r1, l2, r2, lprev, rprev;
|
||||
|
||||
/* Dash state */
|
||||
SHint dashIndex = 0;
|
||||
SHfloat dashLength = 0.0f, strokeLength = 0.0f;
|
||||
SHint dashSize = c->strokeDashPattern.size;
|
||||
SHfloat *dashPattern = c->strokeDashPattern.items;
|
||||
SHint dashOn = 1;
|
||||
|
||||
/* Dash edge points */
|
||||
SHVector2 dash1, dash2;
|
||||
SHVector2 dashL1, dashR1;
|
||||
SHVector2 dashL2, dashR2;
|
||||
SHfloat nextDashLength, dashOffset;
|
||||
|
||||
/* Discard odd dash segment */
|
||||
dashSize -= dashSize % 2;
|
||||
|
||||
/* Init previous so compiler doesn't warn
|
||||
for uninitialized usage */
|
||||
SET2(tprev, 0,0); SET2(dprev, 0,0);
|
||||
SET2(lprev, 0,0); SET2(rprev, 0,0);
|
||||
|
||||
|
||||
/* Walk over subdivision vertices */
|
||||
for (i1=0; i1<vertsize; ++i1) {
|
||||
|
||||
if (loop) {
|
||||
/* Start new contour if exists */
|
||||
if (contourStart < vertsize)
|
||||
i1 = contourStart;
|
||||
else break;
|
||||
}
|
||||
|
||||
start = end = loop = close = segend = 0;
|
||||
i2 = i1 + 1;
|
||||
|
||||
if (i1 == contourStart) {
|
||||
/* Contour has started. Get length */
|
||||
contourLength = p->vertices.items[i1].flags;
|
||||
start = 1;
|
||||
}
|
||||
|
||||
if (contourLength <= 1) {
|
||||
/* Discard empty contours. */
|
||||
contourStart = i1 + 1;
|
||||
loop = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
v1 = &p->vertices.items[i1];
|
||||
v2 = &p->vertices.items[i2];
|
||||
|
||||
if (i2 == contourStart + contourLength-1) {
|
||||
/* Contour has ended. Check close */
|
||||
close = v2->flags & SH_VERTEX_FLAG_CLOSE;
|
||||
end = 1;
|
||||
}
|
||||
|
||||
if (i1 == contourStart + contourLength-1) {
|
||||
/* Loop back to first edge. Check close */
|
||||
close = v1->flags & SH_VERTEX_FLAG_CLOSE;
|
||||
i2 = contourStart+1;
|
||||
contourStart = i1 + 1;
|
||||
i1 = i2 - 1;
|
||||
loop = 1;
|
||||
}
|
||||
|
||||
if (!start && !loop) {
|
||||
/* We are inside a contour. Check segment end. */
|
||||
segend = (v1->flags & SH_VERTEX_FLAG_SEGEND);
|
||||
}
|
||||
|
||||
if (dashSize > 0 && start &&
|
||||
(contourStart == 0 || c->strokeDashPhaseReset)) {
|
||||
|
||||
/* Reset pattern phase at contour start */
|
||||
dashLength = -c->strokeDashPhase;
|
||||
strokeLength = 0.0f;
|
||||
dashIndex = 0;
|
||||
dashOn = 1;
|
||||
|
||||
if (dashLength < 0.0f) {
|
||||
/* Consume dash segments forward to reach stroke start */
|
||||
while (dashLength + dashPattern[dashIndex] <= 0.0f) {
|
||||
dashLength += dashPattern[dashIndex];
|
||||
dashIndex = (dashIndex + 1) % dashSize;
|
||||
dashOn = !dashOn; }
|
||||
|
||||
}else if (dashLength > 0.0f) {
|
||||
/* Consume dash segments backward to return to stroke start */
|
||||
dashIndex = dashSize;
|
||||
while (dashLength > 0.0f) {
|
||||
dashIndex = dashIndex ? (dashIndex-1) : (dashSize-1);
|
||||
dashLength -= dashPattern[dashIndex];
|
||||
dashOn = !dashOn; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Subdiv segment vertices and points */
|
||||
v1 = &p->vertices.items[i1];
|
||||
v2 = &p->vertices.items[i2];
|
||||
p1 = &v1->point;
|
||||
p2 = &v2->point;
|
||||
|
||||
/* Direction vector */
|
||||
SET2(d, p2->x-p1->x, p2->y-p1->y);
|
||||
norm = NORM2(d);
|
||||
if (norm == 0.0f) d = dprev;
|
||||
else DIV2(d, norm);
|
||||
|
||||
/* Perpendicular vector */
|
||||
SET2(t, -d.y, d.x);
|
||||
MUL2(t, w);
|
||||
cross = CROSS2(t,tprev);
|
||||
|
||||
/* Left and right edge points */
|
||||
SET2V(l1, (*p1)); ADD2V(l1, t);
|
||||
SET2V(r1, (*p1)); SUB2V(r1, t);
|
||||
SET2V(l2, (*p2)); ADD2V(l2, t);
|
||||
SET2V(r2, (*p2)); SUB2V(r2, t);
|
||||
|
||||
/* Check if join needed */
|
||||
if ((segend || (loop && close)) && dashOn) {
|
||||
|
||||
switch (c->strokeJoinStyle) {
|
||||
case VG_JOIN_ROUND:
|
||||
|
||||
/* Add a round join to stroke */
|
||||
if (cross >= 0.0f)
|
||||
shStrokeJoinRound(p, p1, &lprev, &tprev, &l1, &t);
|
||||
else{
|
||||
SHVector2 _t, _tprev;
|
||||
SET2(_t, -t.x, -t.y);
|
||||
SET2(_tprev, -tprev.x, -tprev.y);
|
||||
shStrokeJoinRound(p, p1, &r1, &_t, &rprev, &_tprev);
|
||||
}
|
||||
|
||||
break;
|
||||
case VG_JOIN_MITER:
|
||||
|
||||
/* Add a miter join to stroke */
|
||||
mlength = 1/SH_COS((ANGLE2(t, tprev))/2);
|
||||
if (mlength <= mlimit) {
|
||||
if (cross > 0.0f)
|
||||
shStrokeJoinMiter(p, p1, &lprev, &dprev, &l1, &d);
|
||||
else if (cross < 0.0f)
|
||||
shStrokeJoinMiter(p, p1, &rprev, &dprev, &r1, &d);
|
||||
break;
|
||||
}/* Else fall down to bevel */
|
||||
|
||||
case VG_JOIN_BEVEL:
|
||||
|
||||
/* Add a bevel join to stroke */
|
||||
if (cross > 0.0f)
|
||||
shPushStrokeTri(p, &l1, &lprev, p1);
|
||||
else if (cross < 0.0f)
|
||||
shPushStrokeTri(p, &r1, &rprev, p1);
|
||||
|
||||
break;
|
||||
}
|
||||
}else if (!start && !loop && dashOn) {
|
||||
|
||||
/* Fill gap with previous (= bevel join) */
|
||||
if (cross > 0.0f)
|
||||
shPushStrokeTri(p, &l1, &lprev, p1);
|
||||
else if (cross < 0.0f)
|
||||
shPushStrokeTri(p, &r1, &rprev, p1);
|
||||
}
|
||||
|
||||
|
||||
/* Apply cap to start of a non-closed contour or
|
||||
if we are dashing and dash segment is on */
|
||||
if ((dashSize == 0 && loop && !close) ||
|
||||
(dashSize > 0 && start && dashOn)) {
|
||||
switch (c->strokeCapStyle) {
|
||||
case VG_CAP_ROUND:
|
||||
shStrokeCapRound(p, p1, &t, 1); break;
|
||||
case VG_CAP_SQUARE:
|
||||
shStrokeCapSquare(p, p1, &t, 1); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (loop)
|
||||
continue;
|
||||
|
||||
/* Handle dashing */
|
||||
if (dashSize > 0) {
|
||||
|
||||
/* Start with beginning of subdiv segment */
|
||||
SET2V(dash1, (*p1)); SET2V(dashL1, l1); SET2V(dashR1, r1);
|
||||
|
||||
do {
|
||||
/* Interpolate point on the current subdiv segment */
|
||||
nextDashLength = dashLength + dashPattern[dashIndex];
|
||||
dashOffset = (nextDashLength - strokeLength) / norm;
|
||||
if (dashOffset > 1.0f) dashOffset = 1;
|
||||
SET2V(dash2, (*p2)); SUB2V(dash2, (*p1));
|
||||
MUL2(dash2, dashOffset); ADD2V(dash2, (*p1));
|
||||
|
||||
/* Left and right edge points */
|
||||
SET2V(dashL2, dash2); ADD2V(dashL2, t);
|
||||
SET2V(dashR2, dash2); SUB2V(dashR2, t);
|
||||
|
||||
/* Add quad for this dash segment */
|
||||
if (dashOn) shPushStrokeQuad(p, &dashL2, &dashL1, &dashR1, &dashR2);
|
||||
|
||||
/* Move to next dash segment if inside this subdiv segment */
|
||||
if (nextDashLength <= strokeLength + norm) {
|
||||
dashIndex = (dashIndex + 1) % dashSize;
|
||||
dashLength = nextDashLength;
|
||||
dashOn = !dashOn;
|
||||
SET2V(dash1, dash2);
|
||||
SET2V(dashL1, dashL2);
|
||||
SET2V(dashR1, dashR2);
|
||||
|
||||
/* Apply cap to dash segment */
|
||||
switch (c->strokeCapStyle) {
|
||||
case VG_CAP_ROUND:
|
||||
shStrokeCapRound(p, &dash1, &t, dashOn); break;
|
||||
case VG_CAP_SQUARE:
|
||||
shStrokeCapSquare(p, &dash1, &t, dashOn); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Consume dash segments until subdiv end met */
|
||||
} while (nextDashLength < strokeLength + norm);
|
||||
|
||||
}else{
|
||||
|
||||
/* Add quad for this line segment */
|
||||
shPushStrokeQuad(p, &l2, &l1, &r1, &r2);
|
||||
}
|
||||
|
||||
|
||||
/* Apply cap to end of a non-closed contour or
|
||||
if we are dashing and dash segment is on */
|
||||
if ((dashSize == 0 && end && !close) ||
|
||||
(dashSize > 0 && end && dashOn)) {
|
||||
switch (c->strokeCapStyle) {
|
||||
case VG_CAP_ROUND:
|
||||
shStrokeCapRound(p, p2, &t, 0); break;
|
||||
case VG_CAP_SQUARE:
|
||||
shStrokeCapSquare(p, p2, &t, 0); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Save previous edge */
|
||||
strokeLength += norm;
|
||||
SET2V(lprev, l2);
|
||||
SET2V(rprev, r2);
|
||||
dprev = d;
|
||||
tprev = t;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------
|
||||
* Transforms the tessellation vertices using the given matrix
|
||||
*-------------------------------------------------------------*/
|
||||
|
||||
void shTransformVertices(SHMatrix3x3 *m, SHPath *p)
|
||||
{
|
||||
SHVector2 *v;
|
||||
int i = 0;
|
||||
|
||||
for (i = p->vertices.size-1; i>=0; --i) {
|
||||
v = (&p->vertices.items[i].point);
|
||||
TRANSFORM2((*v), (*m)); }
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------
|
||||
* Finds the tight bounding box of path's tesselation
|
||||
* vertices. Depends on whether the path had been
|
||||
* tesselated in user or surface space.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
void shFindBoundbox(SHPath *p)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (p->vertices.size == 0) {
|
||||
SET2(p->min, 0,0);
|
||||
SET2(p->max, 0,0);
|
||||
return;
|
||||
}
|
||||
|
||||
p->min.x = p->max.x = p->vertices.items[0].point.x;
|
||||
p->min.y = p->max.y = p->vertices.items[0].point.y;
|
||||
|
||||
for (i=0; i<p->vertices.size; ++i) {
|
||||
|
||||
SHVector2 *v = &p->vertices.items[i].point;
|
||||
if (v->x < p->min.x) p->min.x = v->x;
|
||||
if (v->x > p->max.x) p->max.x = v->x;
|
||||
if (v->y < p->min.y) p->min.y = v->y;
|
||||
if (v->y > p->max.y) p->max.y = v->y;
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------
|
||||
* Outputs a tight bounding box of a path in path's own
|
||||
* coordinate system.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
VG_API_CALL void vgPathBounds(VGPath path,
|
||||
VGfloat * minX, VGfloat * minY,
|
||||
VGfloat * width, VGfloat * height)
|
||||
{
|
||||
SHPath *p = NULL;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
VG_RETURN_ERR_IF(!shIsValidPath(context, path),
|
||||
VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
|
||||
|
||||
VG_RETURN_ERR_IF(minX == NULL || minY == NULL ||
|
||||
width == NULL || height == NULL,
|
||||
VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
|
||||
|
||||
/* TODO: check output pointer alignment */
|
||||
|
||||
p = (SHPath*)path;
|
||||
VG_RETURN_ERR_IF(!(p->caps & VG_PATH_CAPABILITY_PATH_BOUNDS),
|
||||
VG_PATH_CAPABILITY_ERROR, VG_NO_RETVAL);
|
||||
|
||||
/* Update path geometry */
|
||||
shFlattenPath(p, 0);
|
||||
shFindBoundbox(p);
|
||||
|
||||
/* Output bounds */
|
||||
*minX = p->min.x;
|
||||
*minY = p->min.y;
|
||||
*width = p->max.x - p->min.x;
|
||||
*height = p->max.y - p->min.y;
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------
|
||||
* Outputs a bounding box of a path defined by its control
|
||||
* points that is guaranteed to enclose the path geometry
|
||||
* after applying the current path-user-to-surface transform
|
||||
*------------------------------------------------------------*/
|
||||
|
||||
VG_API_CALL void vgPathTransformedBounds(VGPath path,
|
||||
VGfloat * minX, VGfloat * minY,
|
||||
VGfloat * width, VGfloat * height)
|
||||
{
|
||||
SHPath *p = NULL;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
VG_RETURN_ERR_IF(!shIsValidPath(context, path),
|
||||
VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
|
||||
|
||||
VG_RETURN_ERR_IF(minX == NULL || minY == NULL ||
|
||||
width == NULL || height == NULL,
|
||||
VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
|
||||
|
||||
/* TODO: check output pointer alignment */
|
||||
|
||||
p = (SHPath*)path;
|
||||
VG_RETURN_ERR_IF(!(p->caps & VG_PATH_CAPABILITY_PATH_BOUNDS),
|
||||
VG_PATH_CAPABILITY_ERROR, VG_NO_RETVAL);
|
||||
|
||||
/* Update path geometry */
|
||||
shFlattenPath(p, 1);
|
||||
shFindBoundbox(p);
|
||||
|
||||
/* Output bounds */
|
||||
*minX = p->min.x;
|
||||
*minY = p->min.y;
|
||||
*width = p->max.x - p->min.x;
|
||||
*height = p->max.y - p->min.y;
|
||||
|
||||
/* Invalidate subdivision for rendering */
|
||||
p->cacheDataValid = VG_FALSE;
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
VG_API_CALL VGfloat vgPathLength(VGPath path,
|
||||
VGint startSegment, VGint numSegments)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
VG_API_CALL void vgPointAlongPath(VGPath path,
|
||||
VGint startSegment, VGint numSegments,
|
||||
VGfloat distance,
|
||||
VGfloat * x, VGfloat * y,
|
||||
VGfloat * tangentX, VGfloat * tangentY)
|
||||
{
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SH_GEOMETRY_H
|
||||
|
||||
#include "shDefs.h"
|
||||
#include "shContext.h"
|
||||
#include "shVectors.h"
|
||||
#include "shPath.h"
|
||||
|
||||
void shFlattenPath(SHPath *p, SHint surfaceSpace);
|
||||
void shStrokePath(VGContext* c, SHPath *p);
|
||||
void shTransformVertices(SHMatrix3x3 *m, SHPath *p);
|
||||
void shFindBoundbox(SHPath *p);
|
||||
|
||||
#endif /* __SH_GEOMETRY_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SHIMAGE_H
|
||||
#define __SHIMAGE_H
|
||||
|
||||
#include "shDefs.h"
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* ColorFormat holds the data necessary to pack/unpack color
|
||||
* components from a pixel of each supported image format
|
||||
*-----------------------------------------------------------*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
VGImageFormat vgformat;
|
||||
SHuint8 bytes;
|
||||
|
||||
SHuint32 rmask;
|
||||
SHuint8 rshift;
|
||||
SHuint8 rmax;
|
||||
|
||||
SHuint32 gmask;
|
||||
SHuint8 gshift;
|
||||
SHuint8 gmax;
|
||||
|
||||
SHuint32 bmask;
|
||||
SHuint8 bshift;
|
||||
SHuint8 bmax;
|
||||
|
||||
SHuint32 amask;
|
||||
SHuint8 ashift;
|
||||
SHuint8 amax;
|
||||
|
||||
GLenum glintformat;
|
||||
GLenum glformat;
|
||||
GLenum gltype;
|
||||
|
||||
} SHImageFormatDesc;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SHfloat r,g,b,a;
|
||||
|
||||
} SHColor;
|
||||
|
||||
void SHColor_ctor(SHColor *c);
|
||||
void SHColor_dtor(SHColor *c);
|
||||
|
||||
#define _ITEM_T SHColor
|
||||
#define _ARRAY_T SHColorArray
|
||||
#define _FUNC_T shColorArray
|
||||
#define _ARRAY_DECLARE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SHuint8 *data;
|
||||
SHint width;
|
||||
SHint height;
|
||||
SHImageFormatDesc fd;
|
||||
|
||||
SHint texwidth;
|
||||
SHint texheight;
|
||||
SHfloat texwidthK;
|
||||
SHfloat texheightK;
|
||||
GLuint texture;
|
||||
|
||||
} SHImage;
|
||||
|
||||
void SHImage_ctor(SHImage *i);
|
||||
void SHImage_dtor(SHImage *i);
|
||||
|
||||
#define _ITEM_T SHImage*
|
||||
#define _ARRAY_T SHImageArray
|
||||
#define _FUNC_T shImageArray
|
||||
#define _ARRAY_DECLARE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
/*-------------------------------------------------------
|
||||
* Color operators
|
||||
*-------------------------------------------------------*/
|
||||
|
||||
#define CSET(c, rr,gg,bb,aa) { c.r=rr; c.g=gg; c.b=bb; c.a=aa; }
|
||||
#define CSETC(c1, c2) { c1.r=c2.r; c1.g=c2.g; c1.b=c2.b; c1.a=c2.a; }
|
||||
|
||||
#define CSUB(c1, rr,gg,bb,aa) { c.r-=rr; c.g-=gg; c.b-=bb; c.a-=aa; }
|
||||
#define CSUBC(c1, c2) { c1.r-=c2.r; c1.g-=c2.g; c1.b-=c2.b; c1.a-=c2.a; }
|
||||
#define CSUBCTO(c1, c2, c3) { c3.r=c1.r-c2.r; c3.g=c1.g-c2.g; c3.b=c1.b-c2.b; c3.a=c1.a-c2.a; }
|
||||
|
||||
#define CADD(c1, rr,gg,bb,aa) { c.r+=rr; c.g+=gg; c.b+=bb; c.a+=aa; }
|
||||
#define CADDC(c1, c2) { c1.r+=c2.r; c1.g+=c2.g; c1.b+=c2.b; c1.a+=c2.a; }
|
||||
#define CADDTO(c1, c2, c3) { c3.r=c1.r+c2.r; c3.g=c1.g+c2.g; c3.b=c1.b+c2.b; c3.a=c1.a+c2.a; }
|
||||
#define CADDCK(c1, c2, k) { c1.r+=k*c2.r; c1.g+=k*c2.g; c1.b+=k*c2.b; c1.a+=k*c2.a; }
|
||||
|
||||
#define CMUL(c, s) { c.r*=s; c.g*=s; c.b*=s; c.a*=s; }
|
||||
#define CDIV(c, s) { c.r/=s; c.g/=s; c.b/=s; c.a/=s; }
|
||||
|
||||
#define CPREMUL(c) { c.r*=c.a; c.g*=c.a; c.b*=c.a; }
|
||||
#define CUNPREMUL(c) { c.r/=c.a; c.g/=c.a; c.b/=c.a; }
|
||||
|
||||
/*-------------------------------------------------------
|
||||
* Color-to-memory functions
|
||||
*-------------------------------------------------------*/
|
||||
|
||||
#define CSTORE_RGBA1D_8(c, rgba, x) { \
|
||||
rgba[x*4+0] = (SHuint8)SH_FLOOR(c.r * 255); \
|
||||
rgba[x*4+1] = (SHuint8)SH_FLOOR(c.g * 255); \
|
||||
rgba[x*4+2] = (SHuint8)SH_FLOOR(c.b * 255); \
|
||||
rgba[x*4+3] = (SHuint8)SH_FLOOR(c.a * 255); }
|
||||
|
||||
#define CSTORE_RGBA1D_F(c, rgba, x) { \
|
||||
rgba[x*4+0] = c.r; \
|
||||
rgba[x*4+1] = c.g; \
|
||||
rgba[x*4+2] = c.b; \
|
||||
rgba[x*4+3] = c.a; }
|
||||
|
||||
#define CSTORE_RGBA2D_8(c, rgba, x, y, width) { \
|
||||
rgba[y*width*4+x*4+0] = (SHuint8)SH_FLOOR(c.r * 255); \
|
||||
rgba[y*width*4+x*4+1] = (SHuint8)SH_FLOOR(c.g * 255); \
|
||||
rgba[y*width*4+x*4+2] = (SHuint8)SH_FLOOR(c.b * 255); \
|
||||
rgba[y*width*4+x*4+3] = (SHuint8)SH_FLOOR(c.a * 255); }
|
||||
|
||||
#define CSTORE_RGBA2D_F(c, rgba, x, y, width) { \
|
||||
rgba[y*width*4+x*4+0] = c.r; \
|
||||
rgba[y*width*4+x*4+1] = c.g; \
|
||||
rgba[y*width*4+x*4+2] = c.b; \
|
||||
rgba[y*width*4+x*4+3] = c.a; }
|
||||
|
||||
#define INT2COLCOORD(i, max) ( (SHfloat)i / (SHfloat)max )
|
||||
#define COL2INTCOORD(c, max) ( (SHuint)SH_FLOOR(c * (SHfloat)max + 0.5f) )
|
||||
|
||||
void shStoreColor(SHColor *c, void *data, SHImageFormatDesc *f);
|
||||
void shLoadColor(SHColor *c, const void *data, SHImageFormatDesc *f);
|
||||
|
||||
|
||||
#endif /* __SHIMAGE_H */
|
||||
@@ -1,806 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <vg/openvg.h>
|
||||
#include "shContext.h"
|
||||
#include "shPaint.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define _ITEM_T SHStop
|
||||
#define _ARRAY_T SHStopArray
|
||||
#define _FUNC_T shStopArray
|
||||
#define _COMPARE_T(s1,s2) 0
|
||||
#define _ARRAY_DEFINE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
#define _ITEM_T SHPaint*
|
||||
#define _ARRAY_T SHPaintArray
|
||||
#define _FUNC_T shPaintArray
|
||||
#define _ARRAY_DEFINE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
// We currently do not use gradients which need textures, so disable them to
|
||||
// prevent freeing resources outside the correct OpenGL thread/context.
|
||||
#define SH_NO_PAINT_TEXTURE
|
||||
|
||||
void SHPaint_ctor(SHPaint *p)
|
||||
{
|
||||
int i;
|
||||
|
||||
p->type = VG_PAINT_TYPE_COLOR;
|
||||
CSET(p->color, 0,0,0,1);
|
||||
SH_INITOBJ(SHStopArray, p->instops);
|
||||
SH_INITOBJ(SHStopArray, p->stops);
|
||||
p->premultiplied = VG_FALSE;
|
||||
p->spreadMode = VG_COLOR_RAMP_SPREAD_PAD;
|
||||
p->tilingMode = VG_TILE_FILL;
|
||||
for (i=0; i<4; ++i) p->linearGradient[i] = 0.0f;
|
||||
for (i=0; i<5; ++i) p->radialGradient[i] = 0.0f;
|
||||
p->pattern = VG_INVALID_HANDLE;
|
||||
|
||||
#ifndef SH_NO_PAINT_TEXTURE
|
||||
glGenTextures(1, &p->texture);
|
||||
glBindTexture(GL_TEXTURE_1D, p->texture);
|
||||
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, SH_GRADIENT_TEX_SIZE, 0,
|
||||
GL_RGBA, GL_FLOAT, NULL);
|
||||
#else
|
||||
p->texture = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SHPaint_dtor(SHPaint *p)
|
||||
{
|
||||
SH_DEINITOBJ(SHStopArray, p->instops);
|
||||
SH_DEINITOBJ(SHStopArray, p->stops);
|
||||
|
||||
#ifndef SH_NO_PAINT_TEXTURE
|
||||
if (glIsTexture(p->texture))
|
||||
glDeleteTextures(1, &p->texture);
|
||||
#endif
|
||||
}
|
||||
|
||||
VG_API_CALL VGPaint vgCreatePaint(void)
|
||||
{
|
||||
SHPaint *p = NULL;
|
||||
VG_GETCONTEXT(VG_INVALID_HANDLE);
|
||||
|
||||
/* Create new paint object */
|
||||
SH_NEWOBJ(SHPaint, p);
|
||||
VG_RETURN_ERR_IF(!p, VG_OUT_OF_MEMORY_ERROR,
|
||||
VG_INVALID_HANDLE);
|
||||
|
||||
/* Add to resource list */
|
||||
shPaintArrayPushBack(&context->paints, p);
|
||||
|
||||
VG_RETURN((VGPaint)p);
|
||||
}
|
||||
|
||||
VG_API_CALL void vgDestroyPaint(VGPaint paint)
|
||||
{
|
||||
SHint index;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
/* Check if handle valid */
|
||||
index = shPaintArrayFind(&context->paints, (SHPaint*)paint);
|
||||
VG_RETURN_ERR_IF(index == -1, VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
|
||||
|
||||
/* Delete object and remove resource */
|
||||
SH_DELETEOBJ(SHPaint, (SHPaint*)paint);
|
||||
shPaintArrayRemoveAt(&context->paints, index);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
VG_API_CALL void vgSetPaint(VGPaint paint, VGbitfield paintModes)
|
||||
{
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
/* Check if handle valid */
|
||||
VG_RETURN_ERR_IF(!shIsValidPaint(context, paint) &&
|
||||
paint != VG_INVALID_HANDLE,
|
||||
VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
|
||||
|
||||
/* Check for invalid mode */
|
||||
VG_RETURN_ERR_IF(paintModes & ~(VG_STROKE_PATH | VG_FILL_PATH),
|
||||
VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
|
||||
|
||||
/* Set stroke / fill */
|
||||
if (paintModes & VG_STROKE_PATH)
|
||||
context->strokePaint = (SHPaint*)paint;
|
||||
if (paintModes & VG_FILL_PATH)
|
||||
context->fillPaint = (SHPaint*)paint;
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
VG_API_CALL void vgPaintPattern(VGPaint paint, VGImage pattern)
|
||||
{
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
/* Check if handle valid */
|
||||
VG_RETURN_ERR_IF(!shIsValidPaint(context, paint),
|
||||
VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
|
||||
|
||||
/* Check if pattern image valid */
|
||||
VG_RETURN_ERR_IF(!shIsValidImage(context, pattern),
|
||||
VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
|
||||
|
||||
/* TODO: Check if pattern image is current rendering target */
|
||||
|
||||
/* Set pattern image */
|
||||
((SHPaint*)paint)->pattern = pattern;
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
void shUpdateColorRampTexture(SHPaint *p)
|
||||
{
|
||||
#ifndef SH_NO_PAINT_TEXTURE
|
||||
SHint s=0;
|
||||
SHStop *stop1, *stop2;
|
||||
SHfloat rgba[SH_GRADIENT_TEX_COORDSIZE];
|
||||
SHint x1=0, x2=0, dx, x;
|
||||
SHColor dc, c;
|
||||
SHfloat k;
|
||||
|
||||
/* Write first pixel color */
|
||||
stop1 = &p->stops.items[0];
|
||||
CSTORE_RGBA1D_F(stop1->color, rgba, x1);
|
||||
|
||||
/* Walk stops */
|
||||
for (s=1; s<p->stops.size; ++s, x1=x2, stop1=stop2) {
|
||||
|
||||
/* Pick next stop */
|
||||
stop2 = &p->stops.items[s];
|
||||
x2 = (SHint)(stop2->offset * (SH_GRADIENT_TEX_SIZE-1));
|
||||
|
||||
SH_ASSERT(x1 >= 0 && x1 < SH_GRADIENT_TEX_SIZE &&
|
||||
x2 >= 0 && x2 < SH_GRADIENT_TEX_SIZE &&
|
||||
x1 <= x2);
|
||||
|
||||
dx = x2 - x1;
|
||||
CSUBCTO(stop2->color, stop1->color, dc);
|
||||
|
||||
/* Interpolate inbetween */
|
||||
for (x=x1+1; x<=x2; ++x) {
|
||||
|
||||
k = (SHfloat)(x-x1)/dx;
|
||||
CSETC(c, stop1->color);
|
||||
CADDCK(c, dc, k);
|
||||
CSTORE_RGBA1D_F(c, rgba, x);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update texture image */
|
||||
glBindTexture(GL_TEXTURE_1D, p->texture);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, SH_GRADIENT_TEX_SIZE,
|
||||
GL_RGBA, GL_FLOAT, rgba);
|
||||
#else
|
||||
printf("ShivaVG: gradients not supported!");
|
||||
#endif
|
||||
}
|
||||
|
||||
void shValidateInputStops(SHPaint *p)
|
||||
{
|
||||
SHStop *instop, stop = {0, {0,0,0,0}};
|
||||
SHfloat lastOffset=0.0f;
|
||||
int i;
|
||||
|
||||
shStopArrayClear(&p->stops);
|
||||
shStopArrayReserve(&p->stops, p->instops.size);
|
||||
|
||||
/* Assure input stops are properly defined */
|
||||
for (i=0; i<p->instops.size; ++i) {
|
||||
|
||||
/* Copy stop color */
|
||||
instop = &p->instops.items[i];
|
||||
stop.color = instop->color;
|
||||
|
||||
/* Offset must be in [0,1] */
|
||||
if (instop->offset < 0.0f || instop->offset > 1.0f)
|
||||
continue;
|
||||
|
||||
/* Discard whole sequence if not in ascending order */
|
||||
if (instop->offset < lastOffset)
|
||||
{shStopArrayClear(&p->stops); break;}
|
||||
|
||||
/* Add stop at offset 0 with same color if first not at 0 */
|
||||
if (p->stops.size == 0 && instop->offset != 0.0f) {
|
||||
stop.offset = 0.0f;
|
||||
shStopArrayPushBackP(&p->stops, &stop);}
|
||||
|
||||
/* Add current stop to array */
|
||||
stop.offset = instop->offset;
|
||||
shStopArrayPushBackP(&p->stops, &stop);
|
||||
|
||||
/* Save last offset */
|
||||
lastOffset = instop->offset;
|
||||
}
|
||||
|
||||
/* Add stop at offset 1 with same color if last not at 1 */
|
||||
if (p->stops.size > 0 && lastOffset != 1.0f) {
|
||||
stop.offset = 1.0f;
|
||||
shStopArrayPushBackP(&p->stops, &stop);
|
||||
}
|
||||
|
||||
/* Add 2 default stops if no valid found */
|
||||
if (p->stops.size == 0) {
|
||||
/* First opaque black */
|
||||
stop.offset = 0.0f;
|
||||
CSET(stop.color, 0,0,0,1);
|
||||
shStopArrayPushBackP(&p->stops, &stop);
|
||||
/* Last opaque white */
|
||||
stop.offset = 1.0f;
|
||||
CSET(stop.color, 1,1,1,1);
|
||||
shStopArrayPushBackP(&p->stops, &stop);
|
||||
}
|
||||
|
||||
/* Update texture */
|
||||
shUpdateColorRampTexture(p);
|
||||
}
|
||||
|
||||
void shGenerateStops(SHPaint *p, SHfloat minOffset, SHfloat maxOffset,
|
||||
SHStopArray *outStops)
|
||||
{
|
||||
SHStop *s1,*s2;
|
||||
SHint i1,i2;
|
||||
SHfloat o=0.0f;
|
||||
SHfloat ostep=0.0f;
|
||||
SHint istep=1;
|
||||
SHint istart=0;
|
||||
SHint iend=p->stops.size-1;
|
||||
SHint minDone=0;
|
||||
SHint maxDone=0;
|
||||
SHStop outStop;
|
||||
|
||||
/* Start below zero? */
|
||||
if (minOffset < 0.0f) {
|
||||
if (p->spreadMode == VG_COLOR_RAMP_SPREAD_PAD) {
|
||||
/* Add min offset stop */
|
||||
outStop = p->stops.items[0];
|
||||
outStop.offset = minOffset;
|
||||
shStopArrayPushBackP(outStops, &outStop);
|
||||
/* Add max offset stop and exit */
|
||||
if (maxOffset < 0.0f) {
|
||||
outStop.offset = maxOffset;
|
||||
shStopArrayPushBackP(outStops, &outStop);
|
||||
return; }
|
||||
}else{
|
||||
/* Pad starting offset to nearest factor of 2 */
|
||||
SHint ioff = (SHint)SH_FLOOR(minOffset);
|
||||
o = (SHfloat)(ioff - (ioff & 1));
|
||||
}
|
||||
}
|
||||
|
||||
/* Construct stops until max offset reached */
|
||||
for (i1=istart, i2=istart+istep; maxDone!=1;
|
||||
i1+=istep, i2+=istep, o+=ostep) {
|
||||
|
||||
/* All stops consumed? */
|
||||
if (i1==iend) { switch(p->spreadMode) {
|
||||
|
||||
case VG_COLOR_RAMP_SPREAD_PAD:
|
||||
/* Pick last stop */
|
||||
outStop = p->stops.items[i1];
|
||||
if (!minDone) {
|
||||
/* Add min offset stop with last color */
|
||||
outStop.offset = minOffset;
|
||||
shStopArrayPushBackP(outStops, &outStop); }
|
||||
/* Add max offset stop with last color */
|
||||
outStop.offset = maxOffset;
|
||||
shStopArrayPushBackP(outStops, &outStop);
|
||||
return;
|
||||
|
||||
case VG_COLOR_RAMP_SPREAD_REPEAT:
|
||||
/* Reset iteration */
|
||||
i1=istart; i2=istart+istep;
|
||||
/* Add stop1 if past min offset */
|
||||
if (minDone) {
|
||||
outStop = p->stops.items[0];
|
||||
outStop.offset = o;
|
||||
shStopArrayPushBackP(outStops, &outStop); }
|
||||
break;
|
||||
|
||||
case VG_COLOR_RAMP_SPREAD_REFLECT:
|
||||
/* Reflect iteration direction */
|
||||
istep = -istep;
|
||||
i2 = i1 + istep;
|
||||
iend = (istep==1) ? p->stops.size-1 : 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 2 stops and their offset distance */
|
||||
s1 = &p->stops.items[i1];
|
||||
s2 = &p->stops.items[i2];
|
||||
ostep = s2->offset - s1->offset;
|
||||
ostep = SH_ABS(ostep);
|
||||
|
||||
/* Add stop1 if reached min offset */
|
||||
if (!minDone && o+ostep > minOffset) {
|
||||
minDone = 1;
|
||||
outStop = *s1;
|
||||
outStop.offset = o;
|
||||
shStopArrayPushBackP(outStops, &outStop);
|
||||
}
|
||||
|
||||
/* Mark done if reached max offset */
|
||||
if (o+ostep > maxOffset)
|
||||
maxDone = 1;
|
||||
|
||||
/* Add stop2 if past min offset */
|
||||
if (minDone) {
|
||||
outStop = *s2;
|
||||
outStop.offset = o+ostep;
|
||||
shStopArrayPushBackP(outStops, &outStop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void shSetGradientTexGLState(SHPaint *p)
|
||||
{
|
||||
#ifndef SH_NO_PAINT_TEXTURE
|
||||
glBindTexture(GL_TEXTURE_1D, p->texture);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
switch (p->spreadMode) {
|
||||
case VG_COLOR_RAMP_SPREAD_PAD:
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); break;
|
||||
case VG_COLOR_RAMP_SPREAD_REPEAT:
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); break;
|
||||
case VG_COLOR_RAMP_SPREAD_REFLECT:
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); break;
|
||||
}
|
||||
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
glColor4f(1,1,1,1);
|
||||
#else
|
||||
printf("ShivaVG: gradients not supported!");
|
||||
#endif
|
||||
}
|
||||
|
||||
void shSetPatternTexGLState(SHPaint *p, VGContext *c)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, ((SHImage*)p->pattern)->texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
switch(p->tilingMode) {
|
||||
case VG_TILE_FILL:
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR,
|
||||
(GLfloat*)&c->tileFillColor);
|
||||
break;
|
||||
case VG_TILE_PAD:
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
break;
|
||||
case VG_TILE_REPEAT:
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
break;
|
||||
case VG_TILE_REFLECT:
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
|
||||
break;
|
||||
}
|
||||
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
glColor4f(1,1,1,1);
|
||||
}
|
||||
|
||||
int shDrawLinearGradientMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
|
||||
VGPaintMode mode, GLenum texUnit)
|
||||
{
|
||||
SHint i;
|
||||
SHfloat n;
|
||||
|
||||
SHfloat x1 = p->linearGradient[0];
|
||||
SHfloat y1 = p->linearGradient[1];
|
||||
SHfloat x2 = p->linearGradient[2];
|
||||
SHfloat y2 = p->linearGradient[3];
|
||||
SHVector2 c, ux, uy;
|
||||
SHVector2 cc, uux, uuy;
|
||||
|
||||
SHMatrix3x3 *m = 0;
|
||||
SHMatrix3x3 mi;
|
||||
SHint invertible;
|
||||
SHVector2 corners[4];
|
||||
SHfloat minOffset = 0.0f;
|
||||
SHfloat maxOffset = 0.0f;
|
||||
SHfloat left = 0.0f;
|
||||
SHfloat right = 0.0f;
|
||||
SHVector2 l1,r1,l2,r2;
|
||||
|
||||
/* Pick paint transform matrix */
|
||||
SH_GETCONTEXT(0);
|
||||
if (mode == VG_FILL_PATH)
|
||||
m = &context->fillTransform;
|
||||
else if (mode == VG_STROKE_PATH)
|
||||
m = &context->strokeTransform;
|
||||
|
||||
/* Gradient center and unit vectors */
|
||||
SET2(c, x1, y1);
|
||||
SET2(ux, x2-x1, y2-y1);
|
||||
SET2(uy, -ux.y, ux.x);
|
||||
n = NORM2(ux);
|
||||
DIV2(ux, n);
|
||||
NORMALIZE2(uy);
|
||||
|
||||
/* Apply paint-to-user transformation */
|
||||
ADD2V(ux, c); ADD2V(uy, c);
|
||||
TRANSFORM2TO(c, (*m), cc);
|
||||
TRANSFORM2TO(ux, (*m), uux);
|
||||
TRANSFORM2TO(uy, (*m), uuy);
|
||||
SUB2V(ux,c); SUB2V(uy,c);
|
||||
SUB2V(uux,cc); SUB2V(uuy,cc);
|
||||
|
||||
/* Boundbox corners */
|
||||
SET2(corners[0], min->x, min->y);
|
||||
SET2(corners[1], max->x, min->y);
|
||||
SET2(corners[2], max->x, max->y);
|
||||
SET2(corners[3], min->x, max->y);
|
||||
|
||||
/* Find inverse transformation (back to paint space) */
|
||||
invertible = shInvertMatrix(m, &mi);
|
||||
if (!invertible || n==0.0f) {
|
||||
|
||||
/* Fill boundbox with color at offset 1 */
|
||||
SHColor *c = &p->stops.items[p->stops.size-1].color;
|
||||
glColor4fv((GLfloat*)c); glBegin(GL_QUADS);
|
||||
for (i=0; i<4; ++i) glVertex2fv((GLfloat*)&corners[i]);
|
||||
glEnd();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------*/
|
||||
|
||||
for (i=0; i<4; ++i) {
|
||||
|
||||
/* Find min/max offset and perpendicular span */
|
||||
SHfloat o, s;
|
||||
TRANSFORM2(corners[i], mi);
|
||||
SUB2V(corners[i], c);
|
||||
o = DOT2(corners[i], ux) / n;
|
||||
s = DOT2(corners[i], uy);
|
||||
if (o < minOffset || i==0) minOffset = o;
|
||||
if (o > maxOffset || i==0) maxOffset = o;
|
||||
if (s < left || i==0) left = s;
|
||||
if (s > right || i==0) right = s;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*/
|
||||
|
||||
/* Corners of boundbox in gradient system */
|
||||
SET2V(l1, cc); SET2V(r1, cc);
|
||||
SET2V(l2, cc); SET2V(r2, cc);
|
||||
OFFSET2V(l1, uuy, left); OFFSET2V(l1, uux, minOffset * n);
|
||||
OFFSET2V(r1, uuy, right); OFFSET2V(r1, uux, minOffset * n);
|
||||
OFFSET2V(l2, uuy, left); OFFSET2V(l2, uux, maxOffset * n);
|
||||
OFFSET2V(r2, uuy, right); OFFSET2V(r2, uux, maxOffset * n);
|
||||
|
||||
/* Draw quad using color-ramp texture */
|
||||
glActiveTexture(texUnit);
|
||||
shSetGradientTexGLState(p);
|
||||
|
||||
glEnable(GL_TEXTURE_1D);
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
|
||||
glMultiTexCoord1f(texUnit, minOffset);
|
||||
glVertex2fv((GLfloat*)&r1);
|
||||
glVertex2fv((GLfloat*)&l1);
|
||||
|
||||
glMultiTexCoord1f(texUnit, maxOffset);
|
||||
glVertex2fv((GLfloat*)&r2);
|
||||
glVertex2fv((GLfloat*)&l2);
|
||||
|
||||
glEnd();
|
||||
glDisable(GL_TEXTURE_1D);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int shDrawRadialGradientMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
|
||||
VGPaintMode mode, GLenum texUnit)
|
||||
{
|
||||
SHint i, j;
|
||||
float a, n;
|
||||
|
||||
SHfloat cx = p->radialGradient[0];
|
||||
SHfloat cy = p->radialGradient[1];
|
||||
SHfloat fx = p->radialGradient[2];
|
||||
SHfloat fy = p->radialGradient[3];
|
||||
float r = p->radialGradient[4];
|
||||
float fcx, fcy, rr, C;
|
||||
|
||||
SHVector2 ux;
|
||||
SHVector2 uy;
|
||||
SHVector2 c, f;
|
||||
SHVector2 cf;
|
||||
|
||||
SHMatrix3x3 *m = 0;
|
||||
SHMatrix3x3 mi;
|
||||
SHint invertible;
|
||||
SHVector2 corners[4];
|
||||
SHVector2 fcorners[4];
|
||||
SHfloat minOffset=0.0f;
|
||||
SHfloat maxOffset=0.0f;
|
||||
|
||||
SHint maxI=0, maxJ=0;
|
||||
SHfloat maxA=0.0f;
|
||||
SHfloat startA=0.0f;
|
||||
|
||||
int numsteps = 100;
|
||||
float step = 2*PI/numsteps;
|
||||
SHVector2 tmin, tmax;
|
||||
SHVector2 min1, max1, min2, max2;
|
||||
|
||||
/* Pick paint transform matrix */
|
||||
SH_GETCONTEXT(0);
|
||||
if (mode == VG_FILL_PATH)
|
||||
m = &context->fillTransform;
|
||||
else if (mode == VG_STROKE_PATH)
|
||||
m = &context->strokeTransform;
|
||||
|
||||
/* Move focus into circle if outside */
|
||||
SET2(cf, fx,fy);
|
||||
SUB2(cf, cx,cy);
|
||||
n = NORM2(cf);
|
||||
if (n > r) {
|
||||
DIV2(cf, n);
|
||||
fx = cx + 0.995f * r * cf.x;
|
||||
fy = cy + 0.995f * r * cf.y;
|
||||
}
|
||||
|
||||
/* Precalculations */
|
||||
rr = r*r;
|
||||
fcx = fx - cx;
|
||||
fcy = fy - cy;
|
||||
C = fcx*fcx + fcy*fcy - rr;
|
||||
|
||||
/* Apply paint-to-user transformation
|
||||
to focus and unit vectors */
|
||||
SET2(f, fx, fy);
|
||||
SET2(c, cx, cy);
|
||||
SET2(ux, 1, 0);
|
||||
SET2(uy, 0, 1);
|
||||
ADD2(ux, cx, cy);
|
||||
ADD2(uy, cx, cy);
|
||||
TRANSFORM2(f, (*m));
|
||||
TRANSFORM2(c, (*m));
|
||||
TRANSFORM2(ux, (*m));
|
||||
TRANSFORM2(uy, (*m));
|
||||
SUB2V(ux, c); SUB2V(uy, c);
|
||||
|
||||
/* Boundbox corners */
|
||||
SET2(corners[0], min->x, min->y);
|
||||
SET2(corners[1], max->x, min->y);
|
||||
SET2(corners[2], max->x, max->y);
|
||||
SET2(corners[3], min->x, max->y);
|
||||
|
||||
/* Find inverse transformation (back to paint space) */
|
||||
invertible = shInvertMatrix(m, &mi);
|
||||
if (!invertible || r <= 0.0f) {
|
||||
|
||||
/* Fill boundbox with color at offset 1 */
|
||||
SHColor *c = &p->stops.items[p->stops.size-1].color;
|
||||
glColor4fv((GLfloat*)c); glBegin(GL_QUADS);
|
||||
for (i=0; i<4; ++i) glVertex2fv((GLfloat*)&corners[i]);
|
||||
glEnd();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------*/
|
||||
|
||||
/* Find min/max offset */
|
||||
for (i=0; i<4; ++i) {
|
||||
|
||||
/* Transform to paint space */
|
||||
SHfloat ax,ay, A,B,D,t, off;
|
||||
TRANSFORM2TO(corners[i], mi, fcorners[i]);
|
||||
SUB2(fcorners[i], fx, fy);
|
||||
n = NORM2(fcorners[i]);
|
||||
if (n == 0.0f) {
|
||||
|
||||
/* Avoid zero-length vectors */
|
||||
off = 0.0f;
|
||||
|
||||
}else{
|
||||
|
||||
/* Distance from focus to circle at corner angle */
|
||||
DIV2(fcorners[i], n);
|
||||
ax = fcorners[i].x;
|
||||
ay = fcorners[i].y;
|
||||
A = ax*ax + ay*ay;
|
||||
B = 2 * (fcx*ax + fcy*ay);
|
||||
D = B*B - 4*A*C;
|
||||
t = (-B + SH_SQRT(D)) / (2*A);
|
||||
|
||||
/* Relative offset of boundbox corner */
|
||||
if (D <= 0.0f) off = 1.0f;
|
||||
else off = n / t;
|
||||
}
|
||||
|
||||
/* Find smallest and largest offset */
|
||||
if (off < minOffset || i==0) minOffset = off;
|
||||
if (off > maxOffset || i==0) maxOffset = off;
|
||||
}
|
||||
|
||||
/* Is transformed focus inside original boundbox? */
|
||||
if (f.x >= min->x && f.x <= max->x &&
|
||||
f.y >= min->y && f.y <= max->y) {
|
||||
|
||||
/* Draw whole circle */
|
||||
minOffset = 0.0f;
|
||||
startA = 0.0f;
|
||||
maxA = 2*PI;
|
||||
|
||||
}else{
|
||||
|
||||
/* Find most distant corner pair */
|
||||
for (i=0; i<3; ++i) {
|
||||
if (ISZERO2(fcorners[i])) continue;
|
||||
for (j=i+1; j<4; ++j) {
|
||||
if (ISZERO2(fcorners[j])) continue;
|
||||
a = ANGLE2N(fcorners[i], fcorners[j]);
|
||||
if (a > maxA || maxA == 0.0f)
|
||||
{maxA=a; maxI=i; maxJ=j;}
|
||||
}}
|
||||
|
||||
/* Pick starting angle */
|
||||
if (CROSS2(fcorners[maxI],fcorners[maxJ]) > 0.0f)
|
||||
startA = shVectorOrientation(&fcorners[maxI]);
|
||||
else startA = shVectorOrientation(&fcorners[maxJ]);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*/
|
||||
|
||||
/* TODO: for minOffset we'd actually need to find minimum
|
||||
of the gradient function when X and Y are substitued
|
||||
with a line equation for each bound-box edge. As a
|
||||
workaround we use 0.0f for now. */
|
||||
minOffset = 0.0f;
|
||||
step = PI/50;
|
||||
numsteps = (SHint)SH_CEIL(maxA / step) + 1;
|
||||
|
||||
glActiveTexture(texUnit);
|
||||
shSetGradientTexGLState(p);
|
||||
|
||||
glEnable(GL_TEXTURE_1D);
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
/* Walk the steps and draw gradient mesh */
|
||||
for (i=0, a=startA; i<numsteps; ++i, a+=step) {
|
||||
|
||||
/* Distance from focus to circle border
|
||||
at current angle (gradient space) */
|
||||
float ax = SH_COS(a);
|
||||
float ay = SH_SIN(a);
|
||||
float A = ax*ax + ay*ay;
|
||||
float B = 2 * (fcx*ax + fcy*ay);
|
||||
float D = B*B - 4*A*C;
|
||||
float t = (-B + SH_SQRT(D)) / (2*A);
|
||||
if (D <= 0.0f) t = 0.0f;
|
||||
|
||||
/* Vectors pointing towards minimum and maximum
|
||||
offset at current angle (gradient space) */
|
||||
tmin.x = ax * t * minOffset;
|
||||
tmin.y = ay * t * minOffset;
|
||||
tmax.x = ax * t * maxOffset;
|
||||
tmax.y = ay * t * maxOffset;
|
||||
|
||||
/* Transform back to user space */
|
||||
min2.x = f.x + tmin.x * ux.x + tmin.y * uy.x;
|
||||
min2.y = f.y + tmin.x * ux.y + tmin.y * uy.y;
|
||||
max2.x = f.x + tmax.x * ux.x + tmax.y * uy.x;
|
||||
max2.y = f.y + tmax.x * ux.y + tmax.y * uy.y;
|
||||
|
||||
/* Draw quad */
|
||||
if (i!=0) {
|
||||
glMultiTexCoord1f(texUnit, minOffset);
|
||||
glVertex2fv((GLfloat*)&min1);
|
||||
glVertex2fv((GLfloat*)&min2);
|
||||
glMultiTexCoord1f(texUnit, maxOffset);
|
||||
glVertex2fv((GLfloat*)&max2);
|
||||
glVertex2fv((GLfloat*)&max1);
|
||||
}
|
||||
|
||||
/* Save prev points */
|
||||
min1 = min2;
|
||||
max1 = max2;
|
||||
}
|
||||
|
||||
glEnd();
|
||||
glDisable(GL_TEXTURE_1D);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int shDrawPatternMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
|
||||
VGPaintMode mode, GLenum texUnit)
|
||||
{
|
||||
SHMatrix3x3 *m = 0;
|
||||
SHMatrix3x3 mi;
|
||||
SHfloat migl[16];
|
||||
SHint invertible;
|
||||
SHVector2 corners[4];
|
||||
VGfloat sx, sy;
|
||||
SHImage *img;
|
||||
int i;
|
||||
|
||||
/* Pick paint transform matrix */
|
||||
SH_GETCONTEXT(0);
|
||||
if (mode == VG_FILL_PATH)
|
||||
m = &context->fillTransform;
|
||||
else if (mode == VG_STROKE_PATH)
|
||||
m = &context->strokeTransform;
|
||||
|
||||
/* Boundbox corners */
|
||||
SET2(corners[0], min->x, min->y);
|
||||
SET2(corners[1], max->x, min->y);
|
||||
SET2(corners[2], max->x, max->y);
|
||||
SET2(corners[3], min->x, max->y);
|
||||
|
||||
/* Find inverse transformation (back to paint space) */
|
||||
invertible = shInvertMatrix(m, &mi);
|
||||
if (!invertible) {
|
||||
|
||||
/* Fill boundbox with tile fill color */
|
||||
SHColor *c = &context->tileFillColor;
|
||||
glColor4fv((GLfloat*)c); glBegin(GL_QUADS);
|
||||
for (i=0; i<4; ++i) glVertex2fv((GLfloat*)&corners[i]);
|
||||
glEnd();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Setup texture coordinate transform */
|
||||
img = (SHImage*)p->pattern;
|
||||
sx = 1.0f/(VGfloat)img->texwidth;
|
||||
sy = 1.0f/(VGfloat)img->texheight;
|
||||
|
||||
glActiveTexture(texUnit);
|
||||
shMatrixToGL(&mi, migl);
|
||||
glMatrixMode(GL_TEXTURE);
|
||||
glPushMatrix();
|
||||
glScalef(sx, sy, 1.0f);
|
||||
glMultMatrixf(migl);
|
||||
|
||||
|
||||
/* Draw boundbox with same texture coordinates
|
||||
that will get transformed back to paint space */
|
||||
shSetPatternTexGLState(p, context);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
for (i=0; i<4; ++i) {
|
||||
glMultiTexCoord2f(texUnit, corners[i].x, corners[i].y);
|
||||
glVertex2fv((GLfloat*)&corners[i]);
|
||||
}
|
||||
|
||||
glEnd();
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
return 1;
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SHPAINT_H
|
||||
#define __SHPAINT_H
|
||||
|
||||
#include "shDefs.h"
|
||||
#include "shArrays.h"
|
||||
#include "shImage.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float offset;
|
||||
SHColor color;
|
||||
|
||||
} SHStop;
|
||||
|
||||
#define _ITEM_T SHStop
|
||||
#define _ARRAY_T SHStopArray
|
||||
#define _FUNC_T shStopArray
|
||||
#define _ARRAY_DECLARE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
VGPaintType type;
|
||||
SHColor color;
|
||||
SHColorArray colors;
|
||||
SHStopArray instops;
|
||||
SHStopArray stops;
|
||||
VGboolean premultiplied;
|
||||
VGColorRampSpreadMode spreadMode;
|
||||
VGTilingMode tilingMode;
|
||||
SHfloat linearGradient[4];
|
||||
SHfloat radialGradient[5];
|
||||
GLuint texture;
|
||||
VGImage pattern;
|
||||
|
||||
} SHPaint;
|
||||
|
||||
#define SH_GRADIENT_TEX_SIZE 1024
|
||||
|
||||
void SHPaint_ctor(SHPaint *p);
|
||||
void SHPaint_dtor(SHPaint *p);
|
||||
|
||||
#define _ITEM_T SHPaint*
|
||||
#define _ARRAY_T SHPaintArray
|
||||
#define _FUNC_T shPaintArray
|
||||
#define _ARRAY_DECLARE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
void shValidateInputStops(SHPaint *p);
|
||||
void shSetGradientTexGLState(SHPaint *p);
|
||||
|
||||
int shDrawLinearGradientMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
|
||||
VGPaintMode mode, GLenum texUnit);
|
||||
|
||||
int shDrawRadialGradientMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
|
||||
VGPaintMode mode, GLenum texUnit);
|
||||
|
||||
int shDrawPatternMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
|
||||
VGPaintMode mode, GLenum texUnit);
|
||||
|
||||
|
||||
#endif /* __SHPAINT_H */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SHPATH_H
|
||||
#define __SHPATH_H
|
||||
|
||||
#include "shVectors.h"
|
||||
#include "shArrays.h"
|
||||
|
||||
/* Helper structures for subdivision */
|
||||
typedef struct {
|
||||
SHVector2 p1;
|
||||
SHVector2 p2;
|
||||
SHVector2 p3;
|
||||
} SHQuad;
|
||||
|
||||
typedef struct {
|
||||
SHVector2 p1;
|
||||
SHVector2 p2;
|
||||
SHVector2 p3;
|
||||
SHVector2 p4;
|
||||
} SHCubic;
|
||||
|
||||
typedef struct {
|
||||
SHVector2 p1;
|
||||
SHVector2 p2;
|
||||
SHfloat a1;
|
||||
SHfloat a2;
|
||||
} SHArc;
|
||||
|
||||
/* SHVertex */
|
||||
typedef struct
|
||||
{
|
||||
SHVector2 point;
|
||||
SHVector2 tangent;
|
||||
SHfloat length;
|
||||
SHuint flags;
|
||||
|
||||
} SHVertex;
|
||||
|
||||
/* Vertex flags for contour definition */
|
||||
#define SH_VERTEX_FLAG_CLOSE (1 << 0)
|
||||
#define SH_VERTEX_FLAG_SEGEND (1 << 1)
|
||||
#define SH_SEGMENT_TYPE_COUNT 13
|
||||
|
||||
/* Vertex array */
|
||||
#define _ITEM_T SHVertex
|
||||
#define _ARRAY_T SHVertexArray
|
||||
#define _FUNC_T shVertexArray
|
||||
#define _ARRAY_DECLARE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
|
||||
/* SHPath */
|
||||
typedef struct SHPath
|
||||
{
|
||||
/* Properties */
|
||||
VGint format;
|
||||
SHfloat scale;
|
||||
SHfloat bias;
|
||||
SHint segHint;
|
||||
SHint dataHint;
|
||||
VGbitfield caps;
|
||||
VGPathDatatype datatype;
|
||||
|
||||
/* Raw data */
|
||||
SHuint8 *segs;
|
||||
void *data;
|
||||
SHint segCount;
|
||||
SHint dataCount;
|
||||
|
||||
/* Subdivision */
|
||||
SHVertexArray vertices;
|
||||
SHVector2 min, max;
|
||||
|
||||
/* Additional stroke geometry (dash vertices if
|
||||
path dashed or triangle vertices if width > 1 */
|
||||
SHVector2Array stroke;
|
||||
|
||||
/* Cache */
|
||||
VGboolean cacheDataValid;
|
||||
|
||||
VGboolean cacheTransformInit;
|
||||
SHMatrix3x3 cacheTransform;
|
||||
|
||||
VGboolean cacheStrokeInit;
|
||||
VGboolean cacheStrokeTessValid;
|
||||
SHfloat cacheStrokeLineWidth;
|
||||
VGCapStyle cacheStrokeCapStyle;
|
||||
VGJoinStyle cacheStrokeJoinStyle;
|
||||
SHfloat cacheStrokeMiterLimit;
|
||||
SHfloat cacheStrokeDashPhase;
|
||||
VGboolean cacheStrokeDashPhaseReset;
|
||||
|
||||
} SHPath;
|
||||
|
||||
void SHPath_ctor(SHPath *p);
|
||||
void SHPath_dtor(SHPath *p);
|
||||
|
||||
|
||||
/* Processing normalization flags */
|
||||
#define SH_PROCESS_SIMPLIFY_LINES (1 << 0)
|
||||
#define SH_PROCESS_SIMPLIFY_CURVES (1 << 1)
|
||||
#define SH_PROCESS_CENTRALIZE_ARCS (1 << 2)
|
||||
#define SH_PROCESS_REPAIR_ENDS (1 << 3)
|
||||
|
||||
/* Segment callback function type */
|
||||
typedef void (*SegmentFunc) (SHPath *p, VGPathSegment segment,
|
||||
VGPathCommand originalCommand,
|
||||
SHfloat *data, void *userData);
|
||||
|
||||
/* Processes raw path data into normalized segments */
|
||||
void shProcessPathData(SHPath *p, int flags,
|
||||
SegmentFunc callback,
|
||||
void *userData);
|
||||
|
||||
|
||||
/* Pointer-to-path array */
|
||||
#define _ITEM_T SHPath*
|
||||
#define _ARRAY_T SHPathArray
|
||||
#define _FUNC_T shPathArray
|
||||
#define _ARRAY_DECLARE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
#endif /* __SHPATH_H */
|
||||
@@ -1,597 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <vg/openvg.h>
|
||||
#include "shDefs.h"
|
||||
#include "shExtensions.h"
|
||||
#include "shContext.h"
|
||||
#include "shPath.h"
|
||||
#include "shImage.h"
|
||||
#include "shGeometry.h"
|
||||
#include "shPaint.h"
|
||||
|
||||
void shPremultiplyFramebuffer()
|
||||
{
|
||||
/* Multiply target color with its own alpha */
|
||||
glBlendFunc(GL_ZERO, GL_DST_ALPHA);
|
||||
}
|
||||
|
||||
void shUnpremultiplyFramebuffer()
|
||||
{
|
||||
/* TODO: hmmmm..... any idea? */
|
||||
}
|
||||
|
||||
void updateBlendingStateGL(VGContext *c, int alphaIsOne)
|
||||
{
|
||||
/* Most common drawing mode (SRC_OVER with alpha=1)
|
||||
as well as SRC is optimized by turning OpenGL
|
||||
blending off. In other cases its turned on. */
|
||||
|
||||
switch (c->blendMode)
|
||||
{
|
||||
case VG_BLEND_SRC:
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glDisable(GL_BLEND); break;
|
||||
|
||||
case VG_BLEND_SRC_IN:
|
||||
glBlendFunc(GL_DST_ALPHA, GL_ZERO);
|
||||
glEnable(GL_BLEND); break;
|
||||
|
||||
case VG_BLEND_DST_IN:
|
||||
glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
|
||||
glEnable(GL_BLEND); break;
|
||||
|
||||
case VG_BLEND_SRC_OUT_SH:
|
||||
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
|
||||
glEnable(GL_BLEND); break;
|
||||
|
||||
case VG_BLEND_DST_OUT_SH:
|
||||
glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnable(GL_BLEND); break;
|
||||
|
||||
case VG_BLEND_SRC_ATOP_SH:
|
||||
glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnable(GL_BLEND); break;
|
||||
|
||||
case VG_BLEND_DST_ATOP_SH:
|
||||
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
|
||||
glEnable(GL_BLEND); break;
|
||||
|
||||
case VG_BLEND_DST_OVER:
|
||||
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
|
||||
glEnable(GL_BLEND); break;
|
||||
|
||||
case VG_BLEND_SRC_OVER: default:
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
if (alphaIsOne) glDisable(GL_BLEND);
|
||||
else glEnable(GL_BLEND); break;
|
||||
};
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Draws the triangles representing the stroke of a path.
|
||||
*-----------------------------------------------------------*/
|
||||
|
||||
static void shDrawStroke(SHPath *p)
|
||||
{
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(2, GL_FLOAT, 0, p->stroke.items);
|
||||
glDrawArrays(GL_TRIANGLES, 0, p->stroke.size);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Draws the subdivided vertices in the OpenGL mode given
|
||||
* (this could be VG_TRIANGLE_FAN or VG_LINE_STRIP).
|
||||
*-----------------------------------------------------------*/
|
||||
|
||||
static void shDrawVertices(SHPath *p, GLenum mode)
|
||||
{
|
||||
int start = 0;
|
||||
int size = 0;
|
||||
|
||||
/* We separate vertex arrays by contours to properly
|
||||
handle the fill modes */
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(SHVertex), p->vertices.items);
|
||||
|
||||
while (start < p->vertices.size) {
|
||||
size = p->vertices.items[start].flags;
|
||||
glDrawArrays(mode, start, size);
|
||||
start += size;
|
||||
}
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------
|
||||
* Draw a single quad that covers the bounding box of a path
|
||||
*-------------------------------------------------------------*/
|
||||
|
||||
static void shDrawBoundBox(VGContext *c, SHPath *p, VGPaintMode mode)
|
||||
{
|
||||
SHfloat K = 1.0f;
|
||||
if (mode == VG_STROKE_PATH)
|
||||
K = SH_CEIL(c->strokeMiterLimit * c->strokeLineWidth) + 1.0f;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(p->min.x-K, p->min.y-K);
|
||||
glVertex2f(p->max.x+K, p->min.y-K);
|
||||
glVertex2f(p->max.x+K, p->max.y+K);
|
||||
glVertex2f(p->min.x-K, p->max.y+K);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------
|
||||
* Constructs & draws colored OpenGL primitives that cover the
|
||||
* given bounding box to represent the currently selected
|
||||
* stroke or fill paint
|
||||
*--------------------------------------------------------------*/
|
||||
|
||||
static void shDrawPaintMesh(VGContext *c, SHVector2 *min, SHVector2 *max,
|
||||
VGPaintMode mode, GLenum texUnit)
|
||||
{
|
||||
SHPaint *p = 0;
|
||||
SHVector2 pmin, pmax;
|
||||
SHfloat K = 1.0f;
|
||||
|
||||
/* Pick the right paint */
|
||||
if (mode == VG_FILL_PATH) {
|
||||
p = (c->fillPaint ? c->fillPaint : &c->defaultPaint);
|
||||
}else if (mode == VG_STROKE_PATH) {
|
||||
p = (c->strokePaint ? c->strokePaint : &c->defaultPaint);
|
||||
K = SH_CEIL(c->strokeMiterLimit * c->strokeLineWidth) + 1.0f;
|
||||
}
|
||||
|
||||
/* We want to be sure to cover every pixel of this path so better
|
||||
take a pixel more than leave some out (multisampling is tricky). */
|
||||
SET2V(pmin, (*min)); SUB2(pmin, K,K);
|
||||
SET2V(pmax, (*max)); ADD2(pmax, K,K);
|
||||
|
||||
/* Construct appropriate OpenGL primitives so as
|
||||
to fill the stencil mask with select paint */
|
||||
|
||||
switch (p->type) {
|
||||
case VG_PAINT_TYPE_LINEAR_GRADIENT:
|
||||
shDrawLinearGradientMesh(p, min, max, mode, texUnit);
|
||||
break;
|
||||
|
||||
case VG_PAINT_TYPE_RADIAL_GRADIENT:
|
||||
shDrawRadialGradientMesh(p, min, max, mode, texUnit);
|
||||
break;
|
||||
|
||||
case VG_PAINT_TYPE_PATTERN:
|
||||
if (p->pattern != VG_INVALID_HANDLE) {
|
||||
shDrawPatternMesh(p, min, max, mode, texUnit);
|
||||
break;
|
||||
}/* else behave as a color paint */
|
||||
|
||||
case VG_PAINT_TYPE_COLOR:
|
||||
glColor4fv((GLfloat*)&p->color);
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(pmin.x, pmin.y);
|
||||
glVertex2f(pmax.x, pmin.y);
|
||||
glVertex2f(pmax.x, pmax.y);
|
||||
glVertex2f(pmin.x, pmax.y);
|
||||
glEnd();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VGboolean shIsTessCacheValid (VGContext *c, SHPath *p)
|
||||
{
|
||||
SHfloat nX, nY;
|
||||
SHVector2 X, Y;
|
||||
SHMatrix3x3 mi;//, mchange;
|
||||
VGboolean valid = VG_TRUE;
|
||||
|
||||
if (p->cacheDataValid == VG_FALSE) {
|
||||
valid = VG_FALSE;
|
||||
}
|
||||
else if (p->cacheTransformInit == VG_FALSE) {
|
||||
valid = VG_FALSE;
|
||||
}
|
||||
else if (shInvertMatrix( &p->cacheTransform, &mi ) == VG_FALSE) {
|
||||
valid = VG_FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: Compare change matrix for any scale or shear */
|
||||
// MULMATMAT( c->pathTransform, mi, mchange );
|
||||
SET2( X, mi.m[0][0], mi.m[1][0] );
|
||||
SET2( Y, mi.m[0][1], mi.m[1][1] );
|
||||
nX = NORM2( X ); nY = NORM2( Y );
|
||||
if (nX > 1.01f || nX < 0.99 ||
|
||||
nY > 1.01f || nY < 0.99)
|
||||
valid = VG_FALSE;
|
||||
}
|
||||
|
||||
if (valid == VG_FALSE)
|
||||
{
|
||||
/* Update cache */
|
||||
p->cacheDataValid = VG_TRUE;
|
||||
p->cacheTransformInit = VG_TRUE;
|
||||
p->cacheTransform = c->pathTransform;
|
||||
p->cacheStrokeTessValid = VG_FALSE;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
VGboolean shIsStrokeCacheValid (VGContext *c, SHPath *p)
|
||||
{
|
||||
VGboolean valid = VG_TRUE;
|
||||
|
||||
if (p->cacheStrokeInit == VG_FALSE) {
|
||||
valid = VG_FALSE;
|
||||
}
|
||||
else if (p->cacheStrokeTessValid == VG_FALSE) {
|
||||
valid = VG_FALSE;
|
||||
}
|
||||
else if (c->strokeDashPattern.size > 0) {
|
||||
valid = VG_FALSE;
|
||||
}
|
||||
else if (p->cacheStrokeLineWidth != c->strokeLineWidth ||
|
||||
p->cacheStrokeCapStyle != c->strokeCapStyle ||
|
||||
p->cacheStrokeJoinStyle != c->strokeJoinStyle ||
|
||||
p->cacheStrokeMiterLimit != c->strokeMiterLimit) {
|
||||
valid = VG_FALSE;
|
||||
}
|
||||
|
||||
if (valid == VG_FALSE)
|
||||
{
|
||||
/* Update cache */
|
||||
p->cacheStrokeInit = VG_TRUE;
|
||||
p->cacheStrokeTessValid = VG_TRUE;
|
||||
p->cacheStrokeLineWidth = c->strokeLineWidth;
|
||||
p->cacheStrokeCapStyle = c->strokeCapStyle;
|
||||
p->cacheStrokeJoinStyle = c->strokeJoinStyle;
|
||||
p->cacheStrokeMiterLimit = c->strokeMiterLimit;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Tessellates / strokes the path and draws it according to
|
||||
* VGContext state.
|
||||
*-----------------------------------------------------------*/
|
||||
|
||||
VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
|
||||
{
|
||||
SHPath *p;
|
||||
SHMatrix3x3 mi;
|
||||
SHfloat mgl[16];
|
||||
SHPaint *fill, *stroke;
|
||||
SHRectangle *rect;
|
||||
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
VG_RETURN_ERR_IF(!shIsValidPath(context, path),
|
||||
VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
|
||||
|
||||
VG_RETURN_ERR_IF(paintModes & (~(VG_STROKE_PATH | VG_FILL_PATH)),
|
||||
VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
|
||||
|
||||
/* Check whether scissoring is enabled and scissor
|
||||
rectangle is valid */
|
||||
if (context->scissoring == VG_TRUE) {
|
||||
rect = &context->scissor.items[0];
|
||||
if (context->scissor.size == 0) VG_RETURN( VG_NO_RETVAL );
|
||||
if (rect->w <= 0.0f || rect->h <= 0.0f) VG_RETURN( VG_NO_RETVAL );
|
||||
glScissor( (GLint)rect->x, (GLint)rect->y, (GLint)rect->w, (GLint)rect->h );
|
||||
glEnable( GL_SCISSOR_TEST );
|
||||
}
|
||||
|
||||
p = (SHPath*)path;
|
||||
|
||||
/* If user-to-surface matrix invertible tessellate in
|
||||
surface space for better path resolution */
|
||||
if (shIsTessCacheValid( context, p ) == VG_FALSE)
|
||||
{
|
||||
if (shInvertMatrix(&context->pathTransform, &mi)) {
|
||||
shFlattenPath(p, 1);
|
||||
shTransformVertices(&mi, p);
|
||||
}else shFlattenPath(p, 0);
|
||||
shFindBoundbox(p);
|
||||
}
|
||||
|
||||
/* TODO: Turn antialiasing on/off */
|
||||
// glDisable(GL_LINE_SMOOTH);
|
||||
// glDisable(GL_POLYGON_SMOOTH);
|
||||
// glEnable(GL_MULTISAMPLE);
|
||||
|
||||
/* Pick paint if available or default*/
|
||||
fill = (context->fillPaint ? context->fillPaint : &context->defaultPaint);
|
||||
stroke = (context->strokePaint ? context->strokePaint : &context->defaultPaint);
|
||||
|
||||
/* Apply transformation */
|
||||
shMatrixToGL(&context->pathTransform, mgl);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glMultMatrixf(mgl);
|
||||
|
||||
if (paintModes & VG_FILL_PATH) {
|
||||
|
||||
/* Tesselate into stencil */
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
|
||||
if( context->fillRule == VG_EVEN_ODD )
|
||||
{
|
||||
glStencilFunc(GL_ALWAYS, 0, 0);
|
||||
glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
|
||||
}
|
||||
else
|
||||
{
|
||||
// pseudo non-zero fill rule. Fill everything at least covered once, don't
|
||||
// care for possible decrements.
|
||||
// TODO implement real non-zero fill-rule
|
||||
glStencilFunc(GL_ALWAYS, 1, 1);
|
||||
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
|
||||
}
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
shDrawVertices(p, GL_TRIANGLE_FAN);
|
||||
|
||||
/* Setup blending */
|
||||
updateBlendingStateGL(context,
|
||||
fill->type == VG_PAINT_TYPE_COLOR &&
|
||||
fill->color.a == 1.0f);
|
||||
|
||||
/* Draw paint where stencil odd */
|
||||
glStencilFunc(GL_EQUAL, 1, 1);
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
shDrawPaintMesh(context, &p->min, &p->max, VG_FILL_PATH, GL_TEXTURE0);
|
||||
|
||||
/* Clear stencil for sure */
|
||||
/* TODO: Is there any way to do this safely along
|
||||
with the paint generation pass?? */
|
||||
glDisable(GL_BLEND);
|
||||
// glDisable(GL_MULTISAMPLE);
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
shDrawBoundBox(context, p, VG_FILL_PATH);
|
||||
|
||||
/* Reset state */
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
// glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
/* TODO: Turn antialiasing on/off */
|
||||
// glDisable(GL_LINE_SMOOTH);
|
||||
// glDisable(GL_POLYGON_SMOOTH);
|
||||
// glEnable(GL_MULTISAMPLE);
|
||||
|
||||
if ((paintModes & VG_STROKE_PATH) &&
|
||||
context->strokeLineWidth > 0.0f) {
|
||||
|
||||
#if 0
|
||||
if (1) {/*context->strokeLineWidth > 1.0f) {*/
|
||||
#endif
|
||||
if (shIsStrokeCacheValid( context, p ) == VG_FALSE)
|
||||
{
|
||||
/* Generate stroke triangles in user space */
|
||||
shVector2ArrayClear(&p->stroke);
|
||||
shStrokePath(context, p);
|
||||
}
|
||||
|
||||
/* Stroke into stencil */
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glStencilFunc(GL_NOTEQUAL, 1, 1);
|
||||
glStencilOp(GL_KEEP, GL_INCR, GL_INCR);
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
shDrawStroke(p);
|
||||
|
||||
/* Setup blending */
|
||||
updateBlendingStateGL(context,
|
||||
stroke->type == VG_PAINT_TYPE_COLOR &&
|
||||
stroke->color.a == 1.0f);
|
||||
|
||||
/* Draw paint where stencil odd */
|
||||
glStencilFunc(GL_EQUAL, 1, 1);
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
shDrawPaintMesh(context, &p->min, &p->max, VG_STROKE_PATH, GL_TEXTURE0);
|
||||
|
||||
/* Clear stencil for sure */
|
||||
glDisable(GL_BLEND);
|
||||
// glDisable(GL_MULTISAMPLE);
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
shDrawBoundBox(context, p, VG_STROKE_PATH);
|
||||
|
||||
/* Reset state */
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
// glDisable(GL_BLEND);
|
||||
#if 0
|
||||
}else{
|
||||
|
||||
/* Simulate thin stroke by alpha */
|
||||
SHColor c = stroke->color;
|
||||
if (context->strokeLineWidth < 1.0f)
|
||||
c.a *= context->strokeLineWidth;
|
||||
|
||||
/* Draw contour as a line */
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4fv((GLfloat*)&c);
|
||||
shDrawVertices(p, GL_LINE_STRIP);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
if (context->scissoring == VG_TRUE)
|
||||
glDisable( GL_SCISSOR_TEST );
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
VG_API_CALL void vgDrawImage(VGImage image)
|
||||
{
|
||||
SHImage *i;
|
||||
SHfloat mgl[16];
|
||||
SHfloat texGenS[4] = {0,0,0,0};
|
||||
SHfloat texGenT[4] = {0,0,0,0};
|
||||
SHPaint *fill;
|
||||
SHVector2 min, max;
|
||||
SHRectangle *rect;
|
||||
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
VG_RETURN_ERR_IF(!shIsValidImage(context, image),
|
||||
VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
|
||||
|
||||
/* TODO: check if image is current render target */
|
||||
|
||||
/* Check whether scissoring is enabled and scissor
|
||||
rectangle is valid */
|
||||
if (context->scissoring == VG_TRUE) {
|
||||
rect = &context->scissor.items[0];
|
||||
if (context->scissor.size == 0) VG_RETURN( VG_NO_RETVAL );
|
||||
if (rect->w <= 0.0f || rect->h <= 0.0f) VG_RETURN( VG_NO_RETVAL );
|
||||
glScissor( (GLint)rect->x, (GLint)rect->y, (GLint)rect->w, (GLint)rect->h );
|
||||
glEnable( GL_SCISSOR_TEST );
|
||||
}
|
||||
|
||||
/* Apply image-user-to-surface transformation */
|
||||
i = (SHImage*)image;
|
||||
shMatrixToGL(&context->imageTransform, mgl);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glMultMatrixf(mgl);
|
||||
|
||||
/* Clamp to edge for proper filtering, modulate for multiply mode */
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, i->texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
|
||||
/* Adjust antialiasing to settings */
|
||||
if (context->imageQuality == VG_IMAGE_QUALITY_NONANTIALIASED) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
}else{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
}
|
||||
|
||||
/* Generate image texture coords automatically */
|
||||
texGenS[0] = 1.0f / i->texwidth;
|
||||
texGenT[1] = 1.0f / i->texheight;
|
||||
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
|
||||
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
|
||||
glTexGenfv(GL_S, GL_OBJECT_PLANE, texGenS);
|
||||
glTexGenfv(GL_T, GL_OBJECT_PLANE, texGenT);
|
||||
glEnable(GL_TEXTURE_GEN_S);
|
||||
glEnable(GL_TEXTURE_GEN_T);
|
||||
|
||||
/* Pick fill paint */
|
||||
fill = (context->fillPaint ? context->fillPaint : &context->defaultPaint);
|
||||
|
||||
/* Use paint color when multiplying with a color-paint */
|
||||
if (context->imageMode == VG_DRAW_IMAGE_MULTIPLY &&
|
||||
fill->type == VG_PAINT_TYPE_COLOR)
|
||||
glColor4fv((GLfloat*)&fill->color);
|
||||
else glColor4f(1,1,1,1);
|
||||
|
||||
|
||||
/* Check image drawing mode */
|
||||
if (context->imageMode == VG_DRAW_IMAGE_MULTIPLY &&
|
||||
fill->type != VG_PAINT_TYPE_COLOR) {
|
||||
|
||||
/* Draw image quad into stencil */
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glStencilFunc(GL_ALWAYS, 1, 1);
|
||||
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
|
||||
glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2i(0, 0);
|
||||
glVertex2i(i->width, 0);
|
||||
glVertex2i(i->width, i->height);
|
||||
glVertex2i(0, i->height);
|
||||
glEnd();
|
||||
|
||||
/* Setup blending */
|
||||
updateBlendingStateGL(context, 0);
|
||||
|
||||
/* Draw gradient mesh where stencil 1*/
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glStencilFunc(GL_EQUAL, 1, 1);
|
||||
glStencilOp(GL_ZERO,GL_ZERO,GL_ZERO);
|
||||
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
|
||||
|
||||
SET2(min,0,0);
|
||||
SET2(max, (SHfloat)i->width, (SHfloat)i->height);
|
||||
if (fill->type == VG_PAINT_TYPE_RADIAL_GRADIENT) {
|
||||
shDrawRadialGradientMesh(fill, &min, &max, VG_FILL_PATH, GL_TEXTURE1);
|
||||
}else if (fill->type == VG_PAINT_TYPE_LINEAR_GRADIENT) {
|
||||
shDrawLinearGradientMesh(fill, &min, &max, VG_FILL_PATH, GL_TEXTURE1);
|
||||
}else if (fill->type == VG_PAINT_TYPE_PATTERN) {
|
||||
shDrawPatternMesh(fill, &min, &max, VG_FILL_PATH, GL_TEXTURE1); }
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
}else if (context->imageMode == VG_DRAW_IMAGE_STENCIL) {
|
||||
|
||||
|
||||
}else{/* Either normal mode or multiplying with a color-paint */
|
||||
|
||||
/* Setup blending */
|
||||
updateBlendingStateGL(context, 0);
|
||||
|
||||
/* Draw textured quad */
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2i(0, 0);
|
||||
glVertex2i(i->width, 0);
|
||||
glVertex2i(i->width, i->height);
|
||||
glVertex2i(0, i->height);
|
||||
glEnd();
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
|
||||
glDisable(GL_TEXTURE_GEN_S);
|
||||
glDisable(GL_TEXTURE_GEN_T);
|
||||
glPopMatrix();
|
||||
|
||||
if (context->scissoring == VG_TRUE)
|
||||
glDisable( GL_SCISSOR_TEST );
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "shVectors.h"
|
||||
|
||||
#define _ITEM_T SHVector2
|
||||
#define _ARRAY_T SHVector2Array
|
||||
#define _FUNC_T shVector2Array
|
||||
#define _COMPARE_T(v1,v2) EQ2V(v1,v2)
|
||||
#define _ARRAY_DEFINE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
void SHVector2_ctor(SHVector2 *v) {
|
||||
v->x=0.0f; v->y=0.0f;
|
||||
}
|
||||
|
||||
void SHVector2_dtor(SHVector2 *v) {
|
||||
}
|
||||
|
||||
void SHVector3_ctor(SHVector3 *v) {
|
||||
v->x=0.0f; v->y=0.0f; v->z=0.0f;
|
||||
}
|
||||
|
||||
void SHVector3_dtor(SHVector3 *v) {
|
||||
}
|
||||
|
||||
void SHVector4_ctor(SHVector4 *v) {
|
||||
v->x=0.0f; v->y=0.0f; v->z=0.0f; v->w=0.0f;
|
||||
}
|
||||
|
||||
void SHVector4_dtor(SHVector4 *v) {
|
||||
}
|
||||
|
||||
void SHRectangle_ctor(SHRectangle *r) {
|
||||
r->x=0.0f; r->y=0.0f; r->w=0.0f; r->h=0.0f;
|
||||
}
|
||||
|
||||
void SHRectangle_dtor(SHRectangle *r) {
|
||||
}
|
||||
|
||||
void shRectangleSet(SHRectangle *r, SHfloat x,
|
||||
SHfloat y, SHfloat w, SHfloat h)
|
||||
{
|
||||
r->x=x; r->y=y; r->w=w; r->h=h;
|
||||
}
|
||||
|
||||
void SHMatrix3x3_ctor(SHMatrix3x3 *mt)
|
||||
{
|
||||
IDMAT((*mt));
|
||||
}
|
||||
|
||||
void SHMatrix3x3_dtor(SHMatrix3x3 *mt)
|
||||
{
|
||||
}
|
||||
|
||||
void shMatrixToGL(SHMatrix3x3 *m, SHfloat mgl[16])
|
||||
{
|
||||
/* When 2D vectors are specified OpenGL defaults Z to 0.0f so we
|
||||
have to shift the third column of our 3x3 matrix to right */
|
||||
mgl[0] = m->m[0][0]; mgl[4] = m->m[0][1]; mgl[8] = 0.0f; mgl[12] = m->m[0][2];
|
||||
mgl[1] = m->m[1][0]; mgl[5] = m->m[1][1]; mgl[9] = 0.0f; mgl[13] = m->m[1][2];
|
||||
mgl[2] = m->m[2][0]; mgl[6] = m->m[2][1]; mgl[10] = 1.0f; mgl[14] = m->m[2][1];
|
||||
mgl[3] = 0.0f; mgl[7] = 0.0f; mgl[11] = 0.0f; mgl[15] = 1.0f;
|
||||
}
|
||||
|
||||
int shInvertMatrix(SHMatrix3x3 *m, SHMatrix3x3 *mout)
|
||||
{
|
||||
/* Calculate determinant */
|
||||
SHfloat D0 = m->m[1][1]*m->m[2][2] - m->m[2][1]*m->m[1][2];
|
||||
SHfloat D1 = m->m[2][0]*m->m[1][2] - m->m[1][0]*m->m[2][2];
|
||||
SHfloat D2 = m->m[1][0]*m->m[2][1] - m->m[2][0]*m->m[1][1];
|
||||
SHfloat D = m->m[0][0]*D0 + m->m[0][1]*D1 + m->m[0][2]*D2;
|
||||
|
||||
/* Check if singular */
|
||||
if( D == 0.0f ) return 0;
|
||||
D = 1.0f / D;
|
||||
|
||||
/* Calculate inverse */
|
||||
mout->m[0][0] = D * D0;
|
||||
mout->m[1][0] = D * D1;
|
||||
mout->m[2][0] = D * D2;
|
||||
mout->m[0][1] = D * (m->m[2][1]*m->m[0][2] - m->m[0][1]*m->m[2][2]);
|
||||
mout->m[1][1] = D * (m->m[0][0]*m->m[2][2] - m->m[2][0]*m->m[0][2]);
|
||||
mout->m[2][1] = D * (m->m[2][0]*m->m[0][1] - m->m[0][0]*m->m[2][1]);
|
||||
mout->m[0][2] = D * (m->m[0][1]*m->m[1][2] - m->m[1][1]*m->m[0][2]);
|
||||
mout->m[1][2] = D * (m->m[1][0]*m->m[0][2] - m->m[0][0]*m->m[1][2]);
|
||||
mout->m[2][2] = D * (m->m[0][0]*m->m[1][1] - m->m[1][0]*m->m[0][1]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
SHfloat shVectorOrientation(SHVector2 *v) {
|
||||
SHfloat norm = (SHfloat)NORM2((*v));
|
||||
SHfloat cosa = v->x/norm;
|
||||
SHfloat sina = v->y/norm;
|
||||
return (SHfloat)(sina>=0 ? SH_ACOS(cosa) : 2*PI-SH_ACOS(cosa));
|
||||
}
|
||||
|
||||
int shLineLineXsection(SHVector2 *o1, SHVector2 *v1,
|
||||
SHVector2 *o2, SHVector2 *v2,
|
||||
SHVector2 *xsection)
|
||||
{
|
||||
SHfloat rightU = o2->x - o1->x;
|
||||
SHfloat rightD = o2->y - o1->y;
|
||||
|
||||
SHfloat D = v1->x * (-v2->y) - v1->y * (-v2->x);
|
||||
SHfloat DX = rightU * (-v2->y) - rightD * (-v2->x);
|
||||
/*SHfloat DY = v1.x * rightD - v1.y * rightU;*/
|
||||
|
||||
SHfloat t1;
|
||||
|
||||
if (D == 0.0f)
|
||||
return 0;
|
||||
|
||||
t1 = DX / D;
|
||||
|
||||
xsection->x = o1->x + t1*v1->x;
|
||||
xsection->y = o1->y + t1*v1->y;
|
||||
return 1;
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SHVECTORS_H
|
||||
#define __SHVECTORS_H
|
||||
|
||||
#include "shDefs.h"
|
||||
|
||||
/* Vector structures
|
||||
*--------------------------------------------------------------*/
|
||||
typedef struct
|
||||
{
|
||||
SHfloat x,y;
|
||||
} SHVector2;
|
||||
|
||||
void SHVector2_ctor(SHVector2 *v);
|
||||
void SHVector2_dtor(SHVector2 *v);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SHfloat x,y,z;
|
||||
} SHVector3;
|
||||
|
||||
void SHVector3_ctor(SHVector3 *v);
|
||||
void SHVector3_dtor(SHVector3 *v);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SHfloat x,y,z,w;
|
||||
} SHVector4;
|
||||
|
||||
void SHVector4_ctor(SHVector4 *v);
|
||||
void SHVector4_dtor(SHVector4 *v);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SHfloat x,y,w,h;
|
||||
} SHRectangle;
|
||||
|
||||
void SHRectangle_ctor(SHRectangle *r);
|
||||
void SHRectangle_dtor(SHRectangle *r);
|
||||
void shRectangleSet(SHRectangle *r, SHfloat x,
|
||||
SHfloat y, SHfloat w, SHfloat h);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SHfloat m[3][3];
|
||||
} SHMatrix3x3;
|
||||
|
||||
void SHMatrix3x3_ctor(SHMatrix3x3 *m);
|
||||
void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
|
||||
/*------------------------------------------------------------
|
||||
* Vector Arrays
|
||||
*------------------------------------------------------------*/
|
||||
|
||||
#define _ITEM_T SHVector2
|
||||
#define _ARRAY_T SHVector2Array
|
||||
#define _FUNC_T shVector2Array
|
||||
#define _ARRAY_DECLARE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
/*--------------------------------------------------------
|
||||
* Macros for typical vector operations. The only way to
|
||||
* inline in C is to actually write a macro
|
||||
*--------------------------------------------------------- */
|
||||
|
||||
#define SET2(v,xs,ys) { v.x=xs; v.y=ys; }
|
||||
#define SET3(v,xs,ys,zs) { v.x=xs; v.y=ys; v.z=zs; }
|
||||
#define SET4(v,xs,ys,zs,ws) { v.x=xs; v.y=ys; v.z=zs; v.w=ws; }
|
||||
|
||||
#define SET2V(v1,v2) { v1.x=v2.x; v1.y=v2.y; }
|
||||
#define SET3V(v1,v2) { v1.x=v2.x; v1.y=v2.y; v1.z=v2.z; }
|
||||
#define SET4V(v1,v2) { v1.x=v2.x; v1.y=v2.y; v1.z=v2.z; v1.w=v2.w; }
|
||||
|
||||
#define EQ2(v,xx,yy) ( v.x==xx && v.y==yy )
|
||||
#define EQ3(v,xx,yy,zz) ( v.x==xx && v.y==yy && v.z==zz )
|
||||
#define EQ4(v,xx,yy,zz,ww) ( v.x==xx && v.y==yy && v.z==zz && v.w==ww )
|
||||
|
||||
#define ISZERO2(v) ( v.x==0.0f && v.y==0.0f )
|
||||
#define ISZERO3(v) ( v.x==0.0f && v.y==0.0f && v.z==0.0f)
|
||||
#define ISZERO4(v) ( v.x==0.0f && v.y==0.0f && v.z==0.0f && v.w==0.0f )
|
||||
|
||||
#define EQ2V(v1,v2) ( v1.x==v2.x && v1.y==v2.y )
|
||||
#define EQ3V(v1,v2) ( v1.x==v2.x && v1.y==v2.y && v1.z==v2.z )
|
||||
#define EQ4V(v1,v2) ( v1.x==v2.x && v1.y==v2.y && v1.z==v2.z && v1.w==v2.w )
|
||||
|
||||
#define ADD2(v,xx,yy) { v.x+=xx; v.y+=yy; }
|
||||
#define ADD3(v,xx,yy,zz) { v.x+=xx; v.y+=yy; v.z+=zz; }
|
||||
#define ADD4(v,xx,yy,zz,ww) { v.x+=xx; v.y+=yy; v.z+=zz; v.w+=ww; }
|
||||
|
||||
#define ADD2V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; }
|
||||
#define ADD3V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; v1.z+=v2.z; }
|
||||
#define ADD4V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; v1.z+=v2.z; v1.w+=v2.w; }
|
||||
|
||||
#define SUB2(v,xx,yy) { v.x-=xx; v.y-=yy; }
|
||||
#define SUB3(v,xx,yy,zz) { v.x-=xx; v.y-=yy; v.z-=zz; }
|
||||
#define SUB4(v,xx,yy,zz,ww) { v.x-=xx; v.y-=yy; v.z-=zz; v.w-=v2.w; }
|
||||
|
||||
#define SUB2V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; }
|
||||
#define SUB3V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; v1.z-=v2.z; }
|
||||
#define SUB4V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; v1.z-=v2.z; v1.w-=v2.w; }
|
||||
|
||||
#define MUL2(v,f) { v.x*=f; v.y*=f; }
|
||||
#define MUL3(v,f) { v.x*=f; v.y*=f; v.z*=z; }
|
||||
#define MUL4(v,f) { v.x*=f; v.y*=f; v.z*=z; v.w*=w; }
|
||||
|
||||
#define DIV2(v,f) { v.x/=f; v.y/=f; }
|
||||
#define DIV3(v,f) { v.x/=f; v.y/=f; v.z/=z; }
|
||||
#define DIV4(v,f) { v.x/=f; v.y/=f; v.z/=z; v.w/=w; }
|
||||
|
||||
#define ABS2(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); }
|
||||
#define ABS3(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); v.z=SH_ABS(v.z); }
|
||||
#define ABS4(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); v.z=SH_ABS(v.z); v.w=SH_ABS(v.w); }
|
||||
|
||||
#define NORMSQ2(v) (v.x*v.x + v.y*v.y)
|
||||
#define NORMSQ3(v) (v.x*v.x + v.y*v.y + v.z*v.z)
|
||||
#define NORMSQ4(v) (v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w)
|
||||
|
||||
#define NORM2(v) SH_SQRT(NORMSQ2(v))
|
||||
#define NORM3(v) SH_SQRT(NORMSQ3(v))
|
||||
#define NORM4(v) SH_SQRT(NORMSQ4(v))
|
||||
|
||||
#define NORMALIZE2(v) { SHfloat n=NORM2(v); v.x/=n; v.y/=n; }
|
||||
#define NORMALIZE3(v) { SHfloat n=NORM3(v); v.x/=n; v.y/=n; v.z/=n; }
|
||||
#define NORMALIZE4(v) { SHfloat n=NORM4(v); v.x/=n; v.y/=n; v.z/=n; v.w/=w; }
|
||||
|
||||
#define DOT2(v1,v2) (v1.x*v2.x + v1.y*v2.y)
|
||||
#define DOT3(v1,v2) (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)
|
||||
#define DOT4(v1,v2) (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w)
|
||||
|
||||
#define CROSS2(v1,v2) (v1.x*v2.y - v2.x*v1.y)
|
||||
|
||||
#define ANGLE2(v1,v2) (SH_ACOS( DOT2(v1,v2) / (NORM2(v1)*NORM2(v2)) ))
|
||||
#define ANGLE2N(v1,v2) (SH_ACOS( DOT2(v1,v2) ))
|
||||
|
||||
#define OFFSET2V(v, o, s) { v.x += o.x*s; v.y += o.y*s; }
|
||||
#define OFFSET3V(v, o, s) { v.x += o.x*s; v.y += o.y*s; v.z += o.z*s; }
|
||||
#define OFFSET4V(v, o, s) { v.x += o.x*s; v.y += o.y*s; v.z += o.z*s; v.w += o.w*s; }
|
||||
|
||||
/*-----------------------------------------------------
|
||||
* Macros for matrix operations
|
||||
*-----------------------------------------------------*/
|
||||
|
||||
#define SETMAT(mat, m00, m01, m02, m10, m11, m12, m20, m21, m22) { \
|
||||
mat.m[0][0] = m00; mat.m[0][1] = m01; mat.m[0][2] = m02; \
|
||||
mat.m[1][0] = m10; mat.m[1][1] = m11; mat.m[1][2] = m12; \
|
||||
mat.m[2][0] = m20; mat.m[2][1] = m21; mat.m[2][2] = m22; }
|
||||
|
||||
#define SETMATMAT(m1, m2) { \
|
||||
int i,j; \
|
||||
for(i=0;i<3;i++) \
|
||||
for(j=0;j<3;j++) \
|
||||
m1.m[i][j] = m2.m[i][j]; }
|
||||
|
||||
#define MULMATS(mat, s) { \
|
||||
int i,j; \
|
||||
for(i=0;i<3;i++) \
|
||||
for(j=0;j<3;j++) \
|
||||
mat.m[i][j] *= s; }
|
||||
|
||||
#define DIVMATS(mat, s) { \
|
||||
int i,j; \
|
||||
for(i=0;i<3;i++) \
|
||||
for(j=0;j<3;j++) \
|
||||
mat.m[i][j] /= s; }
|
||||
|
||||
#define MULMATMAT(m1, m2, mout) { \
|
||||
int i,j; \
|
||||
for(i=0;i<3;i++) \
|
||||
for(j=0;j<3;j++) \
|
||||
mout.m[i][j] = \
|
||||
m1.m[i][0] * m2.m[0][j] + \
|
||||
m1.m[i][1] * m2.m[1][j] + \
|
||||
m1.m[i][2] * m2.m[2][j]; }
|
||||
|
||||
#define IDMAT(mat) SETMAT(mat, 1,0,0, 0,1,0, 0,0,1)
|
||||
|
||||
#define TRANSLATEMATL(mat, tx, ty) { \
|
||||
SHMatrix3x3 trans,temp; \
|
||||
SETMAT(trans, 1,0,tx, 0,1,ty, 0,0,1); \
|
||||
MULMATMAT(trans, mat, temp); \
|
||||
SETMATMAT(mat, temp); }
|
||||
|
||||
#define TRANSLATEMATR(mat, tx, ty) { \
|
||||
SHMatrix3x3 trans,temp; \
|
||||
SETMAT(trans, 1,0,tx, 0,1,ty, 0,0,1); \
|
||||
MULMATMAT(mat, trans, temp); \
|
||||
SETMATMAT(mat, temp); }
|
||||
|
||||
#define SCALEMATL(mat, sx, sy) { \
|
||||
SHMatrix3x3 scale, temp; \
|
||||
SETMAT(scale, sx,0,0, 0,sy,0, 0,0,1); \
|
||||
MULMATMAT(scale, mat, temp); \
|
||||
SETMATMAT(mat, temp); }
|
||||
|
||||
#define SCALEMATR(mat, sx, sy) { \
|
||||
SHMatrix3x3 scale, temp; \
|
||||
SETMAT(scale, sx,0,0, 0,sy,0, 0,0,1); \
|
||||
MULMATMAT(mat, scale, temp); \
|
||||
SETMATMAT(mat, temp); }
|
||||
|
||||
#define SHEARMATL(mat, shx, shy) {\
|
||||
SHMatrix3x3 shear, temp;\
|
||||
SETMAT(shear, 1,shx,0, shy,1,0, 0,0,1); \
|
||||
MULMATMAT(shear, mat, temp); \
|
||||
SETMATMAT(mat, temp); }
|
||||
|
||||
#define SHEARMATR(mat, shx, shy) {\
|
||||
SHMatrix3x3 shear, temp;\
|
||||
SETMAT(shear, 1,shx,0, shy,1,0, 0,0,1); \
|
||||
MULMATMAT(mat, shear, temp); \
|
||||
SETMATMAT(mat, temp); }
|
||||
|
||||
#define ROTATEMATL(mat, a) { \
|
||||
SHfloat cosa=SH_COS(a), sina=SH_SIN(a); \
|
||||
SHMatrix3x3 rot, temp; \
|
||||
SETMAT(rot, cosa,-sina,0, sina,cosa,0, 0,0,1); \
|
||||
MULMATMAT(rot, mat, temp); \
|
||||
SETMATMAT(mat, temp); }
|
||||
|
||||
#define ROTATEMATR(mat, a) { \
|
||||
SHfloat cosa=SH_COS(a), sina=SH_SIN(a); \
|
||||
SHMatrix3x3 rot, temp; \
|
||||
SETMAT(rot, cosa,-sina,0, sina,cosa,0, 0,0,1); \
|
||||
MULMATMAT(mat, rot, temp); \
|
||||
SETMATMAT(mat, temp); }
|
||||
|
||||
#define TRANSFORM2TO(v, mat, vout) { \
|
||||
vout.x = v.x*mat.m[0][0] + v.y*mat.m[0][1] + 1*mat.m[0][2]; \
|
||||
vout.y = v.x*mat.m[1][0] + v.y*mat.m[1][1] + 1*mat.m[1][2]; }
|
||||
|
||||
#define TRANSFORM2(v, mat) { \
|
||||
SHVector2 temp; TRANSFORM2TO(v, mat, temp); v = temp; }
|
||||
|
||||
#define TRANSFORM2DIRTO(v, mat, vout) { \
|
||||
vout.x = v.x*mat.m[0][0] + v.y*mat.m[0][1]; \
|
||||
vout.y = v.x*mat.m[1][0] + v.y*mat.m[1][1]; }
|
||||
|
||||
#define TRANSFORM2DIR(v, mat) { \
|
||||
SHVector2 temp; TRANSFORM2DIRTO(v, mat, temp); v = temp; }
|
||||
|
||||
|
||||
/*--------------------------------------------------------
|
||||
* Additional functions
|
||||
*--------------------------------------------------------- */
|
||||
|
||||
void shMatrixToGL(SHMatrix3x3 *m, SHfloat mgl[16]);
|
||||
|
||||
SHint shInvertMatrix(SHMatrix3x3 *m, SHMatrix3x3 *mout);
|
||||
|
||||
SHfloat shVectorOrientation(SHVector2 *v);
|
||||
|
||||
int shLineLineXsection(SHVector2 *o1, SHVector2 *v1,
|
||||
SHVector2 *o2, SHVector2 *v2,
|
||||
SHVector2 *xsection);
|
||||
|
||||
#endif/* __SHVECTORS_H */
|
||||
@@ -1,425 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Ivan Leben
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <vg/openvg.h>
|
||||
#include <vg/vgu.h>
|
||||
#include "shDefs.h"
|
||||
#include "shContext.h"
|
||||
#include <math.h>
|
||||
|
||||
static VGUErrorCode shAppend(VGPath path, SHint commSize, const VGubyte *comm,
|
||||
SHint dataSize, const VGfloat *data)
|
||||
{
|
||||
VGErrorCode err = VG_NO_ERROR;
|
||||
VGPathDatatype type = vgGetParameterf(path, VG_PATH_DATATYPE);
|
||||
VGfloat scale = vgGetParameterf(path, VG_PATH_SCALE);
|
||||
VGfloat bias = vgGetParameterf(path, VG_PATH_BIAS);
|
||||
SH_ASSERT(dataSize <= 26);
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case VG_PATH_DATATYPE_S_8: {
|
||||
|
||||
SHint8 data8[26]; int i;
|
||||
for (i=0; i<dataSize; ++i)
|
||||
data8[i] = (SHint8)SH_FLOOR((data[i] - bias) / scale + 0.5f);
|
||||
vgAppendPathData(path, commSize, comm, data8);
|
||||
|
||||
break;}
|
||||
case VG_PATH_DATATYPE_S_16: {
|
||||
|
||||
SHint16 data16[26]; int i;
|
||||
for (i=0; i<dataSize; ++i)
|
||||
data16[i] = (SHint16)SH_FLOOR((data[i] - bias) / scale + 0.5f);
|
||||
vgAppendPathData(path, commSize, comm, data16);
|
||||
|
||||
break;}
|
||||
case VG_PATH_DATATYPE_S_32: {
|
||||
|
||||
SHint32 data32[26]; int i;
|
||||
for (i=0; i<dataSize; ++i)
|
||||
data32[i] = (SHint32)SH_FLOOR((data[i] - bias) / scale + 0.5f);
|
||||
vgAppendPathData(path, commSize, comm, data32);
|
||||
|
||||
break;}
|
||||
default: {
|
||||
|
||||
VGfloat dataF[26]; int i;
|
||||
for (i=0; i<dataSize; ++i)
|
||||
dataF[i] = (data[i] - bias) / scale;
|
||||
vgAppendPathData(path, commSize, comm, dataF);
|
||||
|
||||
break;}
|
||||
}
|
||||
|
||||
err = vgGetError();
|
||||
if (err == VG_PATH_CAPABILITY_ERROR)
|
||||
return VGU_PATH_CAPABILITY_ERROR;
|
||||
else if (err == VG_BAD_HANDLE_ERROR)
|
||||
return VGU_BAD_HANDLE_ERROR;
|
||||
else if (err == VG_OUT_OF_MEMORY_ERROR)
|
||||
return VGU_OUT_OF_MEMORY_ERROR;
|
||||
|
||||
return VGU_NO_ERROR;
|
||||
}
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguLine(VGPath path,
|
||||
VGfloat x0, VGfloat y0,
|
||||
VGfloat x1, VGfloat y1)
|
||||
{
|
||||
VGUErrorCode err = VGU_NO_ERROR;
|
||||
const VGubyte comm[] = {VG_MOVE_TO_ABS, VG_LINE_TO_ABS};
|
||||
|
||||
VGfloat data[4];
|
||||
data[0] = x0; data[1] = y0;
|
||||
data[2] = x1; data[3] = y1;
|
||||
|
||||
err = shAppend(path, 2, comm, 4, data);
|
||||
return err;
|
||||
}
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguPolygon(VGPath path,
|
||||
const VGfloat * points, VGint count,
|
||||
VGboolean closed)
|
||||
{
|
||||
VGint i;
|
||||
VGubyte *comm = NULL;
|
||||
VGUErrorCode err = VGU_NO_ERROR;
|
||||
|
||||
if (points == NULL || count <= 0)
|
||||
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||||
/* TODO: check points array alignment */
|
||||
|
||||
comm = (VGubyte*)malloc( (count+1) * sizeof(VGubyte) );
|
||||
if (comm == NULL) return VGU_OUT_OF_MEMORY_ERROR;
|
||||
|
||||
comm[0] = VG_MOVE_TO_ABS;
|
||||
for (i=1; i<count; ++i)
|
||||
comm[i] = VG_LINE_TO_ABS;
|
||||
comm[count] = VG_CLOSE_PATH;
|
||||
|
||||
if (closed) err = shAppend(path, count+1, comm, count*2, points);
|
||||
else err = shAppend(path, count, comm, count*2, points);
|
||||
|
||||
free(comm);
|
||||
return err;
|
||||
}
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguRect(VGPath path,
|
||||
VGfloat x, VGfloat y,
|
||||
VGfloat width, VGfloat height)
|
||||
{
|
||||
VGUErrorCode err = VGU_NO_ERROR;
|
||||
|
||||
VGubyte comm[5] = {
|
||||
VG_MOVE_TO_ABS, VG_HLINE_TO_REL,
|
||||
VG_VLINE_TO_REL, VG_HLINE_TO_REL,
|
||||
VG_CLOSE_PATH };
|
||||
|
||||
VGfloat data[5];
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
||||
data[0] = x; data[1] = y;
|
||||
data[2] = width; data[3] = height;
|
||||
data[4] = -width;
|
||||
|
||||
err = shAppend(path, 5, comm, 5, data);
|
||||
return err;
|
||||
}
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguRoundRect(VGPath path,
|
||||
VGfloat x, VGfloat y,
|
||||
VGfloat width, VGfloat height,
|
||||
VGfloat arcWidth, VGfloat arcHeight)
|
||||
{
|
||||
VGUErrorCode err = VGU_NO_ERROR;
|
||||
|
||||
VGubyte comm[10] = {
|
||||
VG_MOVE_TO_ABS,
|
||||
VG_HLINE_TO_REL, VG_SCCWARC_TO_REL,
|
||||
VG_VLINE_TO_REL, VG_SCCWARC_TO_REL,
|
||||
VG_HLINE_TO_REL, VG_SCCWARC_TO_REL,
|
||||
VG_VLINE_TO_REL, VG_SCCWARC_TO_REL,
|
||||
VG_CLOSE_PATH };
|
||||
|
||||
VGfloat data[26];
|
||||
VGfloat rx, ry;
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
||||
SH_CLAMP(arcWidth, 0.0f, width);
|
||||
SH_CLAMP(arcHeight, 0.0f, height);
|
||||
rx = arcWidth/2;
|
||||
ry = arcHeight/2;
|
||||
|
||||
data[0] = x + rx; data[1] = y;
|
||||
|
||||
data[2] = width - arcWidth;
|
||||
data[3] = rx; data[4] = ry; data[5] = 0;
|
||||
data[6] = rx; data[7] = ry;
|
||||
|
||||
data[8] = height - arcHeight;
|
||||
data[9] = rx; data[10] = ry; data[11] = 0;
|
||||
data[12] = -rx; data[13] = ry;
|
||||
|
||||
data[14] = -(width - arcWidth);
|
||||
data[15] = rx; data[16] = ry; data[17] = 0;
|
||||
data[18] = -rx; data[19] = -ry;
|
||||
|
||||
data[20] = -(height - arcHeight);
|
||||
data[21] = rx; data[22] = ry; data[23] = 0;
|
||||
data[24] = rx; data[25] = -ry;
|
||||
|
||||
err = shAppend(path, 10, comm, 26, data);
|
||||
return err;
|
||||
}
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguEllipse(VGPath path,
|
||||
VGfloat cx, VGfloat cy,
|
||||
VGfloat width, VGfloat height)
|
||||
{
|
||||
VGUErrorCode err = VGU_NO_ERROR;
|
||||
|
||||
const VGubyte comm[] = {
|
||||
VG_MOVE_TO_ABS, VG_SCCWARC_TO_REL,
|
||||
VG_SCCWARC_TO_REL, VG_CLOSE_PATH};
|
||||
|
||||
VGfloat data[12];
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
||||
data[0] = cx + width/2; data[1] = cy;
|
||||
|
||||
data[2] = width/2; data[3] = height/2; data[4] = 0;
|
||||
data[5] = -width; data[6] = 0;
|
||||
|
||||
data[7] = width/2; data[8] = height/2; data[9] = 0;
|
||||
data[10] = width; data[11] = 0;
|
||||
|
||||
err = shAppend(path, 4, comm, 12, data);
|
||||
return err;
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguArc(VGPath path,
|
||||
VGfloat x, VGfloat y,
|
||||
VGfloat width, VGfloat height,
|
||||
VGfloat startAngle, VGfloat angleExtent,
|
||||
VGUArcType arcType)
|
||||
{
|
||||
VGUErrorCode err = VGU_NO_ERROR;
|
||||
|
||||
VGubyte commStart[1] = {VG_MOVE_TO_ABS};
|
||||
VGfloat dataStart[2];
|
||||
|
||||
VGubyte commArcCCW[1] = {VG_SCCWARC_TO_ABS};
|
||||
VGubyte commArcCW[1] = {VG_SCWARC_TO_ABS};
|
||||
VGfloat dataArc[5];
|
||||
|
||||
VGubyte commEndPie[2] = {VG_LINE_TO_ABS, VG_CLOSE_PATH};
|
||||
VGfloat dataEndPie[2];
|
||||
|
||||
VGubyte commEndChord[1] = {VG_CLOSE_PATH};
|
||||
VGfloat dataEndChord[1] = {0.0f};
|
||||
|
||||
VGfloat alast, a = 0.0f;
|
||||
VGfloat rx = width/2, ry = height/2;
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
||||
if (arcType != VGU_ARC_OPEN &&
|
||||
arcType != VGU_ARC_CHORD &&
|
||||
arcType != VGU_ARC_PIE)
|
||||
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
||||
startAngle = SH_DEG2RAD(startAngle);
|
||||
angleExtent = SH_DEG2RAD(angleExtent);
|
||||
alast = startAngle + angleExtent;
|
||||
|
||||
dataStart[0] = x + SH_COS(startAngle) * rx;
|
||||
dataStart[1] = y + SH_SIN(startAngle) * ry;
|
||||
err = shAppend(path, 1, commStart, 2, dataStart);
|
||||
if (err != VGU_NO_ERROR) return err;
|
||||
|
||||
dataArc[0] = rx;
|
||||
dataArc[1] = ry;
|
||||
dataArc[2] = 0.0f;
|
||||
|
||||
if (angleExtent > 0) {
|
||||
|
||||
a = startAngle + PI;
|
||||
while (a < alast) {
|
||||
dataArc[3] = x + SH_COS(a) * rx;
|
||||
dataArc[4] = y + SH_SIN(a) * ry;
|
||||
err = shAppend(path, 1, commArcCCW, 5, dataArc);
|
||||
if (err != VGU_NO_ERROR) return err;
|
||||
a += PI; }
|
||||
|
||||
dataArc[3] = x + SH_COS(alast) * rx;
|
||||
dataArc[4] = y + SH_SIN(alast) * ry;
|
||||
err = shAppend(path, 1, commArcCCW, 5, dataArc);
|
||||
if (err != VGU_NO_ERROR) return err;
|
||||
|
||||
}else{
|
||||
|
||||
a = startAngle - PI;
|
||||
while (a > alast) {
|
||||
dataArc[3] = x + SH_COS(a) * rx;
|
||||
dataArc[4] = y + SH_SIN(a) * ry;
|
||||
err = shAppend(path, 1, commArcCW, 5, dataArc);
|
||||
if (err != VGU_NO_ERROR) return err;
|
||||
a -= PI; }
|
||||
|
||||
dataArc[3] = x + SH_COS(alast) * rx;
|
||||
dataArc[4] = y + SH_SIN(alast) * ry;
|
||||
err = shAppend(path, 1, commArcCW, 5, dataArc);
|
||||
if (err != VGU_NO_ERROR) return err;
|
||||
}
|
||||
|
||||
|
||||
if (arcType == VGU_ARC_PIE) {
|
||||
dataEndPie[0] = x; dataEndPie[1] = y;
|
||||
err = shAppend(path, 2, commEndPie, 2, dataEndPie);
|
||||
}else if (arcType == VGU_ARC_CHORD) {
|
||||
err = shAppend(path, 1, commEndChord, 0, dataEndChord);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguComputeWarpQuadToSquare(VGfloat sx0, VGfloat sy0,
|
||||
VGfloat sx1, VGfloat sy1,
|
||||
VGfloat sx2, VGfloat sy2,
|
||||
VGfloat sx3, VGfloat sy3,
|
||||
VGfloat *matrix)
|
||||
{
|
||||
/* Basic idea taken from the reference implementation */
|
||||
VGfloat mat[3][3];
|
||||
VGfloat det, det00, det01, det02;
|
||||
|
||||
if( !matrix )
|
||||
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
||||
if( vguComputeWarpSquareToQuad( sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3,
|
||||
(VGfloat*)mat )
|
||||
== VGU_BAD_WARP_ERROR )
|
||||
return VGU_BAD_WARP_ERROR;
|
||||
|
||||
// Invert the matrix...
|
||||
det00 = mat[1][1]*mat[2][2] - mat[2][1]*mat[1][2];
|
||||
det01 = mat[2][0]*mat[1][2] - mat[1][0]*mat[2][2];
|
||||
det02 = mat[1][0]*mat[2][1] - mat[2][0]*mat[1][1];
|
||||
|
||||
det = mat[0][0]*det00 + mat[0][1]*det01 + mat[0][2]*det02;
|
||||
if( det == 0.0f )
|
||||
return VGU_BAD_WARP_ERROR;
|
||||
|
||||
det = 1 / det;
|
||||
|
||||
matrix[0] = det * det00;
|
||||
matrix[3] = det * det01;
|
||||
matrix[6] = det * det02;
|
||||
matrix[1] = det * (mat[2][1]*mat[0][2] - mat[0][1]*mat[2][2]);
|
||||
matrix[4] = det * (mat[0][0]*mat[2][2] - mat[2][0]*mat[0][2]);
|
||||
matrix[7] = det * (mat[2][0]*mat[0][1] - mat[0][0]*mat[2][1]);
|
||||
matrix[2] = det * (mat[0][1]*mat[1][2] - mat[1][1]*mat[0][2]);
|
||||
matrix[5] = det * (mat[1][0]*mat[0][2] - mat[0][0]*mat[1][2]);
|
||||
matrix[8] = det * (mat[0][0]*mat[1][1] - mat[1][0]*mat[0][1]);
|
||||
|
||||
return VGU_NO_ERROR;
|
||||
}
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguComputeWarpSquareToQuad(VGfloat dx0, VGfloat dy0,
|
||||
VGfloat dx1, VGfloat dy1,
|
||||
VGfloat dx2, VGfloat dy2,
|
||||
VGfloat dx3, VGfloat dy3,
|
||||
VGfloat * matrix)
|
||||
{
|
||||
/* Taken from https://github.com/mvr/shivavg (who has taken it from the
|
||||
reference implementation) */
|
||||
|
||||
VGfloat diffx1 = dx1 - dx3;
|
||||
VGfloat diffy1 = dy1 - dy3;
|
||||
VGfloat diffx2 = dx2 - dx3;
|
||||
VGfloat diffy2 = dy2 - dy3;
|
||||
|
||||
VGfloat det = diffx1*diffy2 - diffx2*diffy1;
|
||||
|
||||
VGfloat sumx = dx0 - dx1 + dx3 - dx2;
|
||||
VGfloat sumy = dy0 - dy1 + dy3 - dy2;
|
||||
|
||||
VGfloat g, h, oodet;
|
||||
|
||||
if(!matrix)
|
||||
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
||||
if(det == 0.0f)
|
||||
return VGU_BAD_WARP_ERROR;
|
||||
|
||||
if(sumx == 0.0f && sumy == 0.0f)
|
||||
{
|
||||
/* Affine mapping */
|
||||
matrix[0] = dx1 - dx0;
|
||||
matrix[1] = dy1 - dy0;
|
||||
matrix[2] = 0.0f;
|
||||
matrix[3] = dx3 - dx1;
|
||||
matrix[4] = dy3 - dy1;
|
||||
matrix[5] = 0.0f;
|
||||
matrix[6] = dx0;
|
||||
matrix[7] = dy0;
|
||||
matrix[8] = 1.0f;
|
||||
return VGU_NO_ERROR;
|
||||
}
|
||||
|
||||
oodet = 1.0f / det;
|
||||
g = (sumx*diffy2 - diffx2*sumy) * oodet;
|
||||
h = (diffx1*sumy - sumx*diffy1) * oodet;
|
||||
|
||||
matrix[0] = dx1-dx0+g*dx1;
|
||||
matrix[1] = dy1-dy0+g*dy1;
|
||||
matrix[2] = g;
|
||||
matrix[3] = dx2-dx0+h*dx2;
|
||||
matrix[4] = dy2-dy0+h*dy2;
|
||||
matrix[5] = h;
|
||||
matrix[6] = dx0;
|
||||
matrix[7] = dy0;
|
||||
matrix[8] = 1.0f;
|
||||
|
||||
return VGU_NO_ERROR;
|
||||
}
|
||||
|
||||
VGU_API_CALL VGUErrorCode vguComputeWarpQuadToQuad(VGfloat dx0, VGfloat dy0,
|
||||
VGfloat dx1, VGfloat dy1,
|
||||
VGfloat dx2, VGfloat dy2,
|
||||
VGfloat dx3, VGfloat dy3,
|
||||
VGfloat sx0, VGfloat sy0,
|
||||
VGfloat sx1, VGfloat sy1,
|
||||
VGfloat sx2, VGfloat sy2,
|
||||
VGfloat sx3, VGfloat sy3,
|
||||
VGfloat * matrix)
|
||||
{
|
||||
return VGU_NO_ERROR;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// osg::Operation to initialize the OpenVG context used for path rendering
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "VGInitOperation.hxx"
|
||||
#include <vg/openvg.h>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
VGInitOperation::VGInitOperation():
|
||||
GraphicsOperation("canvas::VGInit", false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void VGInitOperation::operator()(osg::GraphicsContext* context)
|
||||
{
|
||||
GLint vp[4];
|
||||
glGetIntegerv(GL_VIEWPORT, vp);
|
||||
|
||||
// ATTENTION: If using another OpenVG implementation ensure it doesn't
|
||||
// change any OpenGL state!
|
||||
vgCreateContextSH(vp[2], vp[3]);
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,46 +0,0 @@
|
||||
// osg::Operation to initialize the OpenVG context used for path rendering
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_VG_INITOPERATION_HXX_
|
||||
#define CANVAS_VG_INITOPERATION_HXX_
|
||||
|
||||
#include <osg/GraphicsThread>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
/**
|
||||
* Deferred graphics operation to setup OpenVG which needs a valid OpenGL
|
||||
* context. Pass to osg::GraphicsContext::add and ensure it's executed before
|
||||
* doing any path rendering
|
||||
*/
|
||||
class VGInitOperation:
|
||||
public osg::GraphicsOperation
|
||||
{
|
||||
public:
|
||||
|
||||
VGInitOperation();
|
||||
virtual void operator()(osg::GraphicsContext* context);
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_VG_INITOPERATION_HXX_ */
|
||||
@@ -1,94 +0,0 @@
|
||||
// Canvas forward declarations
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef SG_CANVAS_FWD_HXX_
|
||||
#define SG_CANVAS_FWD_HXX_
|
||||
|
||||
#include <simgear/props/propsfwd.hxx>
|
||||
#include <simgear/structure/SGWeakPtr.hxx>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osgText/Font>
|
||||
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/weak_ptr.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
#define SG_FWD_DECL(name)\
|
||||
class name;\
|
||||
typedef SGSharedPtr<name> name##Ptr;\
|
||||
typedef SGWeakPtr<name> name##WeakPtr;
|
||||
|
||||
SG_FWD_DECL(Canvas)
|
||||
SG_FWD_DECL(Element)
|
||||
SG_FWD_DECL(Group)
|
||||
SG_FWD_DECL(Image)
|
||||
SG_FWD_DECL(Map)
|
||||
SG_FWD_DECL(Path)
|
||||
SG_FWD_DECL(Text)
|
||||
SG_FWD_DECL(Window)
|
||||
|
||||
SG_FWD_DECL(Event)
|
||||
SG_FWD_DECL(CustomEvent)
|
||||
SG_FWD_DECL(MouseEvent)
|
||||
|
||||
#undef SG_FWD_DECL
|
||||
|
||||
#define SG_FWD_DECL(name)\
|
||||
class name;\
|
||||
typedef boost::shared_ptr<name> name##Ptr;\
|
||||
typedef boost::weak_ptr<name> name##WeakPtr;
|
||||
|
||||
SG_FWD_DECL(Placement)
|
||||
SG_FWD_DECL(SystemAdapter)
|
||||
|
||||
#undef SG_FWD_DECL
|
||||
|
||||
class EventManager;
|
||||
class EventVisitor;
|
||||
|
||||
struct EventTarget;
|
||||
typedef std::deque<EventTarget> EventPropagationPath;
|
||||
|
||||
typedef std::map<std::string, const SGPropertyNode*> Style;
|
||||
typedef ElementPtr (*ElementFactory)( const CanvasWeakPtr&,
|
||||
const SGPropertyNode_ptr&,
|
||||
const Style&,
|
||||
Element* );
|
||||
|
||||
typedef osg::ref_ptr<osgText::Font> FontPtr;
|
||||
|
||||
typedef std::vector<PlacementPtr> Placements;
|
||||
typedef boost::function<Placements( SGPropertyNode*,
|
||||
CanvasPtr )> PlacementFactory;
|
||||
|
||||
typedef boost::function<void(const EventPtr&)> EventListener;
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
|
||||
#endif /* SG_CANVAS_FWD_HXX_ */
|
||||
@@ -1,31 +0,0 @@
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS
|
||||
CanvasElement.hxx
|
||||
CanvasGroup.hxx
|
||||
CanvasImage.hxx
|
||||
CanvasMap.hxx
|
||||
CanvasPath.hxx
|
||||
CanvasText.hxx
|
||||
)
|
||||
|
||||
set(DETAIL_HEADERS
|
||||
detail/add_segment_variadic.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
CanvasElement.cxx
|
||||
CanvasGroup.cxx
|
||||
CanvasImage.cxx
|
||||
CanvasMap.cxx
|
||||
CanvasPath.cxx
|
||||
CanvasText.cxx
|
||||
)
|
||||
|
||||
simgear_scene_component(canvas-elements canvas/elements "${SOURCES}" "${HEADERS}")
|
||||
simgear_component(canvas-elements/detail canvas/elements/detail "" "${DETAIL_HEADERS}")
|
||||
|
||||
add_boost_test(canvas_element
|
||||
SOURCES canvas_element_test.cpp
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
@@ -1,851 +0,0 @@
|
||||
// Interface for 2D Canvas element
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/CanvasEventVisitor.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/math/SGMisc.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/scene/material/parseBlendFunc.hxx>
|
||||
|
||||
#include <osg/Drawable>
|
||||
#include <osg/Geode>
|
||||
#include <osg/Scissor>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
const std::string NAME_TRANSFORM = "tf";
|
||||
|
||||
/**
|
||||
* glScissor with coordinates relative to different reference frames.
|
||||
*/
|
||||
class Element::RelativeScissor:
|
||||
public osg::Scissor
|
||||
{
|
||||
public:
|
||||
|
||||
ReferenceFrame _coord_reference;
|
||||
osg::observer_ptr<osg::Node> _node;
|
||||
|
||||
RelativeScissor(osg::Node* node = NULL):
|
||||
_coord_reference(GLOBAL),
|
||||
_node(node)
|
||||
{
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
}
|
||||
|
||||
/** Copy constructor using CopyOp to manage deep vs shallow copy. */
|
||||
RelativeScissor( const RelativeScissor& vp,
|
||||
const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ):
|
||||
Scissor(vp, copyop),
|
||||
_coord_reference(vp._coord_reference),
|
||||
_node(vp._node)
|
||||
{}
|
||||
|
||||
META_StateAttribute(simgear, RelativeScissor, SCISSOR);
|
||||
|
||||
/** Return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. */
|
||||
virtual int compare(const StateAttribute& sa) const
|
||||
{
|
||||
// check the types are equal and then create the rhs variable
|
||||
// used by the COMPARE_StateAttribute_Parameter macros below.
|
||||
COMPARE_StateAttribute_Types(RelativeScissor,sa)
|
||||
|
||||
// compare each parameter in turn against the rhs.
|
||||
COMPARE_StateAttribute_Parameter(_x)
|
||||
COMPARE_StateAttribute_Parameter(_y)
|
||||
COMPARE_StateAttribute_Parameter(_width)
|
||||
COMPARE_StateAttribute_Parameter(_height)
|
||||
COMPARE_StateAttribute_Parameter(_coord_reference)
|
||||
COMPARE_StateAttribute_Parameter(_node)
|
||||
|
||||
return 0; // passed all the above comparison macros, must be equal.
|
||||
}
|
||||
|
||||
virtual void apply(osg::State& state) const
|
||||
{
|
||||
if( _width <= 0 || _height <= 0 )
|
||||
return;
|
||||
|
||||
const osg::Viewport* vp = state.getCurrentViewport();
|
||||
float w2 = 0.5 * vp->width(),
|
||||
h2 = 0.5 * vp->height();
|
||||
|
||||
osg::Matrix model_view
|
||||
(
|
||||
w2, 0, 0, 0,
|
||||
0, h2, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
w2, h2, 0, 1
|
||||
);
|
||||
model_view.preMult(state.getProjectionMatrix());
|
||||
|
||||
if( _coord_reference != GLOBAL )
|
||||
{
|
||||
osg::Node* ref_obj = _node.get();
|
||||
|
||||
if( _coord_reference == PARENT )
|
||||
{
|
||||
if( _node->getNumParents() < 1 )
|
||||
{
|
||||
SG_LOG(SG_GL, SG_WARN, "RelativeScissor: missing parent.");
|
||||
return;
|
||||
}
|
||||
|
||||
ref_obj = _node->getParent(0);
|
||||
}
|
||||
|
||||
osg::MatrixList const& parent_matrices = ref_obj->getWorldMatrices();
|
||||
assert( !parent_matrices.empty() );
|
||||
model_view.preMult(parent_matrices.front());
|
||||
}
|
||||
|
||||
const osg::Vec2 scale( model_view(0,0), model_view(1,1)),
|
||||
offset(model_view(3,0), model_view(3,1));
|
||||
|
||||
// TODO check/warn for rotation?
|
||||
GLint x = SGMiscf::roundToInt(scale.x() * _x + offset.x()),
|
||||
y = SGMiscf::roundToInt(scale.y() * _y + offset.y()),
|
||||
w = SGMiscf::roundToInt(std::fabs(scale.x()) * _width),
|
||||
h = SGMiscf::roundToInt(std::fabs(scale.y()) * _height);
|
||||
|
||||
if( scale.x() < 0 )
|
||||
x -= w;
|
||||
if( scale.y() < 0 )
|
||||
y -= h;
|
||||
|
||||
glScissor(x, y, w, h);
|
||||
}
|
||||
|
||||
bool contains(const osg::Vec2f& pos) const
|
||||
{
|
||||
return _x <= pos.x() && pos.x() <= _x + _width
|
||||
&& _y <= pos.y() && pos.y() <= _y + _height;
|
||||
}
|
||||
|
||||
bool contains( const osg::Vec2f& global_pos,
|
||||
const osg::Vec2f& parent_pos,
|
||||
const osg::Vec2f& local_pos ) const
|
||||
{
|
||||
switch( _coord_reference )
|
||||
{
|
||||
case GLOBAL: return contains(global_pos);
|
||||
case PARENT: return contains(parent_pos);
|
||||
case LOCAL: return contains(local_pos);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Element::OSGUserData::OSGUserData(ElementPtr element):
|
||||
element(element)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Element::~Element()
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return;
|
||||
|
||||
for(unsigned int i = 0; i < _transform->getNumChildren(); ++i)
|
||||
{
|
||||
OSGUserData* ud =
|
||||
static_cast<OSGUserData*>(_transform->getChild(i)->getUserData());
|
||||
|
||||
if( ud )
|
||||
// Ensure parent is cleared to prevent accessing released memory if an
|
||||
// element somehow survives longer than his parent.
|
||||
ud->element->_parent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::onDestroy()
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return;
|
||||
|
||||
// The transform node keeps a reference on this element, so ensure it is
|
||||
// deleted.
|
||||
BOOST_FOREACH(osg::Group* parent, _transform->getParents())
|
||||
{
|
||||
parent->removeChild(_transform.get());
|
||||
}
|
||||
|
||||
// Hide in case someone still holds a reference
|
||||
setVisible(false);
|
||||
removeListener();
|
||||
|
||||
_parent = 0;
|
||||
_transform = 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Element::getParent() const
|
||||
{
|
||||
return _parent;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasWeakPtr Element::getCanvas() const
|
||||
{
|
||||
return _canvas;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::update(double dt)
|
||||
{
|
||||
if( !isVisible() )
|
||||
return;
|
||||
|
||||
// Trigger matrix update
|
||||
getMatrix();
|
||||
|
||||
// Update bounding box on manual update (manual updates pass zero dt)
|
||||
if( dt == 0 && _drawable )
|
||||
_drawable->getBound();
|
||||
|
||||
if( (_attributes_dirty & BLEND_FUNC) && _transform.valid() )
|
||||
{
|
||||
parseBlendFunc(
|
||||
_transform->getOrCreateStateSet(),
|
||||
_node->getChild("blend-source"),
|
||||
_node->getChild("blend-destination"),
|
||||
_node->getChild("blend-source-rgb"),
|
||||
_node->getChild("blend-destination-rgb"),
|
||||
_node->getChild("blend-source-alpha"),
|
||||
_node->getChild("blend-destination-alpha")
|
||||
);
|
||||
_attributes_dirty &= ~BLEND_FUNC;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::addEventListener( const std::string& type_str,
|
||||
const EventListener& cb )
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_INFO,
|
||||
"addEventListener(" << _node->getPath() << ", " << type_str << ")"
|
||||
);
|
||||
|
||||
_listener[ Event::getOrRegisterType(type_str) ].push_back(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::clearEventListener()
|
||||
{
|
||||
_listener.clear();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::accept(EventVisitor& visitor)
|
||||
{
|
||||
if( !isVisible() )
|
||||
return false;
|
||||
|
||||
return visitor.apply(*this);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::ascend(EventVisitor& visitor)
|
||||
{
|
||||
if( _parent )
|
||||
return _parent->accept(visitor);
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::traverse(EventVisitor& visitor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::handleEvent(const EventPtr& event)
|
||||
{
|
||||
ListenerMap::iterator listeners = _listener.find(event->getType());
|
||||
if( listeners == _listener.end() )
|
||||
return false;
|
||||
|
||||
BOOST_FOREACH(EventListener const& listener, listeners->second)
|
||||
try
|
||||
{
|
||||
listener(event);
|
||||
}
|
||||
catch( std::exception const& ex )
|
||||
{
|
||||
SG_LOG(
|
||||
SG_GENERAL,
|
||||
SG_WARN,
|
||||
"canvas::Element: event handler error: '" << ex.what() << "'"
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::dispatchEvent(const EventPtr& event)
|
||||
{
|
||||
EventPropagationPath path;
|
||||
path.push_back( EventTarget(this) );
|
||||
|
||||
for( Element* parent = _parent;
|
||||
parent != NULL;
|
||||
parent = parent->_parent )
|
||||
path.push_front( EventTarget(parent) );
|
||||
|
||||
CanvasPtr canvas = _canvas.lock();
|
||||
if( !canvas )
|
||||
return false;
|
||||
|
||||
return canvas->propagateEvent(event, path);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::hitBound( const osg::Vec2f& global_pos,
|
||||
const osg::Vec2f& parent_pos,
|
||||
const osg::Vec2f& local_pos ) const
|
||||
{
|
||||
if( _scissor && !_scissor->contains(global_pos, parent_pos, local_pos) )
|
||||
return false;
|
||||
|
||||
const osg::Vec3f pos3(parent_pos, 0);
|
||||
|
||||
// Drawables have a bounding box...
|
||||
if( _drawable )
|
||||
return _drawable->getBound().contains(osg::Vec3f(local_pos, 0));
|
||||
else if( _transform.valid() )
|
||||
// ... for other elements, i.e. groups only a bounding sphere is available
|
||||
return _transform->getBound().contains(osg::Vec3f(parent_pos, 0));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setVisible(bool visible)
|
||||
{
|
||||
if( _transform.valid() )
|
||||
// TODO check if we need another nodemask
|
||||
_transform->setNodeMask(visible ? 0xffffffff : 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::isVisible() const
|
||||
{
|
||||
return _transform.valid() && _transform->getNodeMask() != 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::MatrixTransform* Element::getMatrixTransform()
|
||||
{
|
||||
return _transform.get();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::MatrixTransform const* Element::getMatrixTransform() const
|
||||
{
|
||||
return _transform.get();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const
|
||||
{
|
||||
getMatrix();
|
||||
const osg::Matrix& m = _transform->getInverseMatrix();
|
||||
return osg::Vec2f
|
||||
(
|
||||
m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
|
||||
m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1)
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
|
||||
{
|
||||
if( parent == _node
|
||||
&& child->getNameString() == NAME_TRANSFORM )
|
||||
{
|
||||
if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
|
||||
_transform_types.resize( child->getIndex() + 1 );
|
||||
|
||||
_transform_types[ child->getIndex() ] = TT_NONE;
|
||||
_attributes_dirty |= TRANSFORM;
|
||||
return;
|
||||
}
|
||||
else if( parent->getParent() == _node
|
||||
&& parent->getNameString() == NAME_TRANSFORM )
|
||||
{
|
||||
assert(parent->getIndex() < static_cast<int>(_transform_types.size()));
|
||||
|
||||
const std::string& name = child->getNameString();
|
||||
|
||||
TransformType& type = _transform_types[parent->getIndex()];
|
||||
|
||||
if( name == "m" )
|
||||
type = TT_MATRIX;
|
||||
else if( name == "t" )
|
||||
type = TT_TRANSLATE;
|
||||
else if( name == "rot" )
|
||||
type = TT_ROTATE;
|
||||
else if( name == "s" )
|
||||
type = TT_SCALE;
|
||||
|
||||
_attributes_dirty |= TRANSFORM;
|
||||
return;
|
||||
}
|
||||
|
||||
childAdded(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
|
||||
{
|
||||
if( parent == _node )
|
||||
{
|
||||
if( child->getNameString() == NAME_TRANSFORM )
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return;
|
||||
|
||||
if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_WARN,
|
||||
"Element::childRemoved: unknown transform: " << child->getPath()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
_transform_types[ child->getIndex() ] = TT_NONE;
|
||||
|
||||
while( !_transform_types.empty() && _transform_types.back() == TT_NONE )
|
||||
_transform_types.pop_back();
|
||||
|
||||
_attributes_dirty |= TRANSFORM;
|
||||
return;
|
||||
}
|
||||
else if( StyleInfo const* style = getStyleInfo(child->getNameString()) )
|
||||
{
|
||||
if( setStyle(getParentStyle(child), style) )
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
childRemoved(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::valueChanged(SGPropertyNode* child)
|
||||
{
|
||||
SGPropertyNode *parent = child->getParent();
|
||||
if( parent == _node )
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
if( boost::starts_with(name, "data-") )
|
||||
return;
|
||||
else if( StyleInfo const* style_info = getStyleInfo(name) )
|
||||
{
|
||||
SGPropertyNode const* style = child;
|
||||
if( isStyleEmpty(child) )
|
||||
{
|
||||
child->clearValue();
|
||||
style = getParentStyle(child);
|
||||
}
|
||||
setStyle(style, style_info);
|
||||
return;
|
||||
}
|
||||
else if( name == "update" )
|
||||
return update(0);
|
||||
else if( boost::starts_with(name, "blend-") )
|
||||
return (void)(_attributes_dirty |= BLEND_FUNC);
|
||||
}
|
||||
else if( parent
|
||||
&& parent->getParent() == _node
|
||||
&& parent->getNameString() == NAME_TRANSFORM )
|
||||
{
|
||||
_attributes_dirty |= TRANSFORM;
|
||||
return;
|
||||
}
|
||||
|
||||
childChanged(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::setStyle( const SGPropertyNode* child,
|
||||
const StyleInfo* style_info )
|
||||
{
|
||||
return canApplyStyle(child) && setStyleImpl(child, style_info);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setClip(const std::string& clip)
|
||||
{
|
||||
osg::StateSet* ss = getOrCreateStateSet();
|
||||
if( !ss )
|
||||
return;
|
||||
|
||||
if( clip.empty() || clip == "auto" )
|
||||
{
|
||||
ss->removeAttribute(osg::StateAttribute::SCISSOR);
|
||||
_scissor = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO generalize CSS property parsing
|
||||
const std::string RECT("rect(");
|
||||
if( !boost::ends_with(clip, ")")
|
||||
|| !boost::starts_with(clip, RECT) )
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string sep(", \t\npx");
|
||||
int comp = 0;
|
||||
float values[4];
|
||||
|
||||
for(size_t pos = RECT.size(); comp < 4; ++comp)
|
||||
{
|
||||
pos = clip.find_first_not_of(sep, pos);
|
||||
if( pos == std::string::npos || pos == clip.size() - 1 )
|
||||
break;
|
||||
|
||||
char *end = 0;
|
||||
values[comp] = strtod(&clip[pos], &end);
|
||||
if( end == &clip[pos] || !end )
|
||||
break;
|
||||
|
||||
pos = end - &clip[0];
|
||||
}
|
||||
|
||||
if( comp < 4 )
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip);
|
||||
return;
|
||||
}
|
||||
|
||||
float width = values[1] - values[3],
|
||||
height = values[2] - values[0];
|
||||
|
||||
if( width < 0 || height < 0 )
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "Canvas: negative clip size: " << clip);
|
||||
return;
|
||||
}
|
||||
|
||||
if( !_scissor )
|
||||
_scissor = new RelativeScissor(_transform.get());
|
||||
|
||||
// <top>, <right>, <bottom>, <left>
|
||||
_scissor->x() = SGMiscf::roundToInt(values[3]);
|
||||
_scissor->y() = SGMiscf::roundToInt(values[0]);
|
||||
_scissor->width() = SGMiscf::roundToInt(width);
|
||||
_scissor->height() = SGMiscf::roundToInt(height);
|
||||
|
||||
SGPropertyNode* clip_frame = _node->getChild("clip-frame", 0);
|
||||
if( clip_frame )
|
||||
valueChanged(clip_frame);
|
||||
else
|
||||
_scissor->_coord_reference = GLOBAL;
|
||||
|
||||
ss->setAttributeAndModes(_scissor);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setClipFrame(ReferenceFrame rf)
|
||||
{
|
||||
if( _scissor )
|
||||
_scissor->_coord_reference = rf;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Element::getBoundingBox() const
|
||||
{
|
||||
if( _drawable )
|
||||
return _drawable->getBound();
|
||||
|
||||
osg::BoundingBox bb;
|
||||
|
||||
if( _transform.valid() )
|
||||
bb.expandBy(_transform->getBound());
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Element::getTightBoundingBox() const
|
||||
{
|
||||
return getTransformedBounds(getMatrix());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Element::getTransformedBounds(const osg::Matrix& m) const
|
||||
{
|
||||
if( !_drawable )
|
||||
return osg::BoundingBox();
|
||||
|
||||
osg::BoundingBox transformed;
|
||||
const osg::BoundingBox& bb = _drawable->getBound();
|
||||
for(int i = 0; i < 4; ++i)
|
||||
transformed.expandBy( bb.corner(i) * m );
|
||||
|
||||
return transformed;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Matrix Element::getMatrix() const
|
||||
{
|
||||
if( !_transform )
|
||||
return osg::Matrix::identity();
|
||||
|
||||
if( !(_attributes_dirty & TRANSFORM) )
|
||||
return _transform->getMatrix();
|
||||
|
||||
osg::Matrix m;
|
||||
for( size_t i = 0; i < _transform_types.size(); ++i )
|
||||
{
|
||||
// Skip unused indizes...
|
||||
if( _transform_types[i] == TT_NONE )
|
||||
continue;
|
||||
|
||||
SGPropertyNode* tf_node = _node->getChild("tf", i, true);
|
||||
|
||||
// Build up the matrix representation of the current transform node
|
||||
osg::Matrix tf;
|
||||
switch( _transform_types[i] )
|
||||
{
|
||||
case TT_MATRIX:
|
||||
tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
|
||||
tf_node->getDoubleValue("m[1]", 0),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[6]", 0),
|
||||
|
||||
tf_node->getDoubleValue("m[2]", 0),
|
||||
tf_node->getDoubleValue("m[3]", 1),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[7]", 0),
|
||||
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
|
||||
tf_node->getDoubleValue("m[4]", 0),
|
||||
tf_node->getDoubleValue("m[5]", 0),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[8]", 1) );
|
||||
break;
|
||||
case TT_TRANSLATE:
|
||||
tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
|
||||
tf_node->getDoubleValue("t[1]", 0),
|
||||
0 ) );
|
||||
break;
|
||||
case TT_ROTATE:
|
||||
tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
|
||||
break;
|
||||
case TT_SCALE:
|
||||
{
|
||||
float sx = tf_node->getDoubleValue("s[0]", 1);
|
||||
// sy defaults to sx...
|
||||
tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m.postMult( tf );
|
||||
}
|
||||
_transform->setMatrix(m);
|
||||
_attributes_dirty &= ~TRANSFORM;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Element::StyleSetters Element::_style_setters;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Element::Element( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
PropertyBasedElement(node),
|
||||
_canvas( canvas ),
|
||||
_parent( parent ),
|
||||
_attributes_dirty( 0 ),
|
||||
_transform( new osg::MatrixTransform ),
|
||||
_style( parent_style ),
|
||||
_scissor( 0 ),
|
||||
_drawable( 0 )
|
||||
{
|
||||
staticInit();
|
||||
|
||||
SG_LOG
|
||||
(
|
||||
SG_GL,
|
||||
SG_DEBUG,
|
||||
"New canvas element " << node->getPath()
|
||||
);
|
||||
|
||||
// Ensure elements are drawn in order they appear in the element tree
|
||||
_transform->getOrCreateStateSet()
|
||||
->setRenderBinDetails
|
||||
(
|
||||
0,
|
||||
"PreOrderBin",
|
||||
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
|
||||
);
|
||||
|
||||
_transform->setUserData( new OSGUserData(this) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::staticInit()
|
||||
{
|
||||
if( isInit<Element>() )
|
||||
return;
|
||||
|
||||
addStyle("clip", "", &Element::setClip, false);
|
||||
addStyle("clip-frame", "", &Element::setClipFrame, false);
|
||||
addStyle("visible", "", &Element::setVisible, false);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::isStyleEmpty(const SGPropertyNode* child) const
|
||||
{
|
||||
return !child
|
||||
|| simgear::strutils::strip(child->getStringValue()).empty();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::canApplyStyle(const SGPropertyNode* child) const
|
||||
{
|
||||
if( _node == child->getParent() )
|
||||
return true;
|
||||
|
||||
// Parent values do not override if element has own value
|
||||
return isStyleEmpty( _node->getChild(child->getName()) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::setStyleImpl( const SGPropertyNode* child,
|
||||
const StyleInfo* style_info )
|
||||
{
|
||||
const StyleSetter* style_setter = style_info
|
||||
? &style_info->setter
|
||||
: getStyleSetter(child->getNameString());
|
||||
while( style_setter )
|
||||
{
|
||||
if( style_setter->func(*this, child) )
|
||||
return true;
|
||||
style_setter = style_setter->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const Element::StyleInfo*
|
||||
Element::getStyleInfo(const std::string& name) const
|
||||
{
|
||||
StyleSetters::const_iterator setter = _style_setters.find(name);
|
||||
if( setter == _style_setters.end() )
|
||||
return 0;
|
||||
|
||||
return &setter->second;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const Element::StyleSetter*
|
||||
Element::getStyleSetter(const std::string& name) const
|
||||
{
|
||||
const StyleInfo* info = getStyleInfo(name);
|
||||
return info ? &info->setter : 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const SGPropertyNode*
|
||||
Element::getParentStyle(const SGPropertyNode* child) const
|
||||
{
|
||||
// Try to get value from parent...
|
||||
if( _parent )
|
||||
{
|
||||
Style::const_iterator style =
|
||||
_parent->_style.find(child->getNameString());
|
||||
if( style != _parent->_style.end() )
|
||||
return style->second;
|
||||
}
|
||||
|
||||
// ...or reset to default if none is available
|
||||
return child; // TODO somehow get default value for each style?
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setDrawable( osg::Drawable* drawable )
|
||||
{
|
||||
_drawable = drawable;
|
||||
assert( _drawable );
|
||||
|
||||
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
|
||||
geode->addDrawable(_drawable);
|
||||
_transform->addChild(geode);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::StateSet* Element::getOrCreateStateSet()
|
||||
{
|
||||
if( _drawable.valid() )
|
||||
return _drawable->getOrCreateStateSet();
|
||||
if( _transform.valid() )
|
||||
return _transform->getOrCreateStateSet();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setupStyle()
|
||||
{
|
||||
BOOST_FOREACH( Style::value_type style, _style )
|
||||
setStyle(style.second);
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,627 +0,0 @@
|
||||
///@file Interface for 2D Canvas elements
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_ELEMENT_HXX_
|
||||
#define CANVAS_ELEMENT_HXX_
|
||||
|
||||
#include <simgear/canvas/canvas_fwd.hxx>
|
||||
#include <simgear/canvas/CanvasEvent.hxx>
|
||||
#include <simgear/props/PropertyBasedElement.hxx>
|
||||
#include <simgear/misc/stdint.hxx> // for uint32_t
|
||||
|
||||
#include <osg/BoundingBox>
|
||||
#include <osg/MatrixTransform>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/type_traits/is_base_of.hpp>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Drawable;
|
||||
}
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
/**
|
||||
* Baseclass for Elements displayed inside a Canvas.
|
||||
*/
|
||||
class Element:
|
||||
public PropertyBasedElement
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Store pointer to window as user data
|
||||
*/
|
||||
class OSGUserData:
|
||||
public osg::Referenced
|
||||
{
|
||||
public:
|
||||
ElementPtr element;
|
||||
OSGUserData(ElementPtr element);
|
||||
};
|
||||
|
||||
typedef boost::function<bool(Element&, const SGPropertyNode*)>
|
||||
StyleSetterFunc;
|
||||
typedef boost::function<void(Element&, const SGPropertyNode*)>
|
||||
StyleSetterFuncUnchecked;
|
||||
struct StyleSetter:
|
||||
public SGReferenced
|
||||
{
|
||||
StyleSetterFunc func;
|
||||
SGSharedPtr<StyleSetter> next;
|
||||
};
|
||||
struct StyleInfo
|
||||
{
|
||||
StyleSetter setter; ///< Function(s) for setting this style
|
||||
std::string type; ///< Interpolation type
|
||||
bool inheritable; ///< Whether children can inherit this style from
|
||||
/// their parents
|
||||
};
|
||||
|
||||
/**
|
||||
* Coordinate reference frame (eg. "clip" property)
|
||||
*/
|
||||
enum ReferenceFrame
|
||||
{
|
||||
GLOBAL, ///< Global coordinates
|
||||
PARENT, ///< Coordinates relative to parent coordinate frame
|
||||
LOCAL ///< Coordinates relative to local coordinates (parent
|
||||
/// coordinates with local transformations applied)
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
virtual ~Element() = 0;
|
||||
virtual void onDestroy();
|
||||
|
||||
ElementPtr getParent() const;
|
||||
CanvasWeakPtr getCanvas() const;
|
||||
|
||||
/**
|
||||
* Called every frame to update internal state
|
||||
*
|
||||
* @param dt Frame time in seconds
|
||||
*/
|
||||
virtual void update(double dt);
|
||||
|
||||
bool addEventListener(const std::string& type, const EventListener& cb);
|
||||
virtual void clearEventListener();
|
||||
|
||||
virtual bool accept(EventVisitor& visitor);
|
||||
virtual bool ascend(EventVisitor& visitor);
|
||||
virtual bool traverse(EventVisitor& visitor);
|
||||
|
||||
virtual bool handleEvent(const EventPtr& event);
|
||||
bool dispatchEvent(const EventPtr& event);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param global_pos Position in global (canvas) coordinate frame
|
||||
* @param parent_pos Position in parent coordinate frame
|
||||
* @param local_pos Position in local (element) coordinate frame
|
||||
*/
|
||||
virtual bool hitBound( const osg::Vec2f& global_pos,
|
||||
const osg::Vec2f& parent_pos,
|
||||
const osg::Vec2f& local_pos ) const;
|
||||
|
||||
/**
|
||||
* Set visibility of the element.
|
||||
*/
|
||||
void setVisible(bool visible);
|
||||
|
||||
/**
|
||||
* Get whether the element is visible or hidden.
|
||||
*/
|
||||
bool isVisible() const;
|
||||
|
||||
osg::MatrixTransform* getMatrixTransform();
|
||||
osg::MatrixTransform const* getMatrixTransform() const;
|
||||
|
||||
/**
|
||||
* Transform position to local coordinages.
|
||||
*/
|
||||
osg::Vec2f posToLocal(const osg::Vec2f& pos) const;
|
||||
|
||||
virtual void childAdded( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void childRemoved( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void valueChanged(SGPropertyNode * child);
|
||||
|
||||
virtual bool setStyle( const SGPropertyNode* child,
|
||||
const StyleInfo* style_info = 0 );
|
||||
|
||||
/**
|
||||
* Set clipping shape
|
||||
*
|
||||
* @note Only "rect(<top>, <right>, <bottom>, <left>)" is supported
|
||||
* @see http://www.w3.org/TR/CSS21/visufx.html#propdef-clip
|
||||
*/
|
||||
void setClip(const std::string& clip);
|
||||
|
||||
/**
|
||||
* Clipping coordinates reference frame
|
||||
*/
|
||||
void setClipFrame(ReferenceFrame rf);
|
||||
|
||||
/**
|
||||
* Get bounding box (may not be as tight as bounding box returned by
|
||||
* #getTightBoundingBox)
|
||||
*/
|
||||
osg::BoundingBox getBoundingBox() const;
|
||||
|
||||
/**
|
||||
* Get tight bounding box (child points are transformed to elements
|
||||
* coordinate space before calculating the bounding box).
|
||||
*/
|
||||
osg::BoundingBox getTightBoundingBox() const;
|
||||
|
||||
/**
|
||||
* Get bounding box with children/drawables transformed by passed matrix
|
||||
*/
|
||||
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
||||
|
||||
/**
|
||||
* Get the transformation matrix (product of all transforms)
|
||||
*/
|
||||
osg::Matrix getMatrix() const;
|
||||
|
||||
/**
|
||||
* Create an canvas Element
|
||||
*
|
||||
* @tparam Derived Type of element (needs to derive from Element)
|
||||
*/
|
||||
template<typename Derived>
|
||||
static
|
||||
typename boost::enable_if<
|
||||
boost::is_base_of<Element, Derived>,
|
||||
ElementPtr
|
||||
>::type create( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& style = Style(),
|
||||
Element* parent = NULL )
|
||||
{
|
||||
return ElementPtr( new Derived(canvas, node, style, parent) );
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
enum Attributes
|
||||
{
|
||||
TRANSFORM = 1,
|
||||
BLEND_FUNC = TRANSFORM << 1,
|
||||
LAST_ATTRIBUTE = BLEND_FUNC << 1
|
||||
};
|
||||
|
||||
enum TransformType
|
||||
{
|
||||
TT_NONE,
|
||||
TT_MATRIX,
|
||||
TT_TRANSLATE,
|
||||
TT_ROTATE,
|
||||
TT_SCALE
|
||||
};
|
||||
|
||||
class RelativeScissor;
|
||||
|
||||
CanvasWeakPtr _canvas;
|
||||
Element *_parent;
|
||||
|
||||
mutable uint32_t _attributes_dirty;
|
||||
|
||||
osg::observer_ptr<osg::MatrixTransform> _transform;
|
||||
std::vector<TransformType> _transform_types;
|
||||
|
||||
Style _style;
|
||||
RelativeScissor *_scissor;
|
||||
|
||||
typedef std::vector<EventListener> Listener;
|
||||
typedef std::map<int, Listener> ListenerMap;
|
||||
|
||||
ListenerMap _listener;
|
||||
|
||||
typedef std::map<std::string, StyleInfo> StyleSetters;
|
||||
static StyleSetters _style_setters;
|
||||
|
||||
static void staticInit();
|
||||
|
||||
Element( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent );
|
||||
|
||||
/**
|
||||
* Returns false on first call and true on any successive call. Use to
|
||||
* perform initialization tasks which are only required once per element
|
||||
* type.
|
||||
*
|
||||
* @tparam Derived (Derived) class type
|
||||
*/
|
||||
template<class Derived>
|
||||
static bool isInit()
|
||||
{
|
||||
static bool is_init = false;
|
||||
if( is_init )
|
||||
return true;
|
||||
|
||||
is_init = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a function for setting a style specified by the given property
|
||||
*
|
||||
* @param name Property name
|
||||
* @param type Interpolation type
|
||||
* @param setter Setter function
|
||||
*
|
||||
* @tparam T1 Type of value used to retrieve value from property
|
||||
* node
|
||||
* @tparam T2 Type of value the setter function expects
|
||||
* @tparam Derived Type of class the setter can be applied to
|
||||
*
|
||||
* @note T1 needs to be convertible to T2
|
||||
*/
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
class Derived
|
||||
>
|
||||
static
|
||||
StyleSetter
|
||||
addStyle( const std::string& name,
|
||||
const std::string& type,
|
||||
const boost::function<void (Derived&, T2)>& setter,
|
||||
bool inheritable = true )
|
||||
{
|
||||
StyleInfo& style_info = _style_setters[ name ];
|
||||
if( !type.empty() )
|
||||
{
|
||||
if( !style_info.type.empty() && type != style_info.type )
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_WARN,
|
||||
"changing animation type for '" << name << "': "
|
||||
<< style_info.type << " -> " << type
|
||||
);
|
||||
|
||||
style_info.type = type;
|
||||
}
|
||||
// TODO check if changed?
|
||||
style_info.inheritable = inheritable;
|
||||
|
||||
StyleSetter* style = &style_info.setter;
|
||||
while( style->next )
|
||||
style = style->next;
|
||||
if( style->func )
|
||||
style = style->next = new StyleSetter;
|
||||
|
||||
style->func = boost::bind
|
||||
(
|
||||
&type_match<Derived>::call,
|
||||
_1,
|
||||
_2,
|
||||
bindStyleSetter<T1>(name, setter)
|
||||
);
|
||||
return *style;
|
||||
}
|
||||
|
||||
template<
|
||||
typename T,
|
||||
class Derived
|
||||
>
|
||||
static
|
||||
StyleSetter
|
||||
addStyle( const std::string& name,
|
||||
const std::string& type,
|
||||
const boost::function<void (Derived&, T)>& setter,
|
||||
bool inheritable = true )
|
||||
{
|
||||
return addStyle<T, T>(name, type, setter, inheritable);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T,
|
||||
class Derived
|
||||
>
|
||||
static
|
||||
StyleSetter
|
||||
addStyle( const std::string& name,
|
||||
const std::string& type,
|
||||
void (Derived::*setter)(T),
|
||||
bool inheritable = true )
|
||||
{
|
||||
return addStyle<T, T>
|
||||
(
|
||||
name,
|
||||
type,
|
||||
boost::function<void (Derived&, T)>(setter),
|
||||
inheritable
|
||||
);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
class Derived
|
||||
>
|
||||
static
|
||||
StyleSetterFunc
|
||||
addStyle( const std::string& name,
|
||||
const std::string& type,
|
||||
void (Derived::*setter)(T2),
|
||||
bool inheritable = true )
|
||||
{
|
||||
return addStyle<T1>
|
||||
(
|
||||
name,
|
||||
type,
|
||||
boost::function<void (Derived&, T2)>(setter),
|
||||
inheritable
|
||||
);
|
||||
}
|
||||
|
||||
template<
|
||||
class Derived
|
||||
>
|
||||
static
|
||||
StyleSetter
|
||||
addStyle( const std::string& name,
|
||||
const std::string& type,
|
||||
void (Derived::*setter)(const std::string&),
|
||||
bool inheritable = true )
|
||||
{
|
||||
return addStyle<const char*, const std::string&>
|
||||
(
|
||||
name,
|
||||
type,
|
||||
boost::function<void (Derived&, const std::string&)>(setter),
|
||||
inheritable
|
||||
);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T,
|
||||
class Derived,
|
||||
class Other,
|
||||
class OtherRef
|
||||
>
|
||||
static
|
||||
StyleSetter
|
||||
addStyle( const std::string& name,
|
||||
const std::string& type,
|
||||
void (Other::*setter)(T),
|
||||
OtherRef Derived::*instance_ref,
|
||||
bool inheritable = true )
|
||||
{
|
||||
return addStyle<T, T>
|
||||
(
|
||||
name,
|
||||
type,
|
||||
bindOther(setter, instance_ref),
|
||||
inheritable
|
||||
);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
class Derived,
|
||||
class Other,
|
||||
class OtherRef
|
||||
>
|
||||
static
|
||||
StyleSetter
|
||||
addStyle( const std::string& name,
|
||||
const std::string& type,
|
||||
void (Other::*setter)(T2),
|
||||
OtherRef Derived::*instance_ref,
|
||||
bool inheritable = true )
|
||||
{
|
||||
return addStyle<T1>
|
||||
(
|
||||
name,
|
||||
type,
|
||||
bindOther(setter, instance_ref),
|
||||
inheritable
|
||||
);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
class Derived,
|
||||
class Other,
|
||||
class OtherRef
|
||||
>
|
||||
static
|
||||
StyleSetter
|
||||
addStyle( const std::string& name,
|
||||
const std::string& type,
|
||||
const boost::function<void (Other&, T2)>& setter,
|
||||
OtherRef Derived::*instance_ref,
|
||||
bool inheritable = true )
|
||||
{
|
||||
return addStyle<T1>
|
||||
(
|
||||
name,
|
||||
type,
|
||||
bindOther(setter, instance_ref),
|
||||
inheritable
|
||||
);
|
||||
}
|
||||
|
||||
template<
|
||||
class Derived,
|
||||
class Other,
|
||||
class OtherRef
|
||||
>
|
||||
static
|
||||
StyleSetter
|
||||
addStyle( const std::string& name,
|
||||
const std::string& type,
|
||||
void (Other::*setter)(const std::string&),
|
||||
OtherRef Derived::*instance_ref,
|
||||
bool inheritable = true )
|
||||
{
|
||||
return addStyle<const char*, const std::string&>
|
||||
(
|
||||
name,
|
||||
type,
|
||||
boost::function<void (Other&, const std::string&)>(setter),
|
||||
instance_ref,
|
||||
inheritable
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T, class Derived, class Other, class OtherRef>
|
||||
static
|
||||
boost::function<void (Derived&, T)>
|
||||
bindOther( void (Other::*setter)(T), OtherRef Derived::*instance_ref )
|
||||
{
|
||||
return boost::bind(setter, boost::bind(instance_ref, _1), _2);
|
||||
}
|
||||
|
||||
template<typename T, class Derived, class Other, class OtherRef>
|
||||
static
|
||||
boost::function<void (Derived&, T)>
|
||||
bindOther( const boost::function<void (Other&, T)>& setter,
|
||||
OtherRef Derived::*instance_ref )
|
||||
{
|
||||
return boost::bind
|
||||
(
|
||||
setter,
|
||||
boost::bind
|
||||
(
|
||||
&reference_from_pointer<Other, OtherRef>,
|
||||
boost::bind(instance_ref, _1)
|
||||
),
|
||||
_2
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T1, typename T2, class Derived>
|
||||
static
|
||||
StyleSetterFuncUnchecked
|
||||
bindStyleSetter( const std::string& name,
|
||||
const boost::function<void (Derived&, T2)>& setter )
|
||||
{
|
||||
return boost::bind
|
||||
(
|
||||
setter,
|
||||
// We will only call setters with Derived instances, so we can safely
|
||||
// cast here.
|
||||
boost::bind(&derived_cast<Derived>, _1),
|
||||
boost::bind(&getValue<T1>, _2)
|
||||
);
|
||||
}
|
||||
|
||||
bool isStyleEmpty(const SGPropertyNode* child) const;
|
||||
bool canApplyStyle(const SGPropertyNode* child) const;
|
||||
bool setStyleImpl( const SGPropertyNode* child,
|
||||
const StyleInfo* style_info = 0 );
|
||||
|
||||
const StyleInfo* getStyleInfo(const std::string& name) const;
|
||||
const StyleSetter* getStyleSetter(const std::string& name) const;
|
||||
const SGPropertyNode* getParentStyle(const SGPropertyNode* child) const;
|
||||
|
||||
virtual void childAdded(SGPropertyNode * child) {}
|
||||
virtual void childRemoved(SGPropertyNode * child){}
|
||||
virtual void childChanged(SGPropertyNode * child){}
|
||||
|
||||
void setDrawable(osg::Drawable* drawable);
|
||||
|
||||
/**
|
||||
* Get stateset of drawable if available or use transform otherwise
|
||||
*/
|
||||
osg::StateSet* getOrCreateStateSet();
|
||||
|
||||
void setupStyle();
|
||||
|
||||
private:
|
||||
|
||||
osg::ref_ptr<osg::Drawable> _drawable;
|
||||
|
||||
Element(const Element&);// = delete
|
||||
|
||||
template<class Derived>
|
||||
static Derived& derived_cast(Element& el)
|
||||
{
|
||||
return static_cast<Derived&>(el);
|
||||
}
|
||||
|
||||
template<class T, class SharedPtr>
|
||||
static T& reference_from_pointer(const SharedPtr& p)
|
||||
{
|
||||
return *get_pointer(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to call a function only of the element type can be converted to
|
||||
* the required type.
|
||||
*
|
||||
* @return Whether the function has been called
|
||||
*/
|
||||
template<class Derived>
|
||||
struct type_match
|
||||
{
|
||||
static bool call( Element& el,
|
||||
const SGPropertyNode* prop,
|
||||
const StyleSetterFuncUnchecked& func )
|
||||
{
|
||||
Derived* d = dynamic_cast<Derived*>(&el);
|
||||
if( !d )
|
||||
return false;
|
||||
func(*d, prop);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
|
||||
template<>
|
||||
struct enum_traits<canvas::Element::ReferenceFrame>
|
||||
{
|
||||
static const char* name()
|
||||
{
|
||||
return "canvas::Element::ReferenceFrame";
|
||||
}
|
||||
|
||||
static canvas::Element::ReferenceFrame defVal()
|
||||
{
|
||||
return canvas::Element::GLOBAL;
|
||||
}
|
||||
|
||||
static bool validate(int frame)
|
||||
{
|
||||
return frame >= canvas::Element::GLOBAL
|
||||
&& frame <= canvas::Element::LOCAL;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_ELEMENT_HXX_ */
|
||||
@@ -1,421 +0,0 @@
|
||||
// A group of 2D Canvas elements
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasGroup.hxx"
|
||||
#include "CanvasImage.hxx"
|
||||
#include "CanvasMap.hxx"
|
||||
#include "CanvasPath.hxx"
|
||||
#include "CanvasText.hxx"
|
||||
#include <simgear/canvas/CanvasEventVisitor.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lambda/core.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
/**
|
||||
* Add canvas Element type to factory map
|
||||
*/
|
||||
template<typename ElementType>
|
||||
void add(ElementFactories& factories)
|
||||
{
|
||||
ElementType::staticInit();
|
||||
factories[ElementType::TYPE_NAME] = &Element::create<ElementType>;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementFactories Group::_child_factories;
|
||||
const std::string Group::TYPE_NAME = "group";
|
||||
|
||||
void warnTransformExpired(const char* member_name)
|
||||
{
|
||||
SG_LOG( SG_GENERAL,
|
||||
SG_WARN,
|
||||
"canvas::Group::" << member_name << ": Group has expired." );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::staticInit()
|
||||
{
|
||||
if( isInit<Group>() )
|
||||
return;
|
||||
|
||||
add<Group>(_child_factories);
|
||||
add<Image>(_child_factories);
|
||||
add<Map >(_child_factories);
|
||||
add<Path >(_child_factories);
|
||||
add<Text >(_child_factories);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Group::Group( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
Element(canvas, node, parent_style, parent)
|
||||
{
|
||||
staticInit();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Group::~Group()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Group::createChild( const std::string& type,
|
||||
const std::string& id )
|
||||
{
|
||||
SGPropertyNode* node = _node->addChild(type, 0, false);
|
||||
if( !id.empty() )
|
||||
node->setStringValue("id", id);
|
||||
|
||||
return getChild(node);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Group::getChild(const SGPropertyNode* node)
|
||||
{
|
||||
return findChild(node, "");
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Group::getChild(const std::string& id)
|
||||
{
|
||||
return findChild(0, id);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Group::getOrCreateChild( const std::string& type,
|
||||
const std::string& id )
|
||||
{
|
||||
ElementPtr child = getChild(id);
|
||||
if( child )
|
||||
{
|
||||
if( child->getProps()->getNameString() == type )
|
||||
return child;
|
||||
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_WARN,
|
||||
"Group::getOrCreateChild: type missmatch! "
|
||||
"('" << type << "' != '" << child->getProps()->getName() << "', "
|
||||
"id = '" << id << "')"
|
||||
);
|
||||
|
||||
return ElementPtr();
|
||||
}
|
||||
|
||||
return createChild(type, id);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Group::getElementById(const std::string& id)
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
{
|
||||
warnTransformExpired("getElementById");
|
||||
return ElementPtr();
|
||||
}
|
||||
|
||||
std::vector<GroupPtr> groups;
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
{
|
||||
const ElementPtr& el = getChildByIndex(i);
|
||||
if( el->get<std::string>("id") == id )
|
||||
return el;
|
||||
|
||||
Group* group = dynamic_cast<Group*>(el.get());
|
||||
if( group )
|
||||
groups.push_back(group);
|
||||
}
|
||||
|
||||
BOOST_FOREACH( GroupPtr group, groups )
|
||||
{
|
||||
ElementPtr el = group->getElementById(id);
|
||||
if( el )
|
||||
return el;
|
||||
}
|
||||
|
||||
return ElementPtr();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::clearEventListener()
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return warnTransformExpired("clearEventListener");
|
||||
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
getChildByIndex(i)->clearEventListener();
|
||||
|
||||
Element::clearEventListener();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::update(double dt)
|
||||
{
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
getChildByIndex(i)->update(dt);
|
||||
|
||||
Element::update(dt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Group::traverse(EventVisitor& visitor)
|
||||
{
|
||||
// Iterate in reverse order as last child is displayed on top
|
||||
for(size_t i = _transform->getNumChildren(); i --> 0;)
|
||||
{
|
||||
if( getChildByIndex(i)->accept(visitor) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Group::setStyle( const SGPropertyNode* style,
|
||||
const StyleInfo* style_info )
|
||||
{
|
||||
if( !canApplyStyle(style) )
|
||||
return false;
|
||||
|
||||
bool handled = setStyleImpl(style, style_info);
|
||||
if( style_info->inheritable )
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
{
|
||||
warnTransformExpired("setStyle");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
handled |= getChildByIndex(i)->setStyle(style, style_info);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const
|
||||
{
|
||||
osg::BoundingBox bb;
|
||||
if( !_transform.valid() )
|
||||
{
|
||||
warnTransformExpired("getTransformedBounds");
|
||||
return bb;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
{
|
||||
const ElementPtr& child = getChildByIndex(i);
|
||||
if( !child->getMatrixTransform()->getNodeMask() )
|
||||
continue;
|
||||
|
||||
bb.expandBy
|
||||
(
|
||||
child->getTransformedBounds
|
||||
(
|
||||
child->getMatrixTransform()->getMatrix() * m
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementFactory Group::getChildFactory(const std::string& type) const
|
||||
{
|
||||
ElementFactories::iterator child_factory = _child_factories.find(type);
|
||||
if( child_factory != _child_factories.end() )
|
||||
return child_factory->second;
|
||||
|
||||
return ElementFactory();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::childAdded(SGPropertyNode* child)
|
||||
{
|
||||
if( child->getParent() != _node )
|
||||
return;
|
||||
|
||||
ElementFactory child_factory = getChildFactory( child->getNameString() );
|
||||
if( child_factory )
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return warnTransformExpired("childAdded");
|
||||
|
||||
ElementPtr element = child_factory(_canvas, child, _style, this);
|
||||
|
||||
// Add to osg scene graph...
|
||||
_transform->addChild( element->getMatrixTransform() );
|
||||
|
||||
// ...and ensure correct ordering
|
||||
handleZIndexChanged(element);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
StyleInfo const* style = getStyleInfo(child->getNameString());
|
||||
if( style && style->inheritable )
|
||||
_style[ child->getNameString() ] = child;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::childRemoved(SGPropertyNode* node)
|
||||
{
|
||||
if( node->getParent() != _node )
|
||||
return;
|
||||
|
||||
if( getChildFactory(node->getNameString()) )
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
// If transform is destroyed also all children are destroyed, so we can
|
||||
// not do anything here.
|
||||
return;
|
||||
|
||||
ElementPtr child = getChild(node);
|
||||
if( !child )
|
||||
SG_LOG
|
||||
(
|
||||
SG_GL,
|
||||
SG_WARN,
|
||||
"can't removed unknown child " << node->getDisplayName()
|
||||
);
|
||||
else
|
||||
{
|
||||
// Remove child from the scenegraph (this automatically invalidates the
|
||||
// reference on the element hold by the MatrixTransform)
|
||||
child->onDestroy();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_style.erase( node->getNameString() );
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::childChanged(SGPropertyNode* node)
|
||||
{
|
||||
SGPropertyNode* parent = node->getParent();
|
||||
SGPropertyNode* grand_parent = parent ? parent->getParent() : NULL;
|
||||
|
||||
if( grand_parent == _node
|
||||
&& node->getNameString() == "z-index" )
|
||||
return handleZIndexChanged(getChild(parent), node->getIntValue());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::handleZIndexChanged(ElementPtr child, int z_index)
|
||||
{
|
||||
if( !child || !_transform.valid() )
|
||||
return;
|
||||
|
||||
osg::ref_ptr<osg::MatrixTransform> tf = child->getMatrixTransform();
|
||||
size_t index = _transform->getChildIndex(tf),
|
||||
index_new = index;
|
||||
|
||||
for(;; ++index_new)
|
||||
{
|
||||
if( index_new + 1 == _transform->getNumChildren() )
|
||||
break;
|
||||
|
||||
// Move to end of block with same index (= move upwards until the next
|
||||
// element has a higher index)
|
||||
if( getChildByIndex(index_new + 1)->get<int>("z-index", 0) > z_index )
|
||||
break;
|
||||
}
|
||||
|
||||
if( index_new == index )
|
||||
{
|
||||
// We were not able to move upwards so now try downwards
|
||||
for(;; --index_new)
|
||||
{
|
||||
if( index_new == 0 )
|
||||
break;
|
||||
|
||||
// Move to end of block with same index (= move downwards until the
|
||||
// previous element has the same or a lower index)
|
||||
if( getChildByIndex(index_new - 1)->get<int>("z-index", 0) <= z_index )
|
||||
break;
|
||||
}
|
||||
|
||||
if( index == index_new )
|
||||
return;
|
||||
}
|
||||
|
||||
_transform->removeChild(index);
|
||||
_transform->insertChild(index_new, tf);
|
||||
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_INFO,
|
||||
"canvas::Group: Moved element " << index << " to position " << index_new
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Group::getChildByIndex(size_t index) const
|
||||
{
|
||||
assert(_transform.valid());
|
||||
OSGUserData* ud =
|
||||
static_cast<OSGUserData*>(_transform->getChild(index)->getUserData());
|
||||
assert(ud);
|
||||
return ud->element;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Group::findChild( const SGPropertyNode* node,
|
||||
const std::string& id ) const
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
{
|
||||
warnTransformExpired("findChild");
|
||||
return ElementPtr();
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
{
|
||||
ElementPtr el = getChildByIndex(i);
|
||||
|
||||
if( node )
|
||||
{
|
||||
if( el->getProps() == node )
|
||||
return el;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( el->get<std::string>("id") == id )
|
||||
return el;
|
||||
}
|
||||
}
|
||||
|
||||
return ElementPtr();
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,124 +0,0 @@
|
||||
// A group of 2D Canvas elements
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_GROUP_HXX_
|
||||
#define CANVAS_GROUP_HXX_
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
typedef std::map<std::string, ElementFactory> ElementFactories;
|
||||
|
||||
class Group:
|
||||
public Element
|
||||
{
|
||||
public:
|
||||
static const std::string TYPE_NAME;
|
||||
static void staticInit();
|
||||
|
||||
typedef std::list< std::pair< const SGPropertyNode*,
|
||||
ElementPtr
|
||||
>
|
||||
> ChildList;
|
||||
|
||||
Group( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style = Style(),
|
||||
Element* parent = 0 );
|
||||
virtual ~Group();
|
||||
|
||||
ElementPtr createChild( const std::string& type,
|
||||
const std::string& id = "" );
|
||||
ElementPtr getChild(const SGPropertyNode* node);
|
||||
ElementPtr getChild(const std::string& id);
|
||||
ElementPtr getOrCreateChild( const std::string& type,
|
||||
const std::string& id );
|
||||
|
||||
template<class T>
|
||||
SGSharedPtr<T> createChild(const std::string& id = "")
|
||||
{
|
||||
return dynamic_cast<T*>( createChild(T::TYPE_NAME, id).get() );
|
||||
}
|
||||
|
||||
template<class T>
|
||||
SGSharedPtr<T> getChild(const SGPropertyNode* node)
|
||||
{
|
||||
return dynamic_cast<T*>( getChild(node).get() );
|
||||
}
|
||||
|
||||
template<class T>
|
||||
SGSharedPtr<T> getChild(const std::string& id)
|
||||
{
|
||||
return dynamic_cast<T*>( getChild(id).get() );
|
||||
}
|
||||
|
||||
template<class T>
|
||||
SGSharedPtr<T> getOrCreateChild(const std::string& id)
|
||||
{
|
||||
return dynamic_cast<T*>( getOrCreateChild(T::TYPE_NAME, id).get() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get first child with given id (breadth-first search)
|
||||
*
|
||||
* @param id Id (value if property node 'id') of element
|
||||
*/
|
||||
ElementPtr getElementById(const std::string& id);
|
||||
|
||||
virtual void clearEventListener();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
||||
virtual bool traverse(EventVisitor& visitor);
|
||||
|
||||
virtual bool setStyle( const SGPropertyNode* child,
|
||||
const StyleInfo* style_info = 0 );
|
||||
|
||||
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
||||
|
||||
protected:
|
||||
|
||||
static ElementFactories _child_factories;
|
||||
|
||||
/**
|
||||
* Overload in derived classes to allow for more/other types of elements
|
||||
* to be managed.
|
||||
*/
|
||||
virtual ElementFactory getChildFactory(const std::string& type) const;
|
||||
|
||||
virtual void childAdded(SGPropertyNode * child);
|
||||
virtual void childRemoved(SGPropertyNode * child);
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
|
||||
void handleZIndexChanged(ElementPtr child, int z_index = 0);
|
||||
|
||||
ElementPtr getChildByIndex(size_t index) const;
|
||||
ElementPtr findChild( const SGPropertyNode* node,
|
||||
const std::string& id ) const;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_GROUP_HXX_ */
|
||||
@@ -1,805 +0,0 @@
|
||||
// An image on the Canvas
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasImage.hxx"
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/CanvasMgr.hxx>
|
||||
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
#include <simgear/scene/util/parse_color.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#include <osg/Array>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osgDB/Registry>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
/**
|
||||
* Callback to enable/disable rendering of canvas displayed inside windows or
|
||||
* other canvases.
|
||||
*/
|
||||
class CullCallback:
|
||||
public osg::Drawable::CullCallback
|
||||
{
|
||||
public:
|
||||
CullCallback(const CanvasWeakPtr& canvas);
|
||||
void cullNextFrame();
|
||||
|
||||
private:
|
||||
CanvasWeakPtr _canvas;
|
||||
mutable bool _cull_next_frame;
|
||||
|
||||
virtual bool cull( osg::NodeVisitor* nv,
|
||||
osg::Drawable* drawable,
|
||||
osg::RenderInfo* renderInfo ) const;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CullCallback::CullCallback(const CanvasWeakPtr& canvas):
|
||||
_canvas( canvas ),
|
||||
_cull_next_frame( false )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void CullCallback::cullNextFrame()
|
||||
{
|
||||
_cull_next_frame = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool CullCallback::cull( osg::NodeVisitor* nv,
|
||||
osg::Drawable* drawable,
|
||||
osg::RenderInfo* renderInfo ) const
|
||||
{
|
||||
CanvasPtr canvas = _canvas.lock();
|
||||
if( canvas )
|
||||
canvas->enableRendering();
|
||||
|
||||
if( !_cull_next_frame )
|
||||
// TODO check if window/image should be culled
|
||||
return false;
|
||||
|
||||
_cull_next_frame = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const std::string Image::TYPE_NAME = "image";
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::staticInit()
|
||||
{
|
||||
if( isInit<Image>() )
|
||||
return;
|
||||
|
||||
addStyle("fill", "color", &Image::setFill);
|
||||
addStyle("outset", "", &Image::setOutset);
|
||||
addStyle("preserveAspectRatio", "", &Image::setPreserveAspectRatio);
|
||||
addStyle("slice", "", &Image::setSlice);
|
||||
addStyle("slice-width", "", &Image::setSliceWidth);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Image::Image( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
Element(canvas, node, parent_style, parent),
|
||||
_texture(new osg::Texture2D),
|
||||
_node_src_rect( node->getNode("source", 0, true) ),
|
||||
_src_rect(0,0),
|
||||
_region(0,0)
|
||||
{
|
||||
staticInit();
|
||||
|
||||
_geom = new osg::Geometry;
|
||||
_geom->setUseDisplayList(false);
|
||||
|
||||
osg::StateSet *stateSet = _geom->getOrCreateStateSet();
|
||||
stateSet->setTextureAttributeAndModes(0, _texture.get());
|
||||
stateSet->setDataVariance(osg::Object::STATIC);
|
||||
|
||||
// allocate arrays for the image
|
||||
_vertices = new osg::Vec3Array(4);
|
||||
_vertices->setDataVariance(osg::Object::DYNAMIC);
|
||||
_geom->setVertexArray(_vertices);
|
||||
|
||||
_texCoords = new osg::Vec2Array(4);
|
||||
_texCoords->setDataVariance(osg::Object::DYNAMIC);
|
||||
_geom->setTexCoordArray(0, _texCoords, osg::Array::BIND_PER_VERTEX);
|
||||
|
||||
_colors = new osg::Vec4Array(1);
|
||||
_colors->setDataVariance(osg::Object::DYNAMIC);
|
||||
_geom->setColorArray(_colors, osg::Array::BIND_OVERALL);
|
||||
|
||||
_prim = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
|
||||
_prim->set(osg::PrimitiveSet::QUADS, 0, 4);
|
||||
_prim->setDataVariance(osg::Object::DYNAMIC);
|
||||
_geom->addPrimitiveSet(_prim);
|
||||
|
||||
setDrawable(_geom);
|
||||
|
||||
setFill("#ffffff"); // TODO how should we handle default values?
|
||||
setupStyle();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Image::~Image()
|
||||
{
|
||||
if( _http_request )
|
||||
_http_request->abort("image destroyed");
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::update(double dt)
|
||||
{
|
||||
Element::update(dt);
|
||||
|
||||
osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>
|
||||
(
|
||||
_geom->getOrCreateStateSet()
|
||||
->getTextureAttribute(0, osg::StateAttribute::TEXTURE)
|
||||
);
|
||||
simgear::canvas::CanvasPtr canvas = _src_canvas.lock();
|
||||
|
||||
if( (_attributes_dirty & SRC_CANVAS)
|
||||
// check if texture has changed (eg. due to resizing)
|
||||
|| (canvas && texture != canvas->getTexture()) )
|
||||
{
|
||||
_geom->getOrCreateStateSet()
|
||||
->setTextureAttribute(0, canvas ? canvas->getTexture() : 0);
|
||||
|
||||
if( !canvas || canvas->isInit() )
|
||||
_attributes_dirty &= ~SRC_CANVAS;
|
||||
}
|
||||
|
||||
if( !_attributes_dirty )
|
||||
return;
|
||||
|
||||
const SGRect<int>& tex_dim = getTextureDimensions();
|
||||
|
||||
// http://www.w3.org/TR/css3-background/#border-image-slice
|
||||
|
||||
// The ‘fill’ keyword, if present, causes the middle part of the image to be
|
||||
// preserved. (By default it is discarded, i.e., treated as empty.)
|
||||
bool fill = (_slice.getKeyword() == "fill");
|
||||
|
||||
if( _attributes_dirty & DEST_SIZE )
|
||||
{
|
||||
size_t num_vertices = (_slice.isValid() ? (fill ? 9 : 8) : 1) * 4;
|
||||
|
||||
if( num_vertices != _prim->getNumPrimitives() )
|
||||
{
|
||||
_vertices->resize(num_vertices);
|
||||
_texCoords->resize(num_vertices);
|
||||
_prim->setCount(num_vertices);
|
||||
_prim->dirty();
|
||||
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/css3-background/#border-image-outset
|
||||
SGRect<float> region = _region;
|
||||
if( _outset.isValid() )
|
||||
{
|
||||
const CSSBorder::Offsets& outset = _outset.getAbsOffsets(tex_dim);
|
||||
region.t() -= outset.t;
|
||||
region.r() += outset.r;
|
||||
region.b() += outset.b;
|
||||
region.l() -= outset.l;
|
||||
}
|
||||
|
||||
if( !_slice.isValid() )
|
||||
{
|
||||
setQuad(0, region.getMin(), region.getMax());
|
||||
|
||||
if( !_preserve_aspect_ratio.scaleToFill() )
|
||||
// We need to update texture coordinates to keep the aspect ratio
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Image slice, 9-scale, whatever it is called. The four corner images
|
||||
stay unscaled (tl, tr, bl, br) whereas the other parts are scaled to
|
||||
fill the remaining space up to the specified size.
|
||||
|
||||
x[0] x[1] x[2] x[3]
|
||||
| | | |
|
||||
-------------------- - y[0]
|
||||
| tl | top | tr |
|
||||
-------------------- - y[1]
|
||||
| | | |
|
||||
| l | | r |
|
||||
| e | center | i |
|
||||
| f | | g |
|
||||
| t | | h |
|
||||
| | | t |
|
||||
-------------------- - y[2]
|
||||
| bl | bottom | br |
|
||||
-------------------- - y[3]
|
||||
*/
|
||||
|
||||
const CSSBorder::Offsets& slice =
|
||||
(_slice_width.isValid() ? _slice_width : _slice)
|
||||
.getAbsOffsets(tex_dim);
|
||||
float x[4] = {
|
||||
region.l(),
|
||||
region.l() + slice.l,
|
||||
region.r() - slice.r,
|
||||
region.r()
|
||||
};
|
||||
float y[4] = {
|
||||
region.t(),
|
||||
region.t() + slice.t,
|
||||
region.b() - slice.b,
|
||||
region.b()
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
for(int ix = 0; ix < 3; ++ix)
|
||||
for(int iy = 0; iy < 3; ++iy)
|
||||
{
|
||||
if( ix == 1 && iy == 1 && !fill )
|
||||
// The ‘fill’ keyword, if present, causes the middle part of the
|
||||
// image to be filled.
|
||||
continue;
|
||||
|
||||
setQuad( i++,
|
||||
SGVec2f(x[ix ], y[iy ]),
|
||||
SGVec2f(x[ix + 1], y[iy + 1]) );
|
||||
}
|
||||
}
|
||||
|
||||
_vertices->dirty();
|
||||
_attributes_dirty &= ~DEST_SIZE;
|
||||
_geom->dirtyBound();
|
||||
}
|
||||
|
||||
if( _attributes_dirty & SRC_RECT )
|
||||
{
|
||||
SGRect<float> src_rect = _src_rect;
|
||||
if( !_node_src_rect->getBoolValue("normalized", true) )
|
||||
{
|
||||
src_rect.t() /= tex_dim.height();
|
||||
src_rect.r() /= tex_dim.width();
|
||||
src_rect.b() /= tex_dim.height();
|
||||
src_rect.l() /= tex_dim.width();
|
||||
}
|
||||
|
||||
// Image coordinate systems y-axis is flipped
|
||||
std::swap(src_rect.t(), src_rect.b());
|
||||
|
||||
if( !_slice.isValid() )
|
||||
{
|
||||
// Image scaling preserving aspect ratio. Change texture coordinates to
|
||||
// scale image accordingly.
|
||||
//
|
||||
// TODO allow to specify what happens to not filled space (eg. color,
|
||||
// or texture repeat/mirror)
|
||||
//
|
||||
// http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
|
||||
if( !_preserve_aspect_ratio.scaleToFill() )
|
||||
{
|
||||
osg::BoundingBox const& bb = getBoundingBox();
|
||||
float dst_width = bb._max.x() - bb._min.x(),
|
||||
dst_height = bb._max.y() - bb._min.y();
|
||||
float scale_x = dst_width / tex_dim.width(),
|
||||
scale_y = dst_height / tex_dim.height();
|
||||
|
||||
float scale = _preserve_aspect_ratio.scaleToFit()
|
||||
? std::min(scale_x, scale_y)
|
||||
: std::max(scale_x, scale_y);
|
||||
|
||||
if( scale_x != scale )
|
||||
{
|
||||
float d = scale_x / scale - 1;
|
||||
if( _preserve_aspect_ratio.alignX()
|
||||
== SVGpreserveAspectRatio::ALIGN_MIN )
|
||||
{
|
||||
src_rect.r() += d;
|
||||
}
|
||||
else if( _preserve_aspect_ratio.alignX()
|
||||
== SVGpreserveAspectRatio::ALIGN_MAX )
|
||||
{
|
||||
src_rect.l() -= d;
|
||||
}
|
||||
else
|
||||
{
|
||||
src_rect.l() -= d / 2;
|
||||
src_rect.r() += d / 2;
|
||||
}
|
||||
}
|
||||
|
||||
if( scale_y != scale )
|
||||
{
|
||||
float d = scale_y / scale - 1;
|
||||
if( _preserve_aspect_ratio.alignY()
|
||||
== SVGpreserveAspectRatio::ALIGN_MIN )
|
||||
{
|
||||
src_rect.b() -= d;
|
||||
}
|
||||
else if( _preserve_aspect_ratio.alignY()
|
||||
== SVGpreserveAspectRatio::ALIGN_MAX )
|
||||
{
|
||||
src_rect.t() += d;
|
||||
}
|
||||
else
|
||||
{
|
||||
src_rect.t() += d / 2;
|
||||
src_rect.b() -= d / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setQuadUV(0, src_rect.getMin(), src_rect.getMax());
|
||||
}
|
||||
else
|
||||
{
|
||||
const CSSBorder::Offsets& slice = _slice.getRelOffsets(tex_dim);
|
||||
float x[4] = {
|
||||
src_rect.l(),
|
||||
src_rect.l() + slice.l,
|
||||
src_rect.r() - slice.r,
|
||||
src_rect.r()
|
||||
};
|
||||
float y[4] = {
|
||||
src_rect.t(),
|
||||
src_rect.t() - slice.t,
|
||||
src_rect.b() + slice.b,
|
||||
src_rect.b()
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
for(int ix = 0; ix < 3; ++ix)
|
||||
for(int iy = 0; iy < 3; ++iy)
|
||||
{
|
||||
if( ix == 1 && iy == 1 && !fill )
|
||||
// The ‘fill’ keyword, if present, causes the middle part of the
|
||||
// image to be filled.
|
||||
continue;
|
||||
|
||||
setQuadUV( i++,
|
||||
SGVec2f(x[ix ], y[iy ]),
|
||||
SGVec2f(x[ix + 1], y[iy + 1]) );
|
||||
}
|
||||
}
|
||||
|
||||
_texCoords->dirty();
|
||||
_attributes_dirty &= ~SRC_RECT;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::valueChanged(SGPropertyNode* child)
|
||||
{
|
||||
// If the image is switched from invisible to visible, and it shows a
|
||||
// canvas, we need to delay showing it by one frame to ensure the canvas is
|
||||
// updated before the image is displayed.
|
||||
//
|
||||
// As canvas::Element handles and filters changes to the "visible" property
|
||||
// we can not check this in Image::childChanged but instead have to override
|
||||
// Element::valueChanged.
|
||||
if( !isVisible()
|
||||
&& child->getParent() == _node
|
||||
&& child->getNameString() == "visible"
|
||||
&& child->getBoolValue() )
|
||||
{
|
||||
CullCallback* cb = static_cast<CullCallback*>(_geom->getCullCallback());
|
||||
if( cb )
|
||||
cb->cullNextFrame();
|
||||
}
|
||||
|
||||
Element::valueChanged(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSrcCanvas(CanvasPtr canvas)
|
||||
{
|
||||
CanvasPtr src_canvas = _src_canvas.lock(),
|
||||
self_canvas = _canvas.lock();
|
||||
|
||||
if( src_canvas )
|
||||
src_canvas->removeParentCanvas(self_canvas);
|
||||
if( self_canvas )
|
||||
self_canvas->removeChildCanvas(src_canvas);
|
||||
|
||||
_src_canvas = src_canvas = canvas;
|
||||
_attributes_dirty |= SRC_CANVAS;
|
||||
_geom->setCullCallback(canvas ? new CullCallback(canvas) : 0);
|
||||
|
||||
if( src_canvas )
|
||||
{
|
||||
setupDefaultDimensions();
|
||||
|
||||
if( self_canvas )
|
||||
{
|
||||
self_canvas->addChildCanvas(src_canvas);
|
||||
src_canvas->addParentCanvas(self_canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasWeakPtr Image::getSrcCanvas() const
|
||||
{
|
||||
return _src_canvas;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setImage(osg::Image *img)
|
||||
{
|
||||
// remove canvas...
|
||||
setSrcCanvas( CanvasPtr() );
|
||||
|
||||
_texture->setResizeNonPowerOfTwoHint(false);
|
||||
_texture->setImage(img);
|
||||
_texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
||||
_texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
||||
_geom->getOrCreateStateSet()
|
||||
->setTextureAttributeAndModes(0, _texture);
|
||||
|
||||
if( img )
|
||||
setupDefaultDimensions();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setFill(const std::string& fill)
|
||||
{
|
||||
osg::Vec4 color(1,1,1,1);
|
||||
if( !fill.empty() // If no color is given default to white
|
||||
&& !parseColor(fill, color) )
|
||||
return;
|
||||
|
||||
_colors->front() = color;
|
||||
_colors->dirty();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setOutset(const std::string& outset)
|
||||
{
|
||||
_outset = CSSBorder::parse(outset);
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setPreserveAspectRatio(const std::string& scale)
|
||||
{
|
||||
_preserve_aspect_ratio = SVGpreserveAspectRatio::parse(scale);
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSlice(const std::string& slice)
|
||||
{
|
||||
_slice = CSSBorder::parse(slice);
|
||||
_attributes_dirty |= SRC_RECT | DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSliceWidth(const std::string& width)
|
||||
{
|
||||
_slice_width = CSSBorder::parse(width);
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const SGRect<float>& Image::getRegion() const
|
||||
{
|
||||
return _region;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Image::handleEvent(const EventPtr& event)
|
||||
{
|
||||
bool handled = Element::handleEvent(event);
|
||||
|
||||
CanvasPtr src_canvas = _src_canvas.lock();
|
||||
if( !src_canvas )
|
||||
return handled;
|
||||
|
||||
MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get());
|
||||
if( mouse_event )
|
||||
{
|
||||
mouse_event.reset( new MouseEvent(*mouse_event) );
|
||||
|
||||
mouse_event->client_pos = mouse_event->local_pos
|
||||
- toOsg(_region.getMin());
|
||||
|
||||
osg::Vec2f size(_region.width(), _region.height());
|
||||
if( _outset.isValid() )
|
||||
{
|
||||
CSSBorder::Offsets outset =
|
||||
_outset.getAbsOffsets(getTextureDimensions());
|
||||
|
||||
mouse_event->client_pos += osg::Vec2f(outset.l, outset.t);
|
||||
size.x() += outset.l + outset.r;
|
||||
size.y() += outset.t + outset.b;
|
||||
}
|
||||
|
||||
// Scale event pos according to canvas view size vs. displayed/screen size
|
||||
mouse_event->client_pos.x() *= src_canvas->getViewWidth() / size.x();
|
||||
mouse_event->client_pos.y() *= src_canvas->getViewHeight()/ size.y();
|
||||
mouse_event->local_pos = mouse_event->client_pos;
|
||||
|
||||
handled |= src_canvas->handleMouseEvent(mouse_event);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::childChanged(SGPropertyNode* child)
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
|
||||
if( child->getParent() == _node_src_rect )
|
||||
{
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
|
||||
if( name == "left" )
|
||||
_src_rect.setLeft( child->getFloatValue() );
|
||||
else if( name == "right" )
|
||||
_src_rect.setRight( child->getFloatValue() );
|
||||
else if( name == "top" )
|
||||
_src_rect.setTop( child->getFloatValue() );
|
||||
else if( name == "bottom" )
|
||||
_src_rect.setBottom( child->getFloatValue() );
|
||||
|
||||
return;
|
||||
}
|
||||
else if( child->getParent() != _node )
|
||||
return;
|
||||
|
||||
if( name == "x" )
|
||||
{
|
||||
_region.setX( child->getFloatValue() );
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
else if( name == "y" )
|
||||
{
|
||||
_region.setY( child->getFloatValue() );
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
else if( name == "size" )
|
||||
{
|
||||
if( child->getIndex() == 0 )
|
||||
_region.setWidth( child->getFloatValue() );
|
||||
else
|
||||
_region.setHeight( child->getFloatValue() );
|
||||
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
else if( name == "src" || name == "file" )
|
||||
{
|
||||
if( name == "file" )
|
||||
SG_LOG(SG_GL, SG_WARN, "'file' is deprecated. Use 'src' instead");
|
||||
|
||||
// Abort pending request
|
||||
if( _http_request )
|
||||
{
|
||||
_http_request->abort("setting new image");
|
||||
_http_request.reset();
|
||||
}
|
||||
|
||||
static const std::string PROTOCOL_SEP = "://";
|
||||
|
||||
std::string url = child->getStringValue(),
|
||||
protocol, path;
|
||||
|
||||
size_t sep_pos = url.find(PROTOCOL_SEP);
|
||||
if( sep_pos != std::string::npos )
|
||||
{
|
||||
protocol = url.substr(0, sep_pos);
|
||||
path = url.substr(sep_pos + PROTOCOL_SEP.length());
|
||||
}
|
||||
else
|
||||
path = url;
|
||||
|
||||
if( protocol == "canvas" )
|
||||
{
|
||||
CanvasPtr canvas = _canvas.lock();
|
||||
if( !canvas )
|
||||
{
|
||||
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No canvas available");
|
||||
return;
|
||||
}
|
||||
|
||||
CanvasMgr* canvas_mgr = canvas->getCanvasMgr();
|
||||
if( !canvas_mgr )
|
||||
{
|
||||
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Failed to get CanvasMgr");
|
||||
return;
|
||||
}
|
||||
|
||||
const SGPropertyNode* canvas_node =
|
||||
canvas_mgr->getPropertyRoot()
|
||||
->getParent()
|
||||
->getNode( path );
|
||||
if( !canvas_node )
|
||||
{
|
||||
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such canvas: " << path);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO add support for other means of addressing canvases (eg. by
|
||||
// name)
|
||||
CanvasPtr src_canvas = canvas_mgr->getCanvas( canvas_node->getIndex() );
|
||||
if( !src_canvas )
|
||||
{
|
||||
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Invalid canvas: " << path);
|
||||
return;
|
||||
}
|
||||
|
||||
setSrcCanvas(src_canvas);
|
||||
}
|
||||
else if( protocol == "http" || protocol == "https" )
|
||||
// TODO check https
|
||||
{
|
||||
_http_request =
|
||||
Canvas::getSystemAdapter()
|
||||
->getHTTPClient()
|
||||
->load(url)
|
||||
// TODO handle capture of 'this'
|
||||
->done(this, &Image::handleImageLoadDone);
|
||||
}
|
||||
else
|
||||
{
|
||||
setImage( Canvas::getSystemAdapter()->getImage(path) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setupDefaultDimensions()
|
||||
{
|
||||
if( !_src_rect.width() || !_src_rect.height() )
|
||||
{
|
||||
// Show whole image by default
|
||||
_node_src_rect->setBoolValue("normalized", true);
|
||||
_node_src_rect->setFloatValue("right", 1);
|
||||
_node_src_rect->setFloatValue("bottom", 1);
|
||||
}
|
||||
|
||||
if( !_region.width() || !_region.height() )
|
||||
{
|
||||
// Default to image size.
|
||||
// TODO handle showing only part of image?
|
||||
const SGRect<int>& dim = getTextureDimensions();
|
||||
_node->setFloatValue("size[0]", dim.width());
|
||||
_node->setFloatValue("size[1]", dim.height());
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGRect<int> Image::getTextureDimensions() const
|
||||
{
|
||||
CanvasPtr canvas = _src_canvas.lock();
|
||||
SGRect<int> dim(0,0);
|
||||
|
||||
// Use canvas/image dimensions rather than texture dimensions, as they could
|
||||
// be resized internally by OpenSceneGraph (eg. to nearest power of two).
|
||||
if( canvas )
|
||||
{
|
||||
dim.setRight( canvas->getViewWidth() );
|
||||
dim.setBottom( canvas->getViewHeight() );
|
||||
}
|
||||
else if( _texture )
|
||||
{
|
||||
osg::Image* img = _texture->getImage();
|
||||
|
||||
if( img )
|
||||
{
|
||||
dim.setRight( img->s() );
|
||||
dim.setBottom( img->t() );
|
||||
}
|
||||
}
|
||||
|
||||
return dim;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setQuad(size_t index, const SGVec2f& tl, const SGVec2f& br)
|
||||
{
|
||||
int i = index * 4;
|
||||
(*_vertices)[i + 0].set(tl.x(), tl.y(), 0);
|
||||
(*_vertices)[i + 1].set(br.x(), tl.y(), 0);
|
||||
(*_vertices)[i + 2].set(br.x(), br.y(), 0);
|
||||
(*_vertices)[i + 3].set(tl.x(), br.y(), 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setQuadUV(size_t index, const SGVec2f& tl, const SGVec2f& br)
|
||||
{
|
||||
int i = index * 4;
|
||||
(*_texCoords)[i + 0].set(tl.x(), tl.y());
|
||||
(*_texCoords)[i + 1].set(br.x(), tl.y());
|
||||
(*_texCoords)[i + 2].set(br.x(), br.y());
|
||||
(*_texCoords)[i + 3].set(tl.x(), br.y());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::handleImageLoadDone(HTTP::Request* req)
|
||||
{
|
||||
// Ignore stale/expired requests
|
||||
if( _http_request != req )
|
||||
return;
|
||||
_http_request.reset();
|
||||
|
||||
if( req->responseCode() != 200 )
|
||||
{
|
||||
SG_LOG(SG_IO, SG_WARN, "failed to download '" << req->url() << "': "
|
||||
<< req->responseReason());
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string ext = SGPath(req->path()).extension(),
|
||||
mime = req->responseMime();
|
||||
|
||||
SG_LOG(SG_IO, SG_INFO, "received " << req->url() <<
|
||||
" (ext=" << ext << ", MIME=" << mime << ")");
|
||||
|
||||
const std::string& img_data =
|
||||
static_cast<HTTP::MemoryRequest*>(req)->responseBody();
|
||||
osgDB::Registry* reg = osgDB::Registry::instance();
|
||||
|
||||
// First try to detect image type by extension
|
||||
osgDB::ReaderWriter* rw = reg->getReaderWriterForExtension(ext);
|
||||
if( rw && loadImage(*rw, img_data, *req, "extension") )
|
||||
return;
|
||||
|
||||
// Now try with MIME type
|
||||
rw = reg->getReaderWriterForMimeType(mime);
|
||||
if( rw && loadImage(*rw, img_data, *req, "MIME type") )
|
||||
return;
|
||||
|
||||
SG_LOG(SG_IO, SG_WARN, "unable to read image '" << req->url() << "'");
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Image::loadImage( osgDB::ReaderWriter& reader,
|
||||
const std::string& data,
|
||||
HTTP::Request& request,
|
||||
const std::string& type )
|
||||
{
|
||||
SG_LOG(SG_IO, SG_DEBUG, "use image reader detected by " << type);
|
||||
|
||||
std::istringstream data_strm(data);
|
||||
osgDB::ReaderWriter::ReadResult result = reader.readImage(data_strm);
|
||||
if( result.success() )
|
||||
{
|
||||
setImage( result.takeImage() );
|
||||
return true;
|
||||
}
|
||||
|
||||
SG_LOG(SG_IO, SG_WARN, "failed to read image '" << request.url() << "': "
|
||||
<< result.message());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,146 +0,0 @@
|
||||
// An image on the Canvas
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_IMAGE_HXX_
|
||||
#define CANVAS_IMAGE_HXX_
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
|
||||
#include <simgear/canvas/canvas_fwd.hxx>
|
||||
#include <simgear/io/HTTPClient.hxx>
|
||||
#include <simgear/misc/CSSBorder.hxx>
|
||||
#include <simgear/misc/SVGpreserveAspectRatio.hxx>
|
||||
#include <osg/Texture2D>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace HTTP { class Request; }
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class Image:
|
||||
public Element
|
||||
{
|
||||
public:
|
||||
static const std::string TYPE_NAME;
|
||||
static void staticInit();
|
||||
|
||||
/**
|
||||
* @param node Property node containing settings for this image:
|
||||
* rect/[left/right/top/bottom] Dimensions of source
|
||||
* rect
|
||||
* size[0-1] Dimensions of rectangle
|
||||
* [x,y] Position of rectangle
|
||||
*/
|
||||
Image( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style = Style(),
|
||||
Element* parent = 0 );
|
||||
virtual ~Image();
|
||||
|
||||
virtual void update(double dt);
|
||||
virtual void valueChanged(SGPropertyNode* child);
|
||||
|
||||
void setSrcCanvas(CanvasPtr canvas);
|
||||
CanvasWeakPtr getSrcCanvas() const;
|
||||
|
||||
void setImage(osg::Image *img);
|
||||
void setFill(const std::string& fill);
|
||||
|
||||
/**
|
||||
* @see http://www.w3.org/TR/css3-background/#border-image-outset
|
||||
*/
|
||||
void setOutset(const std::string& outset);
|
||||
|
||||
/**
|
||||
* @see
|
||||
* http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
|
||||
*/
|
||||
void setPreserveAspectRatio(const std::string& scale);
|
||||
|
||||
/**
|
||||
* Set image slice (aka. 9-scale)
|
||||
*
|
||||
* @see http://www.w3.org/TR/css3-background/#border-image-slice
|
||||
*/
|
||||
void setSlice(const std::string& slice);
|
||||
|
||||
/**
|
||||
* Set image slice width.
|
||||
*
|
||||
* By default the size of the 9-scale grid is the same as specified
|
||||
* with setSlice/"slice". Using this method allows setting values
|
||||
* different to the size in the source image.
|
||||
*
|
||||
* @see http://www.w3.org/TR/css3-background/#border-image-width
|
||||
*/
|
||||
void setSliceWidth(const std::string& width);
|
||||
|
||||
const SGRect<float>& getRegion() const;
|
||||
|
||||
bool handleEvent(const EventPtr& event);
|
||||
|
||||
protected:
|
||||
|
||||
enum ImageAttributes
|
||||
{
|
||||
SRC_RECT = LAST_ATTRIBUTE << 1, // Source image rectangle
|
||||
DEST_SIZE = SRC_RECT << 1, // Element size
|
||||
SRC_CANVAS = DEST_SIZE << 1
|
||||
};
|
||||
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
|
||||
void setupDefaultDimensions();
|
||||
SGRect<int> getTextureDimensions() const;
|
||||
|
||||
void setQuad(size_t index, const SGVec2f& tl, const SGVec2f& br);
|
||||
void setQuadUV(size_t index, const SGVec2f& tl, const SGVec2f& br);
|
||||
|
||||
void handleImageLoadDone(HTTP::Request*);
|
||||
bool loadImage( osgDB::ReaderWriter& reader,
|
||||
const std::string& data,
|
||||
HTTP::Request& request,
|
||||
const std::string& type );
|
||||
|
||||
osg::ref_ptr<osg::Texture2D> _texture;
|
||||
// TODO optionally forward events to canvas
|
||||
CanvasWeakPtr _src_canvas;
|
||||
HTTP::Request_ptr _http_request;
|
||||
|
||||
osg::ref_ptr<osg::Geometry> _geom;
|
||||
osg::ref_ptr<osg::DrawArrays>_prim;
|
||||
osg::ref_ptr<osg::Vec3Array> _vertices;
|
||||
osg::ref_ptr<osg::Vec2Array> _texCoords;
|
||||
osg::ref_ptr<osg::Vec4Array> _colors;
|
||||
|
||||
SGPropertyNode *_node_src_rect;
|
||||
SGRect<float> _src_rect,
|
||||
_region;
|
||||
|
||||
SVGpreserveAspectRatio _preserve_aspect_ratio;
|
||||
|
||||
CSSBorder _outset,
|
||||
_slice,
|
||||
_slice_width;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace canvas
|
||||
|
||||
#endif /* CANVAS_IMAGE_HXX_ */
|
||||
@@ -1,246 +0,0 @@
|
||||
// A group of 2D Canvas elements which get automatically transformed according
|
||||
// to the map parameters.
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasMap.hxx"
|
||||
#include "map/geo_node_pair.hxx"
|
||||
#include "map/projection.hxx"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#define LOG_GEO_RET(msg) \
|
||||
{\
|
||||
SG_LOG\
|
||||
(\
|
||||
SG_GENERAL,\
|
||||
SG_WARN,\
|
||||
msg << " (" << child->getStringValue()\
|
||||
<< ", " << child->getPath() << ")"\
|
||||
);\
|
||||
return;\
|
||||
}
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const std::string GEO = "-geo";
|
||||
const std::string Map::TYPE_NAME = "map";
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::staticInit()
|
||||
{
|
||||
Group::staticInit();
|
||||
|
||||
if( isInit<Map>() )
|
||||
return;
|
||||
|
||||
// Do some initialization if needed...
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Map::Map( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
Group(canvas, node, parent_style, parent),
|
||||
// TODO make projection configurable
|
||||
_projection(new SansonFlamsteedProjection),
|
||||
_projection_dirty(true)
|
||||
{
|
||||
staticInit();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Map::~Map()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::update(double dt)
|
||||
{
|
||||
for( GeoNodes::iterator it = _geo_nodes.begin();
|
||||
it != _geo_nodes.end();
|
||||
++it )
|
||||
{
|
||||
GeoNodePair* geo_node = it->second.get();
|
||||
if( !geo_node->isComplete()
|
||||
|| (!geo_node->isDirty() && !_projection_dirty) )
|
||||
continue;
|
||||
|
||||
GeoCoord lat = parseGeoCoord(geo_node->getLat());
|
||||
if( lat.type != GeoCoord::LATITUDE )
|
||||
continue;
|
||||
|
||||
GeoCoord lon = parseGeoCoord(geo_node->getLon());
|
||||
if( lon.type != GeoCoord::LONGITUDE )
|
||||
continue;
|
||||
|
||||
Projection::ScreenPosition pos =
|
||||
_projection->worldToScreen(lat.value, lon.value);
|
||||
|
||||
geo_node->setScreenPos(pos.x, pos.y);
|
||||
|
||||
// geo_node->print();
|
||||
geo_node->setDirty(false);
|
||||
}
|
||||
_projection_dirty = false;
|
||||
|
||||
Group::update(dt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
|
||||
{
|
||||
if( !boost::ends_with(child->getNameString(), GEO) )
|
||||
return Element::childAdded(parent, child);
|
||||
|
||||
_geo_nodes[child].reset(new GeoNodePair());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
|
||||
{
|
||||
if( !boost::ends_with(child->getNameString(), GEO) )
|
||||
return Element::childRemoved(parent, child);
|
||||
|
||||
// TODO remove from other node
|
||||
_geo_nodes.erase(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::valueChanged(SGPropertyNode * child)
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
|
||||
if( !boost::ends_with(name, GEO) )
|
||||
return Group::valueChanged(child);
|
||||
|
||||
GeoNodes::iterator it_geo_node = _geo_nodes.find(child);
|
||||
if( it_geo_node == _geo_nodes.end() )
|
||||
LOG_GEO_RET("geo node not found!")
|
||||
GeoNodePair* geo_node = it_geo_node->second.get();
|
||||
|
||||
geo_node->setDirty();
|
||||
|
||||
if( geo_node->getStatus() & GeoNodePair::INCOMPLETE )
|
||||
{
|
||||
// Detect lat, lon tuples...
|
||||
GeoCoord coord = parseGeoCoord(child->getStringValue());
|
||||
int index_other = -1;
|
||||
|
||||
switch( coord.type )
|
||||
{
|
||||
case GeoCoord::LATITUDE:
|
||||
index_other = child->getIndex() + 1;
|
||||
geo_node->setNodeLat(child);
|
||||
break;
|
||||
case GeoCoord::LONGITUDE:
|
||||
index_other = child->getIndex() - 1;
|
||||
geo_node->setNodeLon(child);
|
||||
break;
|
||||
default:
|
||||
LOG_GEO_RET("Invalid geo coord")
|
||||
}
|
||||
|
||||
SGPropertyNode *other = child->getParent()->getChild(name, index_other);
|
||||
if( !other )
|
||||
return;
|
||||
|
||||
GeoCoord coord_other = parseGeoCoord(other->getStringValue());
|
||||
if( coord_other.type == GeoCoord::INVALID
|
||||
|| coord_other.type == coord.type )
|
||||
return;
|
||||
|
||||
GeoNodes::iterator it_geo_node_other = _geo_nodes.find(other);
|
||||
if( it_geo_node_other == _geo_nodes.end() )
|
||||
LOG_GEO_RET("other geo node not found!")
|
||||
GeoNodePair* geo_node_other = it_geo_node_other->second.get();
|
||||
|
||||
// Let use both nodes use the same GeoNodePair instance
|
||||
if( geo_node_other != geo_node )
|
||||
it_geo_node_other->second = it_geo_node->second;
|
||||
|
||||
if( coord_other.type == GeoCoord::LATITUDE )
|
||||
geo_node->setNodeLat(other);
|
||||
else
|
||||
geo_node->setNodeLon(other);
|
||||
|
||||
// Set name for resulting screen coordinate nodes
|
||||
geo_node->setTargetName( name.substr(0, name.length() - GEO.length()) );
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::childChanged(SGPropertyNode * child)
|
||||
{
|
||||
if( child->getParent() != _node )
|
||||
return Group::childChanged(child);
|
||||
|
||||
if( child->getNameString() == "ref-lat"
|
||||
|| child->getNameString() == "ref-lon" )
|
||||
_projection->setWorldPosition( _node->getDoubleValue("ref-lat"),
|
||||
_node->getDoubleValue("ref-lon") );
|
||||
else if( child->getNameString() == "hdg" )
|
||||
_projection->setOrientation(child->getFloatValue());
|
||||
else if( child->getNameString() == "range" )
|
||||
_projection->setRange(child->getDoubleValue());
|
||||
else if( child->getNameString() == "screen-range" )
|
||||
_projection->setScreenRange(child->getDoubleValue());
|
||||
else
|
||||
return Group::childChanged(child);
|
||||
|
||||
_projection_dirty = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Map::GeoCoord Map::parseGeoCoord(const std::string& val) const
|
||||
{
|
||||
GeoCoord coord;
|
||||
if( val.length() < 2 )
|
||||
return coord;
|
||||
|
||||
if( val[0] == 'N' || val[0] == 'S' )
|
||||
coord.type = GeoCoord::LATITUDE;
|
||||
else if( val[0] == 'E' || val[0] == 'W' )
|
||||
coord.type = GeoCoord::LONGITUDE;
|
||||
else
|
||||
return coord;
|
||||
|
||||
char* end;
|
||||
coord.value = strtod(&val[1], &end);
|
||||
|
||||
if( end != &val[val.length()] )
|
||||
{
|
||||
coord.type = GeoCoord::INVALID;
|
||||
return coord;
|
||||
}
|
||||
|
||||
if( val[0] == 'S' || val[0] == 'W' )
|
||||
coord.value *= -1;
|
||||
|
||||
return coord;
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,86 +0,0 @@
|
||||
// A group of 2D Canvas elements which get automatically transformed according
|
||||
// to the map parameters.
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_MAP_HXX_
|
||||
#define CANVAS_MAP_HXX_
|
||||
|
||||
#include "CanvasGroup.hxx"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
class GeoNodePair;
|
||||
class HorizontalProjection;
|
||||
class Map:
|
||||
public Group
|
||||
{
|
||||
public:
|
||||
static const std::string TYPE_NAME;
|
||||
static void staticInit();
|
||||
|
||||
Map( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent = 0 );
|
||||
virtual ~Map();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
||||
virtual void childAdded( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void childRemoved( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void valueChanged(SGPropertyNode * child);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
|
||||
typedef boost::unordered_map< SGPropertyNode*,
|
||||
boost::shared_ptr<GeoNodePair>
|
||||
> GeoNodes;
|
||||
GeoNodes _geo_nodes;
|
||||
boost::shared_ptr<HorizontalProjection> _projection;
|
||||
bool _projection_dirty;
|
||||
|
||||
struct GeoCoord
|
||||
{
|
||||
GeoCoord():
|
||||
type(INVALID)
|
||||
{}
|
||||
enum
|
||||
{
|
||||
INVALID,
|
||||
LATITUDE,
|
||||
LONGITUDE
|
||||
} type;
|
||||
double value;
|
||||
};
|
||||
|
||||
GeoCoord parseGeoCoord(const std::string& val) const;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_MAP_HXX_ */
|
||||
@@ -1,642 +0,0 @@
|
||||
// An OpenVG path on the Canvas
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasPath.hxx"
|
||||
#include <simgear/scene/util/parse_color.hxx>
|
||||
|
||||
#include <osg/Drawable>
|
||||
|
||||
#include <vg/openvg.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
typedef std::vector<VGubyte> CmdList;
|
||||
typedef std::vector<VGfloat> CoordList;
|
||||
|
||||
|
||||
static const VGubyte shCoordsPerCommand[] = {
|
||||
0, /* VG_CLOSE_PATH */
|
||||
2, /* VG_MOVE_TO */
|
||||
2, /* VG_LINE_TO */
|
||||
1, /* VG_HLINE_TO */
|
||||
1, /* VG_VLINE_TO */
|
||||
4, /* VG_QUAD_TO */
|
||||
6, /* VG_CUBIC_TO */
|
||||
2, /* VG_SQUAD_TO */
|
||||
4, /* VG_SCUBIC_TO */
|
||||
5, /* VG_SCCWARC_TO */
|
||||
5, /* VG_SCWARC_TO */
|
||||
5, /* VG_LCCWARC_TO */
|
||||
5 /* VG_LCWARC_TO */
|
||||
};
|
||||
static const VGubyte shNumCommands = sizeof(shCoordsPerCommand)
|
||||
/ sizeof(shCoordsPerCommand[0]);
|
||||
|
||||
/**
|
||||
* Helper to split and convert comma/whitespace separated floating point
|
||||
* values
|
||||
*/
|
||||
std::vector<float> splitAndConvert(const char del[], const std::string& str);
|
||||
|
||||
class Path::PathDrawable:
|
||||
public osg::Drawable
|
||||
{
|
||||
public:
|
||||
PathDrawable(Path* path):
|
||||
_path_element(path),
|
||||
_path(VG_INVALID_HANDLE),
|
||||
_paint(VG_INVALID_HANDLE),
|
||||
_paint_fill(VG_INVALID_HANDLE),
|
||||
_attributes_dirty(~0),
|
||||
_mode(0),
|
||||
_fill_rule(VG_EVEN_ODD),
|
||||
_stroke_width(1),
|
||||
_stroke_linecap(VG_CAP_BUTT)
|
||||
{
|
||||
setSupportsDisplayList(false);
|
||||
setDataVariance(Object::DYNAMIC);
|
||||
|
||||
setUpdateCallback(new PathUpdateCallback());
|
||||
}
|
||||
|
||||
virtual ~PathDrawable()
|
||||
{
|
||||
if( _path != VG_INVALID_HANDLE )
|
||||
vgDestroyPath(_path);
|
||||
if( _paint != VG_INVALID_HANDLE )
|
||||
vgDestroyPaint(_paint);
|
||||
if( _paint_fill != VG_INVALID_HANDLE )
|
||||
vgDestroyPaint(_paint_fill);
|
||||
}
|
||||
|
||||
virtual const char* className() const { return "PathDrawable"; }
|
||||
virtual osg::Object* cloneType() const { return new PathDrawable(_path_element); }
|
||||
virtual osg::Object* clone(const osg::CopyOp&) const { return new PathDrawable(_path_element); }
|
||||
|
||||
/**
|
||||
* Replace the current path segments with the new ones
|
||||
*
|
||||
* @param cmds List of OpenVG path commands
|
||||
* @param coords List of coordinates/parameters used by #cmds
|
||||
*/
|
||||
void setSegments(const CmdList& cmds, const CoordList& coords)
|
||||
{
|
||||
_cmds = cmds;
|
||||
_coords = coords;
|
||||
|
||||
_attributes_dirty |= (PATH | BOUNDING_BOX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set path fill paint ("none" if not filled)
|
||||
*/
|
||||
void setFill(const std::string& fill)
|
||||
{
|
||||
if( fill.empty() || fill == "none" )
|
||||
{
|
||||
_mode &= ~VG_FILL_PATH;
|
||||
}
|
||||
else if( parseColor(fill, _fill_color) )
|
||||
{
|
||||
_mode |= VG_FILL_PATH;
|
||||
_attributes_dirty |= FILL_COLOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_WARN,
|
||||
"canvas::Path Unknown fill: " << fill
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set path fill rule ("pseudo-nonzero" or "evenodd")
|
||||
*
|
||||
* @warning As the current nonzero implementation causes sever artifacts
|
||||
* for every concave path we call it pseudo-nonzero, so that
|
||||
* everyone is warned that it won't work as expected :)
|
||||
*/
|
||||
void setFillRule(const std::string& fill_rule)
|
||||
{
|
||||
if( fill_rule == "pseudo-nonzero" )
|
||||
_fill_rule = VG_NON_ZERO;
|
||||
else // if( fill_rule == "evenodd" )
|
||||
_fill_rule = VG_EVEN_ODD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set path stroke paint ("none" if no stroke)
|
||||
*/
|
||||
void setStroke(const std::string& stroke)
|
||||
{
|
||||
if( stroke.empty() || stroke == "none" )
|
||||
{
|
||||
_mode &= ~VG_STROKE_PATH;
|
||||
}
|
||||
else if( parseColor(stroke, _stroke_color) )
|
||||
{
|
||||
_mode |= VG_STROKE_PATH;
|
||||
_attributes_dirty |= STROKE_COLOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_WARN,
|
||||
"canvas::Path Unknown stroke: " << stroke
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set stroke width
|
||||
*/
|
||||
void setStrokeWidth(float width)
|
||||
{
|
||||
_stroke_width = width;
|
||||
_attributes_dirty |= BOUNDING_BOX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set stroke dash (line stipple)
|
||||
*/
|
||||
void setStrokeDashArray(const std::string& dash)
|
||||
{
|
||||
_stroke_dash = splitAndConvert(",\t\n ", dash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set stroke-linecap
|
||||
*
|
||||
* @see http://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty
|
||||
*/
|
||||
void setStrokeLinecap(const std::string& linecap)
|
||||
{
|
||||
if( linecap == "round" )
|
||||
_stroke_linecap = VG_CAP_ROUND;
|
||||
else if( linecap == "square" )
|
||||
_stroke_linecap = VG_CAP_SQUARE;
|
||||
else
|
||||
_stroke_linecap = VG_CAP_BUTT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw callback
|
||||
*/
|
||||
virtual void drawImplementation(osg::RenderInfo& renderInfo) const
|
||||
{
|
||||
if( _attributes_dirty & PATH )
|
||||
return;
|
||||
|
||||
osg::State* state = renderInfo.getState();
|
||||
assert(state);
|
||||
|
||||
state->setActiveTextureUnit(0);
|
||||
state->setClientActiveTextureUnit(0);
|
||||
state->disableAllVertexArrays();
|
||||
|
||||
bool was_blend_enabled = state->getLastAppliedMode(GL_BLEND);
|
||||
bool was_stencil_enabled = state->getLastAppliedMode(GL_STENCIL_TEST);
|
||||
osg::StateAttribute const* blend_func =
|
||||
state->getLastAppliedAttribute(osg::StateAttribute::BLENDFUNC);
|
||||
|
||||
// Initialize/Update the paint
|
||||
if( _attributes_dirty & STROKE_COLOR )
|
||||
{
|
||||
if( _paint == VG_INVALID_HANDLE )
|
||||
_paint = vgCreatePaint();
|
||||
|
||||
vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color._v);
|
||||
|
||||
_attributes_dirty &= ~STROKE_COLOR;
|
||||
}
|
||||
|
||||
// Initialize/update fill paint
|
||||
if( _attributes_dirty & FILL_COLOR )
|
||||
{
|
||||
if( _paint_fill == VG_INVALID_HANDLE )
|
||||
_paint_fill = vgCreatePaint();
|
||||
|
||||
vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color._v);
|
||||
|
||||
_attributes_dirty &= ~FILL_COLOR;
|
||||
}
|
||||
|
||||
// Setup paint
|
||||
if( _mode & VG_STROKE_PATH )
|
||||
{
|
||||
vgSetPaint(_paint, VG_STROKE_PATH);
|
||||
|
||||
vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
|
||||
vgSeti(VG_STROKE_CAP_STYLE, _stroke_linecap);
|
||||
vgSetfv( VG_STROKE_DASH_PATTERN,
|
||||
_stroke_dash.size(),
|
||||
_stroke_dash.empty() ? 0 : &_stroke_dash[0] );
|
||||
}
|
||||
if( _mode & VG_FILL_PATH )
|
||||
{
|
||||
vgSetPaint(_paint_fill, VG_FILL_PATH);
|
||||
|
||||
vgSeti(VG_FILL_RULE, _fill_rule);
|
||||
}
|
||||
|
||||
// And finally draw the path
|
||||
if( _mode )
|
||||
vgDrawPath(_path, _mode);
|
||||
|
||||
VGErrorCode err = vgGetError();
|
||||
if( err != VG_NO_ERROR )
|
||||
SG_LOG(SG_GL, SG_ALERT, "vgError: " << err);
|
||||
|
||||
// Restore OpenGL state (TODO check if more is needed or integrate
|
||||
// better with OpenSceneGraph)
|
||||
if( was_blend_enabled ) glEnable(GL_BLEND);
|
||||
if( was_stencil_enabled ) glEnable(GL_STENCIL_TEST);
|
||||
if( blend_func ) blend_func->apply(*state);
|
||||
}
|
||||
|
||||
osg::BoundingBox getTransformedBounds(const osg::Matrix& mat) const
|
||||
{
|
||||
osg::BoundingBox bb;
|
||||
|
||||
osg::Vec2f cur(0, 0), // VG "Current point" (in local coordinates)
|
||||
sub(0, 0); // beginning of current sub path
|
||||
VGubyte cmd_index = 0;
|
||||
for( size_t i = 0, ci = 0;
|
||||
i < _cmds.size() && ci < _coords.size();
|
||||
++i, ci += shCoordsPerCommand[cmd_index] )
|
||||
{
|
||||
VGubyte rel = _cmds[i] & 1,
|
||||
cmd = _cmds[i] & ~1;
|
||||
|
||||
cmd_index = cmd / 2;
|
||||
if( cmd_index >= shNumCommands )
|
||||
return osg::BoundingBox();
|
||||
|
||||
const VGubyte max_coords = 3;
|
||||
osg::Vec2f points[max_coords];
|
||||
VGubyte num_coords = 0;
|
||||
|
||||
switch( cmd )
|
||||
{
|
||||
case VG_CLOSE_PATH:
|
||||
cur = sub;
|
||||
break;
|
||||
case VG_MOVE_TO:
|
||||
case VG_LINE_TO:
|
||||
case VG_SQUAD_TO:
|
||||
// x0, y0
|
||||
points[ num_coords++ ].set(_coords[ci], _coords[ci + 1]);
|
||||
break;
|
||||
case VG_HLINE_TO:
|
||||
// x0
|
||||
points[ num_coords++ ].set( _coords[ci] + (rel ? cur.x() : 0),
|
||||
cur.y() );
|
||||
// We have handled rel/abs already, so no need to do it again...
|
||||
rel = 0;
|
||||
break;
|
||||
case VG_VLINE_TO:
|
||||
// y0
|
||||
points[ num_coords++ ].set( cur.x(),
|
||||
_coords[ci] + (rel ? cur.y() : 0) );
|
||||
// We have handled rel/abs already, so no need to do it again...
|
||||
rel = 0;
|
||||
break;
|
||||
case VG_QUAD_TO:
|
||||
case VG_SCUBIC_TO:
|
||||
// x0,y0,x1,y1
|
||||
points[ num_coords++ ].set(_coords[ci ], _coords[ci + 1]);
|
||||
points[ num_coords++ ].set(_coords[ci + 2], _coords[ci + 3]);
|
||||
break;
|
||||
case VG_CUBIC_TO:
|
||||
// x0,y0,x1,y1,x2,y2
|
||||
points[ num_coords++ ].set(_coords[ci ], _coords[ci + 1]);
|
||||
points[ num_coords++ ].set(_coords[ci + 2], _coords[ci + 3]);
|
||||
points[ num_coords++ ].set(_coords[ci + 4], _coords[ci + 5]);
|
||||
break;
|
||||
case VG_SCCWARC_TO:
|
||||
case VG_SCWARC_TO:
|
||||
case VG_LCCWARC_TO:
|
||||
case VG_LCWARC_TO:
|
||||
// rh,rv,rot,x0,y0
|
||||
points[ num_coords++ ].set(_coords[ci + 3], _coords[ci + 4]);
|
||||
break;
|
||||
default:
|
||||
SG_LOG(SG_GL, SG_WARN, "Unknown VG command: " << (int)cmd);
|
||||
return osg::BoundingBox();
|
||||
}
|
||||
|
||||
assert(num_coords <= max_coords);
|
||||
for(VGubyte i = 0; i < num_coords; ++i)
|
||||
{
|
||||
if( rel )
|
||||
points[i] += cur;
|
||||
|
||||
bb.expandBy( transformPoint(mat, points[i]) );
|
||||
}
|
||||
|
||||
if( num_coords > 0 )
|
||||
{
|
||||
cur = points[ num_coords - 1 ];
|
||||
|
||||
if( cmd == VG_MOVE_TO )
|
||||
sub = cur;
|
||||
}
|
||||
}
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the bounding box
|
||||
*/
|
||||
virtual osg::BoundingBox computeBound() const
|
||||
{
|
||||
if( _path == VG_INVALID_HANDLE || (_attributes_dirty & PATH) )
|
||||
return osg::BoundingBox();
|
||||
|
||||
VGfloat min[2], size[2];
|
||||
vgPathBounds(_path, &min[0], &min[1], &size[0], &size[1]);
|
||||
|
||||
_attributes_dirty &= ~BOUNDING_BOX;
|
||||
|
||||
// vgPathBounds doesn't take stroke width into account
|
||||
float ext = 0.5 * _stroke_width;
|
||||
|
||||
return osg::BoundingBox
|
||||
(
|
||||
min[0] - ext, min[1] - ext, -0.1,
|
||||
min[0] + size[0] + ext, min[1] + size[1] + ext, 0.1
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
enum Attributes
|
||||
{
|
||||
PATH = 0x0001,
|
||||
STROKE_COLOR = PATH << 1,
|
||||
FILL_COLOR = STROKE_COLOR << 1,
|
||||
BOUNDING_BOX = FILL_COLOR << 1
|
||||
};
|
||||
|
||||
Path *_path_element;
|
||||
|
||||
mutable VGPath _path;
|
||||
mutable VGPaint _paint;
|
||||
mutable VGPaint _paint_fill;
|
||||
mutable uint32_t _attributes_dirty;
|
||||
|
||||
CmdList _cmds;
|
||||
CoordList _coords;
|
||||
|
||||
VGbitfield _mode;
|
||||
osg::Vec4f _fill_color;
|
||||
VGFillRule _fill_rule;
|
||||
osg::Vec4f _stroke_color;
|
||||
VGfloat _stroke_width;
|
||||
std::vector<VGfloat> _stroke_dash;
|
||||
VGCapStyle _stroke_linecap;
|
||||
|
||||
osg::Vec3f transformPoint( const osg::Matrix& m,
|
||||
osg::Vec2f pos ) const
|
||||
{
|
||||
return osg::Vec3
|
||||
(
|
||||
m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
|
||||
m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize/Update the OpenVG path
|
||||
*/
|
||||
void update()
|
||||
{
|
||||
if( !vgHasContextSH() )
|
||||
return;
|
||||
|
||||
if( _attributes_dirty & PATH )
|
||||
{
|
||||
const VGbitfield caps = VG_PATH_CAPABILITY_APPEND_TO
|
||||
| VG_PATH_CAPABILITY_MODIFY
|
||||
| VG_PATH_CAPABILITY_PATH_BOUNDS;
|
||||
|
||||
if( _path == VG_INVALID_HANDLE )
|
||||
_path = vgCreatePath(
|
||||
VG_PATH_FORMAT_STANDARD,
|
||||
VG_PATH_DATATYPE_F,
|
||||
1.f, 0.f, // scale,bias
|
||||
_cmds.size(), _coords.size(),
|
||||
caps
|
||||
);
|
||||
else
|
||||
vgClearPath(_path, caps);
|
||||
|
||||
if( !_cmds.empty() && !_coords.empty() )
|
||||
vgAppendPathData(_path, _cmds.size(), &_cmds[0], &_coords[0]);
|
||||
|
||||
_attributes_dirty &= ~PATH;
|
||||
_attributes_dirty |= BOUNDING_BOX;
|
||||
}
|
||||
|
||||
if( _attributes_dirty & BOUNDING_BOX )
|
||||
{
|
||||
dirtyBound();
|
||||
|
||||
// Recalculate bounding box now (prevent race condition)
|
||||
getBound();
|
||||
}
|
||||
}
|
||||
|
||||
struct PathUpdateCallback:
|
||||
public osg::Drawable::UpdateCallback
|
||||
{
|
||||
virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
|
||||
{
|
||||
static_cast<PathDrawable*>(drawable)->update();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const std::string Path::TYPE_NAME = "path";
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::staticInit()
|
||||
{
|
||||
if( isInit<Path>() )
|
||||
return;
|
||||
|
||||
PathDrawableRef Path::*path = &Path::_path;
|
||||
|
||||
addStyle("fill", "color", &PathDrawable::setFill, path);
|
||||
addStyle("fill-rule", "", &PathDrawable::setFillRule, path);
|
||||
addStyle("stroke", "color", &PathDrawable::setStroke, path);
|
||||
addStyle("stroke-width", "numeric", &PathDrawable::setStrokeWidth, path);
|
||||
addStyle("stroke-dasharray", "", &PathDrawable::setStrokeDashArray, path);
|
||||
addStyle("stroke-linecap", "", &PathDrawable::setStrokeLinecap, path);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path::Path( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
Element(canvas, node, parent_style, parent),
|
||||
_path( new PathDrawable(this) )
|
||||
{
|
||||
staticInit();
|
||||
|
||||
setDrawable(_path);
|
||||
setupStyle();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path::~Path()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::update(double dt)
|
||||
{
|
||||
if( _attributes_dirty & (CMDS | COORDS) )
|
||||
{
|
||||
_path->setSegments
|
||||
(
|
||||
_node->getChildValues<VGubyte, int>("cmd"),
|
||||
_node->getChildValues<VGfloat, float>("coord")
|
||||
);
|
||||
|
||||
_attributes_dirty &= ~(CMDS | COORDS);
|
||||
}
|
||||
|
||||
Element::update(dt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Path::getTransformedBounds(const osg::Matrix& m) const
|
||||
{
|
||||
return _path->getTransformedBounds(m);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::moveTo(float x_abs, float y_abs)
|
||||
{
|
||||
return addSegment(VG_MOVE_TO_ABS, x_abs, y_abs);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::move(float x_rel, float y_rel)
|
||||
{
|
||||
return addSegment(VG_MOVE_TO_REL, x_rel, y_rel);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::lineTo(float x_abs, float y_abs)
|
||||
{
|
||||
return addSegment(VG_LINE_TO_ABS, x_abs, y_abs);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::line(float x_rel, float y_rel)
|
||||
{
|
||||
return addSegment(VG_LINE_TO_REL, x_rel, y_rel);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::horizTo(float x_abs)
|
||||
{
|
||||
return addSegment(VG_HLINE_TO_ABS, x_abs);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::horiz(float x_rel)
|
||||
{
|
||||
return addSegment(VG_HLINE_TO_REL, x_rel);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::vertTo(float y_abs)
|
||||
{
|
||||
return addSegment(VG_VLINE_TO_ABS, y_abs);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::vert(float y_rel)
|
||||
{
|
||||
return addSegment(VG_VLINE_TO_REL, y_rel);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::close()
|
||||
{
|
||||
return addSegment(VG_CLOSE_PATH);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::childRemoved(SGPropertyNode* child)
|
||||
{
|
||||
childChanged(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::childChanged(SGPropertyNode* child)
|
||||
{
|
||||
if( child->getParent() != _node )
|
||||
return;
|
||||
|
||||
if( child->getNameString() == "cmd" )
|
||||
_attributes_dirty |= CMDS;
|
||||
else if( child->getNameString() == "coord" )
|
||||
_attributes_dirty |= COORDS;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
std::vector<float> splitAndConvert(const char del[], const std::string& str)
|
||||
{
|
||||
std::vector<float> values;
|
||||
size_t pos = 0;
|
||||
for(;;)
|
||||
{
|
||||
pos = str.find_first_not_of(del, pos);
|
||||
if( pos == std::string::npos )
|
||||
break;
|
||||
|
||||
char *end = 0;
|
||||
float val = strtod(&str[pos], &end);
|
||||
if( end == &str[pos] || !end )
|
||||
break;
|
||||
|
||||
values.push_back(val);
|
||||
pos = end - &str[0];
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,89 +0,0 @@
|
||||
// An OpenVG path on the Canvas
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_PATH_HXX_
|
||||
#define CANVAS_PATH_HXX_
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
#include <boost/preprocessor/iteration/iterate.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
class Path:
|
||||
public Element
|
||||
{
|
||||
public:
|
||||
static const std::string TYPE_NAME;
|
||||
static void staticInit();
|
||||
|
||||
Path( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent = 0 );
|
||||
virtual ~Path();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
||||
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
||||
|
||||
#define BOOST_PP_ITERATION_LIMITS (0, 6)
|
||||
#define BOOST_PP_FILENAME_1 \
|
||||
<simgear/canvas/elements/detail/add_segment_variadic.hxx>
|
||||
#include BOOST_PP_ITERATE()
|
||||
|
||||
/** Move path cursor */
|
||||
Path& moveTo(float x_abs, float y_abs);
|
||||
Path& move(float x_rel, float y_rel);
|
||||
|
||||
/** Add a line */
|
||||
Path& lineTo(float x_abs, float y_abs);
|
||||
Path& line(float x_rel, float y_rel);
|
||||
|
||||
/** Add a horizontal line */
|
||||
Path& horizTo(float x_abs);
|
||||
Path& horiz(float x_rel);
|
||||
|
||||
/** Add a vertical line */
|
||||
Path& vertTo(float y_abs);
|
||||
Path& vert(float y_rel);
|
||||
|
||||
/** Close the path (implicit lineTo to first point of path) */
|
||||
Path& close();
|
||||
|
||||
protected:
|
||||
|
||||
enum PathAttributes
|
||||
{
|
||||
CMDS = LAST_ATTRIBUTE << 1,
|
||||
COORDS = CMDS << 1
|
||||
};
|
||||
|
||||
class PathDrawable;
|
||||
typedef osg::ref_ptr<PathDrawable> PathDrawableRef;
|
||||
PathDrawableRef _path;
|
||||
|
||||
virtual void childRemoved(SGPropertyNode * child);
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_PATH_HXX_ */
|
||||
@@ -1,667 +0,0 @@
|
||||
// A text on the Canvas
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasText.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
||||
#include <simgear/scene/util/parse_color.hxx>
|
||||
#include <osg/Version>
|
||||
#include <osgText/Text>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
class Text::TextOSG:
|
||||
public osgText::Text
|
||||
{
|
||||
public:
|
||||
|
||||
TextOSG(canvas::Text* text);
|
||||
|
||||
void setFontResolution(int res);
|
||||
void setCharacterAspect(float aspect);
|
||||
void setLineHeight(float factor);
|
||||
void setFill(const std::string& fill);
|
||||
void setBackgroundColor(const std::string& fill);
|
||||
|
||||
SGVec2i sizeForWidth(int w) const;
|
||||
osg::Vec2 handleHit(const osg::Vec2f& pos);
|
||||
|
||||
virtual osg::BoundingBox computeBound() const;
|
||||
|
||||
protected:
|
||||
|
||||
canvas::Text *_text_element;
|
||||
|
||||
virtual void computePositions(unsigned int contextID) const;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Text::TextOSG::TextOSG(canvas::Text* text):
|
||||
_text_element(text)
|
||||
{
|
||||
setBackdropImplementation(NO_DEPTH_BUFFER);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::TextOSG::setFontResolution(int res)
|
||||
{
|
||||
TextBase::setFontResolution(res, res);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::TextOSG::setCharacterAspect(float aspect)
|
||||
{
|
||||
setCharacterSize(getCharacterHeight(), aspect);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::TextOSG::setLineHeight(float factor)
|
||||
{
|
||||
setLineSpacing(factor - 1);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::TextOSG::setFill(const std::string& fill)
|
||||
{
|
||||
// if( fill == "none" )
|
||||
// TODO No text
|
||||
// else
|
||||
osg::Vec4 color;
|
||||
if( parseColor(fill, color) )
|
||||
setColor( color );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::TextOSG::setBackgroundColor(const std::string& fill)
|
||||
{
|
||||
osg::Vec4 color;
|
||||
if( parseColor(fill, color) )
|
||||
setBoundingBoxColor( color );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// simplified version of osgText::Text::computeGlyphRepresentation() to
|
||||
// just calculate the size for a given weight. Glpyh calculations/creating
|
||||
// is not necessary for this...
|
||||
SGVec2i Text::TextOSG::sizeForWidth(int w) const
|
||||
{
|
||||
if( _text.empty() )
|
||||
return SGVec2i(0, 0);
|
||||
|
||||
osgText::Font* activefont = const_cast<osgText::Font*>(getActiveFont());
|
||||
if( !activefont )
|
||||
return SGVec2i(-1, -1);
|
||||
|
||||
float max_width_safe = _maximumWidth;
|
||||
const_cast<TextOSG*>(this)->_maximumWidth = w;
|
||||
|
||||
SGRecti bb;
|
||||
|
||||
osg::Vec2 startOfLine_coords(0.0f,0.0f);
|
||||
osg::Vec2 cursor(startOfLine_coords);
|
||||
osg::Vec2 local(0.0f,0.0f);
|
||||
|
||||
unsigned int previous_charcode = 0;
|
||||
unsigned int line_length = 0;
|
||||
bool horizontal = _layout != VERTICAL;
|
||||
bool kerning = true;
|
||||
|
||||
float hr = _characterHeight;
|
||||
float wr = hr / getCharacterAspectRatio();
|
||||
|
||||
// osg should really care more about const :-/
|
||||
osgText::String& text = const_cast<osgText::String&>(_text);
|
||||
typedef osgText::String::iterator TextIterator;
|
||||
|
||||
for( TextIterator itr = text.begin(); itr != text.end(); )
|
||||
{
|
||||
// record the start of the current line
|
||||
TextIterator startOfLine_itr = itr;
|
||||
|
||||
// find the end of the current line.
|
||||
osg::Vec2 endOfLine_coords(cursor);
|
||||
TextIterator endOfLine_itr =
|
||||
const_cast<TextOSG*>(this)->computeLastCharacterOnLine(
|
||||
endOfLine_coords, itr, text.end()
|
||||
);
|
||||
|
||||
line_length = endOfLine_itr - startOfLine_itr;
|
||||
|
||||
// Set line position to correct alignment.
|
||||
switch( _layout )
|
||||
{
|
||||
case LEFT_TO_RIGHT:
|
||||
{
|
||||
switch( _alignment )
|
||||
{
|
||||
// nothing to be done for these
|
||||
//case LEFT_TOP:
|
||||
//case LEFT_CENTER:
|
||||
//case LEFT_BOTTOM:
|
||||
//case LEFT_BASE_LINE:
|
||||
//case LEFT_BOTTOM_BASE_LINE:
|
||||
// break;
|
||||
case CENTER_TOP:
|
||||
case CENTER_CENTER:
|
||||
case CENTER_BOTTOM:
|
||||
case CENTER_BASE_LINE:
|
||||
case CENTER_BOTTOM_BASE_LINE:
|
||||
cursor.x() = (cursor.x() - endOfLine_coords.x()) * 0.5f;
|
||||
break;
|
||||
case RIGHT_TOP:
|
||||
case RIGHT_CENTER:
|
||||
case RIGHT_BOTTOM:
|
||||
case RIGHT_BASE_LINE:
|
||||
case RIGHT_BOTTOM_BASE_LINE:
|
||||
cursor.x() = cursor.x() - endOfLine_coords.x();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RIGHT_TO_LEFT:
|
||||
{
|
||||
switch( _alignment )
|
||||
{
|
||||
case LEFT_TOP:
|
||||
case LEFT_CENTER:
|
||||
case LEFT_BOTTOM:
|
||||
case LEFT_BASE_LINE:
|
||||
case LEFT_BOTTOM_BASE_LINE:
|
||||
cursor.x() = 2 * cursor.x() - endOfLine_coords.x();
|
||||
break;
|
||||
case CENTER_TOP:
|
||||
case CENTER_CENTER:
|
||||
case CENTER_BOTTOM:
|
||||
case CENTER_BASE_LINE:
|
||||
case CENTER_BOTTOM_BASE_LINE:
|
||||
cursor.x() = cursor.x()
|
||||
+ (cursor.x() - endOfLine_coords.x()) * 0.5f;
|
||||
break;
|
||||
// nothing to be done for these
|
||||
//case RIGHT_TOP:
|
||||
//case RIGHT_CENTER:
|
||||
//case RIGHT_BOTTOM:
|
||||
//case RIGHT_BASE_LINE:
|
||||
//case RIGHT_BOTTOM_BASE_LINE:
|
||||
// break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VERTICAL:
|
||||
{
|
||||
switch( _alignment )
|
||||
{
|
||||
// TODO: current behaviour top baselines lined up in both cases - need to implement
|
||||
// top of characters alignment - Question is this necessary?
|
||||
// ... otherwise, nothing to be done for these 6 cases
|
||||
//case LEFT_TOP:
|
||||
//case CENTER_TOP:
|
||||
//case RIGHT_TOP:
|
||||
// break;
|
||||
//case LEFT_BASE_LINE:
|
||||
//case CENTER_BASE_LINE:
|
||||
//case RIGHT_BASE_LINE:
|
||||
// break;
|
||||
case LEFT_CENTER:
|
||||
case CENTER_CENTER:
|
||||
case RIGHT_CENTER:
|
||||
cursor.y() = cursor.y()
|
||||
+ (cursor.y() - endOfLine_coords.y()) * 0.5f;
|
||||
break;
|
||||
case LEFT_BOTTOM_BASE_LINE:
|
||||
case CENTER_BOTTOM_BASE_LINE:
|
||||
case RIGHT_BOTTOM_BASE_LINE:
|
||||
cursor.y() = cursor.y() - (line_length * _characterHeight);
|
||||
break;
|
||||
case LEFT_BOTTOM:
|
||||
case CENTER_BOTTOM:
|
||||
case RIGHT_BOTTOM:
|
||||
cursor.y() = 2 * cursor.y() - endOfLine_coords.y();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( itr != endOfLine_itr )
|
||||
{
|
||||
|
||||
for(;itr != endOfLine_itr;++itr)
|
||||
{
|
||||
unsigned int charcode = *itr;
|
||||
|
||||
osgText::Glyph* glyph = activefont->getGlyph(_fontSize, charcode);
|
||||
if( glyph )
|
||||
{
|
||||
float width = (float) (glyph->getWidth()) * wr;
|
||||
float height = (float) (glyph->getHeight()) * hr;
|
||||
|
||||
if( _layout == RIGHT_TO_LEFT )
|
||||
{
|
||||
cursor.x() -= glyph->getHorizontalAdvance() * wr;
|
||||
}
|
||||
|
||||
// adjust cursor position w.r.t any kerning.
|
||||
if( kerning && previous_charcode )
|
||||
{
|
||||
switch( _layout )
|
||||
{
|
||||
case LEFT_TO_RIGHT:
|
||||
{
|
||||
osg::Vec2 delta( activefont->getKerning( previous_charcode,
|
||||
charcode,
|
||||
_kerningType ) );
|
||||
cursor.x() += delta.x() * wr;
|
||||
cursor.y() += delta.y() * hr;
|
||||
break;
|
||||
}
|
||||
case RIGHT_TO_LEFT:
|
||||
{
|
||||
osg::Vec2 delta( activefont->getKerning( charcode,
|
||||
previous_charcode,
|
||||
_kerningType ) );
|
||||
cursor.x() -= delta.x() * wr;
|
||||
cursor.y() -= delta.y() * hr;
|
||||
break;
|
||||
}
|
||||
case VERTICAL:
|
||||
break; // no kerning when vertical.
|
||||
}
|
||||
}
|
||||
|
||||
local = cursor;
|
||||
osg::Vec2 bearing( horizontal ? glyph->getHorizontalBearing()
|
||||
: glyph->getVerticalBearing() );
|
||||
local.x() += bearing.x() * wr;
|
||||
local.y() += bearing.y() * hr;
|
||||
|
||||
// set up the coords of the quad
|
||||
osg::Vec2 upLeft = local + osg::Vec2(0.f, height);
|
||||
osg::Vec2 lowLeft = local;
|
||||
osg::Vec2 lowRight = local + osg::Vec2(width, 0.f);
|
||||
osg::Vec2 upRight = local + osg::Vec2(width, height);
|
||||
|
||||
// move the cursor onto the next character.
|
||||
// also expand bounding box
|
||||
switch( _layout )
|
||||
{
|
||||
case LEFT_TO_RIGHT:
|
||||
cursor.x() += glyph->getHorizontalAdvance() * wr;
|
||||
bb.expandBy(lowLeft.x(), lowLeft.y());
|
||||
bb.expandBy(upRight.x(), upRight.y());
|
||||
break;
|
||||
case VERTICAL:
|
||||
cursor.y() -= glyph->getVerticalAdvance() * hr;
|
||||
bb.expandBy(upLeft.x(), upLeft.y());
|
||||
bb.expandBy(lowRight.x(), lowRight.y());
|
||||
break;
|
||||
case RIGHT_TO_LEFT:
|
||||
bb.expandBy(lowRight.x(), lowRight.y());
|
||||
bb.expandBy(upLeft.x(), upLeft.y());
|
||||
break;
|
||||
}
|
||||
previous_charcode = charcode;
|
||||
}
|
||||
}
|
||||
|
||||
// skip over spaces and return.
|
||||
while( itr != text.end() && *itr == ' ' )
|
||||
++itr;
|
||||
if( itr != text.end() && *itr == '\n' )
|
||||
++itr;
|
||||
}
|
||||
else
|
||||
{
|
||||
++itr;
|
||||
}
|
||||
|
||||
// move to new line.
|
||||
switch( _layout )
|
||||
{
|
||||
case LEFT_TO_RIGHT:
|
||||
{
|
||||
startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
|
||||
cursor = startOfLine_coords;
|
||||
previous_charcode = 0;
|
||||
break;
|
||||
}
|
||||
case RIGHT_TO_LEFT:
|
||||
{
|
||||
startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
|
||||
cursor = startOfLine_coords;
|
||||
previous_charcode = 0;
|
||||
break;
|
||||
}
|
||||
case VERTICAL:
|
||||
{
|
||||
startOfLine_coords.x() += _characterHeight * (1.0 + _lineSpacing)
|
||||
/ getCharacterAspectRatio();
|
||||
cursor = startOfLine_coords;
|
||||
previous_charcode = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const_cast<TextOSG*>(this)->_maximumWidth = max_width_safe;
|
||||
|
||||
return bb.size();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Vec2 Text::TextOSG::handleHit(const osg::Vec2f& pos)
|
||||
{
|
||||
float line_height = _characterHeight + _lineSpacing;
|
||||
|
||||
// TODO check with align other than TOP
|
||||
float first_line_y = -0.5 * _lineSpacing;//_offset.y() - _characterHeight;
|
||||
size_t line = std::max<int>(0, (pos.y() - first_line_y) / line_height);
|
||||
|
||||
if( _textureGlyphQuadMap.empty() )
|
||||
return osg::Vec2(-1, -1);
|
||||
|
||||
// TODO check when it can be larger
|
||||
assert( _textureGlyphQuadMap.size() == 1 );
|
||||
|
||||
const GlyphQuads& glyphquad = _textureGlyphQuadMap.begin()->second;
|
||||
const GlyphQuads::Glyphs& glyphs = glyphquad._glyphs;
|
||||
const GlyphQuads::Coords2& coords = glyphquad._coords;
|
||||
const GlyphQuads::LineNumbers& line_numbers = glyphquad._lineNumbers;
|
||||
|
||||
const float HIT_FRACTION = 0.6;
|
||||
const float character_width = getCharacterHeight()
|
||||
* getCharacterAspectRatio();
|
||||
|
||||
float y = (line + 0.5) * line_height;
|
||||
|
||||
bool line_found = false;
|
||||
for(size_t i = 0; i < line_numbers.size(); ++i)
|
||||
{
|
||||
if( line_numbers[i] != line )
|
||||
{
|
||||
if( !line_found )
|
||||
{
|
||||
if( line_numbers[i] < line )
|
||||
// Wait for the correct line...
|
||||
continue;
|
||||
|
||||
// We have already passed the correct line -> It's empty...
|
||||
return osg::Vec2(0, y);
|
||||
}
|
||||
|
||||
// Next line and not returned -> not before any character
|
||||
// -> return position after last character of line
|
||||
return osg::Vec2(coords[(i - 1) * 4 + 2].x(), y);
|
||||
}
|
||||
|
||||
line_found = true;
|
||||
|
||||
// Get threshold for mouse x position for setting cursor before or after
|
||||
// current character
|
||||
float threshold = coords[i * 4].x()
|
||||
+ HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
|
||||
* character_width;
|
||||
|
||||
if( pos.x() <= threshold )
|
||||
{
|
||||
osg::Vec2 hit(0, y);
|
||||
if( i == 0 || line_numbers[i - 1] != line )
|
||||
// first character of line
|
||||
hit.x() = coords[i * 4].x();
|
||||
else if( coords[(i - 1) * 4].x() == coords[(i - 1) * 4 + 2].x() )
|
||||
// If previous character width is zero set to begin of next character
|
||||
// (Happens eg. with spaces)
|
||||
hit.x() = coords[i * 4].x();
|
||||
else
|
||||
// position at center between characters
|
||||
hit.x() = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
|
||||
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found -> return position after last character
|
||||
return osg::Vec2
|
||||
(
|
||||
coords.back().x(),
|
||||
(_lineCount - 0.5) * line_height
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Text::TextOSG::computeBound() const
|
||||
{
|
||||
osg::BoundingBox bb = osgText::Text::computeBound();
|
||||
|
||||
#if OSG_VERSION_LESS_THAN(3,1,0)
|
||||
if( bb.valid() )
|
||||
{
|
||||
// TODO bounding box still doesn't seem always right (eg. with center
|
||||
// horizontal alignment not completely accurate)
|
||||
bb._min.y() += _offset.y();
|
||||
bb._max.y() += _offset.y();
|
||||
}
|
||||
#endif
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::TextOSG::computePositions(unsigned int contextID) const
|
||||
{
|
||||
if( _textureGlyphQuadMap.empty() || _layout == VERTICAL )
|
||||
return osgText::Text::computePositions(contextID);
|
||||
|
||||
// TODO check when it can be larger
|
||||
assert( _textureGlyphQuadMap.size() == 1 );
|
||||
|
||||
const GlyphQuads& quads = _textureGlyphQuadMap.begin()->second;
|
||||
const GlyphQuads::Glyphs& glyphs = quads._glyphs;
|
||||
const GlyphQuads::Coords2& coords = quads._coords;
|
||||
const GlyphQuads::LineNumbers& line_numbers = quads._lineNumbers;
|
||||
|
||||
float wr = _characterHeight / getCharacterAspectRatio();
|
||||
|
||||
size_t cur_line = static_cast<size_t>(-1);
|
||||
for(size_t i = 0; i < glyphs.size(); ++i)
|
||||
{
|
||||
// Check horizontal offsets
|
||||
|
||||
bool first_char = cur_line != line_numbers[i];
|
||||
cur_line = line_numbers[i];
|
||||
|
||||
bool last_char = (i + 1 == glyphs.size())
|
||||
|| (cur_line != line_numbers[i + 1]);
|
||||
|
||||
if( first_char || last_char )
|
||||
{
|
||||
// From osg/src/osgText/Text.cpp:
|
||||
//
|
||||
// osg::Vec2 upLeft = local+osg::Vec2(0.0f-fHorizQuadMargin, ...);
|
||||
// osg::Vec2 lowLeft = local+osg::Vec2(0.0f-fHorizQuadMargin, ...);
|
||||
// osg::Vec2 lowRight = local+osg::Vec2(width+fHorizQuadMargin, ...);
|
||||
// osg::Vec2 upRight = local+osg::Vec2(width+fHorizQuadMargin, ...);
|
||||
|
||||
float left = coords[i * 4].x(),
|
||||
right = coords[i * 4 + 2].x(),
|
||||
width = glyphs[i]->getWidth() * wr;
|
||||
|
||||
// (local + width + fHoriz) - (local - fHoriz) = width + 2*fHoriz | -width
|
||||
float margin = 0.5f * (right - left - width),
|
||||
cursor_x = left + margin
|
||||
- glyphs[i]->getHorizontalBearing().x() * wr;
|
||||
|
||||
if( first_char )
|
||||
{
|
||||
if( cur_line == 0 || cursor_x < _textBB._min.x() )
|
||||
_textBB._min.x() = cursor_x;
|
||||
}
|
||||
|
||||
if( last_char )
|
||||
{
|
||||
float cursor_w = cursor_x + glyphs[i]->getHorizontalAdvance() * wr;
|
||||
|
||||
if( cur_line == 0 || cursor_w > _textBB._max.x() )
|
||||
_textBB._max.x() = cursor_w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return osgText::Text::computePositions(contextID);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const std::string Text::TYPE_NAME = "text";
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::staticInit()
|
||||
{
|
||||
if( isInit<Text>() )
|
||||
return;
|
||||
|
||||
osg::ref_ptr<TextOSG> Text::*text = &Text::_text;
|
||||
|
||||
addStyle("fill", "color", &TextOSG::setFill, text);
|
||||
addStyle("background", "color", &TextOSG::setBackgroundColor, text);
|
||||
addStyle("character-size",
|
||||
"numeric",
|
||||
static_cast<
|
||||
void (TextOSG::*)(float)
|
||||
> (&TextOSG::setCharacterSize),
|
||||
text);
|
||||
addStyle("character-aspect-ratio",
|
||||
"numeric",
|
||||
&TextOSG::setCharacterAspect, text);
|
||||
addStyle("line-height", "numeric", &TextOSG::setLineHeight, text);
|
||||
addStyle("font-resolution", "numeric", &TextOSG::setFontResolution, text);
|
||||
addStyle("padding", "numeric", &TextOSG::setBoundingBoxMargin, text);
|
||||
// TEXT = 1 default
|
||||
// BOUNDINGBOX = 2
|
||||
// FILLEDBOUNDINGBOX = 4
|
||||
// ALIGNMENT = 8
|
||||
addStyle<int>("draw-mode", "", &TextOSG::setDrawMode, text);
|
||||
addStyle("max-width", "numeric", &TextOSG::setMaximumWidth, text);
|
||||
addStyle("font", "", &Text::setFont);
|
||||
addStyle("alignment", "", &Text::setAlignment);
|
||||
addStyle("text", "", &Text::setText, false);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Text::Text( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
Element(canvas, node, parent_style, parent),
|
||||
_text( new Text::TextOSG(this) )
|
||||
{
|
||||
staticInit();
|
||||
|
||||
setDrawable(_text);
|
||||
_text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
|
||||
_text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
|
||||
_text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
|
||||
|
||||
setupStyle();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Text::~Text()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::setText(const char* text)
|
||||
{
|
||||
_text->setText(text, osgText::String::ENCODING_UTF8);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::setFont(const char* name)
|
||||
{
|
||||
_text->setFont( Canvas::getSystemAdapter()->getFont(name) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::setAlignment(const char* align)
|
||||
{
|
||||
const std::string align_string(align);
|
||||
if( 0 ) return;
|
||||
#define ENUM_MAPPING(enum_val, string_val) \
|
||||
else if( align_string == string_val )\
|
||||
_text->setAlignment( osgText::Text::enum_val );
|
||||
#include "text-alignment.hxx"
|
||||
#undef ENUM_MAPPING
|
||||
else
|
||||
{
|
||||
if( !align_string.empty() )
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_WARN,
|
||||
"canvas::Text: unknown alignment '" << align_string << "'"
|
||||
);
|
||||
_text->setAlignment(osgText::Text::LEFT_BASE_LINE);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
#if 0
|
||||
const char* Text::getAlignment() const
|
||||
{
|
||||
switch( _text->getAlignment() )
|
||||
{
|
||||
#define ENUM_MAPPING(enum_val, string_val) \
|
||||
case osgText::Text::enum_val:\
|
||||
return string_val;
|
||||
#include "text-alignment.hxx"
|
||||
#undef ENUM_MAPPING
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Text::heightForWidth(int w) const
|
||||
{
|
||||
return _text->sizeForWidth(w).y();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Text::maxWidth() const
|
||||
{
|
||||
return _text->sizeForWidth(INT_MAX).x();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const
|
||||
{
|
||||
return _text->handleHit(pos);
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,63 +0,0 @@
|
||||
// A text on the Canvas
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_TEXT_HXX_
|
||||
#define CANVAS_TEXT_HXX_
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
#include <osgText/Text>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class Text:
|
||||
public Element
|
||||
{
|
||||
public:
|
||||
static const std::string TYPE_NAME;
|
||||
static void staticInit();
|
||||
|
||||
Text( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent = 0 );
|
||||
~Text();
|
||||
|
||||
void setText(const char* text);
|
||||
void setFont(const char* name);
|
||||
void setAlignment(const char* align);
|
||||
|
||||
int heightForWidth(int w) const;
|
||||
int maxWidth() const;
|
||||
osg::Vec2 getNearestCursor(const osg::Vec2& pos) const;
|
||||
|
||||
protected:
|
||||
|
||||
class TextOSG;
|
||||
osg::ref_ptr<TextOSG> _text;
|
||||
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_TEXT_HXX_ */
|
||||
@@ -1,66 +0,0 @@
|
||||
/// Unit tests for canvas::Element
|
||||
#define BOOST_TEST_MODULE canvas
|
||||
#include <BoostTestTargetConfig.h>
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
#include "CanvasGroup.hxx"
|
||||
|
||||
namespace sc = simgear::canvas;
|
||||
|
||||
BOOST_AUTO_TEST_CASE( attr_data )
|
||||
{
|
||||
// http://www.w3.org/TR/html5/dom.html#attr-data-*
|
||||
|
||||
#define SG_CHECK_ATTR2PROP(attr, prop)\
|
||||
BOOST_CHECK_EQUAL(sc::Element::attrToDataPropName(attr), prop)
|
||||
|
||||
// If name starts with "data-", for each "-" (U+002D) character in the name
|
||||
// that is followed by a lowercase ASCII letter, remove the "-" (U+002D)
|
||||
// character and replace the character that followed it by the same character
|
||||
// converted to ASCII uppercase.
|
||||
|
||||
SG_CHECK_ATTR2PROP("no-data", "");
|
||||
SG_CHECK_ATTR2PROP("data-blub", "blub");
|
||||
SG_CHECK_ATTR2PROP("data-blub-x-y", "blubXY");
|
||||
SG_CHECK_ATTR2PROP("data-blub-x-y-", "blubXY-");
|
||||
|
||||
#undef SG_CHECK_ATTR2PROP
|
||||
|
||||
#define SG_CHECK_PROP2ATTR(prop, attr)\
|
||||
BOOST_CHECK_EQUAL(sc::Element::dataPropToAttrName(prop), attr)
|
||||
|
||||
// If name contains a "-" (U+002D) character followed by a lowercase ASCII
|
||||
// letter, throw a SyntaxError exception (empty string) and abort these steps.
|
||||
// For each uppercase ASCII letter in name, insert a "-" (U+002D) character
|
||||
// before the character and replace the character with the same character
|
||||
// converted to ASCII lowercase.
|
||||
// Insert the string "data-" at the front of name.
|
||||
|
||||
SG_CHECK_PROP2ATTR("test", "data-test");
|
||||
SG_CHECK_PROP2ATTR("testIt", "data-test-it");
|
||||
SG_CHECK_PROP2ATTR("testIt-Hyphen", "data-test-it--hyphen");
|
||||
SG_CHECK_PROP2ATTR("-test", "");
|
||||
SG_CHECK_PROP2ATTR("test-", "data-test-");
|
||||
|
||||
#undef SG_CHECK_PROP2ATTR
|
||||
|
||||
SGPropertyNode_ptr node = new SGPropertyNode;
|
||||
sc::ElementPtr el =
|
||||
sc::Element::create<sc::Group>(sc::CanvasWeakPtr(), node);
|
||||
|
||||
el->setDataProp("myData", 3);
|
||||
BOOST_CHECK_EQUAL( el->getDataProp<int>("myData"), 3 );
|
||||
BOOST_CHECK_EQUAL( node->getIntValue("data-my-data"), 3 );
|
||||
|
||||
SGPropertyNode* prop = el->getDataProp<SGPropertyNode*>("notExistingProp");
|
||||
BOOST_CHECK( !prop );
|
||||
prop = el->getDataProp<SGPropertyNode*>("myData");
|
||||
BOOST_CHECK( prop );
|
||||
BOOST_CHECK_EQUAL( prop->getParent(), node );
|
||||
BOOST_CHECK_EQUAL( prop->getIntValue(), 3 );
|
||||
|
||||
BOOST_CHECK( el->hasDataProp("myData") );
|
||||
el->removeDataProp("myData");
|
||||
BOOST_CHECK( !el->hasDataProp("myData") );
|
||||
BOOST_CHECK_EQUAL( el->getDataProp("myData", 5), 5 );
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user