diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b353f2d15..177044ee79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,942 +1,951 @@ project(krita) message(STATUS "Using CMake version: ${CMAKE_VERSION}") cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) set(MIN_QT_VERSION 5.9.0) set(MIN_FRAMEWORKS_VERSION 5.44.0) set( CMAKE_CXX_STANDARD 11 ) set( CMAKE_CXX_STANDARD_REQUIRED ON ) if (POLICY CMP0002) cmake_policy(SET CMP0002 OLD) endif() if (POLICY CMP0017) cmake_policy(SET CMP0017 NEW) endif () if (POLICY CMP0022) cmake_policy(SET CMP0022 OLD) endif () if (POLICY CMP0026) cmake_policy(SET CMP0026 OLD) endif() if (POLICY CMP0042) cmake_policy(SET CMP0042 NEW) endif() if (POLICY CMP0046) cmake_policy(SET CMP0046 OLD) endif () if (POLICY CMP0059) cmake_policy(SET CMP0059 OLD) endif() if (POLICY CMP0063) cmake_policy(SET CMP0063 OLD) endif() if (POLICY CMP0054) cmake_policy(SET CMP0054 OLD) endif() if (POLICY CMP0064) cmake_policy(SET CMP0064 OLD) endif() if (POLICY CMP0071) cmake_policy(SET CMP0071 OLD) endif() if (APPLE) set(APPLE_SUPPRESS_X11_WARNING TRUE) set(KDE_SKIP_RPATH_SETTINGS TRUE) set(CMAKE_MACOSX_RPATH 1) set(BUILD_WITH_INSTALL_RPATH 1) add_definitions(-mmacosx-version-min=10.12 -Wno-macro-redefined -Wno-deprecated-register) endif() if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 AND NOT WIN32) add_compile_options($<$:-Wno-suggest-override> -Wextra) endif() ###################### ####################### ## Constants defines ## ####################### ###################### # define common versions of Krita applications, used to generate kritaversion.h # update these version for every release: set(KRITA_VERSION_STRING "4.3.0-prealpha") # Major version: 3 for 3.x, 4 for 4.x, etc. set(KRITA_STABLE_VERSION_MAJOR 4) # Minor version: 0 for 4.0, 1 for 4.1, etc. set(KRITA_STABLE_VERSION_MINOR 3) # Bugfix release version, or 0 for before the first stable release set(KRITA_VERSION_RELEASE 0) # the 4th digit, really only used for the Windows installer: # - [Pre-]Alpha: Starts from 0, increment 1 per release # - Beta: Starts from 50, increment 1 per release # - Stable: Set to 100, bump to 101 if emergency update is needed set(KRITA_VERSION_REVISION 0) set(KRITA_ALPHA 1) # uncomment only for Alpha #set(KRITA_BETA 1) # uncomment only for Beta #set(KRITA_RC 1) # uncomment only for RC set(KRITA_YEAR 2018) # update every year if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC) set(KRITA_STABLE 1) # do not edit endif() message(STATUS "Krita version: ${KRITA_VERSION_STRING}") # Define the generic version of the Krita libraries here # This makes it easy to advance it when the next Krita release comes. # 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series # (2.x) so we're starting with 15 in 3.x series, 16 in 4.x series if(KRITA_STABLE_VERSION_MAJOR EQUAL 4) math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 16") else() # let's make sure we won't forget to update the "16" message(FATAL_ERROR "Reminder: please update offset == 16 used to compute GENERIC_KRITA_LIB_VERSION_MAJOR to something bigger") endif() set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0") set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro") # fetch git revision for the current build set(KRITA_GIT_SHA1_STRING "") set(KRITA_GIT_BRANCH_STRING "") include(GetGitRevisionDescription) get_git_head_hash(GIT_SHA1) get_git_branch(GIT_BRANCH) if(GIT_SHA1) string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1) set(KRITA_GIT_SHA1_STRING ${GIT_SHA1}) if(GIT_BRANCH) set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH}) else() set(KRITA_GIT_BRANCH_STRING "(detached HEAD)") endif() endif() # create test make targets enable_testing() # collect list of broken tests, empty here to start fresh with each cmake run set(KRITA_BROKEN_TESTS "" CACHE INTERNAL "KRITA_BROKEN_TESTS") ############ ############# ## Options ## ############# ############ include(FeatureSummary) if (WIN32) option(USE_DRMINGW "Support the Dr. Mingw crash handler (only on windows)" ON) add_feature_info("Dr. Mingw" USE_DRMINGW "Enable the Dr. Mingw crash handler") if (MINGW) option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON) add_feature_info("Linker Security Flags" USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags") if (USE_MINGW_HARDENING_LINKER) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") # Enable high-entropy ASLR for 64-bit # The image base has to be >4GB for HEASLR to be enabled. # The values used here are kind of arbitrary. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") else (USE_MINGW_HARDENING_LINKER) message(WARNING "Linker Security Flags not enabled!") endif (USE_MINGW_HARDENING_LINKER) endif (MINGW) endif () option(HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal." ON) configure_file(config-hide-safe-asserts.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hide-safe-asserts.h) add_feature_info("Hide Safe Asserts" HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal.") option(USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking." ON) configure_file(config-hash-table-implementaion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hash-table-implementaion.h) add_feature_info("Lock free hash table" USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking.") option(FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true." OFF) add_feature_info("Foundation Build" FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true.") option(KRITA_ENABLE_BROKEN_TESTS "Enable tests that are marked as broken" OFF) add_feature_info("Enable Broken Tests" KRITA_ENABLE_BROKEN_TESTS "Runs broken test when \"make test\" is invoked (use -DKRITA_ENABLE_BROKEN_TESTS=ON to enable).") option(LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode" ON) configure_file(config-limit-long-tests.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-limit-long-tests.h) add_feature_info("Limit long tests" LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode") option(ENABLE_PYTHON_2 "Enables the compiler to look for Python 2.7 instead of Python 3. Some packaged scripts are not compatible with Python 2 and this should only be used if you really have to use 2.7." OFF) option(BUILD_KRITA_QT_DESIGNER_PLUGINS "Build Qt Designer plugins for Krita widgets" OFF) add_feature_info("Build Qt Designer plugins" BUILD_KRITA_QT_DESIGNER_PLUGINS "Builds Qt Designer plugins for Krita widgets (use -DBUILD_KRITA_QT_DESIGNER_PLUGINS=ON to enable).") include(MacroJPEG) ######################################################### ## Look for Python3 It is also searched by KF5, ## ## so we should request the correct version in advance ## ######################################################### function(TestCompileLinkPythonLibs OUTPUT_VARNAME) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES ${PYTHON_INCLUDE_PATH}) set(CMAKE_REQUIRED_LIBRARIES ${PYTHON_LIBRARIES}) if (MINGW) set(CMAKE_REQUIRED_DEFINITIONS -D_hypot=hypot) endif (MINGW) unset(${OUTPUT_VARNAME} CACHE) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { Py_InitializeEx(0); }" ${OUTPUT_VARNAME}) endfunction() if(MINGW) if(ENABLE_PYTHON_2) message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.") else(ENABLE_PYTHON_2) find_package(PythonInterp 3.6 EXACT) find_package(PythonLibs 3.6 EXACT) endif(ENABLE_PYTHON_2) if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) if(ENABLE_PYTHON_2) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonLibrary 3.6) endif(ENABLE_PYTHON_2) TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS) if (NOT CAN_USE_PYTHON_LIBS) message(FATAL_ERROR "Compiling with Python library failed, please check whether the architecture is correct. Python will be disabled.") endif (NOT CAN_USE_PYTHON_LIBS) endif (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) else(MINGW) if(ENABLE_PYTHON_2) find_package(PythonInterp 2.7) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonInterp 3.0) find_package(PythonLibrary 3.0) endif(ENABLE_PYTHON_2) endif(MINGW) ######################## ######################### ## Look for KDE and Qt ## ######################### ######################## find_package(ECM 5.22 REQUIRED NOMODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(ECMOptionalAddSubdirectory) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(GenerateExportHeader) include(ECMMarkAsTest) include(ECMInstallIcons) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) # do not reorder to be alphabetical: this is the order in which the frameworks # depend on each other. find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS Config WidgetsAddons Completion CoreAddons GuiAddons I18n ItemModels ItemViews WindowSystem Archive ) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS Core Gui Widgets Xml Network PrintSupport Svg Test Concurrent ) if (ANDROID) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS AndroidExtras ) endif() if (WIN32) set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES}) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_MSWindowsUseWinTabAPI); } " QT_HAS_WINTAB_SWITCH ) unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_LIBRARIES) option(USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt." ON) add_feature_info("Use Qt's Windows Tablet Support" USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt.") configure_file(config_use_qt_tablet_windows.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_use_qt_tablet_windows.h) endif () set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Gui_LIBRARIES}) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QSurfaceFormat fmt; fmt.setColorSpace(QSurfaceFormat::scRGBColorSpace); fmt.setColorSpace(QSurfaceFormat::bt2020PQColorSpace); } " HAVE_HDR ) configure_file(config-hdr.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hdr.h) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round); QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); } " HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY ) configure_file(config-high-dpi-scale-factor-rounding-policy.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-high-dpi-scale-factor-rounding-policy.h) if (WIN32) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QWindowsWindowFunctions::setHasBorderInFullScreenDefault(true); } " HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT ) configure_file(config-set-has-border-in-full-screen-default.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-set-has-border-in-full-screen-default.h) endif (WIN32) unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_LIBRARIES) include (MacroAddFileDependencies) include (MacroBoolTo01) include (MacroEnsureOutOfSourceBuild) macro_ensure_out_of_source_build("Compiling Krita inside the source directory is not possible. Please refer to the build instruction https://community.kde.org/Krita#Build_Instructions") # Note: OPTIONAL_COMPONENTS does not seem to be reliable # (as of ECM 5.15.0, CMake 3.2) find_package(Qt5Multimedia ${MIN_QT_VERSION}) set_package_properties(Qt5Multimedia PROPERTIES DESCRIPTION "Qt multimedia integration" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide sound support for animations") macro_bool_to_01(Qt5Multimedia_FOUND HAVE_QT_MULTIMEDIA) configure_file(config-qtmultimedia.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-qtmultimedia.h ) if (NOT APPLE) find_package(Qt5Quick ${MIN_QT_VERSION}) set_package_properties(Qt5Quick PROPERTIES DESCRIPTION "QtQuick" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") macro_bool_to_01(Qt5Quick_FOUND HAVE_QT_QUICK) find_package(Qt5QuickWidgets ${MIN_QT_VERSION}) set_package_properties(Qt5QuickWidgets PROPERTIES DESCRIPTION "QtQuickWidgets" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") endif() if (NOT WIN32 AND NOT APPLE AND NOT ANDROID) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras) find_package(Qt5DBus ${MIN_QT_VERSION}) set(HAVE_DBUS ${Qt5DBus_FOUND}) set_package_properties(Qt5DBus PROPERTIES DESCRIPTION "Qt DBUS integration" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide a dbus api on Linux") find_package(KF5Crash ${MIN_FRAMEWORKS_VERSION}) macro_bool_to_01(KF5Crash_FOUND HAVE_KCRASH) set_package_properties(KF5Crash PROPERTIES DESCRIPTION "KDE's Crash Handler" URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kcrash/html/index.html" TYPE OPTIONAL PURPOSE "Optionally used to provide crash reporting on Linux") find_package(X11 REQUIRED COMPONENTS Xinput) set(HAVE_X11 TRUE) add_definitions(-DHAVE_X11) else() set(HAVE_DBUS FALSE) set(HAVE_X11 FALSE) endif() add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_STRICT_ITERATORS -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_NO_URL_CAST_FROM_STRING -DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS ) #if (${Qt5_VERSION} VERSION_GREATER "5.14.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50F00) #elseif (${Qt5_VERSION} VERSION_GREATER "5.13.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50E00) #elseif (${Qt5_VERSION} VERSION_GREATER "5.12.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50D00) #elseif (${Qt5_VERSION} VERSION_GREATER "5.11.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50C00) #if(${Qt5_VERSION} VERSION_GREATER "5.10.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50B00) #if(${Qt5_VERSION} VERSION_GREATER "5.9.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50A00) #else() add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50900) #endif() add_definitions(-DQT_DEPRECATED_WARNINGS) add_definitions(-DTRANSLATION_DOMAIN=\"krita\") # # The reason for this mode is that the Debug mode disable inlining # if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals") endif() option(KRITA_DEVS "For Krita developers. This modifies the DEBUG build type to use -O3 -g, while still enabling Q_ASSERT. This is necessary because the Qt5 cmake modules normally append QT_NO_DEBUG to any build type that is not labeled Debug") if (KRITA_DEVS) set(CMAKE_CXX_FLAGS_DEBUG "-O3 -g" CACHE STRING "" FORCE) endif() if(UNIX) set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m") endif() if(WIN32) if(MSVC) # C4522: 'class' : multiple assignment operators specified set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4522") endif() endif() # KDECompilerSettings adds the `--export-all-symbols` linker flag. # We don't really need it. if(MINGW) string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") endif(MINGW) if(MINGW) # Hack CMake's variables to tell AR to create thin archives to reduce unnecessary writes. # Source of definition: https://github.com/Kitware/CMake/blob/v3.14.1/Modules/Platform/Windows-GNU.cmake#L128 # Thin archives: https://sourceware.org/binutils/docs/binutils/ar.html#index-thin-archives macro(mingw_use_thin_archive lang) foreach(rule CREATE_SHARED_MODULE CREATE_SHARED_LIBRARY LINK_EXECUTABLE) string(REGEX REPLACE "( [^ T]+) " "\\1T " CMAKE_${lang}_${rule} "${CMAKE_${lang}_${rule}}") endforeach() endmacro() mingw_use_thin_archive(CXX) endif(MINGW) # enable exceptions globally kde_enable_exceptions() set(KRITA_DEFAULT_TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/sdk/tests/data/) macro(macro_add_unittest_definitions) add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/") add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}") add_definitions(-DFILES_DEFAULT_DATA_DIR="${KRITA_DEFAULT_TEST_DATA_DIR}") add_definitions(-DSYSTEM_RESOURCES_DATA_DIR="${CMAKE_SOURCE_DIR}/krita/data/") endmacro() # overcome some platform incompatibilities if(WIN32) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif() # set custom krita plugin installdir if (ANDROID) # use default ABI if (NOT ANDROID_ABI) set (ANDROID_ABI armeabi-v7a) endif() set (ANDROID_SDK_ROOT $ENV{ANDROID_SDK_ROOT}) set (KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}) # set (DATA_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/assets) else() set (KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins) endif() ########################### ############################ ## Required dependencies ## ############################ ########################### # FIXME: Still hardcoded if (ANDROID) set (Boost_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/i/${ANDROID_ABI}/include/boost-1_69) set (Boost_LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/i/${ANDROID_ABI}/lib) set (KF5_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/kf5/kde/install/lib) endif() find_package(PNG REQUIRED) list (APPEND ANDROID_EXTRA_LIBS ${PNG_LIBRARY}) if (APPLE) # this is not added correctly on OSX -- see http://forum.kde.org/viewtopic.php?f=139&t=101867&p=221242#p221242 include_directories(SYSTEM ${PNG_INCLUDE_DIR}) endif() add_definitions(-DBOOST_ALL_NO_LIB) find_package(Boost 1.55 REQUIRED COMPONENTS system) include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) ## ## Test for GNU Scientific Library ## find_package(GSL) set_package_properties(GSL PROPERTIES URL "http://www.gnu.org/software/gsl" TYPE RECOMMENDED PURPOSE "Required by Krita's Transform tool.") macro_bool_to_01(GSL_FOUND HAVE_GSL) configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h ) if (GSL_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES}) endif() ########################### ############################ ## Optional dependencies ## ############################ ########################### find_package(ZLIB) set_package_properties(ZLIB PROPERTIES DESCRIPTION "Compression library" URL "http://www.zlib.net/" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic and the PSD plugins") macro_bool_to_01(ZLIB_FOUND HAVE_ZLIB) find_package(OpenEXR) set_package_properties(OpenEXR PROPERTIES DESCRIPTION "High dynamic-range (HDR) image file format" URL "http://www.openexr.com" TYPE OPTIONAL PURPOSE "Required by the Krita OpenEXR filter") macro_bool_to_01(OPENEXR_FOUND HAVE_OPENEXR) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() find_package(TIFF) set_package_properties(TIFF PROPERTIES DESCRIPTION "TIFF Library and Utilities" URL "http://www.remotesensing.org/libtiff" TYPE OPTIONAL PURPOSE "Required by the Krita TIFF filter") if (TIFF_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${TIFF_LIBRARY}) endif() find_package(JPEG) set_package_properties(JPEG PROPERTIES DESCRIPTION "Free library for JPEG image compression. Note: libjpeg8 is NOT supported." URL "http://www.libjpeg-turbo.org" TYPE OPTIONAL PURPOSE "Required by the Krita JPEG filter") if (JPEG_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${JPEG_LIBRARY}) macro_bool_to_01(JPEG_FOUND HAVE_JPEG) endif() find_package(GIF) set_package_properties(GIF PROPERTIES DESCRIPTION "Library for loading and saving gif files." URL "http://giflib.sourceforge.net/" TYPE OPTIONAL PURPOSE "Required by the Krita GIF filter") if (GIF_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${GIF_LIBRARY}) endif() find_package(HEIF "1.3.0") set_package_properties(HEIF PROPERTIES DESCRIPTION "Library for loading and saving heif files." URL "https://github.com/strukturag/libheif" TYPE OPTIONAL PURPOSE "Required by the Krita HEIF filter") find_package(OpenJPEG "2.3.0") set_package_properties(OpenJPEG PROPERTIES DESCRIPTION "Library for loading and saving jp2000 files." URL "http://www.openjpeg.org/" TYPE OPTIONAL PURPOSE "Required by the Krita JP2000 filter") set(LIBRAW_MIN_VERSION "0.16") find_package(LibRaw ${LIBRAW_MIN_VERSION}) set_package_properties(LibRaw PROPERTIES DESCRIPTION "Library to decode RAW images" URL "http://www.libraw.org" TYPE OPTIONAL PURPOSE "Required to build the raw import plugin") find_package(FFTW3) set_package_properties(FFTW3 PROPERTIES DESCRIPTION "A fast, free C FFT library" URL "http://www.fftw.org/" TYPE OPTIONAL PURPOSE "Required by the Krita for fast convolution operators and some G'Mic features") macro_bool_to_01(FFTW3_FOUND HAVE_FFTW3) if (FFTW3_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${FFTW3_LIBRARY}) endif() find_package(OCIO) set_package_properties(OCIO PROPERTIES DESCRIPTION "The OpenColorIO Library" URL "http://www.opencolorio.org" TYPE OPTIONAL PURPOSE "Required by the Krita LUT docker") macro_bool_to_01(OCIO_FOUND HAVE_OCIO) set_package_properties(PythonLibrary PROPERTIES DESCRIPTION "Python Library" URL "http://www.python.org" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYTHONLIBS_FOUND HAVE_PYTHONLIBS) find_package(SIP "4.19.13") set_package_properties(SIP PROPERTIES DESCRIPTION "Support for generating SIP Python bindings" URL "https://www.riverbankcomputing.com/software/sip/download" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(SIP_FOUND HAVE_SIP) find_package(PyQt5 "5.6.0") set_package_properties(PyQt5 PROPERTIES DESCRIPTION "Python bindings for Qt5." URL "https://www.riverbankcomputing.com/software/pyqt/download5" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYQT5_FOUND HAVE_PYQT5) -find_package(VPX "1.7.0") -set_package_properties(VPX PROPERTIES - DESCRIPTION "libvpx video encoder." - URL "https://www.webmproject.org/" +find_package(GSTREAMER "1.5.2") +set_package_properties(GSTREAMER PROPERTIES + DESCRIPTION "gstreamer video encoder." + URL "https://gstreamer.freedesktop.org/" TYPE OPTIONAL PURPOSE "Required by the Krita recorder plugin") -macro_bool_to_01(VPX_FOUND HAVE_VPX) +macro_bool_to_01(GSTREAMER_FOUND HAVE_GSTREAMER) -find_package(YUV) -set_package_properties(YUV PROPERTIES - DESCRIPTION "libyuv." - URL "https://chromium.googlesource.com/libyuv/libyuv/" +find_package(GLIB "2.0") +set_package_properties(GLIB PROPERTIES + DESCRIPTION "glib." + URL "https://www.gtk.org/" TYPE OPTIONAL PURPOSE "Required by the Krita recorder plugin") -macro_bool_to_01(YUV_FOUND HAVE_YUV) +macro_bool_to_01(GLIB_FOUND HAVE_GLIB) + +find_package(GOBJECT "2.0") +set_package_properties(GOBJECT PROPERTIES + DESCRIPTION "gobject." + URL "https://www.gtk.org/" + TYPE OPTIONAL + PURPOSE "Required by the Krita recorder plugin") +macro_bool_to_01(GOBJECT_FOUND HAVE_GOBJECT) + ## ## Look for OpenGL ## # TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes) if(Qt5Gui_OPENGL_IMPLEMENTATION) message(STATUS "Found QtGui OpenGL support") else() message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.") endif() ## ## Test for eigen3 ## find_package(Eigen3 3.0 REQUIRED) set_package_properties(Eigen3 PROPERTIES DESCRIPTION "C++ template library for linear algebra" URL "http://eigen.tuxfamily.org" TYPE REQUIRED) ## ## Test for exiv2 ## find_package(LibExiv2 0.16 REQUIRED) if (ANDROID) list (APPEND ANDROID_EXTRA_LIBS ${LibExiv2_LIBRARIES}) # because libexiv2 depends on libexpat and it is installed in the same folder get_filename_component (_base_dir ${LibExiv2_LIBRARIES} DIRECTORY) list (APPEND ANDROID_EXTRA_LIBS ${_base_dir}/libexpat.so) endif() ## ## Test for lcms ## find_package(LCMS2 2.4 REQUIRED) set_package_properties(LCMS2 PROPERTIES DESCRIPTION "LittleCMS Color management engine" URL "http://www.littlecms.com" TYPE REQUIRED PURPOSE "Will be used for color management and is necessary for Krita") if(LCMS2_FOUND) if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 ) set(HAVE_LCMS24 TRUE) endif() set(HAVE_REQUIRED_LCMS_VERSION TRUE) set(HAVE_LCMS2 TRUE) endif() list (APPEND ANDROID_EXTRA_LIBS ${LCMS2_LIBRARIES}) ## ## Test for Vc ## set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ) set(HAVE_VC FALSE) if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") if(NOT MSVC) find_package(Vc 1.1.0) set_package_properties(Vc PROPERTIES DESCRIPTION "Portable, zero-overhead SIMD library for C++" URL "https://github.com/VcDevel/Vc" TYPE OPTIONAL PURPOSE "Required by the Krita for vectorization") macro_bool_to_01(Vc_FOUND HAVE_VC) endif() endif() configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h ) if(HAVE_VC) message(STATUS "Vc found!") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/vc") include (VcMacros) if(Vc_COMPILER_IS_CLANG) set(ADDITIONAL_VC_FLAGS "-ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() elseif (NOT MSVC) set(ADDITIONAL_VC_FLAGS "-fabi-version=0 -ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() endif() macro(ko_compile_for_all_implementations_no_scalar _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() macro(ko_compile_for_all_implementations _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() endif() set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} ) add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) ## ## Test endianness ## include (TestBigEndian) test_big_endian(CMAKE_WORDS_BIGENDIAN) ## ## Test for qt-poppler ## find_package(Poppler COMPONENTS Qt5) set_package_properties(Poppler PROPERTIES DESCRIPTION "A PDF rendering library" URL "http://poppler.freedesktop.org" TYPE OPTIONAL PURPOSE "Required by the Krita PDF filter.") ## ## Test for quazip ## find_package(QuaZip 0.6) set_package_properties(QuaZip PROPERTIES DESCRIPTION "A library for reading and writing zip files" URL "https://stachenov.github.io/quazip/" TYPE REQUIRED PURPOSE "Needed for reading and writing KRA and ORA files" ) # FIXME: better way to do this? list (APPEND ANDROID_EXTRA_LIBS ${QUAZIP_LIBRARIES} ${EXPAT_LIBRARY} ${KF5_LIBRARIES}/libKF5Completion.so ${KF5_LIBRARIES}/libKF5WindowSystem.so ${KF5_LIBRARIES}/libKF5WidgetsAddons.so ${KF5_LIBRARIES}/libKF5ItemViews.so ${KF5_LIBRARIES}/libKF5ItemModels.so ${KF5_LIBRARIES}/libKF5GuiAddons.so ${KF5_LIBRARIES}/libKF5I18n.so ${KF5_LIBRARIES}/libKF5CoreAddons.so ${KF5_LIBRARIES}/libKF5ConfigGui.so ${KF5_LIBRARIES}/libKF5ConfigCore.so ${KF5_LIBRARIES}/libKF5Archive.so) ## ## Test for Atomics ## include(CheckAtomic) ############################ ############################# ## Add Krita helper macros ## ############################# ############################ include(MacroKritaAddBenchmark) #################### ##################### ## Define includes ## ##################### #################### # for config.h and includes (if any?) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/interfaces ) add_subdirectory(libs) add_subdirectory(plugins) if (BUILD_TESTING) add_subdirectory(benchmarks) endif() add_subdirectory(krita) configure_file(KoConfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h ) configure_file(config_convolution.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_convolution.h) configure_file(config-ocio.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ocio.h ) check_function_exists(powf HAVE_POWF) configure_file(config-powf.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-powf.h) if(WIN32) include(${CMAKE_CURRENT_LIST_DIR}/packaging/windows/installer/ConfigureInstallerNsis.cmake) endif() message("\nBroken tests:") foreach(tst ${KRITA_BROKEN_TESTS}) message(" * ${tst}") endforeach() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/po OR EXISTS ${CMAKE_CURRENT_BINARY_DIR}/po ) find_package(KF5I18n CONFIG REQUIRED) ki18n_install(po) endif() if(DEFINED QTANDROID_EXPORTED_TARGET AND NOT TARGET "create-apk") set (_CMAKE_ANDROID_DIR "${ECM_DIR}/../toolchain") list(LENGTH QTANDROID_EXPORTED_TARGET targetsCount) include(${_CMAKE_ANDROID_DIR}/ECMAndroidDeployQt.cmake) math(EXPR last "${targetsCount}-1") foreach(idx RANGE 0 ${last}) list(GET QTANDROID_EXPORTED_TARGET ${idx} exportedTarget) list(GET ANDROID_APK_DIR ${idx} APK_DIR) if(APK_DIR AND NOT EXISTS "${ANDROID_APK_DIR}/AndroidManifest.xml" AND IS_ABSOLUTE ANDROID_APK_DIR) message(FATAL_ERROR "Cannot find ${APK_DIR}/AndroidManifest.xml according to ANDROID_APK_DIR. ${ANDROID_APK_DIR} ${exportedTarget}") elseif(NOT APK_DIR) get_filename_component(_qt5Core_install_prefix "${Qt5Core_DIR}/../../../" ABSOLUTE) set(APK_DIR "${_qt5Core_install_prefix}/src/android/templates/") endif() ecm_androiddeployqt("${exportedTarget}" "${ECM_ADDITIONAL_FIND_ROOT_PATH}") set_target_properties(create-apk-${exportedTarget} PROPERTIES ANDROID_APK_DIR "${APK_DIR}") endforeach() elseif(ANDROID) message(STATUS "You can export a target by specifying -DQTANDROID_EXPORTED_TARGET= and -DANDROID_APK_DIR=") endif() diff --git a/cmake/modules/FindGLIB.cmake b/cmake/modules/FindGLIB.cmake new file mode 100644 index 0000000000..3d2141d931 --- /dev/null +++ b/cmake/modules/FindGLIB.cmake @@ -0,0 +1,122 @@ +# - Try to find Glib and its components (gio, gobject etc) +# Once done, this will define +# +# GLIB_FOUND - system has Glib +# GLIB_INCLUDE_DIRS - the Glib include directories +# GLIB_LIBRARIES - link these to use Glib +# +# Optionally, the COMPONENTS keyword can be passed to find_package() +# and Glib components can be looked for. Currently, the following +# components can be used, and they define the following variables if +# found: +# +# gio: GLIB_GIO_LIBRARIES +# gobject: GLIB_GOBJECT_LIBRARIES +# gmodule: GLIB_GMODULE_LIBRARIES +# gthread: GLIB_GTHREAD_LIBRARIES +# +# Note that the respective _INCLUDE_DIR variables are not set, since +# all headers are in the same directory as GLIB_INCLUDE_DIRS. +# +# Copyright (C) 2012 Raphael Kubo da Costa +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +find_package(PkgConfig) +pkg_check_modules(PC_GLIB QUIET glib-2.0) + +find_library(GLIB_LIBRARIES + NAMES glib-2.0 + HINTS ${PC_GLIB_LIBDIR} + ${PC_GLIB_LIBRARY_DIRS} +) + +# Files in glib's main include path may include glibconfig.h, which, +# for some odd reason, is normally in $LIBDIR/glib-2.0/include. +get_filename_component(_GLIB_LIBRARY_DIR ${GLIB_LIBRARIES} PATH) +find_path(GLIBCONFIG_INCLUDE_DIR + NAMES glibconfig.h + HINTS ${PC_LIBDIR} ${PC_LIBRARY_DIRS} ${_GLIB_LIBRARY_DIR} + ${PC_GLIB_INCLUDEDIR} ${PC_GLIB_INCLUDE_DIRS} + PATH_SUFFIXES glib-2.0/include +) + +find_path(GLIB_INCLUDE_DIR + NAMES glib.h + HINTS ${PC_GLIB_INCLUDEDIR} + ${PC_GLIB_INCLUDE_DIRS} + PATH_SUFFIXES glib-2.0 +) + +set(GLIB_INCLUDE_DIRS ${GLIB_INCLUDE_DIR} ${GLIBCONFIG_INCLUDE_DIR}) + +# Version detection +if (EXISTS "${GLIBCONFIG_INCLUDE_DIR}/glibconfig.h") + file(READ "${GLIBCONFIG_INCLUDE_DIR}/glibconfig.h" GLIBCONFIG_H_CONTENTS) + string(REGEX MATCH "#define GLIB_MAJOR_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}") + set(GLIB_VERSION_MAJOR "${CMAKE_MATCH_1}") + string(REGEX MATCH "#define GLIB_MINOR_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}") + set(GLIB_VERSION_MINOR "${CMAKE_MATCH_1}") + string(REGEX MATCH "#define GLIB_MICRO_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}") + set(GLIB_VERSION_MICRO "${CMAKE_MATCH_1}") + set(GLIB_VERSION "${GLIB_VERSION_MAJOR}.${GLIB_VERSION_MINOR}.${GLIB_VERSION_MICRO}") +endif () + +# Additional Glib components. We only look for libraries, as not all of them +# have corresponding headers and all headers are installed alongside the main +# glib ones. +foreach (_component ${GLIB_FIND_COMPONENTS}) + if (${_component} STREQUAL "gio") + find_library(GLIB_GIO_LIBRARIES NAMES gio-2.0 HINTS ${_GLIB_LIBRARY_DIR}) + set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GIO_LIBRARIES) + elseif (${_component} STREQUAL "gobject") + find_library(GLIB_GOBJECT_LIBRARIES NAMES gobject-2.0 HINTS ${_GLIB_LIBRARY_DIR}) + set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GOBJECT_LIBRARIES) + elseif (${_component} STREQUAL "gmodule") + find_library(GLIB_GMODULE_LIBRARIES NAMES gmodule-2.0 HINTS ${_GLIB_LIBRARY_DIR}) + set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GMODULE_LIBRARIES) + elseif (${_component} STREQUAL "gthread") + find_library(GLIB_GTHREAD_LIBRARIES NAMES gthread-2.0 HINTS ${_GLIB_LIBRARY_DIR}) + set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GTHREAD_LIBRARIES) + elseif (${_component} STREQUAL "gio-unix") + # gio-unix is compiled as part of the gio library, but the include paths + # are separate from the shared glib ones. Since this is currently only used + # by WebKitGTK we don't go to extraordinary measures beyond pkg-config. + pkg_check_modules(GIO_UNIX QUIET gio-unix-2.0) + endif () +endforeach () + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLIB REQUIRED_VARS GLIB_INCLUDE_DIRS GLIB_LIBRARIES ${ADDITIONAL_REQUIRED_VARS} + VERSION_VAR GLIB_VERSION) + +mark_as_advanced( + GLIBCONFIG_INCLUDE_DIR + GLIB_GIO_LIBRARIES + GLIB_GIO_UNIX_LIBRARIES + GLIB_GMODULE_LIBRARIES + GLIB_GOBJECT_LIBRARIES + GLIB_GTHREAD_LIBRARIES + GLIB_INCLUDE_DIR + GLIB_INCLUDE_DIRS + GLIB_LIBRARIES +) \ No newline at end of file diff --git a/cmake/modules/FindGOBJECT.cmake b/cmake/modules/FindGOBJECT.cmake new file mode 100644 index 0000000000..81b0c7b514 --- /dev/null +++ b/cmake/modules/FindGOBJECT.cmake @@ -0,0 +1,63 @@ +# - Try to find GObject +# Once done this will define +# +# GOBJECT_FOUND - system has GObject +# GOBJECT_INCLUDE_DIR - the GObject include directory +# GOBJECT_LIBRARIES - the libraries needed to use GObject +# GOBJECT_DEFINITIONS - Compiler switches required for using GObject + +# Copyright (c) 2006, Tim Beaulen +# Copyright (c) 2008 Helio Chissini de Castro, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +IF (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) + # in cache already + SET(GObject_FIND_QUIETLY TRUE) +ELSE (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) + SET(GObject_FIND_QUIETLY FALSE) +ENDIF (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) + +IF (NOT WIN32) + FIND_PACKAGE(PkgConfig REQUIRED QUIET) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + PKG_CHECK_MODULES(PKG_GOBJECT2 REQUIRED QUIET gobject-2.0) + SET(GOBJECT_DEFINITIONS ${PKG_GOBJECT2_CFLAGS}) +ENDIF (NOT WIN32) + +FIND_PATH(GOBJECT_INCLUDE_DIR gobject/gobject.h + HINTS ${PKG_GOBJECT2_INCLUDE_DIRS} ${PKG_GOBJECT2_INCLUDEDIR} + PATHS /usr/include/glib-2.0/ + PATH_SUFFIXES glib-2.0 + ) + +FIND_LIBRARY(_GObjectLibs NAMES gobject-2.0 + HINTS + ${PKG_GOBJECT2_LIBRARY_DIRS} + ${PKG_GOBJECT2_LIBDIR} + ) +FIND_LIBRARY(_GModuleLibs NAMES gmodule-2.0 + HINTS + ${PKG_GOBJECT2_LIBRARY_DIRS} + ${PKG_GOBJECT2_LIBDIR} + ) +FIND_LIBRARY(_GThreadLibs NAMES gthread-2.0 + HINTS + ${PKG_GOBJECT2_LIBRARY_DIRS} + ${PKG_GOBJECT2_LIBDIR} + ) +FIND_LIBRARY(_GLibs NAMES glib-2.0 + HINTS + ${PKG_GOBJECT2_LIBRARY_DIRS} + ${PKG_GOBJECT2_LIBDIR} + ) + +SET (GOBJECT_LIBRARIES ${_GObjectLibs} ${_GModuleLibs} ${_GThreadLibs} ${_GLibs}) + +MARK_AS_ADVANCED(GOBJECT_INCLUDE_DIR GOBJECT_LIBRARIES) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GObject DEFAULT_MSG GOBJECT_INCLUDE_DIR GOBJECT_LIBRARIES) \ No newline at end of file diff --git a/cmake/modules/FindGSTREAMER.cmake b/cmake/modules/FindGSTREAMER.cmake new file mode 100644 index 0000000000..2d0e2c7f5b --- /dev/null +++ b/cmake/modules/FindGSTREAMER.cmake @@ -0,0 +1,155 @@ +# - Try to find GStreamer and its plugins +# Once done, this will define +# +# GSTREAMER_FOUND - system has GStreamer +# GSTREAMER_INCLUDE_DIRS - the GStreamer include directories +# GSTREAMER_LIBRARIES - link these to use GStreamer +# +# Additionally, gstreamer-base is always looked for and required, and +# the following related variables are defined: +# +# GSTREAMER_BASE_INCLUDE_DIRS - gstreamer-base's include directory +# GSTREAMER_BASE_LIBRARIES - link to these to use gstreamer-base +# +# Optionally, the COMPONENTS keyword can be passed to find_package() +# and GStreamer plugins can be looked for. Currently, the following +# plugins can be searched, and they define the following variables if +# found: +# +# gstreamer-app: GSTREAMER_APP_INCLUDE_DIRS and GSTREAMER_APP_LIBRARIES +# gstreamer-audio: GSTREAMER_AUDIO_INCLUDE_DIRS and GSTREAMER_AUDIO_LIBRARIES +# gstreamer-fft: GSTREAMER_FFT_INCLUDE_DIRS and GSTREAMER_FFT_LIBRARIES +# gstreamer-mpegts: GSTREAMER_MPEGTS_INCLUDE_DIRS and GSTREAMER_MPEGTS_LIBRARIES +# gstreamer-pbutils: GSTREAMER_PBUTILS_INCLUDE_DIRS and GSTREAMER_PBUTILS_LIBRARIES +# gstreamer-tag: GSTREAMER_TAG_INCLUDE_DIRS and GSTREAMER_TAG_LIBRARIES +# gstreamer-video: GSTREAMER_VIDEO_INCLUDE_DIRS and GSTREAMER_VIDEO_LIBRARIES +# +# Copyright (C) 2012 Raphael Kubo da Costa +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +find_package(PkgConfig REQUIRED QUIET) + +# Helper macro to find a GStreamer plugin (or GStreamer itself) +# _component_prefix is prepended to the _INCLUDE_DIRS and _LIBRARIES variables (eg. "GSTREAMER_AUDIO") +# _pkgconfig_name is the component's pkg-config name (eg. "gstreamer-1.0", or "gstreamer-video-1.0"). +# _header is the component's header, relative to the gstreamer-1.0 directory (eg. "gst/gst.h"). +# _library is the component's library name (eg. "gstreamer-1.0" or "gstvideo-1.0") +macro(FIND_GSTREAMER_COMPONENT _component_prefix _pkgconfig_name _header _library) + pkg_check_modules(PC_${_component_prefix} QUIET ${_pkgconfig_name}) + + find_path(${_component_prefix}_INCLUDE_DIRS + NAMES ${_header} + HINTS ${PC_${_component_prefix}_INCLUDE_DIRS} ${PC_${_component_prefix}_INCLUDEDIR} + PATH_SUFFIXES gstreamer-1.0 + ) + + find_library(${_component_prefix}_LIBRARIES + NAMES ${_library} + HINTS ${PC_${_component_prefix}_LIBRARY_DIRS} ${PC_${_component_prefix}_LIBDIR} + ) +endmacro() + +# ------------------------ +# 1. Find GStreamer itself +# ------------------------ + +# 1.1. Find headers and libraries +FIND_GSTREAMER_COMPONENT(GSTREAMER gstreamer-1.0 gst/gst.h gstreamer-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_CONFIG gstreamer-1.0 gst/gstconfig.h gstreamer-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_BASE gstreamer-base-1.0 gst/base/gstadapter.h gstbase-1.0) + +# 1.2. Check GStreamer version +if (GSTREAMER_INCLUDE_DIRS) + if (EXISTS "${GSTREAMER_INCLUDE_DIRS}/gst/gstversion.h") + file(READ "${GSTREAMER_INCLUDE_DIRS}/gst/gstversion.h" GSTREAMER_VERSION_CONTENTS) + + string(REGEX MATCH "#define +GST_VERSION_MAJOR +\\(([0-9]+)\\)" _dummy "${GSTREAMER_VERSION_CONTENTS}") + set(GSTREAMER_VERSION_MAJOR "${CMAKE_MATCH_1}") + + string(REGEX MATCH "#define +GST_VERSION_MINOR +\\(([0-9]+)\\)" _dummy "${GSTREAMER_VERSION_CONTENTS}") + set(GSTREAMER_VERSION_MINOR "${CMAKE_MATCH_1}") + + string(REGEX MATCH "#define +GST_VERSION_MICRO +\\(([0-9]+)\\)" _dummy "${GSTREAMER_VERSION_CONTENTS}") + set(GSTREAMER_VERSION_MICRO "${CMAKE_MATCH_1}") + + set(GSTREAMER_VERSION "${GSTREAMER_VERSION_MAJOR}.${GSTREAMER_VERSION_MINOR}.${GSTREAMER_VERSION_MICRO}") + endif () +endif () + +if ("${GStreamer_FIND_VERSION}" VERSION_GREATER "${GSTREAMER_VERSION}") + message(FATAL_ERROR "Required version (" ${GStreamer_FIND_VERSION} ") is higher than found version (" ${GSTREAMER_VERSION} ")") +endif () + +# ------------------------- +# 2. Find GStreamer plugins +# ------------------------- + +FIND_GSTREAMER_COMPONENT(GSTREAMER_APP gstreamer-app-1.0 gst/app/gstappsink.h gstapp-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_AUDIO gstreamer-audio-1.0 gst/audio/audio.h gstaudio-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_FFT gstreamer-fft-1.0 gst/fft/gstfft.h gstfft-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_MPEGTS gstreamer-mpegts-1.0>=1.4.0 gst/mpegts/mpegts.h gstmpegts-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_PBUTILS gstreamer-pbutils-1.0 gst/pbutils/pbutils.h gstpbutils-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_TAG gstreamer-tag-1.0 gst/tag/tag.h gsttag-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_VIDEO gstreamer-video-1.0 gst/video/video.h gstvideo-1.0) + +list(APPEND GSTREAMER_INCLUDE_DIRS ${GSTREAMER_CONFIG_INCLUDE_DIRS}) +list(APPEND GSTREAMER_LIBRARIES ${GSTREAMER_CONFIG_LIBRARIES}) + +# ------------------------------------------------ +# 3. Process the COMPONENTS passed to FIND_PACKAGE +# ------------------------------------------------ +set(_GSTREAMER_REQUIRED_VARS GSTREAMER_INCLUDE_DIRS GSTREAMER_LIBRARIES GSTREAMER_VERSION GSTREAMER_BASE_INCLUDE_DIRS GSTREAMER_BASE_LIBRARIES) + +foreach (_component ${GStreamer_FIND_COMPONENTS}) + set(_gst_component "GSTREAMER_${_component}") + string(TOUPPER ${_gst_component} _UPPER_NAME) + + list(APPEND _GSTREAMER_REQUIRED_VARS ${_UPPER_NAME}_INCLUDE_DIRS ${_UPPER_NAME}_LIBRARIES) +endforeach () + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GStreamer REQUIRED_VARS _GSTREAMER_REQUIRED_VARS + VERSION_VAR GSTREAMER_VERSION) + +mark_as_advanced( + GSTREAMER_APP_INCLUDE_DIRS + GSTREAMER_APP_LIBRARIES + GSTREAMER_AUDIO_INCLUDE_DIRS + GSTREAMER_AUDIO_LIBRARIES + GSTREAMER_BASE_INCLUDE_DIRS + GSTREAMER_BASE_LIBRARIES + GSTREAMER_CONFIG_INCLUDE_DIRS + GSTREAMER_CONFIG_LIBRARIES + GSTREAMER_FFT_INCLUDE_DIRS + GSTREAMER_FFT_LIBRARIES + GSTREAMER_INCLUDE_DIRS + GSTREAMER_LIBRARIES + GSTREAMER_MPEGTS_INCLUDE_DIRS + GSTREAMER_MPEGTS_LIBRARIES + GSTREAMER_PBUTILS_INCLUDE_DIRS + GSTREAMER_PBUTILS_LIBRARIES + GSTREAMER_TAG_INCLUDE_DIRS + GSTREAMER_TAG_LIBRARIES + GSTREAMER_VIDEO_INCLUDE_DIRS + GSTREAMER_VIDEO_LIBRARIES +) \ No newline at end of file diff --git a/cmake/modules/FindVPX.cmake b/cmake/modules/FindVPX.cmake deleted file mode 100644 index 3011c8d1c7..0000000000 --- a/cmake/modules/FindVPX.cmake +++ /dev/null @@ -1,54 +0,0 @@ -############################################################################ -# FindVPX.txt -# Copyright (C) 2014 Belledonne Communications, Grenoble France -# -############################################################################ -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program 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 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. -# -############################################################################ -# -# - Find the VPX include file and library -# -# VPX_FOUND - system has VPX -# VPX_INCLUDE_DIRS - the VPX include directory -# VPX_LIBRARIES - The libraries needed to use VPX - -set(_VPX_ROOT_PATHS - ${CMAKE_INSTALL_PREFIX} -) - -find_path(VPX_INCLUDE_DIRS - NAMES vpx/vpx_encoder.h - HINTS _VPX_ROOT_PATHS - PATH_SUFFIXES include -) -if(VPX_INCLUDE_DIRS) - set(HAVE_VPX_VPX_ENCODER_H 1) -endif() - -find_library(VPX_LIBRARIES - NAMES vpx vpxmd - HINTS _VPX_ROOT_PATHS - PATH_SUFFIXES bin lib lib/Win32 -) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(VPX - DEFAULT_MSG - VPX_INCLUDE_DIRS VPX_LIBRARIES HAVE_VPX_VPX_ENCODER_H -) - -mark_as_advanced(VPX_INCLUDE_DIRS VPX_LIBRARIES HAVE_VPX_VPX_ENCODER_H) \ No newline at end of file diff --git a/cmake/modules/FindYUV.cmake b/cmake/modules/FindYUV.cmake deleted file mode 100644 index 933aae10bd..0000000000 --- a/cmake/modules/FindYUV.cmake +++ /dev/null @@ -1,26 +0,0 @@ -set(_YUV_ROOT_PATHS - ${CMAKE_INSTALL_PREFIX} -) - -find_path(YUV_INCLUDE_DIRS - NAMES libyuv.h - HINTS _YUV_ROOT_PATHS - PATH_SUFFIXES include -) -if(YUV_INCLUDE_DIRS) - set(HAVE_YUV_H 1) -endif() - -find_library(YUV_LIBRARIES - NAMES yuv - HINTS _YUV_ROOT_PATHS - PATH_SUFFIXES bin lib lib/Win32 -) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(YUV - DEFAULT_MSG - YUV_INCLUDE_DIRS YUV_LIBRARIES HAVE_YUV_H -) - -mark_as_advanced(YUV_INCLUDE_DIRS YUV_LIBRARIES HAVE_YUV_H) \ No newline at end of file diff --git a/plugins/dockers/CMakeLists.txt b/plugins/dockers/CMakeLists.txt index 54aeb1e1e9..8eaad53529 100644 --- a/plugins/dockers/CMakeLists.txt +++ b/plugins/dockers/CMakeLists.txt @@ -1,39 +1,39 @@ add_subdirectory(layerdocker) if(HAVE_OPENEXR) add_subdirectory(smallcolorselector) endif() add_subdirectory(specificcolorselector) add_subdirectory(digitalmixer) add_subdirectory(advancedcolorselector) add_subdirectory(presetdocker) add_subdirectory(historydocker) add_subdirectory(channeldocker) add_subdirectory(artisticcolorselector) add_subdirectory(tasksetdocker) add_subdirectory(compositiondocker) add_subdirectory(patterndocker) add_subdirectory(griddocker) add_subdirectory(arrangedocker) if(HAVE_OCIO) add_subdirectory(lut) endif() add_subdirectory(overview) add_subdirectory(palettedocker) add_subdirectory(animation) add_subdirectory(presethistory) add_subdirectory(svgcollectiondocker) add_subdirectory(histogram) add_subdirectory(gamutmask) -if(HAVE_VPX AND HAVE_YUV AND HAVE_JPEG) +if(HAVE_GSTREAMER AND HAVE_GLIB AND HAVE_GOBJECT) add_subdirectory(recorder) endif() if(NOT APPLE AND HAVE_QT_QUICK) add_subdirectory(touchdocker) option(ENABLE_CPU_THROTTLE "Build the CPU Throttle Docker" OFF) if (ENABLE_CPU_THROTTLE) add_subdirectory(throttle) endif() endif() add_subdirectory(logdocker) add_subdirectory(snapshotdocker) diff --git a/plugins/dockers/recorder/CMakeLists.txt b/plugins/dockers/recorder/CMakeLists.txt index 903147f086..78fbf2d06e 100644 --- a/plugins/dockers/recorder/CMakeLists.txt +++ b/plugins/dockers/recorder/CMakeLists.txt @@ -1,8 +1,8 @@ include_directories(SYSTEM - ${VPX_INCLUDE_DIR}) + ${GSTREAMER_INCLUDE_DIRS} ${GLIB_INCLUDE_DIRS}) set(KRITA_RECORDERDOCKER_SOURCES recorderdocker.cpp recorderdocker_dock.cpp encoder.cpp ivfenc.cpp) add_library(kritarecorderdocker MODULE ${KRITA_RECORDERDOCKER_SOURCES}) -target_link_libraries(kritarecorderdocker kritaui ${VPX_LIBRARIES} ${YUV_LIBRARIES} ${JPEG_LIBRARIES}) +target_link_libraries(kritarecorderdocker kritaui ${GSTREAMER_LIBRARIES} ${GSTREAMER_BASE_LIBRARIES} ${GLIB_LIBRARIES} ${GOBJECT_LIBRARIES} ${GSTREAMER_APP_LIBRARIES}) install(TARGETS kritarecorderdocker DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/dockers/recorder/encoder.cpp b/plugins/dockers/recorder/encoder.cpp index 62a316b238..96ffe39da5 100644 --- a/plugins/dockers/recorder/encoder.cpp +++ b/plugins/dockers/recorder/encoder.cpp @@ -1,181 +1,77 @@ #include "encoder.h" -#include "ivfenc.h" -#include #include -#include -void Encoder::init(const QString &filename, unsigned int width, unsigned int height) +void Encoder::init(const std::string &filename, int width, int height) { + GstStateChangeReturn state_ret; + gst_init(NULL, NULL); + m_width = width; m_height = height; m_filename = filename; - m_shouldFinish = new std::atomic(); - start(); -} - -static int vpx_img_plane_width(const vpx_image_t* img, int plane) -{ - if (plane > 0 && img->x_chroma_shift > 0) - return (img->d_w + 1) >> img->x_chroma_shift; - else - return img->d_w; -} - -static int vpx_img_plane_height(const vpx_image_t* img, int plane) -{ - if (plane > 0 && img->y_chroma_shift > 0) - return (img->d_h + 1) >> img->y_chroma_shift; - else - return img->d_h; -} - -static int encode_frame(vpx_codec_ctx_t* codec, vpx_image_t* img, int frame_index, int flags, FILE* file) -{ - int got_pkts = 0; - vpx_codec_iter_t iter = nullptr; - const vpx_codec_cx_pkt_t* pkt = nullptr; - const vpx_codec_err_t res = vpx_codec_encode(codec, img, frame_index, 1, flags, VPX_DL_GOOD_QUALITY); - if (res != VPX_CODEC_OK) { - qDebug() << "Failed to encode frame"; - } - while ((pkt = vpx_codec_get_cx_data(codec, &iter)) != NULL) { - got_pkts = 1; - if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { - ivf_write_frame_header(file, pkt->data.frame.pts, pkt->data.frame.sz); - fwrite(pkt->data.frame.buf, pkt->data.frame.sz, 1, file); - } - } - return got_pkts; -} - -void Encoder::run() -{ - qDebug() << "width " << m_width << " height " << m_height; - - if (!vpx_img_alloc(&m_raw, VPX_IMG_FMT_I420, m_width, m_height, 1)) { - qDebug() << "Failed to allocate image."; - return; - } - - vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &m_cfg, 0); - - m_cfg.g_w = m_width; - m_cfg.g_h = m_height; - m_cfg.g_timebase.num = 1; - m_cfg.g_timebase.den = 4; - vpx_codec_err_t m_res; - if ((m_res = vpx_codec_enc_init(&m_codec, vpx_codec_vp9_cx(), &m_cfg, 0))) { - qDebug() << "Failed to initialize encoder." << m_res; - } - - if (vpx_codec_control_(&m_codec, VP9E_SET_LOSSLESS, 1)) { - qDebug() << "Failed to set lossless." << m_res; - } - m_frameCount = 0; - FILE *m_file = fopen(m_filename.toStdString().c_str(), "wb"); - - ivf_write_file_header(m_file, m_width, m_height, 1, 4, 0x30395056, 0); - - for(unsigned int i =0;iload() || (m_empty.available() != m_ringBufferSize)) - { - m_full.acquire(); - - if (m_ringBuffer[m_tail].m_command == RingBufferItem::Command::Finish) - { - break; - } - - libyuv::ABGRToI420(m_ringBuffer[m_tail].m_payload, m_width * 4, yuv[0], m_width, yuv[1], - m_width / 2, yuv[2], m_width / 2, m_width, m_height); - - m_tail = (m_tail + 1) % m_ringBufferSize; - m_empty.release(); - - int flags = 0; - if (m_keyframeInterval > 0 && m_frameCount % m_keyframeInterval == 0) { - flags |= VPX_EFLAG_FORCE_KF; - } - - int plane; - - for (plane = 0; plane < 3; ++plane) { - unsigned char* buf = m_raw.planes[plane]; - const int stride = m_raw.stride[plane]; - const int w = vpx_img_plane_width(&m_raw, plane); - const int h = vpx_img_plane_height(&m_raw, plane); - - if (stride == w) { - std::copy(yuv[plane], yuv[plane] + w * h, buf); - } else { - for (int y = 0; y < h; ++y) { - std::copy(yuv[plane] + w * y, yuv[plane] + w * y + w, buf); - buf += stride; - } - } - } - encode_frame(&m_codec, &m_raw, m_frameCount, flags, m_file); - - m_frameCount++; - qDebug() << "pushed frame" << m_frameCount; + m_pipeline = (GstPipeline*)gst_pipeline_new("mypipeline"); + m_src = (GstAppSrc*)gst_element_factory_make("appsrc", "mysrc"); + m_filter1 = gst_element_factory_make("capsfilter", "myfilter1"); + m_videoconvert = gst_element_factory_make("videoconvert", "vc"); + m_encoder = gst_element_factory_make("vp9enc", "my9enc"); + m_queue = gst_element_factory_make("queue", "qu"); + m_webmmux = gst_element_factory_make("webmmux", "mymux"); + m_sink = gst_element_factory_make("filesink", NULL); + m_timestamp = 0; + if (!m_pipeline || !m_src || !m_filter1 || !m_encoder || /*!m_videoconvert ||*/ !m_webmmux || /*!m_queue ||*/ + !m_sink) { + printf("Error creating pipeline elements!\n"); + exit(2); } - while (encode_frame(&m_codec, nullptr, -1, 0, m_file)) { - } + gst_bin_add_many(GST_BIN(m_pipeline), (GstElement*)m_src, m_filter1, m_videoconvert, m_encoder, m_queue, m_webmmux, + m_sink, NULL); - delete[] yuv[0]; - delete[] yuv[1]; - delete[] yuv[2]; + qDebug() << "width " << m_width << " height " << m_height; - vpx_img_free(&m_raw); - if (vpx_codec_destroy(&m_codec)) { - qDebug() << "Failed to destroy codec."; - } - fseek(m_file, 0, SEEK_SET); - ivf_write_file_header(m_file, m_width, m_height, 1, 4, 0x30395056, m_frameCount); + g_object_set(m_src, "format", GST_FORMAT_TIME, NULL); + g_object_set(m_src, "is-live", true, NULL); + GstCaps* filtercaps1 = + gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "BGRA", "width", G_TYPE_INT, m_width, "height", + G_TYPE_INT, m_height, "framerate", GST_TYPE_FRACTION, 4, 1, NULL); + g_object_set(G_OBJECT(m_filter1), "caps", filtercaps1, NULL); + g_object_set(G_OBJECT(m_sink), "location", filename.c_str(), NULL); + g_object_set(G_OBJECT(m_encoder), "keyframe-max-dist" ,4, NULL); + g_object_set(G_OBJECT(m_encoder), "target-bitrate", 10240000, NULL); + + g_assert(gst_element_link_many((GstElement*)m_src, m_filter1, m_videoconvert, m_encoder, m_queue, m_webmmux, m_sink, + NULL)); + + state_ret = gst_element_set_state((GstElement*)m_pipeline, GST_STATE_PLAYING); + g_assert(state_ret == GST_STATE_CHANGE_ASYNC); m_frameCount = 0; - fclose(m_file); - qDebug() << "finished"; } -void Encoder::pushFrame(uint8_t* data, unsigned int m_width, unsigned int m_height, uint32_t size) +void Encoder::pushFrame(gpointer data, gsize size) { - m_empty.acquire(); - - m_ringBuffer[m_head].m_command = RingBufferItem::Command::Payload; - std::copy(data, data+size, m_ringBuffer[m_head].m_payload); - - m_head = (m_head + 1) % m_ringBufferSize; - m_full.release(); + GstBuffer* buffer = gst_buffer_new_wrapped(data, size); // Actual databuffer + GstFlowReturn ret; // Return value + // Set frame timestamp + GST_BUFFER_PTS(buffer) = m_timestamp; + GST_BUFFER_DTS(buffer) = m_timestamp; + GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale_int(GST_SECOND, 1, 4); + m_timestamp += GST_BUFFER_DURATION(buffer); + ret = gst_app_src_push_buffer(m_src, buffer); // Push data into pipeline + + g_assert(ret == GST_FLOW_OK); + qDebug() << "push frame" << m_frameCount++; } void Encoder::finish() { - m_empty.acquire(); - m_ringBuffer[m_head].m_command = RingBufferItem::Command::Finish; - *m_shouldFinish = true; - m_head = (m_head + 1) % m_ringBufferSize; - m_full.release(); - this->wait(10 * 1000); - - for(unsigned int i =0;i * * 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; 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 ENCODER_H #define ENCODER_H #include + +#include +#include #include #include #include -#include -#include -#include -#include -#include -class Encoder : QThread +class Encoder { - Q_OBJECT - class RingBufferItem - { - public: - enum class Command{ - Payload, - Finish - }; - - Command m_command; - uint8_t *m_payload; - }; - - unsigned int m_width; - unsigned int m_height; - vpx_codec_ctx m_codec; - vpx_image_t m_raw; - vpx_codec_enc_cfg_t m_cfg; - int m_frameCount = 0; - int m_keyframeInterval = 4; - static constexpr int m_ringBufferSize = 5; - std::array m_ringBuffer; - int m_head = 0; - int m_tail = 0; - QSemaphore m_full; - QSemaphore m_empty; - QString m_filename; - std::atomic *m_shouldFinish; + GstPipeline* m_pipeline; + GstAppSrc* m_src; + GstElement* m_filter1; + GstElement* m_encoder; + GstElement* m_videoconvert; + GstElement* m_queue; + GstElement* m_webmmux; + GstElement* m_sink; + int m_width; + int m_height; + GstClockTime m_timestamp; + std::string m_filename; + int m_frameCount; public: Encoder() + : m_pipeline(nullptr) + , m_src(nullptr) + , m_filter1(nullptr) + , m_encoder(nullptr) + , m_videoconvert(nullptr) + , m_queue(nullptr) + , m_webmmux(nullptr) + , m_sink(nullptr) + , m_filename() { } - void run() override; - - void init(const QString &filename, unsigned int width, unsigned int height); - - void pushFrame(uint8_t* data, unsigned int m_width, unsigned int m_height, uint32_t size); - + void init(const std::string &filename, int width, int height); + void pushFrame(gpointer data, gsize size); void finish(); }; #endif diff --git a/plugins/dockers/recorder/ivfenc.cpp b/plugins/dockers/recorder/ivfenc.cpp deleted file mode 100644 index e4e449d8e9..0000000000 --- a/plugins/dockers/recorder/ivfenc.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2013 The WebM project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "ivfenc.h" - -static void mem_put_le16(char* ptr, unsigned int data) -{ - *ptr = static_cast(data & 0xff); - *(ptr + 1) = static_cast((data & 0xff00) >> 8); -} - -static void mem_put_le32(char* ptr, unsigned int data) -{ - *ptr = static_cast(data & 0xff); - *(ptr + 1) = static_cast((data & 0xff00) >> 8); - *(ptr + 2) = static_cast((data & 0xff0000) >> 16); - *(ptr + 3) = static_cast((data & 0xff000000) >> 24); -} - -void ivf_write_file_header(FILE* outfile, unsigned int width, unsigned int height, int num, int den, - unsigned int fourcc, int frame_cnt) -{ - char header[32]; - - header[0] = 'D'; - header[1] = 'K'; - header[2] = 'I'; - header[3] = 'F'; - mem_put_le16(header + 4, 0); // version - mem_put_le16(header + 6, 32); // header size - mem_put_le32(header + 8, fourcc); // fourcc - mem_put_le16(header + 12, width); // width - mem_put_le16(header + 14, height); // height - mem_put_le32(header + 16, den); // rate - mem_put_le32(header + 20, num); // scale - mem_put_le32(header + 24, frame_cnt); // length - mem_put_le32(header + 28, 0); // unused - - fwrite(header, 1, 32, outfile); -} - -void ivf_write_frame_header(FILE* outfile, int64_t pts, size_t frame_size) -{ - char header[12]; - - mem_put_le32(header, (int)frame_size); - mem_put_le32(header + 4, (int)(pts & 0xFFFFFFFF)); - mem_put_le32(header + 8, (int)(pts >> 32)); - fwrite(header, 1, 12, outfile); -} - -void ivf_write_frame_size(FILE* outfile, size_t frame_size) -{ - char header[4]; - - mem_put_le32(header, (int)frame_size); - fwrite(header, 1, 4, outfile); -} diff --git a/plugins/dockers/recorder/ivfenc.h b/plugins/dockers/recorder/ivfenc.h deleted file mode 100644 index f20cc65436..0000000000 --- a/plugins/dockers/recorder/ivfenc.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef VPX_IVFENC_H -#define VPX_IVFENC_H - -#include -#include - -void ivf_write_file_header(FILE *outfile, unsigned int width, unsigned int height, - int num, int den, - unsigned int fourcc, int frame_cnt); - -void ivf_write_frame_header(FILE *outfile, int64_t pts, size_t frame_size); - -void ivf_write_frame_size(FILE *outfile, size_t frame_size); - -#endif // IVFENC_H diff --git a/plugins/dockers/recorder/recorderdocker_dock.cpp b/plugins/dockers/recorder/recorderdocker_dock.cpp index e8aabf2e45..65f4cd299c 100644 --- a/plugins/dockers/recorder/recorderdocker_dock.cpp +++ b/plugins/dockers/recorder/recorderdocker_dock.cpp @@ -1,244 +1,239 @@ /* * Copyright (c) 2009 Cyrille Berger * * 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; version 2.1 of the License. * * 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "recorderdocker_dock.h" #include #include #include #include #include "encoder.h" #include "kis_canvas2.h" #include "kis_image.h" #include "kis_paint_device.h" #include "kis_signal_compressor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include RecorderDockerDock::RecorderDockerDock() : QDockWidget(i18n("Recorder")) , m_canvas(nullptr) , m_imageIdleWatcher(1000) , m_recordEnabled(false) , m_recordCounter(0) , m_encoder(nullptr) { QWidget* page = new QWidget(this); m_layout = new QGridLayout(page); m_recordDirectoryLabel = new QLabel(this); m_recordDirectoryLabel->setText("Directory:"); m_layout->addWidget(m_recordDirectoryLabel, 0, 0, 1, 2); m_recordDirectoryLineEdit = new QLineEdit(this); m_recordDirectoryLineEdit->setText(QDir::homePath()); m_recordDirectoryLineEdit->setReadOnly(true); m_layout->addWidget(m_recordDirectoryLineEdit, 1, 0); m_recordDirectoryPushButton = new QPushButton(this); m_recordDirectoryPushButton->setIcon(KisIconUtils::loadIcon("folder")); m_recordDirectoryPushButton->setToolTip(i18n("Record Video")); m_layout->addWidget(m_recordDirectoryPushButton, 1, 1); m_imageNameLabel = new QLabel(this); m_imageNameLabel->setText("Video Name:"); m_layout->addWidget(m_imageNameLabel, 2, 0, 1, 2); m_imageNameLineEdit = new QLineEdit(this); m_imageNameLineEdit->setText("image"); QRegExp rx("[0-9a-zA-z_]+"); QValidator* validator = new QRegExpValidator(rx, this); m_imageNameLineEdit->setValidator(validator); m_layout->addWidget(m_imageNameLineEdit, 3, 0); m_recordToggleButton = new QPushButton(this); m_recordToggleButton->setCheckable(true); m_recordToggleButton->setIcon(KisIconUtils::loadIcon("media-record")); m_recordToggleButton->setToolTip(i18n("Record Video")); m_layout->addWidget(m_recordToggleButton, 3, 1); m_logLabel = new QLabel(this); m_logLabel->setText("Recent Save:"); m_layout->addWidget(m_logLabel, 4, 0, 1, 2); m_logLineEdit = new QLineEdit(this); m_logLineEdit->setReadOnly(true); m_layout->addWidget(m_logLineEdit, 5, 0, 1, 2); m_spacer = new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding); m_layout->addItem(m_spacer, 6, 0, 1, 2); connect(m_recordDirectoryPushButton, SIGNAL(clicked()), this, SLOT(onSelectRecordFolderButtonClicked())); connect(m_recordToggleButton, SIGNAL(toggled(bool)), this, SLOT(onRecordButtonToggled(bool))); setWidget(page); } void RecorderDockerDock::setCanvas(KoCanvasBase* canvas) { if (m_canvas == canvas) return; setEnabled(canvas != nullptr); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); m_canvas->image()->disconnect(this); } m_canvas = dynamic_cast(canvas); if (m_canvas) { m_imageIdleWatcher.setTrackedImage(m_canvas->image()); connect(&m_imageIdleWatcher, &KisIdleWatcher::startedIdleMode, this, &RecorderDockerDock::generateThumbnail); connect(m_canvas->image(), SIGNAL(sigSizeChanged(QPointF, QPointF)), SLOT(startUpdateCanvasProjection())); } } void RecorderDockerDock::startUpdateCanvasProjection() { m_imageIdleWatcher.startCountdown(); } void RecorderDockerDock::unsetCanvas() { setEnabled(false); m_canvas = nullptr; } void RecorderDockerDock::onRecordButtonToggled(bool enabled) { bool enabled2 = enabled; enableRecord(enabled2, m_recordDirectoryLineEdit->text() % "/" % m_imageNameLineEdit->text()); if (enabled && !enabled2) { disconnect(m_recordToggleButton, SIGNAL(toggle(bool)), this, SLOT(onRecordButtonToggled(bool))); m_recordToggleButton->setChecked(false); connect(m_recordToggleButton, SIGNAL(toggle(bool)), this, SLOT(onRecordButtonToggled(bool))); } } void RecorderDockerDock::onSelectRecordFolderButtonClicked() { QFileDialog dialog(this); dialog.setFileMode(QFileDialog::DirectoryOnly); QString folder = dialog.getExistingDirectory(this, tr("Select Output Folder"), m_recordDirectoryLineEdit->text(), QFileDialog::ShowDirsOnly); m_recordDirectoryLineEdit->setText(folder); } void RecorderDockerDock::enableRecord(bool& enabled, const QString& path) { m_recordEnabled = enabled; if (m_recordEnabled) { m_recordPath = path; QUrl fileUrl(m_recordPath); QString filename = fileUrl.fileName(); QString dirPath = fileUrl.adjusted(QUrl::RemoveFilename).path(); QDir dir(dirPath); if (!dir.exists()) { if (!dir.mkpath(dirPath)) { enabled = m_recordEnabled = false; return; } } - QFileInfoList images = dir.entryInfoList({filename % "_*.vp9"}); + QFileInfoList images = dir.entryInfoList({filename % "_*.webm"}); - QRegularExpression namePattern("^" % filename % "_([0-9]{7}).vp9$"); + QRegularExpression namePattern("^" % filename % "_([0-9]{7}).webm$"); m_recordCounter = -1; Q_FOREACH (auto info, images) { QRegularExpressionMatch match = namePattern.match(info.fileName()); if (match.hasMatch()) { QString count = match.captured(1); int numCount = count.toInt(); if (m_recordCounter < numCount) { m_recordCounter = numCount; } } } if (m_canvas) { m_recordingCanvas = m_canvas; - QString finalFileName = QString(m_recordPath % "_%1.vp9").arg(++m_recordCounter, 7, 10, QChar('0')); + QString finalFileName = QString(m_recordPath % "_%1.webm").arg(++m_recordCounter, 7, 10, QChar('0')); m_encoder = new Encoder(); m_encoder->init(finalFileName.toStdString().c_str(), m_canvas->image()->width(), m_canvas->image()->height()); - - size_t size = m_canvas->image()->width() * m_canvas->image()->height() * 4; - m_data = new quint8[size]; startUpdateCanvasProjection(); } else { enabled = m_recordEnabled = false; return; } } else { if (m_encoder) { m_encoder->finish(); delete m_encoder; m_encoder = nullptr; - if (m_data) - { - delete [] m_data; - m_data = nullptr; - } } } } void RecorderDockerDock::generateThumbnail() { if (m_recordEnabled) { if (m_canvas && m_recordingCanvas == m_canvas) { disconnect(&m_imageIdleWatcher, &KisIdleWatcher::startedIdleMode, this, &RecorderDockerDock::generateThumbnail); if (m_encoder) { KisImageSP image = m_canvas->image(); + gpointer data; + gsize size = image->width() * image->height() * 4; + data = g_malloc(size); image->barrierLock(); KisPaintDeviceSP dev = image->projection(); + dev->readBytes((quint8*)data, 0, 0, image->width(), image->height()); image->unlock(); - dev->readBytes(m_data, 0, 0, image->width(), image->height()); - m_encoder->pushFrame(m_data, image->width(), image->height(), image->width()*image->height()*dev->pixelSize()); + m_encoder->pushFrame(data, size); } connect(&m_imageIdleWatcher, &KisIdleWatcher::startedIdleMode, this, &RecorderDockerDock::generateThumbnail); } } } diff --git a/plugins/dockers/recorder/recorderdocker_dock.h b/plugins/dockers/recorder/recorderdocker_dock.h index d655a8c5e1..82d73feb34 100644 --- a/plugins/dockers/recorder/recorderdocker_dock.h +++ b/plugins/dockers/recorder/recorderdocker_dock.h @@ -1,82 +1,81 @@ /* * Copyright (c) 2019 Shi Yan * * 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; 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 _RECORDER_DOCK_H_ #define _RECORDER_DOCK_H_ -#include "encoder.h" #include "kis_idle_watcher.h" #include #include #include #include #include #include #include #include #include #include class QVBoxLayout; class RecorderWidget; +class Encoder; class RecorderDockerDock : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: RecorderDockerDock(); QString observerName() override { return "RecorderDockerDock"; } void setCanvas(KoCanvasBase* canvas) override; void unsetCanvas() override; private: QGridLayout* m_layout; QPointer m_recordingCanvas; QString m_recordPath; QPointer m_canvas; QLabel* m_recordDirectoryLabel; QLineEdit* m_recordDirectoryLineEdit; QPushButton* m_recordDirectoryPushButton; QLabel* m_imageNameLabel; QLineEdit* m_imageNameLineEdit; QPushButton* m_recordToggleButton; QSpacerItem* m_spacer; QLabel* m_logLabel; QLineEdit* m_logLineEdit; KisIdleWatcher m_imageIdleWatcher; QMutex m_saveMutex; QMutex m_eventMutex; Encoder* m_encoder; - quint8* m_data = nullptr; bool m_recordEnabled; int m_recordCounter; void enableRecord(bool& enabled, const QString& path); private Q_SLOTS: void onRecordButtonToggled(bool enabled); void onSelectRecordFolderButtonClicked(); void startUpdateCanvasProjection(); void generateThumbnail(); }; #endif